Unverified Commit 8c33345a authored by Mygod's avatar Mygod Committed by GitHub

Use Gericop's preference library (#1471)

* New SimpleMenuPreference;
* More Oreo-like preference style;
* I'm tired of fixing all those bugs;
* Remove NumberPickerPreference which nobody likes.
parent 74917eac
......@@ -9,10 +9,9 @@ Cannot connect to server:
Crash: [Submit an issue](https://github.com/shadowsocks/shadowsocks-android/issues/new) with logcat attached, or submit a crash report to Google Play. Then, try wiping app data.
### UI tips
### How to create a widget and/or switch profile based on network connectivity?
* Tap the number to enter the port you wish to use; (if the keyboard doesn't pop up automatically for some reason)
* Use Tasker integration to create a desktop widget.
Use [Tasker](http://tasker.dinglisch.net/) integration.
### How to add QR code from local gallery?
......
......@@ -20,10 +20,13 @@ lazy val commonSettings = Seq(
resConfigs := Seq("fa", "ja", "ko", "ru", "zh-rCN", "zh-rTW"),
resolvers += Resolver.jcenterRepo,
resolvers += Resolver.bintrayRepo("gericop", "maven"),
resolvers += "google" at "https://maven.google.com"
)
val supportLibsVersion = "27.0.2"
val takisoftFixVersion = "27.0.2.0"
lazy val root = Project(id = "shadowsocks-android", base = file("."))
.settings(commonSettings)
.aggregate(plugin, mobile)
......@@ -34,7 +37,11 @@ run in Android := (run in (mobile, Android)).evaluated
lazy val plugin = project
.settings(commonSettings)
.settings(
libraryDependencies += "com.android.support" % "preference-v14" % supportLibsVersion
libraryDependencies ++=
"com.android.support" % "preference-v14" % supportLibsVersion ::
"com.takisoft.fix" % "preference-v7" % takisoftFixVersion ::
"com.takisoft.fix" % "preference-v7-simplemenu" % takisoftFixVersion ::
Nil
)
lazy val mobile = project
......
package com.github.shadowsocks;
import android.app.Activity;
/**
* This helper class is used to solve Java-Scala interop problem. Use onAttach(Context) and remove this class
* when minSdkVersion >= 23.
*
* @author Mygod
*/
public class DialogFragment extends android.app.DialogFragment {
protected void superOnAttach(Activity activity) {
super.onAttach(activity);
}
}
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#7488A1</color>
<color name="material_drawer_selected_text">@color/material_accent_200</color>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.Shadowsocks.Material" parent="MaterialDrawerTheme.Light.DarkToolbar">
<item name="material_drawer_selected_text">@color/material_accent_200</item>
</style>
<style name="StyledScrollerTextAppearance" parent="@android:style/TextAppearance">
<item name="android:textSize">24sp</item>
<item name="android:textStyle">bold</item>
......
......@@ -8,32 +8,32 @@
<SwitchPreference android:key="tcp_fastopen"
android:summary="@string/tcp_fastopen_summary"
android:title="TCP Fast Open"/>
<be.mygod.preference.PreferenceCategory
<PreferenceCategory
android:title="@string/advanced">
<DropDownPreference
<SimpleMenuPreference
android:key="serviceMode"
android:entries="@array/service_modes"
android:entryValues="@array/service_mode_values"
android:defaultValue="vpn"
android:summary="%s"
android:title="@string/service_mode"/>
<be.mygod.preference.NumberPickerPreference
app:min="1025"
app:max="65535"
<AutoSummaryEditTextPreference
android:key="portProxy"
android:summary="%d"
android:inputType="number"
android:maxLength="5"
app:pref_summaryHasText="%s"
android:title="@string/port_proxy"/>
<be.mygod.preference.NumberPickerPreference
app:min="1025"
app:max="65535"
<AutoSummaryEditTextPreference
android:key="portLocalDns"
android:summary="%d"
android:inputType="number"
android:maxLength="5"
app:pref_summaryHasText="%s"
android:title="@string/port_local_dns"/>
<be.mygod.preference.NumberPickerPreference
app:min="1025"
app:max="65535"
<AutoSummaryEditTextPreference
android:key="portTransproxy"
android:summary="%d"
android:inputType="number"
android:maxLength="5"
app:pref_summaryHasText="%s"
android:title="@string/port_transproxy"/>
</be.mygod.preference.PreferenceCategory>
</PreferenceCategory>
</PreferenceScreen>
......@@ -2,51 +2,52 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/com.github.shadowsocks">
<be.mygod.preference.EditTextPreference
<AutoSummaryEditTextPreference
android:key="profileName"
android:summary="%s"
app:pref_summaryHasText="%s"
android:title="@string/profile_name"/>
<be.mygod.preference.PreferenceCategory
<PreferenceCategory
android:title="@string/proxy_cat">
<be.mygod.preference.EditTextPreference
<AutoSummaryEditTextPreference
android:key="proxy"
android:summary="%s"
app:pref_summaryHasText="%s"
android:title="@string/proxy"/>
<be.mygod.preference.NumberPickerPreference
app:min="1"
app:max="65535"
<AutoSummaryEditTextPreference
android:key="remotePortNum"
android:summary="%d"
android:inputType="number"
android:maxLength="5"
app:pref_summaryHasText="%s"
android:title="@string/remote_port"/>
<be.mygod.preference.EditTextPreference
<AutoSummaryEditTextPreference
android:inputType="textPassword"
app:pref_summaryPasswordSubstituteLength="0"
android:key="sitekey"
android:summary="%s"
app:pref_summaryHasText="%s"
android:title="@string/sitekey"/>
<DropDownPreference
<SimpleMenuPreference
android:key="encMethod"
android:entries="@array/enc_method_entry"
android:entryValues="@array/enc_method_value"
android:summary="%s"
android:title="@string/enc_method"/>
</be.mygod.preference.PreferenceCategory>
</PreferenceCategory>
<be.mygod.preference.PreferenceCategory
<PreferenceCategory
android:title="@string/feature_cat">
<DropDownPreference
<SimpleMenuPreference
android:key="route"
android:entries="@array/route_entry"
android:entryValues="@array/route_value"
android:summary="%s"
android:title="@string/route_list"/>
<be.mygod.preference.EditTextPreference
<AutoSummaryEditTextPreference
android:key="remoteDns"
android:title="@string/remote_dns"
android:summary="%s"/>
app:pref_summaryHasText="%s"/>
<SwitchPreference
android:key="isIpv6"
android:summary="@string/ipv6_summary"
......@@ -60,9 +61,9 @@
android:summary="@string/udp_dns_summary"
android:title="@string/udp_dns"/>
</be.mygod.preference.PreferenceCategory>
</PreferenceCategory>
<be.mygod.preference.PreferenceCategory
<PreferenceCategory
android:title="@string/plugin">
<com.github.shadowsocks.preference.IconListPreference
......@@ -70,12 +71,12 @@
android:persistent="false"
android:summary="%s"
android:title="@string/plugin"/>
<be.mygod.preference.EditTextPreference
<AutoSummaryEditTextPreference
android:key="plugin.configure"
android:persistent="false"
android:summary="%s"
app:pref_summaryHasText="%s"
android:title="@string/plugin_configure"/>
</be.mygod.preference.PreferenceCategory>
</PreferenceCategory>
</PreferenceScreen>
......@@ -24,13 +24,13 @@ import android.os.Bundle
import android.support.design.widget.Snackbar
import android.support.v14.preference.SwitchPreference
import android.support.v7.preference.Preference
import be.mygod.preference.PreferenceFragment
import com.github.shadowsocks.ShadowsocksApplication.app
import com.github.shadowsocks.bg.ServiceState
import com.github.shadowsocks.utils.{Key, TcpFastOpen}
import com.takisoft.fix.support.v7.preference.PreferenceFragmentCompatDividers
class GlobalConfigFragment extends PreferenceFragment {
override def onCreatePreferences(bundle: Bundle, key: String) {
class GlobalConfigFragment extends PreferenceFragmentCompatDividers {
override def onCreatePreferencesFix(bundle: Bundle, key: String) {
getPreferenceManager.setPreferenceDataStore(app.dataStore)
app.dataStore.putString(Key.serviceMode, app.dataStore.serviceMode) // temporary workaround for support lib bug
addPreferencesFromResource(R.xml.pref_global)
......
......@@ -33,7 +33,7 @@ import android.os.{Bundle, Handler}
import android.support.customtabs.CustomTabsIntent
import android.support.design.widget.{FloatingActionButton, Snackbar}
import android.support.v4.content.ContextCompat
import android.support.v7.app.AlertDialog
import android.support.v7.app.{AlertDialog, AppCompatActivity}
import android.support.v7.content.res.AppCompatResources
import android.support.v7.preference.PreferenceDataStore
import android.support.v7.widget.RecyclerView.ViewHolder
......@@ -69,7 +69,7 @@ object MainActivity {
var stateListener: Int => Unit = _
}
class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawerItemClickListener
class MainActivity extends AppCompatActivity with ServiceBoundContext with Drawer.OnDrawerItemClickListener
with OnPreferenceDataStoreChangeListener {
import MainActivity._
......@@ -152,8 +152,9 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
rxText.setText(TrafficMonitor.formatTraffic(rxTotal))
txRateText.setText(TrafficMonitor.formatTraffic(txRate) + "/s")
rxRateText.setText(TrafficMonitor.formatTraffic(rxRate) + "/s")
val child = getFragmentManager.findFragmentById(R.id.fragment_holder).asInstanceOf[ToolbarFragment]
if (state != ServiceState.STOPPING && child != null) child.onTrafficUpdated(profileId, txRate, rxRate, txTotal, rxTotal)
val child = getSupportFragmentManager.findFragmentById(R.id.fragment_holder).asInstanceOf[ToolbarFragment]
if (state != ServiceState.STOPPING && child != null)
child.onTrafficUpdated(profileId, txRate, rxRate, txTotal, rxTotal)
}
override def onServiceConnected(): Unit = changeState(bgService.getState)
......@@ -352,7 +353,7 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
}
private def displayFragment(fragment: ToolbarFragment) {
getFragmentManager.beginTransaction().replace(R.id.fragment_holder, fragment).commitAllowingStateLoss()
getSupportFragmentManager.beginTransaction().replace(R.id.fragment_holder, fragment).commitAllowingStateLoss()
drawer.closeDrawer()
}
......@@ -387,7 +388,7 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
}
override def onBackPressed(): Unit = if (drawer.isDrawerOpen) drawer.closeDrawer() else {
val currentFragment = getFragmentManager.findFragmentById(R.id.fragment_holder).asInstanceOf[ToolbarFragment]
val currentFragment = getSupportFragmentManager.findFragmentById(R.id.fragment_holder).asInstanceOf[ToolbarFragment]
if (!currentFragment.onBackPressed())
if (currentFragment.isInstanceOf[ProfilesFragment]) super.onBackPressed()
else drawer.setSelection(DRAWER_PROFILES)
......
......@@ -23,20 +23,19 @@ package com.github.shadowsocks
import android.app.Activity
import android.content.{DialogInterface, Intent}
import android.os.Bundle
import android.support.v7.app.AlertDialog
import android.support.v7.app.{AlertDialog, AppCompatActivity}
import android.support.v7.widget.Toolbar
import com.github.shadowsocks.ShadowsocksApplication.app
import com.github.shadowsocks.plugin.PluginContract
import com.github.shadowsocks.utils.Key
object ProfileConfigActivity {
final val REQUEST_CODE_PLUGIN_HELP = 1
}
class ProfileConfigActivity extends Activity {
class ProfileConfigActivity extends AppCompatActivity {
import ProfileConfigActivity._
private lazy val child = getFragmentManager.findFragmentById(R.id.content).asInstanceOf[ProfileConfigFragment]
private lazy val child = getSupportFragmentManager.findFragmentById(R.id.content).asInstanceOf[ProfileConfigFragment]
override def onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState)
......
......@@ -30,18 +30,19 @@ import android.support.v7.preference.{Preference, PreferenceDataStore}
import android.support.v7.widget.Toolbar.OnMenuItemClickListener
import android.text.TextUtils
import android.view.MenuItem
import be.mygod.preference.{EditTextPreference, PreferenceFragment}
import com.github.shadowsocks.ShadowsocksApplication.app
import com.github.shadowsocks.database.Profile
import com.github.shadowsocks.plugin._
import com.github.shadowsocks.preference.{IconListPreference, OnPreferenceDataStoreChangeListener, PluginConfigurationDialogFragment}
import com.github.shadowsocks.utils.{Action, Key, Utils}
import com.github.shadowsocks.utils.{Action, Key}
import com.takisoft.fix.support.v7.preference.{EditTextPreference, PreferenceFragmentCompatDividers}
object ProfileConfigFragment {
private final val REQUEST_CODE_PLUGIN_CONFIGURE = 1
private final val FRAGMENT_DIALOG_TAG = "android.support.v7.preference.PreferenceFragment.DIALOG"
}
class ProfileConfigFragment extends PreferenceFragment with OnMenuItemClickListener
class ProfileConfigFragment extends PreferenceFragmentCompatDividers with OnMenuItemClickListener
with OnPreferenceDataStoreChangeListener {
import ProfileConfigFragment._
......@@ -51,7 +52,7 @@ class ProfileConfigFragment extends PreferenceFragment with OnMenuItemClickListe
private var pluginConfigure: EditTextPreference = _
private var pluginConfiguration: PluginConfiguration = _
override def onCreatePreferences(bundle: Bundle, key: String) {
override def onCreatePreferencesFix(bundle: Bundle, key: String) {
getPreferenceManager.setPreferenceDataStore(app.dataStore)
app.profileManager.getProfile(getActivity.getIntent.getIntExtra(Action.EXTRA_PROFILE_ID, -1)) match {
case Some(p) =>
......@@ -136,8 +137,12 @@ class ProfileConfigFragment extends PreferenceFragment with OnMenuItemClickListe
private def showPluginEditor() {
val bundle = new Bundle()
bundle.putString("key", Key.pluginConfigure)
bundle.putString(PluginConfigurationDialogFragment.PLUGIN_ID_FRAGMENT_TAG, pluginConfiguration.selected)
displayPreferenceDialog(Key.pluginConfigure, new PluginConfigurationDialogFragment, bundle)
val fragment = new PluginConfigurationDialogFragment
fragment.setArguments(bundle)
fragment.setTargetFragment(this, 0)
fragment.show(getFragmentManager, FRAGMENT_DIALOG_TAG)
}
override def onDisplayPreferenceDialog(preference: Preference): Unit = if (preference.getKey == Key.pluginConfigure) {
......
......@@ -23,8 +23,10 @@ package com.github.shadowsocks
import java.nio.charset.Charset
import android.app.Activity
import android.content.Context
import android.nfc.{NdefMessage, NdefRecord, NfcAdapter}
import android.os.Bundle
import android.support.v4.app.DialogFragment
import android.view.{LayoutInflater, View, ViewGroup}
import android.widget.{ImageView, LinearLayout}
import net.glxn.qrgen.android.QRCode
......@@ -57,11 +59,12 @@ final class QRCodeDialog extends DialogFragment {
image
}
override def onAttach(activity: Activity) {
superOnAttach(activity)
override def onAttach(context: Context) {
super.onAttach(context)
adapter = NfcAdapter.getDefaultAdapter(getActivity)
if (adapter != null) adapter.setNdefPushMessage(new NdefMessage(Array(
new NdefRecord(NdefRecord.TNF_ABSOLUTE_URI, nfcShareItem, Array[Byte](), nfcShareItem))), activity)
new NdefRecord(NdefRecord.TNF_ABSOLUTE_URI, nfcShareItem, Array[Byte](), nfcShareItem))),
context.asInstanceOf[Activity])
}
override def onDetach() {
......
......@@ -26,13 +26,14 @@ import android.content.pm.ShortcutManager
import android.os.{Build, Bundle}
import android.support.v4.content.pm.{ShortcutInfoCompat, ShortcutManagerCompat}
import android.support.v4.graphics.drawable.IconCompat
import android.support.v7.app.AppCompatActivity
import com.github.shadowsocks.bg.ServiceState
import com.github.shadowsocks.utils.Utils
/**
* @author Mygod
*/
class QuickToggleShortcut extends Activity with ServiceBoundContext {
class QuickToggleShortcut extends AppCompatActivity with ServiceBoundContext {
override def onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState)
getIntent.getAction match {
......
......@@ -20,8 +20,7 @@
package com.github.shadowsocks
import android.app.{Activity, TaskStackBuilder}
import android.content.Intent
import android.app.TaskStackBuilder
import android.content.pm.{PackageManager, ShortcutManager}
import android.os.{Build, Bundle}
import android.support.v4.app.ActivityCompat
......@@ -30,9 +29,9 @@ import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.Toolbar
import android.text.TextUtils
import android.widget.Toast
import com.google.zxing.Result
import com.github.shadowsocks.ShadowsocksApplication.app
import com.github.shadowsocks.utils.Parser
import com.google.zxing.Result
import me.dm7.barcodescanner.zxing.ZXingScannerView
object ScannerActivity {
......
......@@ -33,15 +33,16 @@ import android.support.v7.app.AppCompatDelegate
import android.util.Log
import com.evernote.android.job.JobManager
import com.github.shadowsocks.acl.DonaldTrump
import com.github.shadowsocks.bg.{BaseService, ProxyService, TransproxyService, VpnService}
import com.github.shadowsocks.bg.{ProxyService, TransproxyService, VpnService}
import com.github.shadowsocks.database.{DBHelper, Profile, ProfileManager}
import com.github.shadowsocks.preference.OrmLitePreferenceDataStore
import com.github.shadowsocks.preference.{BottomSheetPreferenceDialogFragment, IconListPreference, OrmLitePreferenceDataStore}
import com.github.shadowsocks.utils.CloseUtils._
import com.github.shadowsocks.utils._
import com.google.android.gms.analytics.{GoogleAnalytics, HitBuilders, StandardExceptionParser, Tracker}
import com.google.firebase.FirebaseApp
import com.google.firebase.remoteconfig.FirebaseRemoteConfig
import com.j256.ormlite.logger.LocalLog
import com.takisoft.fix.support.v7.preference.PreferenceFragmentCompat
import scala.collection.JavaConversions._
......@@ -156,6 +157,8 @@ class ShadowsocksApplication extends Application {
if (task.isSuccessful) remoteConfig.activateFetched() else Log.e(TAG, "Failed to fetch config"))
JobManager.create(this).addJobCreator(DonaldTrump)
PreferenceFragmentCompat.registerPreferenceFragment(classOf[IconListPreference],
classOf[BottomSheetPreferenceDialogFragment])
TcpFastOpen.enabled(dataStore.getBoolean(Key.tfo, TcpFastOpen.sendEnabled))
......
......@@ -20,8 +20,8 @@
package com.github.shadowsocks
import android.app.Fragment
import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v7.widget.Toolbar
import android.view.View
......
......@@ -26,7 +26,7 @@ import android.net.Uri
import android.util.Base64
import com.github.shadowsocks.plugin.PluginConfiguration
import com.github.shadowsocks.preference.OrmLitePreferenceDataStore
import com.github.shadowsocks.utils.Key
import com.github.shadowsocks.utils.{Key, Utils}
import com.j256.ormlite.field.{DataType, DatabaseField}
class Profile {
......@@ -106,7 +106,7 @@ class Profile {
def serialize(store: OrmLitePreferenceDataStore) {
store.putString(Key.name, name)
store.putString(Key.host, host)
store.putInt(Key.remotePort, remotePort)
store.putString(Key.remotePort, remotePort.toString)
store.putString(Key.password, password)
store.putString(Key.route, route)
store.putString(Key.remoteDns, remoteDns)
......@@ -123,7 +123,7 @@ class Profile {
// It's assumed that default values are never used, so 0/false/null is always used even if that isn't the case
name = store.getString(Key.name, null)
host = store.getString(Key.host, null)
remotePort = store.getInt(Key.remotePort, 0)
remotePort = Utils.parsePort(store.getString(Key.remotePort), 8388, 1)
password = store.getString(Key.password, null)
method = store.getString(Key.method, null)
route = store.getString(Key.route, null)
......
......@@ -7,7 +7,7 @@ import android.net.Uri
import android.os.Bundle
import android.provider.Settings
import android.support.design.widget.BottomSheetDialog
import android.support.v14.preference.PreferenceDialogFragment
import android.support.v7.preference.PreferenceDialogFragmentCompat
import android.support.v7.widget.RecyclerView.ViewHolder
import android.support.v7.widget.{LinearLayoutManager, RecyclerView}
import android.view.ViewGroup.LayoutParams
......@@ -18,7 +18,7 @@ import com.github.shadowsocks.R
/**
* @author Mygod
*/
final class BottomSheetPreferenceDialogFragment extends PreferenceDialogFragment {
final class BottomSheetPreferenceDialogFragment extends PreferenceDialogFragmentCompat {
private lazy val preference = getPreference.asInstanceOf[IconListPreference]
private lazy val index: Int = preference.selectedEntry
private lazy val entries: Array[CharSequence] = preference.getEntries
......
......@@ -6,15 +6,11 @@ import android.support.annotation.ArrayRes
import android.support.v7.preference.ListPreference
import android.support.v7.preference.Preference.OnPreferenceChangeListener
import android.util.AttributeSet
import be.mygod.preference.DialogPreferencePlus
/**
* @author Mygod
*/
class IconListPreference(context: Context, attrs: AttributeSet = null) extends ListPreference(context, attrs)
with DialogPreferencePlus {
override def createDialog() = new BottomSheetPreferenceDialogFragment()
class IconListPreference(context: Context, attrs: AttributeSet = null) extends ListPreference(context, attrs) {
private var mEntryIcons: Array[Drawable] = _
def selectedEntry: Int = getEntryValues.indexOf(getValue)
......
......@@ -6,7 +6,7 @@ import java.util
import android.support.v7.preference.PreferenceDataStore
import com.github.shadowsocks.database.{DBHelper, KeyValuePair}
import com.github.shadowsocks.utils.Key
import com.github.shadowsocks.utils.{Key, Utils}
import scala.collection.JavaConversions._
......@@ -98,12 +98,19 @@ final class OrmLitePreferenceDataStore(dbHelper: DBHelper) extends PreferenceDat
def registerChangeListener(listener: OnPreferenceDataStoreChangeListener): Unit = listeners += listener
def unregisterChangeListener(listener: OnPreferenceDataStoreChangeListener): Unit = listeners -= listener
private def getLocalPort(key: String) = getInt(key, 0) match {
case 0 => Utils.parsePort(getString(key), 0)
case value =>
putString(key, value.toString)
value
}
def profileId: Int = getInt(Key.id, 0)
def profileId_=(i: Int): Unit = putInt(Key.id, i)
def serviceMode: String = getString(Key.serviceMode, Key.modeVpn)
def portProxy: Int = getInt(Key.portProxy, 0)
def portLocalDns: Int = getInt(Key.portLocalDns, 0)
def portTransproxy: Int = getInt(Key.portTransproxy, 0)
def portProxy: Int = getLocalPort(Key.portProxy)
def portLocalDns: Int = getLocalPort(Key.portLocalDns)
def portTransproxy: Int = getLocalPort(Key.portTransproxy)
def proxyApps: Boolean = getBoolean(Key.proxyApps)
def proxyApps_=(value: Boolean): Unit = putBoolean(Key.proxyApps, value)
......
......@@ -20,9 +20,11 @@
package com.github.shadowsocks.preference
import android.app.AlertDialog
import android.content.DialogInterface
import be.mygod.preference.EditTextPreferenceDialogFragment
import android.support.v7.app.AlertDialog
import android.support.v7.preference.EditTextPreferenceDialogFragmentCompat
import android.view.View
import android.widget.EditText
import com.github.shadowsocks.ProfileConfigActivity
import com.github.shadowsocks.plugin.{PluginContract, PluginManager}
......@@ -33,9 +35,11 @@ object PluginConfigurationDialogFragment {
final val PLUGIN_ID_FRAGMENT_TAG = "com.github.shadowsocks.preference.PluginConfigurationDialogFragment.PLUGIN_ID"
}
class PluginConfigurationDialogFragment extends EditTextPreferenceDialogFragment {
class PluginConfigurationDialogFragment extends EditTextPreferenceDialogFragmentCompat {
import PluginConfigurationDialogFragment._
private var editText: EditText = _
override def onPrepareDialogBuilder(builder: AlertDialog.Builder) {
super.onPrepareDialogBuilder(builder)
val intent = PluginManager.buildIntent(getArguments.getString(PLUGIN_ID_FRAGMENT_TAG), PluginContract.ACTION_HELP)
......@@ -43,4 +47,9 @@ class PluginConfigurationDialogFragment extends EditTextPreferenceDialogFragment
getActivity.startActivityForResult(intent.putExtra(PluginContract.EXTRA_OPTIONS, editText.getText.toString),
ProfileConfigActivity.REQUEST_CODE_PLUGIN_HELP)): DialogInterface.OnClickListener)
}
override def onBindDialogView(view: View) {
super.onBindDialogView(view)
editText = view.findViewById(android.R.id.edit)
}
}
......@@ -152,4 +152,12 @@ object Utils {
}
def ThrowableFuture[T](f: => T): Unit = Future(f) onComplete handleFailure
def parsePort(str: String, default: Int, min: Int = 1025): Int = (try str.toInt catch {
case _: NumberFormatException => default
}) match {
case x if x < min => default
case x if x > 65535 => default
case x => x
}
}
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2015 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="48dp"
android:layout_marginBottom="48dp"
android:overScrollMode="ifContentScrolls">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="?attr/dialogPreferredPadding"
android:paddingEnd="?attr/dialogPreferredPadding"
android:orientation="vertical">
<TextView android:id="@android:id/message"
style="@style/TextAppearance.AppCompat.Subhead"
android:layout_marginBottom="48dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="?android:attr/textColorSecondary" />
<EditText
android:id="@android:id/edit"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</ScrollView>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.Shadowsocks" parent="Theme.ShadowsocksBase"/>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="NumberPickerPreference">
<attr name="max" format="integer" />
<attr name="min" format="integer" />
</declare-styleable>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.Shadowsocks.Material" parent="Theme.AppCompat.Light.NoActionBar"/>
<style name="Theme.ShadowsocksBase" parent="Theme.Shadowsocks.Material">
<style name="Theme.Shadowsocks" parent="PreferenceFixTheme.Light.NoActionBar">
<!--suppress NewApi -->
<item name="android:navigationBarColor">@color/material_primary_700</item>
<item name="colorAccent">@color/material_accent_200</item>
<item name="colorButtonNormal">@color/material_accent_200</item>
<item name="colorPrimary">@color/material_primary_500</item>
<item name="colorPrimaryDark">@color/material_primary_700</item>
<item name="preferenceTheme">@style/Theme.Shadowsocks.PreferenceThemeOverlay</item>
</style>
<style name="Theme.Shadowsocks.Dialog" parent="Theme.AppCompat.Light.Dialog">
<item name="colorAccent">@color/material_primary_500</item>
<item name="colorButtonNormal">@color/material_accent_200</item>
<item name="colorPrimary">@color/material_primary_500</item>
<item name="colorPrimaryDark">@color/material_primary_700</item>
<item name="preferenceTheme">@style/Theme.Shadowsocks.PreferenceThemeOverlay</item>
<item name="windowNoTitle">true</item>
</style>
<!-- Fix for wrong padding on v17-v20 based on: https://github.com/Gericop/Android-Support-Preference-V7-Fix/blob/a6082cb/preference-v7/src/main/res/values-v17/styles.xml -->
<style name="Theme.Shadowsocks" parent="Theme.ShadowsocksBase">
<item name="android:listPreferredItemPaddingStart">16dp</item>
<item name="android:listPreferredItemPaddingEnd">16dp</item>
</style>
<!-- This is needed because the default PreferenceCategory appearance doesn't use bold text style -->
<style name="Preference_TextAppearanceMaterialBody2">
<item name="android:textSize">14sp</item>
<item name="android:fontFamily">sans-serif-medium</item>
<item name="android:textStyle">bold</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
<style name="Theme.Shadowsocks.PreferenceThemeOverlay" parent="@style/PreferenceThemeOverlay.v14.Material">
<item name="preferenceFragmentListStyle">@style/Theme.Shadowsocks.PreferenceFragmentList</item>
</style>
<style name="Theme.Shadowsocks.PreferenceFragmentList" parent="@style/PreferenceFragmentList.Material">
<item name="android:fadeScrollbars">true</item>
<item name="android:scrollbars">vertical</item>
</style>
<style name="NumberPickerStyle">
<item name="android:textSize">18sp</item>
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material.Fix</item>
<item name="preferenceCategory_marginBottom">8dp</item>
</style>
</resources>
/*******************************************************************************/
/* */
/* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> */
/* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> */
/* */
/* This program is free software: you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation, either version 3 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* */
/*******************************************************************************/
package be.mygod.preference
import android.app.DialogFragment
import android.support.v7.preference.DialogPreference
trait DialogPreferencePlus extends DialogPreference {
def createDialog(): DialogFragment
}
/*******************************************************************************/
/* */
/* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> */
/* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> */
/* */
/* This program is free software: you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation, either version 3 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* */
/*******************************************************************************/
package be.mygod.preference
import android.content.Context
import android.support.v7.preference.{EditTextPreference => Parent}
import android.support.v7.widget.AppCompatEditText
import android.text.InputType
import android.util.AttributeSet
import android.view.ViewGroup
import android.widget.FrameLayout
import com.github.shadowsocks.plugin.R
/**
* Fixed EditTextPreference + SummaryPreference with password support!
* Based on: https://github.com/Gericop/Android-Support-Preference-V7-Fix/tree/master/app/src/main/java/android/support/v7/preference
*/
class EditTextPreference(context: Context, attrs: AttributeSet = null) extends Parent(context, attrs)
with DialogPreferencePlus with SummaryPreference {
val editText = new AppCompatEditText(context, attrs)
editText.setId(android.R.id.edit)
{
val arr = context.obtainStyledAttributes(Array(R.attr.dialogPreferredPadding))
val margin = arr.getDimensionPixelOffset(0, 0)
arr.recycle()
val params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
params.setMargins(margin, 0, margin, 0)
editText.setLayoutParams(params)
}
override def createDialog() = new EditTextPreferenceDialogFragment()
override protected def getSummaryValue: String = {
var text = getText
if (text == null) text = ""
val inputType = editText.getInputType
if (inputType == (InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD) ||
inputType == (InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD) ||
inputType == (InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD))
"\u2022" * text.length else text
}
override def setText(text: String): Unit = {
val old = getText
super.setText(text)
if (old != text) notifyChanged()
}
}
/*******************************************************************************/
/* */
/* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> */
/* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> */
/* */
/* This program is free software: you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation, either version 3 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* */
/*******************************************************************************/
package be.mygod.preference
import android.support.v14.preference.PreferenceDialogFragment
import android.support.v7.widget.AppCompatEditText
import android.view.{View, ViewGroup}
class EditTextPreferenceDialogFragment extends PreferenceDialogFragment {
private lazy val preference = getPreference.asInstanceOf[EditTextPreference]
protected lazy val editText: AppCompatEditText = preference.editText
override protected def onBindDialogView(view: View) {
super.onBindDialogView(view)
editText.setText(preference.getText)
val text = editText.getText
if (text != null) editText.setSelection(0, text.length)
val oldParent = editText.getParent.asInstanceOf[ViewGroup]
if (oldParent eq view) return
if (oldParent != null) oldParent.removeView(editText)
val oldEdit = view.findViewById[View](android.R.id.edit)
if (oldEdit == null) return
val container = oldEdit.getParent.asInstanceOf[ViewGroup]
if (container == null) return
container.removeView(oldEdit)
container.addView(editText, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
}
override protected def needInputMethod = true
def onDialogClosed(positiveResult: Boolean): Unit = if (positiveResult) {
val value = editText.getText.toString
if (preference.callChangeListener(value)) preference.setText(value)
}
}
/*******************************************************************************/
/* */
/* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> */
/* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> */
/* */
/* This program is free software: you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation, either version 3 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* */
/*******************************************************************************/
package be.mygod.preference
import android.content.Context
import android.content.res.TypedArray
import android.support.v7.preference.DialogPreference
import android.util.AttributeSet
import android.view.ContextThemeWrapper
import android.widget.NumberPicker
import com.github.shadowsocks.plugin.R
class NumberPickerPreference(private val context: Context, attrs: AttributeSet = null)
extends DialogPreference(context, attrs) with DialogPreferencePlus with SummaryPreference {
private[preference] val picker = new NumberPicker(new ContextThemeWrapper(context, R.style.NumberPickerStyle))
private var value: Int = _
{
val a: TypedArray = context.obtainStyledAttributes(attrs, R.styleable.NumberPickerPreference)
setMin(a.getInt(R.styleable.NumberPickerPreference_min, 0))
setMax(a.getInt(R.styleable.NumberPickerPreference_max, Int.MaxValue - 1))
a.recycle()
}
override def createDialog() = new NumberPickerPreferenceDialogFragment()
def getValue: Int = value
def getMin: Int = if (picker == null) 0 else picker.getMinValue
def getMax: Int = picker.getMaxValue
def setValue(i: Int) {
if (i == value) return
picker.setValue(i)
value = picker.getValue
persistInt(value)
notifyChanged()
}
def setMin(value: Int): Unit = picker.setMinValue(value)
def setMax(value: Int): Unit = picker.setMaxValue(value)
override protected def onGetDefaultValue(a: TypedArray, index: Int): AnyRef =
a.getInt(index, getMin).asInstanceOf[AnyRef]
override protected def onSetInitialValue(restorePersistedValue: Boolean, defaultValue: Any) {
val default = defaultValue.asInstanceOf[Int]
setValue(if (restorePersistedValue) getPersistedInt(default) else default)
}
protected def getSummaryValue: AnyRef = getValue.asInstanceOf[AnyRef]
}
/*******************************************************************************/
/* */
/* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> */
/* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> */
/* */
/* This program is free software: you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation, either version 3 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* */
/*******************************************************************************/
package be.mygod.preference
import android.content.Context
import android.support.v14.preference.PreferenceDialogFragment
import android.view.{View, ViewGroup}
import android.widget.NumberPicker
class NumberPickerPreferenceDialogFragment extends PreferenceDialogFragment {
private lazy val preference = getPreference.asInstanceOf[NumberPickerPreference]
private lazy val picker = preference.picker
override protected def onCreateDialogView(context: Context): NumberPicker = {
val parent = picker.getParent.asInstanceOf[ViewGroup]
if (parent != null) parent.removeView(picker)
picker
}
override protected def onBindDialogView(view: View) {
super.onBindDialogView(view)
picker.setValue(preference.getValue)
}
override protected def needInputMethod = true
def onDialogClosed(positiveResult: Boolean) {
picker.clearFocus() // commit changes
if (positiveResult) {
val value = picker.getValue
if (preference.callChangeListener(value)) preference.setValue(value)
}
}
}
/*******************************************************************************/
/* */
/* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> */
/* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> */
/* */
/* This program is free software: you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation, either version 3 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* */
/*******************************************************************************/
package be.mygod.preference
import android.content.Context
import android.support.v7.preference.{PreferenceViewHolder, PreferenceCategory => Base}
import android.util.AttributeSet
import android.view.ViewGroup.MarginLayoutParams
/**
* Based on: https://github.com/Gericop/Android-Support-Preference-V7-Fix/blob/4c2bb2896895dbedb37b659b36bd2a96b33c1605/preference-v7/src/main/java/com/takisoft/fix/support/v7/preference/PreferenceCategory.java
*
* @author Mygod
*/
class PreferenceCategory(context: Context, attrs: AttributeSet = null) extends Base(context, attrs) {
override def onBindViewHolder(holder: PreferenceViewHolder) {
super.onBindViewHolder(holder)
holder.findViewById(android.R.id.title).getLayoutParams.asInstanceOf[MarginLayoutParams].bottomMargin = 0
}
}
/*******************************************************************************/
/* */
/* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> */
/* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> */
/* */
/* This program is free software: you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation, either version 3 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* */
/*******************************************************************************/
package be.mygod.preference
import android.app.DialogFragment
import android.os.Bundle
import android.support.v14.preference.{PreferenceFragment => Base}
import android.support.v7.preference.{Preference, PreferenceScreen}
import android.view.{LayoutInflater, View, ViewGroup}
abstract class PreferenceFragment extends Base {
override def onCreateView(inflater: LayoutInflater, container: ViewGroup, savedInstanceState: Bundle): View =
super.onCreateView(inflater, container, savedInstanceState)
protected final def displayPreferenceDialog(key: String, fragment: DialogFragment, other: Bundle = null) {
val bundle = new Bundle(1)
bundle.putString("key", key)
if (other != null) bundle.putAll(other)
fragment.setArguments(bundle)
fragment.setTargetFragment(this, 0)
getFragmentManager.beginTransaction()
.add(fragment, "android.support.v14.preference.PreferenceFragment.DIALOG")
.commitAllowingStateLoss()
}
override def onDisplayPreferenceDialog(preference: Preference): Unit = preference match {
case dpp: DialogPreferencePlus => displayPreferenceDialog(preference.getKey, dpp.createDialog())
case _ => super.onDisplayPreferenceDialog(preference)
}
override protected def onCreateAdapter(screen: PreferenceScreen) = new PreferenceGroupAdapter(screen)
override def onResume() {
super.onResume()
getListView.scrollBy(0, 0)
}
}
/*******************************************************************************/
/* */
/* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> */
/* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> */
/* */
/* This program is free software: you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation, either version 3 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* */
/*******************************************************************************/
package be.mygod.preference
import java.lang.reflect.Field
import java.util
import android.os.Build
import android.support.v4.content.ContextCompat
import android.support.v4.view.ViewCompat
import android.support.v7.preference.{PreferenceGroup, PreferenceViewHolder, PreferenceGroupAdapter => Old}
import android.view.{LayoutInflater, View, ViewGroup}
import com.github.shadowsocks.plugin.R
/**
* Fix by: https://github.com/Gericop/Android-Support-Preference-V7-Fix/commit/7de016b007e28a264001a8bb353f110a7f64bb69
*
* @author Mygod
*/
object PreferenceGroupAdapter {
private var preferenceLayoutsField: Field = _
private var fieldResId: Field = _
private var fieldWidgetResId: Field = _
private val preferenceViewHolderConstructor = classOf[PreferenceViewHolder].getDeclaredConstructor(classOf[View])
{
val oldClass = classOf[Old]
preferenceLayoutsField = oldClass.getDeclaredField("mPreferenceLayouts")
preferenceLayoutsField.setAccessible(true)
val c = oldClass.getDeclaredClasses.filter(c => c.getSimpleName == "PreferenceLayout").head
fieldResId = c.getDeclaredField("resId")
fieldResId.setAccessible(true)
fieldWidgetResId = c.getDeclaredField("widgetResId")
fieldWidgetResId.setAccessible(true)
preferenceViewHolderConstructor.setAccessible(true)
}
}
class PreferenceGroupAdapter(group: PreferenceGroup) extends Old(group) {
import PreferenceGroupAdapter._
protected lazy val preferenceLayouts: util.List[AnyRef] =
preferenceLayoutsField.get(this).asInstanceOf[util.List[AnyRef]]
override def onCreateViewHolder(parent: ViewGroup, viewType: Int): PreferenceViewHolder =
if (Build.VERSION.SDK_INT < 21) {
val context = parent.getContext
val inflater = LayoutInflater.from(context)
val pl = preferenceLayouts.get(viewType)
val view = inflater.inflate(fieldResId.get(pl).asInstanceOf[Int], parent, false)
if (view.getBackground == null) {
val array = context.obtainStyledAttributes(null, R.styleable.BackgroundStyle)
var background = array.getDrawable(R.styleable.BackgroundStyle_android_selectableItemBackground)
if (background == null)
background = ContextCompat.getDrawable(context, android.R.drawable.list_selector_background)
array.recycle()
val (s, t, e, b) = (ViewCompat.getPaddingStart(view), view.getPaddingTop,
ViewCompat.getPaddingEnd(view), view.getPaddingBottom)
view.setBackground(background)
ViewCompat.setPaddingRelative(view, s, t, e, b)
}
val widgetFrame = view.findViewById[ViewGroup](android.R.id.widget_frame)
if (widgetFrame != null) {
val widgetResId = fieldWidgetResId.get(pl).asInstanceOf[Int]
if (widgetResId != 0) inflater.inflate(widgetResId, widgetFrame) else widgetFrame.setVisibility(View.GONE)
}
preferenceViewHolderConstructor.newInstance(view)
} else super.onCreateViewHolder(parent, viewType)
}
/*******************************************************************************/
/* */
/* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> */
/* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> */
/* */
/* This program is free software: you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation, either version 3 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* */
/*******************************************************************************/
package be.mygod.preference
import android.support.v7.preference.Preference
/**
* Make your preference support %s in summary. Override getSummaryValue to customize what to put in.
* Based on:
* https://github.com/android/platform_frameworks_base/blob/master/core/java/android/preference/ListPreference.java
*
* @author Mygod
*/
trait SummaryPreference extends Preference {
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: String = {
val summary = super.getSummary
if (summary == null) null else String.format(summary.toString, getSummaryValue)
}
}
package com.github.shadowsocks.plugin
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.widget.Toast
/**
......@@ -10,7 +10,7 @@ import android.widget.Toast
*
* @author Mygod
*/
trait OptionsCapableActivity extends Activity {
trait OptionsCapableActivity extends AppCompatActivity {
protected def pluginOptions(intent: Intent = getIntent): PluginOptions =
try new PluginOptions(intent.getStringExtra(PluginContract.EXTRA_OPTIONS)) catch {
case exc: IllegalArgumentException =>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment