diff --git a/src/main/res/values/attrs.xml b/src/main/res/values/attrs.xml
new file mode 100644
index 00000000..ca0adbc5
--- /dev/null
+++ b/src/main/res/values/attrs.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/res/xml/pref_all.xml b/src/main/res/xml/pref_all.xml
index 95738358..c4df91c0 100644
--- a/src/main/res/xml/pref_all.xml
+++ b/src/main/res/xml/pref_all.xml
@@ -1,5 +1,6 @@
-
+
@@ -37,28 +38,28 @@
android:summary="@string/sitekey_summary"
android:title="@string/sitekey">
-
-
+
-
-
+
{
+ mSpinner.setDropDownVerticalOffset(Utils.dpToPx(getContext, 8 - 48 * mSelectedIndex).toInt) // TODO: scrolling?
+ mSpinner.performClick
+ true
+ })
+ initSummary(mContext, attrs)
+ val a: TypedArray = mContext.obtainStyledAttributes(attrs, R.styleable.DropDownPreference)
+ setEntries(a.getTextArray(R.styleable.DropDownPreference_entries))
+ mEntryValues = a.getTextArray(R.styleable.DropDownPreference_entryValues)
+ a.recycle
+
+ protected override def getSummaryValue = getEntry
+
+ /**
+ * Sets the human-readable entries to be shown in the list. This will be shown in subsequent dialogs.
+ *
+ * Each entry must have a corresponding index in [[setEntryValues(CharSequence[])]].
+ *
+ * @param entries The entries.
+ * @see [[setEntryValues(CharSequence[])]]
+ */
+ def setEntries(entries: Array[CharSequence]) {
+ mEntries = entries
+ mAdapter.clear
+ if (entries != null) for (entry <- entries) mAdapter.add(entry.toString)
+ }
+ /**
+ * @see #setEntries(CharSequence[])
+ * @param entriesResId The entries array as a resource.
+ */
+ def setEntries(entriesResId: Int): Unit = setEntries(getContext.getResources.getTextArray(entriesResId))
+ /**
+ * The list of entries to be shown in the list in subsequent dialogs.
+ *
+ * @return The list as an array.
+ */
+ def getEntries = mEntries
+
+ /**
+ * The array to find the value to save for a preference when an entry from
+ * entries is selected. If a user clicks on the second item in entries, the
+ * second item in this array will be saved to the preference.
+ *
+ * @param entryValues The array to be used as values to save for the preference.
+ */
+ def setEntryValues(entryValues: Array[CharSequence]) = mEntryValues = entryValues
+ /**
+ * @see #setEntryValues(CharSequence[])
+ * @param entryValuesResId The entry values array as a resource.
+ */
+ def setEntryValues(entryValuesResId: Int): Unit =
+ setEntryValues(getContext.getResources.getTextArray(entryValuesResId))
+ /**
+ * Returns the array of values to be saved for the preference.
+ *
+ * @return The array of values.
+ */
+ def getEntryValues: Array[CharSequence] = mEntryValues
+
+ /**
+ * Sets the value of the key. This should be one of the entries in [[getEntryValues]].
+ *
+ * @param value The value to set for the key.
+ */
+ def setValue(value: String) {
+ val i = findIndexOfValue(value)
+ if (i > -1) setValueIndex(i)
+ }
+
+ /**
+ * Sets the value to the given index from the entry values.
+ *
+ * @param index The index of the value to set.
+ */
+ def setValueIndex(index: Int) {
+ val changed = mSelectedIndex != index
+ if (changed || !mValueSet) {
+ mSelectedIndex = index
+ mValueSet = true
+ if (mEntryValues != null) {
+ val value = mEntryValues(index).toString
+ persistString(value)
+ setValue(value)
+ }
+ if (changed) notifyChanged
+ }
+ mSpinner.setSelection(index)
+ }
+
+ /**
+ * Returns the value of the key. This should be one of the entries in [[getEntryValues]].
+ *
+ * @return The value of the key.
+ */
+ def getValue = if (mEntryValues == null) null else mEntryValues(mSelectedIndex).toString
+
+ /**
+ * Returns the entry corresponding to the current value.
+ *
+ * @return The entry corresponding to the current value, or null.
+ */
+ def getEntry = {
+ val index = getValueIndex
+ if (index >= 0 && mEntries != null) mEntries(index) else null
+ }
+
+ /**
+ * Returns the index of the given value (in the entry values array).
+ *
+ * @param value The value whose index should be returned.
+ * @return The index of the value, or -1 if not found.
+ */
+ def findIndexOfValue(value: String): Int = {
+ if (value != null && mEntryValues != null) {
+ var i = mEntryValues.length - 1
+ while (i >= 0) {
+ if (mEntryValues(i) == value) return i
+ i -= 1
+ }
+ }
+ -1
+ }
+
+ def getValueIndex = mSelectedIndex
+
+ protected override def onGetDefaultValue(a: TypedArray, index: Int) = a.getString(index)
+ protected override def onSetInitialValue (restoreValue: Boolean, defaultValue: AnyRef) =
+ setValue(if (restoreValue) getPersistedString(getValue) else defaultValue.asInstanceOf[String])
+
+ @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+ def setDropDownWidth(dimenResId: Int) =
+ mSpinner.setDropDownWidth(mContext.getResources.getDimensionPixelSize(dimenResId))
+
+ protected override def onBindView(@NonNull view: View) {
+ super.onBindView(view)
+ if (view == mSpinner.getParent) return
+ if (mSpinner.getParent != null) mSpinner.getParent.asInstanceOf[ViewGroup].removeView(mSpinner)
+ view.asInstanceOf[ViewGroup].addView(mSpinner, 0)
+ val lp = mSpinner.getLayoutParams
+ lp.width = 0
+ mSpinner.setLayoutParams(lp)
+ }
+}
diff --git a/src/main/scala/com/github/shadowsocks/preferences/SummaryPreference.scala b/src/main/scala/com/github/shadowsocks/preferences/SummaryPreference.scala
new file mode 100644
index 00000000..56523494
--- /dev/null
+++ b/src/main/scala/com/github/shadowsocks/preferences/SummaryPreference.scala
@@ -0,0 +1,45 @@
+package com.github.shadowsocks.preferences
+
+import android.content.Context
+import android.preference.Preference
+import android.util.AttributeSet
+import com.github.shadowsocks.R
+
+/**
+ * Make your preference support %s in summary. Override getSummaryValue to customize what to put in.
+ * Based on: https://github.com/Mygod/mygod-lib-android/blob/eb20c57a0da70bb5dbc27104002f20fb1eb31d76/lib/src/main/scala/tk/mygod/preference/SummaryPreference.scala
+ * @author Mygod
+ */
+trait SummaryPreference extends Preference {
+ private var mSummary: String = _
+
+ protected def initSummary(context: Context, attrs: AttributeSet) {
+ val a = context.obtainStyledAttributes(attrs, R.styleable.SummaryPreference)
+ mSummary = a.getString(R.styleable.SummaryPreference_summary)
+ a.recycle
+ }
+
+ protected def getSummaryValue: AnyRef
+
+ /**
+ * Returns the summary of this SummaryPreference. If the summary has a String formatting marker in it
+ * (i.e. "%s" or "%1$s"), then the current entry value will be substituted in its place.
+ *
+ * @return the summary with appropriate string substitution
+ */
+ override def getSummary = {
+ val entry = getSummaryValue
+ if (mSummary == null || entry == null) super.getSummary else String.format(mSummary, entry)
+ }
+ /**
+ * Sets the summary for this Preference with a CharSequence. If the summary has a String formatting marker in it
+ * (i.e. "%s" or "%1$s"), then the current entry value will be substituted in its place when it's retrieved.
+ *
+ * @param summary The summary for the preference.
+ */
+ override def setSummary(summary: CharSequence) {
+ super.setSummary(summary)
+ if (summary == null && mSummary != null) mSummary = null
+ else if (summary != null && summary != mSummary) mSummary = summary.toString
+ }
+}