Settings/Infos

This commit is contained in:
darken
2022-01-03 08:46:24 +01:00
parent c5231b26aa
commit 394d212258
42 changed files with 1086 additions and 18 deletions
+2
View File
@@ -191,6 +191,8 @@ dependencies {
implementation "androidx.navigation:navigation-fragment-ktx:2.3.5"
implementation "androidx.navigation:navigation-ui-ktx:2.3.5"
implementation 'androidx.preference:preference-ktx:1.1.1'
def work_version = "2.7.1"
implementation "androidx.work:work-runtime:${work_version}"
testImplementation "androidx.work:work-testing:${work_version}"
@@ -0,0 +1,51 @@
package eu.darken.capod.common
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.os.Handler
import android.os.Looper
import dagger.hilt.android.qualifiers.ApplicationContext
import eu.darken.capod.R
import eu.darken.capod.common.debug.logging.log
import eu.darken.capod.common.debug.logging.logTag
import java.util.concurrent.locks.ReentrantLock
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.concurrent.withLock
@Singleton
class ClipboardHelper @Inject constructor(
@ApplicationContext private val context: Context
) {
private val clipboard: ClipboardManager by lazy {
return@lazy if (Looper.getMainLooper() == Looper.myLooper()) {
context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
} else {
// java.lang.RuntimeException · Can't create handler inside thread that has not called Looper.prepare()
log(TAG) { "Clipboard is not initialized on the main thread, applying workaround" }
val lock = ReentrantLock()
val lockCondition = lock.newCondition()
var clipboardManager: ClipboardManager? = null
Handler(Looper.getMainLooper()).postAtFrontOfQueue {
clipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
lock.withLock { lockCondition.signal() }
}
lock.withLock { lockCondition.await() }
clipboardManager!!
}
}
fun copyToClipboard(text: String) {
val clip = ClipData.newPlainText(context.getString(R.string.app_name), text)
clipboard.setPrimaryClip(clip)
}
companion object {
private val TAG = logTag("ClipboardHelper")
}
}
@@ -0,0 +1,33 @@
package eu.darken.capod.common
import android.content.Context
import android.content.Intent
import dagger.Reusable
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject
@Reusable
class EmailTool @Inject constructor(
@ApplicationContext val context: Context
) {
fun build(email: Email, offerChooser: Boolean = false): Intent {
val intent = Intent(Intent.ACTION_SEND)
intent.type = "message/rfc822"
intent.putExtra(Intent.EXTRA_EMAIL, email.receipients.toTypedArray())
intent.addCategory(Intent.CATEGORY_DEFAULT)
intent.putExtra(Intent.EXTRA_SUBJECT, email.subject)
intent.putExtra(Intent.EXTRA_TEXT, email.body)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
return if (offerChooser) Intent.createChooser(intent, null) else intent
}
data class Email(
val receipients: List<String>,
val subject: String,
val body: String
)
}
@@ -0,0 +1,28 @@
package eu.darken.capod.common
import android.content.Context
import android.content.Intent
import android.net.Uri
import dagger.Reusable
import dagger.hilt.android.qualifiers.ApplicationContext
import eu.darken.capod.common.debug.logging.Logging.Priority.ERROR
import eu.darken.capod.common.debug.logging.log
import javax.inject.Inject
@Reusable
class WebpageTool @Inject constructor(
@ApplicationContext private val context: Context,
) {
fun open(address: String) {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(address)).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
try {
context.startActivity(intent)
} catch (e: Exception) {
log(ERROR) { "Failed to launch" }
}
}
}
@@ -12,7 +12,7 @@ class DebugSettings @Inject constructor(
) {
private val prefs by lazy {
context.getSharedPreferences("bugreport_settings", Context.MODE_PRIVATE)
context.getSharedPreferences("settings_debug", Context.MODE_PRIVATE)
}
val isAutoReportEnabled = prefs.createFlowPreference("bugreport.automatic.enabled", true)
@@ -0,0 +1,53 @@
package eu.darken.capod.common.preferences
import androidx.preference.PreferenceDataStore
abstract class PreferenceStoreMapper : PreferenceDataStore() {
override fun getBoolean(key: String, defValue: Boolean): Boolean {
throw NotImplementedError("getBoolean(key=$key, defValue=$defValue)")
}
override fun putBoolean(key: String, value: Boolean) {
throw NotImplementedError("putBoolean(key=$key, defValue=$value)")
}
override fun getString(key: String, defValue: String?): String? {
throw NotImplementedError("getString(key=$key, defValue=$defValue)")
}
override fun putString(key: String, value: String?) {
throw NotImplementedError("putString(key=$key, defValue=$value)")
}
override fun putInt(key: String?, value: Int) {
throw NotImplementedError("putInt(key=$key, defValue=$value)")
}
override fun getInt(key: String?, defValue: Int): Int {
throw NotImplementedError("getInt(key=$key, defValue=$defValue)")
}
override fun putLong(key: String?, value: Long) {
throw NotImplementedError("putLong(key=$key, defValue=$value)")
}
override fun putStringSet(key: String?, values: MutableSet<String>?) {
throw NotImplementedError("putStringSet(key=$key, defValue=$values)")
}
override fun getLong(key: String?, defValue: Long): Long {
throw NotImplementedError("getLong(key=$key, defValue=$defValue)")
}
override fun getFloat(key: String?, defValue: Float): Float {
throw NotImplementedError("getFloat(key=$key, defValue=$defValue)")
}
override fun putFloat(key: String?, value: Float) {
throw NotImplementedError("putFloat(key=$key, defValue=$value)")
}
override fun getStringSet(key: String?, defValues: MutableSet<String>?): MutableSet<String> {
throw NotImplementedError("getStringSet(key=$key, defValue=$defValues)")
}
}
@@ -0,0 +1,12 @@
package eu.darken.androidstarter.common.preferences
import android.content.SharedPreferences
import androidx.preference.PreferenceDataStore
abstract class Settings {
abstract val preferenceDataStore: PreferenceDataStore
abstract val preferences: SharedPreferences
}
@@ -0,0 +1,70 @@
package eu.darken.capod.common.uix
import android.content.SharedPreferences
import android.os.Bundle
import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.annotation.MenuRes
import androidx.annotation.XmlRes
import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.Fragment
import androidx.preference.PreferenceFragmentCompat
import eu.darken.androidstarter.common.preferences.Settings
import eu.darken.capod.main.ui.settings.SettingsFragment
abstract class PreferenceFragment2
: PreferenceFragmentCompat(), SharedPreferences.OnSharedPreferenceChangeListener {
abstract val settings: Settings
@get:XmlRes
abstract val preferenceFile: Int
val toolbar: Toolbar
get() = (parentFragment as SettingsFragment).toolbar
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
toolbar.menu.clear()
return super.onCreateView(inflater, container, savedInstanceState)
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
preferenceManager.preferenceDataStore = settings.preferenceDataStore
settings.preferences.registerOnSharedPreferenceChangeListener(this)
refreshPreferenceScreen()
}
override fun onDestroy() {
settings.preferences.unregisterOnSharedPreferenceChangeListener(this)
super.onDestroy()
}
override fun getCallbackFragment(): Fragment? = parentFragment
fun refreshPreferenceScreen() {
if (preferenceScreen != null) preferenceScreen = null
addPreferencesFromResource(preferenceFile)
onPreferencesCreated()
}
open fun onPreferencesCreated() {
}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
}
fun setupMenu(@MenuRes menuResId: Int, block: (MenuItem) -> Unit) {
toolbar.apply {
menu.clear()
inflateMenu(menuResId)
setOnMenuItemClickListener {
block(it)
true
}
}
}
}
@@ -0,0 +1,38 @@
package eu.darken.capod.main.core
import android.content.Context
import android.content.SharedPreferences
import androidx.preference.PreferenceDataStore
import dagger.hilt.android.qualifiers.ApplicationContext
import eu.darken.androidstarter.common.preferences.Settings
import eu.darken.capod.common.debug.autoreport.DebugSettings
import eu.darken.capod.common.debug.logging.logTag
import eu.darken.capod.common.preferences.PreferenceStoreMapper
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class GeneralSettings @Inject constructor(
@ApplicationContext private val context: Context,
private val debugSettings: DebugSettings,
) : Settings() {
override val preferenceDataStore: PreferenceDataStore = object : PreferenceStoreMapper() {
override fun getBoolean(key: String, defValue: Boolean): Boolean = when (key) {
PK_BUGTRACKING_ENABLED -> debugSettings.isAutoReportEnabled.value
else -> super.getBoolean(key, defValue)
}
override fun putBoolean(key: String, value: Boolean) = when (key) {
PK_BUGTRACKING_ENABLED -> debugSettings.isAutoReportEnabled.update { value }
else -> super.putBoolean(key, value)
}
}
override val preferences: SharedPreferences = context.getSharedPreferences("settings_general", Context.MODE_PRIVATE)
companion object {
internal val TAG = logTag("Core", "Settings")
private const val PK_BUGTRACKING_ENABLED = "core.bugtracking.enabled"
}
}
@@ -8,6 +8,7 @@ import dagger.hilt.android.qualifiers.ApplicationContext
import eu.darken.capod.common.coroutine.DispatcherProvider
import eu.darken.capod.common.debug.recording.core.RecorderModule
import eu.darken.capod.common.livedata.SingleLiveEvent
import eu.darken.capod.common.navigation.navVia
import eu.darken.capod.common.permissions.Permission
import eu.darken.capod.common.permissions.isGrantedOrNotRequired
import eu.darken.capod.common.uix.ViewModel3
@@ -33,7 +34,6 @@ class OverviewFragmentVM @Inject constructor(
private val podMonitor: PodMonitor,
) : ViewModel3(dispatcherProvider = dispatcherProvider) {
private val enabledState: Flow<Boolean> = flow {
emit(false)
}
@@ -100,7 +100,7 @@ class OverviewFragmentVM @Inject constructor(
}
fun goToSettings() = launch {
OverviewFragmentDirections.actionOverviewFragmentToSettingsFragment().navVia(this@OverviewFragmentVM)
}
}
@@ -0,0 +1,120 @@
package eu.darken.capod.main.ui.settings
import android.os.Bundle
import android.os.Parcelable
import android.view.View
import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.viewModels
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import dagger.hilt.android.AndroidEntryPoint
import eu.darken.capod.R
import eu.darken.capod.common.uix.Fragment2
import eu.darken.capod.common.viewbinding.viewBinding
import eu.darken.capod.databinding.SettingsFragmentBinding
import kotlinx.parcelize.Parcelize
@AndroidEntryPoint
class SettingsFragment : Fragment2(R.layout.settings_fragment),
PreferenceFragmentCompat.OnPreferenceStartFragmentCallback {
private val vm: SettingsFragmentVM by viewModels()
private val ui: SettingsFragmentBinding by viewBinding()
val toolbar: Toolbar
get() = ui.toolbar
private val screens = ArrayList<Screen>()
@Parcelize
data class Screen(
val fragmentClass: String,
val screenTitle: String?
) : Parcelable
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
childFragmentManager.addOnBackStackChangedListener {
val backStackCnt = childFragmentManager.backStackEntryCount
val newScreenInfo = when {
backStackCnt < screens.size -> {
// We popped the backstack, restore the underlying screen infos
// If there are none left, we are at the index again
screens.removeLastOrNull()
screens.lastOrNull() ?: Screen(
fragmentClass = SettingsIndexFragment::class.qualifiedName!!,
screenTitle = getString(R.string.settings_label)
)
}
else -> {
// We added the current fragment to the stack, the new fragment's infos were already set, do nothing.
null
}
}
newScreenInfo?.let { setCurrentScreenInfo(it) }
}
if (savedInstanceState == null) {
childFragmentManager
.beginTransaction()
.replace(R.id.content_frame, SettingsIndexFragment())
.commit()
} else {
savedInstanceState.getParcelableArrayList<Screen>(BKEY_SCREEN_INFOS)?.let {
screens.addAll(it)
}
screens.lastOrNull()?.let { setCurrentScreenInfo(it) }
}
ui.toolbar.setNavigationOnClickListener { requireActivity().onBackPressed() }
super.onViewCreated(view, savedInstanceState)
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putParcelableArrayList(BKEY_SCREEN_INFOS, screens)
}
override fun onPreferenceStartFragment(caller: PreferenceFragmentCompat, pref: Preference): Boolean {
val screenInfo = Screen(
fragmentClass = pref.fragment,
screenTitle = pref.title?.toString()
)
val args = Bundle().apply {
putAll(pref.extras)
putString(BKEY_SCREEN_TITLE, screenInfo.screenTitle)
}
val fragment = childFragmentManager.fragmentFactory
.instantiate(this::class.java.classLoader!!, pref.fragment)
.apply {
arguments = args
setTargetFragment(caller, 0)
}
setCurrentScreenInfo(screenInfo)
screens.add(screenInfo)
childFragmentManager.beginTransaction().apply {
replace(R.id.content_frame, fragment)
addToBackStack(null)
}.commit()
return true
}
private fun setCurrentScreenInfo(info: Screen) {
ui.toolbar.apply {
title = info.screenTitle
}
}
companion object {
private const val BKEY_SCREEN_TITLE = "preferenceScreenTitle"
private const val BKEY_SCREEN_INFOS = "preferenceScreenInfos"
}
}
@@ -0,0 +1,13 @@
package eu.darken.capod.main.ui.settings
import androidx.lifecycle.SavedStateHandle
import dagger.hilt.android.lifecycle.HiltViewModel
import eu.darken.capod.common.coroutine.DispatcherProvider
import eu.darken.capod.common.uix.ViewModel2
import javax.inject.Inject
@HiltViewModel
class SettingsFragmentVM @Inject constructor(
private val handle: SavedStateHandle,
private val dispatcherProvider: DispatcherProvider,
) : ViewModel2(dispatcherProvider)
@@ -0,0 +1,41 @@
package eu.darken.capod.main.ui.settings
import android.os.Bundle
import android.view.View
import androidx.preference.Preference
import dagger.hilt.android.AndroidEntryPoint
import eu.darken.androidstarter.common.preferences.Settings
import eu.darken.capod.R
import eu.darken.capod.common.BuildConfigWrap
import eu.darken.capod.common.WebpageTool
import eu.darken.capod.common.uix.PreferenceFragment2
import eu.darken.capod.main.core.GeneralSettings
import javax.inject.Inject
@AndroidEntryPoint
class SettingsIndexFragment : PreferenceFragment2() {
@Inject lateinit var generalSettings: GeneralSettings
override val settings: Settings
get() = generalSettings
override val preferenceFile: Int = R.xml.preferences_index
@Inject lateinit var webpageTool: WebpageTool
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
setupMenu(R.menu.menu_settings_index) { item ->
when (item.itemId) {
R.id.menu_item_twitter -> {
webpageTool.open("https://twitter.com/d4rken")
}
}
}
super.onViewCreated(view, savedInstanceState)
}
override fun onPreferencesCreated() {
findPreference<Preference>("core.changelog")!!.summary = BuildConfigWrap.VERSION_DESCRIPTION
super.onPreferencesCreated()
}
}
@@ -0,0 +1,22 @@
package eu.darken.capod.main.ui.settings.acks
import androidx.annotation.Keep
import androidx.fragment.app.viewModels
import dagger.hilt.android.AndroidEntryPoint
import eu.darken.capod.R
import eu.darken.capod.common.uix.PreferenceFragment2
import eu.darken.capod.main.core.GeneralSettings
import javax.inject.Inject
@Keep
@AndroidEntryPoint
class AcknowledgementsFragment : PreferenceFragment2() {
private val vm: AcknowledgementsFragmentVM by viewModels()
override val preferenceFile: Int = R.xml.preferences_acknowledgements
@Inject lateinit var debugSettings: GeneralSettings
override val settings: GeneralSettings by lazy { debugSettings }
}
@@ -0,0 +1,17 @@
package eu.darken.capod.main.ui.settings.acks
import androidx.lifecycle.SavedStateHandle
import dagger.assisted.AssistedInject
import eu.darken.capod.common.coroutine.DispatcherProvider
import eu.darken.capod.common.debug.logging.logTag
import eu.darken.capod.common.uix.ViewModel3
class AcknowledgementsFragmentVM @AssistedInject constructor(
private val handle: SavedStateHandle,
private val dispatcherProvider: DispatcherProvider
) : ViewModel3(dispatcherProvider) {
companion object {
private val TAG = logTag("Settings", "Acknowledgements", "VM")
}
}
@@ -0,0 +1,25 @@
package eu.darken.capod.main.ui.settings.general
import androidx.annotation.Keep
import androidx.fragment.app.viewModels
import dagger.hilt.android.AndroidEntryPoint
import eu.darken.capod.R
import eu.darken.capod.common.uix.PreferenceFragment2
import eu.darken.capod.main.core.GeneralSettings
import javax.inject.Inject
@Keep
@AndroidEntryPoint
class GeneralSettingsFragment : PreferenceFragment2() {
private val vm: GeneralSettingsFragmentVM by viewModels()
@Inject lateinit var generalSettings: GeneralSettings
override val settings: GeneralSettings
get() = generalSettings
override val preferenceFile: Int = R.xml.preferences_general
}
@@ -0,0 +1,20 @@
package eu.darken.capod.main.ui.settings.general
import androidx.lifecycle.SavedStateHandle
import dagger.hilt.android.lifecycle.HiltViewModel
import eu.darken.capod.common.coroutine.DispatcherProvider
import eu.darken.capod.common.debug.logging.logTag
import eu.darken.capod.common.uix.ViewModel3
import javax.inject.Inject
@HiltViewModel
class GeneralSettingsFragmentVM @Inject constructor(
private val handle: SavedStateHandle,
private val dispatcherProvider: DispatcherProvider,
) : ViewModel3(dispatcherProvider) {
companion object {
private val TAG = logTag("Settings", "General", "VM")
}
}
@@ -0,0 +1,59 @@
package eu.darken.capod.main.ui.settings.support
import android.os.Bundle
import android.view.View
import androidx.annotation.Keep
import androidx.fragment.app.viewModels
import androidx.preference.Preference
import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint
import eu.darken.capod.R
import eu.darken.capod.common.ClipboardHelper
import eu.darken.capod.common.observe2
import eu.darken.capod.common.uix.PreferenceFragment2
import eu.darken.capod.main.core.GeneralSettings
import javax.inject.Inject
@Keep
@AndroidEntryPoint
class SupportFragment : PreferenceFragment2() {
private val vm: SupportFragmentVM by viewModels()
override val preferenceFile: Int = R.xml.preferences_support
@Inject lateinit var generalSettings: GeneralSettings
override val settings: GeneralSettings by lazy { generalSettings }
@Inject lateinit var clipboardHelper: ClipboardHelper
private val installIdPref by lazy { findPreference<Preference>("support.installid")!! }
private val supportMailPref by lazy { findPreference<Preference>("support.email.darken")!! }
override fun onPreferencesCreated() {
installIdPref.setOnPreferenceClickListener {
vm.copyInstallID()
true
}
supportMailPref.setOnPreferenceClickListener {
vm.sendSupportMail()
true
}
super.onPreferencesCreated()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
vm.clipboardEvent.observe2(this) { installId ->
Snackbar.make(requireView(), installId, Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.general_copy_action) {
clipboardHelper.copyToClipboard(installId)
}
.show()
}
vm.emailEvent.observe2(this) { startActivity(it) }
super.onViewCreated(view, savedInstanceState)
}
}
@@ -0,0 +1,49 @@
package eu.darken.capod.main.ui.settings.support
import android.content.Intent
import android.os.Build
import androidx.lifecycle.SavedStateHandle
import dagger.hilt.android.lifecycle.HiltViewModel
import eu.darken.capod.common.BuildConfigWrap
import eu.darken.capod.common.EmailTool
import eu.darken.capod.common.InstallId
import eu.darken.capod.common.coroutine.DispatcherProvider
import eu.darken.capod.common.livedata.SingleLiveEvent
import eu.darken.capod.common.uix.ViewModel3
import javax.inject.Inject
@HiltViewModel
class SupportFragmentVM @Inject constructor(
private val handle: SavedStateHandle,
private val emailTool: EmailTool,
private val installId: InstallId,
private val dispatcherProvider: DispatcherProvider,
) : ViewModel3(dispatcherProvider) {
val emailEvent = SingleLiveEvent<Intent>()
val clipboardEvent = SingleLiveEvent<String>()
fun sendSupportMail() = launch {
val bodyInfo = StringBuilder("\n\n\n")
bodyInfo.append("--- Infos for the developer ---\n")
bodyInfo.append("App version: ").append(BuildConfigWrap.VERSION_DESCRIPTION).append("\n")
bodyInfo.append("Device: ").append(Build.FINGERPRINT).append("\n")
bodyInfo.append("Install ID: ").append(installId.id).append("\n")
val email = EmailTool.Email(
receipients = listOf("support@darken.eu"),
subject = "[CAPod] Question/Suggestion/Request\n",
body = bodyInfo.toString()
)
emailEvent.postValue(emailTool.build(email))
}
fun copyInstallID() = launch {
clipboardEvent.postValue(installId.id)
}
}
@@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal"
android:autoMirrored="true">
<path
android:fillColor="@android:color/white"
android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z" />
</vector>
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M18,2H6c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zM6,4h5v8l-2.5,-1.5L6,12V4z" />
</vector>
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M19.14,12.94c0.04,-0.3 0.06,-0.61 0.06,-0.94c0,-0.32 -0.02,-0.64 -0.07,-0.94l2.03,-1.58c0.18,-0.14 0.23,-0.41 0.12,-0.61l-1.92,-3.32c-0.12,-0.22 -0.37,-0.29 -0.59,-0.22l-2.39,0.96c-0.5,-0.38 -1.03,-0.7 -1.62,-0.94L14.4,2.81c-0.04,-0.24 -0.24,-0.41 -0.48,-0.41h-3.84c-0.24,0 -0.43,0.17 -0.47,0.41L9.25,5.35C8.66,5.59 8.12,5.92 7.63,6.29L5.24,5.33c-0.22,-0.08 -0.47,0 -0.59,0.22L2.74,8.87C2.62,9.08 2.66,9.34 2.86,9.48l2.03,1.58C4.84,11.36 4.8,11.69 4.8,12s0.02,0.64 0.07,0.94l-2.03,1.58c-0.18,0.14 -0.23,0.41 -0.12,0.61l1.92,3.32c0.12,0.22 0.37,0.29 0.59,0.22l2.39,-0.96c0.5,0.38 1.03,0.7 1.62,0.94l0.36,2.54c0.05,0.24 0.24,0.41 0.48,0.41h3.84c0.24,0 0.44,-0.17 0.47,-0.41l0.36,-2.54c0.59,-0.24 1.13,-0.56 1.62,-0.94l2.39,0.96c0.22,0.08 0.47,0 0.59,-0.22l1.92,-3.32c0.12,-0.22 0.07,-0.47 -0.12,-0.61L19.14,12.94zM12,15.6c-1.98,0 -3.6,-1.62 -3.6,-3.6s1.62,-3.6 3.6,-3.6s3.6,1.62 3.6,3.6S13.98,15.6 12,15.6z" />
</vector>
@@ -0,0 +1,19 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M21,12.22C21,6.73 16.74,3 12,3c-4.69,0 -9,3.65 -9,9.28C2.4,12.62 2,13.26 2,14v2c0,1.1 0.9,2 2,2h1v-6.1c0,-3.87 3.13,-7 7,-7s7,3.13 7,7V19h-8v2h8c1.1,0 2,-0.9 2,-2v-1.22c0.59,-0.31 1,-0.92 1,-1.64v-2.3C22,13.14 21.59,12.53 21,12.22z" />
<path
android:fillColor="@android:color/white"
android:pathData="M9,13m-1,0a1,1 0,1 1,2 0a1,1 0,1 1,-2 0" />
<path
android:fillColor="@android:color/white"
android:pathData="M15,13m-1,0a1,1 0,1 1,2 0a1,1 0,1 1,-2 0" />
<path
android:fillColor="@android:color/white"
android:pathData="M18,11.03C17.52,8.18 15.04,6 12.05,6c-3.03,0 -6.29,2.51 -6.03,6.45c2.47,-1.01 4.33,-3.21 4.86,-5.89C12.19,9.19 14.88,11 18,11.03z" />
</vector>
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M20,20H4A2,2 0 0,1 2,18V6A2,2 0 0,1 4,4H20A2,2 0 0,1 22,6V18A2,2 0 0,1 20,20M5,13V15H16V13H5M5,9V11H19V9H5Z" />
</vector>
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M19 3H14.82C14.4 1.84 13.3 1 12 1S9.6 1.84 9.18 3H5C3.9 3 3 3.9 3 5V19C3 20.1 3.9 21 5 21H19C20.1 21 21 20.1 21 19V5C21 3.9 20.1 3 19 3M7 8H9V12H8V9H7V8M10 17V18H7V17.08L9 15H7V14H9.25C9.66 14 10 14.34 10 14.75C10 14.95 9.92 15.14 9.79 15.27L8.12 17H10M11 4C11 3.45 11.45 3 12 3S13 3.45 13 4 12.55 5 12 5 11 4.55 11 4M17 17H12V15H17V17M17 11H12V9H17V11Z" />
</vector>
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M22,24L16.75,19L17.38,21H4.5A2.5,2.5 0 0,1 2,18.5V3.5A2.5,2.5 0 0,1 4.5,1H19.5A2.5,2.5 0 0,1 22,3.5V24M12,6.8C9.32,6.8 7.44,7.95 7.44,7.95C8.47,7.03 10.27,6.5 10.27,6.5L10.1,6.33C8.41,6.36 6.88,7.53 6.88,7.53C5.16,11.12 5.27,14.22 5.27,14.22C6.67,16.03 8.75,15.9 8.75,15.9L9.46,15C8.21,14.73 7.42,13.62 7.42,13.62C7.42,13.62 9.3,14.9 12,14.9C14.7,14.9 16.58,13.62 16.58,13.62C16.58,13.62 15.79,14.73 14.54,15L15.25,15.9C15.25,15.9 17.33,16.03 18.73,14.22C18.73,14.22 18.84,11.12 17.12,7.53C17.12,7.53 15.59,6.36 13.9,6.33L13.73,6.5C13.73,6.5 15.53,7.03 16.56,7.95C16.56,7.95 14.68,6.8 12,6.8M9.93,10.59C10.58,10.59 11.11,11.16 11.1,11.86C11.1,12.55 10.58,13.13 9.93,13.13C9.29,13.13 8.77,12.55 8.77,11.86C8.77,11.16 9.28,10.59 9.93,10.59M14.1,10.59C14.75,10.59 15.27,11.16 15.27,11.86C15.27,12.55 14.75,13.13 14.1,13.13C13.46,13.13 12.94,12.55 12.94,11.86C12.94,11.16 13.45,10.59 14.1,10.59Z" />
</vector>
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M20,4L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2zM20,8l-8,5 -8,-5L4,6l8,5 8,-5v2z" />
</vector>
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24.0"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M20.38,8.53C20.54,8.13 21.06,6.54 20.21,4.39C20.21,4.39 18.9,4 15.91,6C14.66,5.67 13.33,5.62 12,5.62C10.68,5.62 9.34,5.67 8.09,6C5.1,3.97 3.79,4.39 3.79,4.39C2.94,6.54 3.46,8.13 3.63,8.53C2.61,9.62 2,11 2,12.72C2,19.16 6.16,20.61 12,20.61C17.79,20.61 22,19.16 22,12.72C22,11 21.39,9.62 20.38,8.53M12,19.38C7.88,19.38 4.53,19.19 4.53,15.19C4.53,14.24 5,13.34 5.8,12.61C7.14,11.38 9.43,12.03 12,12.03C14.59,12.03 16.85,11.38 18.2,12.61C19,13.34 19.5,14.23 19.5,15.19C19.5,19.18 16.13,19.38 12,19.38M8.86,13.12C8.04,13.12 7.36,14.12 7.36,15.34C7.36,16.57 8.04,17.58 8.86,17.58C9.69,17.58 10.36,16.58 10.36,15.34C10.36,14.11 9.69,13.12 8.86,13.12M15.14,13.12C14.31,13.12 13.64,14.11 13.64,15.34C13.64,16.58 14.31,17.58 15.14,17.58C15.96,17.58 16.64,16.58 16.64,15.34C16.64,14.11 16,13.12 15.14,13.12Z" />
</vector>
+10
View File
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M12,21.35L10.55,20.03C5.4,15.36 2,12.27 2,8.5C2,5.41 4.42,3 7.5,3C9.24,3 10.91,3.81 12,5.08C13.09,3.81 14.76,3 16.5,3C19.58,3 22,5.41 22,8.5C22,12.27 18.6,15.36 13.45,20.03L12,21.35Z" />
</vector>
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M6,18V8H8V18H6M6,4.5H8V6.5H6V4.5M17,4H19V18H17V17.75C17,17.75 15.67,18 15,18A5,5 0 0,1 10,13A5,5 0 0,1 15,8C15.67,8 17,8.25 17,8.25V4M17,10.25C17,10.25 15.67,10 15,10A3,3 0 0,0 12,13A3,3 0 0,0 15,16C15.67,16 17,15.75 17,15.75V10.25Z" />
</vector>
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M13 2V7.08A5.47 5.47 0 0 0 12 7A5.47 5.47 0 0 0 11 7.08V2M16.9 15A5 5 0 0 1 16.73 15.55L20 17.42V22H18V18.58L15.74 17.29A4.94 4.94 0 0 1 8.26 17.29L6 18.58V22H4V17.42L7.27 15.55A5 5 0 0 1 7.1 15H5.3L2.55 16.83L1.45 15.17L4.7 13H7.1A5 5 0 0 1 7.37 12.12L5.81 11.12L2.24 12L1.76 10L6.19 8.92L8.5 10.45A5 5 0 0 1 15.5 10.45L17.77 8.92L22.24 10L21.76 12L18.19 11.11L16.63 12.11A5 5 0 0 1 16.9 13H19.3L22.55 15.16L21.45 16.82L18.7 15M11 14A1 1 0 1 0 10 15A1 1 0 0 0 11 14M15 14A1 1 0 1 0 14 15A1 1 0 0 0 15 14Z" />
</vector>
+10
View File
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M22.46,6C21.69,6.35 20.86,6.58 20,6.69C20.88,6.16 21.56,5.32 21.88,4.31C21.05,4.81 20.13,5.16 19.16,5.36C18.37,4.5 17.26,4 16,4C13.65,4 11.73,5.92 11.73,8.29C11.73,8.63 11.77,8.96 11.84,9.27C8.28,9.09 5.11,7.38 3,4.79C2.63,5.42 2.42,6.16 2.42,6.94C2.42,8.43 3.17,9.75 4.33,10.5C3.62,10.5 2.96,10.3 2.38,10C2.38,10 2.38,10 2.38,10.03C2.38,12.11 3.86,13.85 5.82,14.24C5.46,14.34 5.08,14.39 4.69,14.39C4.42,14.39 4.15,14.36 3.89,14.31C4.43,16 6,17.26 7.89,17.29C6.43,18.45 4.58,19.13 2.56,19.13C2.22,19.13 1.88,19.11 1.54,19.07C3.44,20.29 5.7,21 8.12,21C16,21 20.33,14.46 20.33,8.79C20.33,8.6 20.33,8.42 20.32,8.23C21.16,7.63 21.88,6.87 22.46,6Z" />
</vector>
+5 -3
View File
@@ -7,14 +7,16 @@
android:orientation="vertical"
tools:context=".main.ui.MainActivity">
<androidx.fragment.app.FragmentContainerView
<fragment
android:id="@+id/nav_host"
android:name="eu.darken.capod.main.ui.overview.OverviewFragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/nav_graph" />
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -0,0 +1,26 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".main.ui.MainActivity">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
style="@style/Widget.MaterialComponents.Toolbar.Primary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navigationIcon="@drawable/ic_baseline_arrow_back_24"
app:title="@string/settings_label" />
<FrameLayout
android:id="@+id/content_frame"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
+3 -4
View File
@@ -1,10 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/menu_item_settings"
android:title="@string/settings_general_label" />
<item
android:id="@+id/menu_item_debuglog"
android:title="@string/debug_debuglog_record_action" />
<item
android:id="@+id/menu_item_settings"
android:title="@string/general_settings_label" />
</menu>
@@ -0,0 +1,12 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context="eu.darken.androidstarter.main.ui.MainActivity">
<item
android:id="@+id/menu_item_twitter"
android:icon="@drawable/ic_twitter"
android:orderInCategory="100"
android:tooltipText="Twitter"
android:title="Twitter"
app:showAsAction="always" />
</menu>
@@ -10,6 +10,15 @@
android:name="eu.darken.capod.main.ui.overview.OverviewFragment"
tools:layout="@layout/main_fragment">
<action
android:id="@+id/action_overviewFragment_to_settingsFragment"
app:destination="@id/settingsFragment" />
</fragment>
<fragment
android:id="@+id/settingsFragment"
android:name="eu.darken.capod.main.ui.settings.SettingsFragment"
android:label="SettingsFragment"
tools:layout="@layout/settings_fragment" />
</navigation>
+40 -8
View File
@@ -1,33 +1,39 @@
<resources>
<string name="app_name">CAPod</string>
<string name="app_name_pro">CAPod Pro</string>
<string name="general_error_label">Error</string>
<string name="permission_bluetooth_connect_label">BLUETOOTH CONNECT</string>
<string name="permission_bluetooth_connect_description">Required to be able to connect to paired Bluetooth devices.</string>
<string name="device_status_loading_message">Loading device status</string>
<string name="notification_channel_device_status_label">Device status</string>
<string name="permission_bluetooth_scan_label">BLUETOOTH SCAN</string>
<string name="permission_bluetooth_scan_description">Required to be able to discover and pair nearby Bluetooth devices.</string>
<string name="general_error_label">Error</string>
<string name="general_share_action">Share</string>
<string name="general_done_action">Done</string>
<string name="general_copy_action">Copy</string>
<string name="general_value_unknown_label">Unknown</string>
<string name="general_thank_you_label">Thank you</string>
<string name="general_value_not_available_label">N/A</string>
<string name="debug_debuglog_size_label">Size</string>
<string name="debug_debuglog_size_compressed_label">Compressed size</string>
<string name="debug_notification_channel_label">Debug notifications</string>
<string name="debug_debuglog_file_label">Recorded log file</string>
<string name="debug_debuglog_record_action">Record debug log</string>
<string name="general_settings_label">Settings</string>
<string name="permission_bluetooth_connect_label">BLUETOOTH CONNECT</string>
<string name="permission_bluetooth_connect_description">Required to be able to connect to paired Bluetooth devices.</string>
<string name="permission_bluetooth_scan_label">BLUETOOTH SCAN</string>
<string name="permission_bluetooth_scan_description">Required to be able to discover and pair nearby Bluetooth devices.</string>
<string name="permission_bluetooth_label">BLUETOOTH</string>
<string name="permission_bluetooth_description">Allows applications to connect to paired bluetooth devices.</string>
<string name="permission_access_fine_location_description">Allows an app to access precise location.</string>
<string name="permission_access_fine_location_label">ACCESS_FINE_LOCATION</string>
<string name="pods_dual_left_label">Left pod</string>
<string name="pods_dual_right_label">Right pod</string>
<string name="pods_case_status_label">Case</string>
<string name="pods_case_status_open_label">Open</string>
<string name="pods_case_status_closed_label">Closed</string>
<string name="general_value_not_available_label">N/A</string>
<string name="pods_unknown_raw_data_label">Raw data</string>
<string name="general_value_unknown_label">Unknown</string>
<string name="pods_reception_x_label">Reception %s</string>
<string name="pods_device_color_white_label">White</string>
<string name="pods_device_color_yellow_label">Yellow</string>
@@ -49,4 +55,30 @@
<string name="pods_connection_state_ringing_label">Ringing</string>
<string name="pods_connection_state_hanging_up_label">Hanging up</string>
<string name="pods_status_x_label">Status %s</string>
<string name="settings_label">Settings</string>
<string name="settings_privacy_policy_label">Privacy policy</string>
<string name="settings_privacy_policy_desc">Handling data responsibly.</string>
<string name="settings_licenses_label">Licenses</string>
<string name="settings_category_other_label">Other</string>
<string name="settings_general_label">Settings</string>
<string name="settings_general_description">General tweaks that affect the whole app.</string>
<string name="settings_acknowledgements_label">Acknowledgements</string>
<string name="changelog_label">Changelog</string>
<string name="documentation_label">Documentation</string>
<string name="settings_support_email_developer_label">Email developer</string>
<string name="settings_support_installid_label">Install ID</string>
<string name="settings_support_installid_desc">Automatic error reports are anonymous. Share your install ID if the developer needs to find your error reports.</string>
<string name="settings_support_label">Support</string>
<string name="settings_support_description">If you need some help.</string>
<string name="issue_tracker_label">Issue tracker</string>
<string name="issue_tracker_description">A public issue tracker for bug reports and feature requests (english only).</string>
<string name="discord_label">Discord</string>
<string name="discord_description">A place to hang out in and ask questions.</string>
<string name="settings_support_email_developer_description">Note that I can only respond in german or english.</string>
<string name="settings_debug_autoreports_label">Automatic bug reports</string>
<string name="settings_debug_autoreports_description">Automatically reports issues, e.g. details on an app crash so I can figure out how to fix it.</string>
</resources>
@@ -0,0 +1,103 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<PreferenceCategory
android:title="@string/general_thank_you_label"
app:iconSpaceReserved="false">
<Preference
android:key="thanks.maxpatchs"
android:summary="Thanks for the lovely icons."
android:title="Max Patchs"
app:iconSpaceReserved="false">
<intent
android:action="android.intent.action.VIEW"
android:data="https://twitter.com/maxpatchs" />
</Preference>
</PreferenceCategory>
<PreferenceCategory
android:title="@string/settings_licenses_label"
app:iconSpaceReserved="false">
<Preference
android:key="license.bugsnag"
android:summary="Client side library for crash monitoring via bugsnag.com. (MIT)"
android:title="Bugsnag"
app:iconSpaceReserved="false">
<intent
android:action="android.intent.action.VIEW"
android:data="https://github.com/bugsnag/bugsnag-android" />
</Preference>
<Preference
android:key="license.glide"
android:summary="An image loading and caching library for Android focused on smooth scrolling. (Multiple licenses)"
android:title="Glide"
app:iconSpaceReserved="false">
<intent
android:action="android.intent.action.VIEW"
android:data="https://github.com/bumptech/glide" />
</Preference>
<Preference
android:key="license.materialdesignicons"
android:summary="materialdesignicons.com (SIL Open Font License 1.1 / Attribution 4.0 International)"
android:title="Material Design Icons"
app:iconSpaceReserved="false">
<intent
android:action="android.intent.action.VIEW"
android:data="https://github.com/Templarian/MaterialDesign" />
</Preference>
<Preference
android:key="license.zwiconiconset"
android:summary="Creative Commons Attribution 4.0 International"
android:title="Zwicon Icon Set"
app:iconSpaceReserved="false">
<intent
android:action="android.intent.action.VIEW"
android:data="https://iconduck.com/sets/zwicon-icon-set" />
</Preference>
<Preference
android:key="license.lottie"
android:summary="Airbnb's Lottie for Android. (APACHE 2.0)"
android:title="Lottie"
app:iconSpaceReserved="false">
<intent
android:action="android.intent.action.VIEW"
android:data="https://github.com/airbnb/lottie-android" />
</Preference>
<Preference
android:key="license.kotlin"
android:summary="The Kotlin Programming Language. (APACHE 2.0)"
android:title="Kotlin"
app:iconSpaceReserved="false">
<intent
android:action="android.intent.action.VIEW"
android:data="https://github.com/JetBrains/kotlin" />
</Preference>
<Preference
android:key="license.dagger"
android:summary="A fast dependency injector for Android and Java. (APACHE 2.0)"
android:title="Dagger"
app:iconSpaceReserved="false">
<intent
android:action="android.intent.action.VIEW"
android:data="https://github.com/google/dagger" />
</Preference>
<Preference
android:key="android.android"
android:summary="Android Open Source Project (APACHE 2.0)"
android:title="Android"
app:iconSpaceReserved="false">
<intent
android:action="android.intent.action.VIEW"
android:data="https://source.android.com/source/licenses.html" />
</Preference>
<Preference
android:key="android.logo"
android:summary="The Android robot is reproduced or modified from work created and shared by Google and used according to terms described in the Creative Commons 3.0 Attribution License."
android:title="Android"
app:iconSpaceReserved="false">
<intent
android:action="android.intent.action.VIEW"
android:data="https://developer.android.com/distribute/tools/promote/brand.html" />
</Preference>
</PreferenceCategory>
</PreferenceScreen>
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:title="@string/settings_category_other_label">
<CheckBoxPreference
android:icon="@drawable/ic_spider_thread_onsurface"
android:key="core.bugtracking.enabled"
android:summary="@string/settings_debug_autoreports_description"
android:title="@string/settings_debug_autoreports_label" />
</PreferenceCategory>
</PreferenceScreen>
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<Preference
android:icon="@drawable/ic_baseline_settings_24"
app:fragment="eu.darken.capod.main.ui.settings.general.GeneralSettingsFragment"
app:summary="@string/settings_general_description"
app:title="@string/settings_general_label" />
<Preference
android:icon="@drawable/ic_baseline_support_agent_24"
app:fragment="eu.darken.capod.main.ui.settings.support.SupportFragment"
app:summary="@string/settings_support_description"
app:title="@string/settings_support_label" />
<PreferenceCategory android:title="@string/settings_category_other_label">
<Preference
android:icon="@drawable/ic_changelog_onsurface"
android:key="core.changelog"
android:title="@string/changelog_label"
app:summary="v?.?.?">
<intent
android:action="android.intent.action.VIEW"
android:data="https://github.com/d4rken/capod/releases/latest" />
</Preference>
<Preference
android:icon="@drawable/ic_heart"
app:fragment="eu.darken.capod.main.ui.settings.acks.AcknowledgementsFragment"
app:summary="@string/general_thank_you_label"
app:title="@string/settings_acknowledgements_label" />
<Preference
android:icon="@drawable/ic_baseline_book_24"
app:summary="@string/settings_privacy_policy_desc"
app:title="@string/settings_privacy_policy_label">
<intent
android:action="android.intent.action.VIEW"
android:data="https://github.com/d4rken/capod/blob/main/PRIVACY_POLICY.md" />
</Preference>
</PreferenceCategory>
</PreferenceScreen>
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<Preference
android:icon="@drawable/ic_github_onsurface"
android:summary="@string/issue_tracker_description"
android:title="@string/issue_tracker_label">
<intent
android:action="android.intent.action.VIEW"
android:data="https://github.com/d4rken/capod/issues" />
</Preference>
<Preference
android:icon="@drawable/ic_discord_onsurface"
android:summary="@string/discord_description"
android:title="@string/discord_label">
<intent
android:action="android.intent.action.VIEW"
android:data="https://discord.gg/rrxxng35jq" />
</Preference>
<Preference
android:icon="@drawable/ic_email_onsurface"
android:key="support.email.darken"
android:summary="@string/settings_support_email_developer_description"
android:title="@string/settings_support_email_developer_label" />
<PreferenceCategory app:title="@string/settings_category_other_label">
<Preference
android:icon="@drawable/ic_id_onsurface"
android:key="support.installid"
android:summary="@string/settings_support_installid_desc"
android:title="@string/settings_support_installid_label" />
</PreferenceCategory>
</PreferenceScreen>