Skip to content

Commit 99943f6

Browse files
committed
Extract custom sync server help URL into a separate string
1 parent 2fb40a1 commit 99943f6

File tree

6 files changed

+63
-10
lines changed

6 files changed

+63
-10
lines changed

AnkiDroid/src/main/java/com/ichi2/anki/preferences/HtmlHelpPreference.kt

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,38 +14,54 @@
1414

1515
package com.ichi2.anki.preferences
1616

17-
import android.R
1817
import android.content.Context
1918
import android.text.method.LinkMovementMethod
2019
import android.util.AttributeSet
2120
import android.widget.TextView
22-
import androidx.core.text.HtmlCompat
21+
import androidx.core.text.parseAsHtml
2322
import androidx.preference.Preference
2423
import androidx.preference.PreferenceViewHolder
24+
import com.ichi2.anki.R
2525

2626
/**
27-
* Non-clickable preference that shows help text. In XML, you only want to specify the summary, e.g.
27+
* Non-clickable preference that shows help text.
2828
*
29-
* <com.ichi2.anki.preferences.HtmlHelpPreference android:summary="@string/foo" />
29+
* The summary is parsed as a format string containing HTML,
30+
* and may contain up to three format specifiers as understood by `Sting.format()`,
31+
* arguments for which can be specified using XML attributes such as `app:substitution1`.
3032
*
31-
* The summary is parsed as HTML, which can contain all tags that TextViews can display,
33+
* <com.ichi2.anki.preferences.HtmlHelpPreference
34+
* android:summary="@string/format_string"
35+
* app:substitution1="@string/substitution1"
36+
* />
37+
*
38+
* The summary HTML can contain all tags that TextViews can display,
3239
* including new lines and links. Raw HTML can be entered using the CDATA tag, e.g.
3340
*
34-
* <string name="foo"><![CDATA[<b>Bold text</b>]]></string>
41+
* <string name="format_string"><![CDATA[<b>Hello, %s</b>]]></string>
3542
*/
3643
class HtmlHelpPreference(context: Context, attrs: AttributeSet?) : Preference(context, attrs) {
3744
init {
3845
isSelectable = false
3946
isPersistent = false
4047
}
4148

42-
override fun getSummary() = super.getSummary()?.let {
43-
HtmlCompat.fromHtml(it.toString(), HtmlCompat.FROM_HTML_MODE_LEGACY)
49+
private val substitutions = context.usingStyledAttributes(attrs, R.styleable.HtmlHelpPreference) {
50+
arrayOf(
51+
getString(R.styleable.HtmlHelpPreference_substitution1),
52+
getString(R.styleable.HtmlHelpPreference_substitution2),
53+
getString(R.styleable.HtmlHelpPreference_substitution3),
54+
)
4455
}
4556

57+
override fun getSummary() = super.getSummary()
58+
.toString()
59+
.format(*substitutions)
60+
.parseAsHtml()
61+
4662
override fun onBindViewHolder(holder: PreferenceViewHolder) {
4763
super.onBindViewHolder(holder)
48-
val summary = holder.findViewById(R.id.summary) as TextView
64+
val summary = holder.findViewById(android.R.id.summary) as TextView
4965
summary.movementMethod = LinkMovementMethod.getInstance()
5066
summary.maxHeight = Int.MAX_VALUE
5167
}

AnkiDroid/src/main/java/com/ichi2/anki/preferences/PreferenceUtils.kt

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,19 @@
1515
*/
1616
package com.ichi2.anki.preferences
1717

18+
import android.content.Context
19+
import android.content.res.TypedArray
20+
import android.util.AttributeSet
21+
import androidx.annotation.AttrRes
1822
import androidx.annotation.StringRes
23+
import androidx.annotation.StyleRes
1924
import androidx.fragment.app.DialogFragment
2025
import androidx.fragment.app.Fragment
2126
import androidx.preference.Preference
2227
import androidx.preference.PreferenceFragmentCompat
28+
import kotlin.contracts.ExperimentalContracts
29+
import kotlin.contracts.InvocationKind
30+
import kotlin.contracts.contract
2331

2432
/**
2533
* By implementing this interface, a preference can specify which dialog fragment is opened on click.
@@ -68,3 +76,24 @@ inline fun <reified T : Preference> PreferenceFragmentCompat.requirePreference(@
6876
val key = getString(resId)
6977
return requirePreference(key)
7078
}
79+
80+
/**
81+
* Run a [block] on a [TypedArray] receiver that is recycled at the end.
82+
* @return The return value of the block.
83+
*
84+
* @see android.content.res.Resources.Theme.obtainStyledAttributes
85+
* @see androidx.core.content.withStyledAttributes
86+
*/
87+
@OptIn(ExperimentalContracts::class)
88+
inline fun <T> Context.usingStyledAttributes(
89+
set: AttributeSet?,
90+
attrs: IntArray,
91+
@AttrRes defStyleAttr: Int = 0,
92+
@StyleRes defStyleRes: Int = 0,
93+
block: TypedArray.() -> T
94+
): T {
95+
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
96+
97+
val typedArray = obtainStyledAttributes(set, attrs, defStyleAttr, defStyleRes)
98+
return typedArray.block().also { typedArray.recycle() }
99+
}

AnkiDroid/src/main/res/values/10-preferences.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@
224224
For example, you could use the sync server built into Anki
225225
Desktop to sync the collection when you only have a local network,
226226
and AnkiWeb to sync media when you have general internet access.
227-
<a href="https://docs.ankidroid.org/#_custom_sync_server">Learn more</a>
227+
<a href="%s">Learn more</a>
228228
<br>
229229
<br>Note: unlike in previous AnkiDroid versions,
230230
you must specify the full custom sync URL,

AnkiDroid/src/main/res/values/attrs.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@
4242
<attr name="summaryFormat" format="string"/>
4343
</declare-styleable>
4444

45+
<declare-styleable name="HtmlHelpPreference">
46+
<attr name="substitution1" format="string" />
47+
<attr name="substitution2" format="string" />
48+
<attr name="substitution3" format="string" />
49+
</declare-styleable>
50+
4551
<declare-styleable name="HeaderPreference">
4652
<!-- String array of entries to join in order to build the preference summary -->
4753
<attr name="summaryEntries" format="reference"/>

AnkiDroid/src/main/res/values/constants.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@
165165
<string name="link_distributions">https://apps.ankiweb.net/#download</string>
166166
<string name="link_ankiweb_lost_email_instructions">https://github.com/ankidroid/Anki-Android/wiki/FAQ#forgotten-ankiweb-email-instructions</string>
167167
<string name="link_scoped_storage_faq">https://github.com/ankidroid/Anki-Android/wiki/Storage-Migration-FAQ</string>
168+
<string name="link_custom_sync_server_help_learn_more_en">https://docs.ankidroid.org/#_custom_sync_server</string>
168169

169170
<string-array name="leech_action_values">
170171
<item>0</item>

AnkiDroid/src/main/res/xml/preferences_custom_sync_server.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
android:key="@string/pref_custom_sync_server_screen_key">
1010
<com.ichi2.anki.preferences.HtmlHelpPreference
1111
android:summary="@string/custom_sync_server_help"
12+
app:substitution1="@string/link_custom_sync_server_help_learn_more_en"
1213
search:ignore="true" />
1314
<com.ichi2.anki.preferences.VersatileTextWithASwitchPreference
1415
android:key="@string/custom_sync_server_collection_url_key"

0 commit comments

Comments
 (0)