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.
This commit is contained in:
+2
-3
@@ -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
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
<?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>
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
<?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)
|
||||
|
||||
+2
-2
@@ -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)
|
||||
|
||||
|
||||
+11
-4
@@ -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)
|
||||
|
||||
+12
-3
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
<?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>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="Theme.Shadowsocks" parent="Theme.ShadowsocksBase"/>
|
||||
</resources>
|
||||
@@ -1,7 +0,0 @@
|
||||
<?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>
|
||||
@@ -1,45 +1,13 @@
|
||||
<?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>
|
||||
|
||||
@@ -1,28 +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 android.app.DialogFragment
|
||||
import android.support.v7.preference.DialogPreference
|
||||
|
||||
trait DialogPreferencePlus extends DialogPreference {
|
||||
def createDialog(): DialogFragment
|
||||
}
|
||||
@@ -1,67 +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 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()
|
||||
}
|
||||
}
|
||||
@@ -1,53 +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 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)
|
||||
}
|
||||
}
|
||||
@@ -1,65 +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 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]
|
||||
}
|
||||
@@ -1,52 +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 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,38 +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 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
|
||||
}
|
||||
}
|
||||
@@ -1,55 +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 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)
|
||||
}
|
||||
}
|
||||
@@ -1,87 +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)
|
||||
}
|
||||
@@ -1,45 +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 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)
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
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 =>
|
||||
|
||||
Reference in New Issue
Block a user