Commit 54d95eae authored by Max Lv's avatar Max Lv

Merge pull request #413 from Mygod/master

Add more material design (2)
parents 9569323c 4906ce8a
......@@ -37,6 +37,7 @@ libraryDependencies ++= Seq(
"com.google.android.gms" % "play-services-analytics" % "8.1.0",
"com.android.support" % "support-v4" % "23.1.0",
"com.android.support" % "design" % "23.1.0",
"com.github.jorgecastilloprz" % "fabprogresscircle" % "1.01",
"com.nostra13.universalimageloader" % "universal-image-loader" % "1.9.4",
"com.j256.ormlite" % "ormlite-core" % "4.48",
"com.j256.ormlite" % "ormlite-android" % "4.48"
......
src/main/res/drawable-hdpi/ic_drawer.png

963 Bytes | W: | H:

src/main/res/drawable-hdpi/ic_drawer.png

2.8 KB | W: | H:

src/main/res/drawable-hdpi/ic_drawer.png
src/main/res/drawable-hdpi/ic_drawer.png
src/main/res/drawable-hdpi/ic_drawer.png
src/main/res/drawable-hdpi/ic_drawer.png
  • 2-up
  • Swipe
  • Onion skin
src/main/res/drawable-mdpi/ic_drawer.png

955 Bytes | W: | H:

src/main/res/drawable-mdpi/ic_drawer.png

2.79 KB | W: | H:

src/main/res/drawable-mdpi/ic_drawer.png
src/main/res/drawable-mdpi/ic_drawer.png
src/main/res/drawable-mdpi/ic_drawer.png
src/main/res/drawable-mdpi/ic_drawer.png
  • 2-up
  • Swipe
  • Onion skin
src/main/res/drawable-xhdpi/ic_drawer.png

979 Bytes | W: | H:

src/main/res/drawable-xhdpi/ic_drawer.png

2.84 KB | W: | H:

src/main/res/drawable-xhdpi/ic_drawer.png
src/main/res/drawable-xhdpi/ic_drawer.png
src/main/res/drawable-xhdpi/ic_drawer.png
src/main/res/drawable-xhdpi/ic_drawer.png
  • 2-up
  • Swipe
  • Onion skin
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:duplicateParentState="false">
<include layout="@layout/toolbar_light_dark" />
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:layout_marginBottom="8dp">
<include layout="@layout/toolbar_light_dark"/>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingBottom="8dp"
android:paddingLeft="48dp"
android:paddingStart="48dp"
android:paddingTop="8dp"
android:background="@android:color/white"
android:elevation="1dp" tools:ignore="RtlSymmetry">
<TextView
android:id="@+id/bypassLabel"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:padding="3dp"
android:gravity="center_vertical"
android:layout_marginLeft="16dp"
android:textSize="18sp"
android:text="@string/bypass_apps"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"/>
android:text="@string/bypass_apps"/>
<Switch
android:id="@+id/bypassSwitch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="16dp"/>
</RelativeLayout>
android:layout_marginEnd="6dp"
android:layout_marginRight="6dp"/>
</LinearLayout>
<ListView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
......
......@@ -24,7 +24,7 @@
android:textSize="18sp"
android:padding="3dp"/>
<CheckBox
<Switch
android:id="@+id/itemcheck"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
......
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_height="match_parent">
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_height="match_parent"
tools:ignore="UnknownIdInLayout" android:clipChildren="false">
<include layout="@layout/toolbar_light_dark" />
<fragment android:name="com.github.shadowsocks.ShadowsocksSettings"
android:id="@android:id/content" android:layout_below="@+id/toolbar"
android:layout_width="match_parent" android:layout_height="match_parent" />
<android.support.design.widget.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:backgroundTint="@color/material_blue_500"
android:src="@drawable/ic_cloud_off"
android:layout_gravity="end|bottom"
android:layout_alignBottom="@+id/toolbar"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginRight="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="-28dp" app:elevation="6dp"
app:pressedTranslationZ="6dp" app:borderWidth="0dp" />
android:layout_width="match_parent" android:layout_height="match_parent"/>
<com.github.jorgecastilloprz.FABProgressCircle android:id="@+id/fabProgressCircle" app:reusable="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:elevation="6dp"
android:layout_alignBottom="@+id/toolbar"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginRight="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="@dimen/fab_margin_bottom">
<android.support.design.widget.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:backgroundTint="@color/material_green_600"
android:src="@drawable/ic_cloud_off"
app:pressedTranslationZ="6dp" app:borderWidth="0dp" />
</com.github.jorgecastilloprz.FABProgressCircle>
</RelativeLayout>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="fab_margin_bottom">-28dp</dimen>
</resources>
......@@ -26,7 +26,6 @@
<string name="auto_set_gfwlist">国内路由</string>
<string name="auto_set_gfwlist_summary">绕过所有位于中国的网站</string>
<string name="route_list">路由</string>
<string name="route_list_summary">绕过指定路由列表中的地址</string>
<string name="auto_set_proxy">全局代理</string>
<string name="auto_set_proxy_summary">设置系统代理</string>
<string name="proxied_apps">分应用代理</string>
......@@ -54,7 +53,7 @@
<string name="port_empty">端口号不能为空</string>
<string name="proxy_empty">代理服务器地址不能为空</string>
<string name="crash_alert">检测到意外退出,状态已被重置。</string>
<string name="connecting">正在连接...</string>
<string name="connect">连接</string>
<string name="initializing">正在初始化...</string>
<string name="recovering">重置中...</string>
<string name="loading">正在加载...</string>
......
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="SummaryPreference">
<attr name="summary" format="string" />
</declare-styleable>
<declare-styleable name="DropDownPreference">
<attr name="entries" format="reference" />
<attr name="entryValues" format="reference" />
</declare-styleable>
</resources>
\ No newline at end of file
......@@ -12,7 +12,7 @@
<color name="md__transparent">#00000000</color>
<color name="md__defaultBackground">#FF555555</color>
<color name="material_blue_500">#2196F3</color>
<color name="material_green_600">#43A047</color>
<color name="material_blue_grey_300">#90A4AE</color>
<color name="material_blue_grey_500">#607D8B</color>
<color name="material_blue_grey_700">#455A64</color>
......
<?xml version="1.0" encoding="utf-8"?>
<!-- File created by the Android Action Bar Style Generator
Copyright (C) 2011 The Android Open Source Project
Copyright (C) 2012 readyState Software Ltd
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.
-->
<resources>
<color name="pressed_shadow">#CC33B5E5</color>
<color name="title">#EEE</color>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- TODO: Remove when this issue is fixed: https://github.com/JorgeCastilloPrz/FABProgressCircle/issues/19 -->
<dimen name="fab_margin_bottom">-44dp</dimen>
</resources>
......@@ -37,7 +37,6 @@
<string name="auto_set_gfwlist_summary">Bypass all sites located in China
</string>
<string name="route_list">Route</string>
<string name="route_list_summary">Bypass IP addresses in the route list</string>
<string name="auto_set_proxy">Global Proxy</string>
<string name="auto_set_proxy_summary">Set up system-wide proxy</string>
<string name="proxied_apps">Per-App Proxy</string>
......@@ -75,7 +74,7 @@
<string name="proxy_empty">Proxy should not be empty</string>
<string name="crash_alert">An unexpected exit detected, the context has been reset.
</string>
<string name="connecting">Connecting…</string>
<string name="connect">Connect</string>
<string name="initializing">Initializing…</string>
<string name="recovering">Resetting…</string>
<string name="loading">Loading…</string>
......
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/com.github.shadowsocks">
<PreferenceCategory
android:title="@string/proxy_cat">
......@@ -37,40 +38,41 @@
android:summary="@string/sitekey_summary"
android:title="@string/sitekey">
</com.github.shadowsocks.preferences.PasswordEditTextPreference>
<com.github.shadowsocks.preferences.SummaryListPreference
<com.github.shadowsocks.preferences.DropDownPreference
android:defaultValue="rc4"
android:key="encMethod"
android:entries="@array/enc_method_entry"
android:entryValues="@array/enc_method_value"
app:entries="@array/enc_method_entry"
app:entryValues="@array/enc_method_value"
app:summary="%s"
android:title="@string/enc_method">
</com.github.shadowsocks.preferences.SummaryListPreference>
</com.github.shadowsocks.preferences.DropDownPreference>
</PreferenceCategory>
<PreferenceCategory
android:title="@string/feature_cat">
<com.github.shadowsocks.preferences.SummaryListPreference
<com.github.shadowsocks.preferences.DropDownPreference
android:defaultValue="all"
android:key="route"
android:entries="@array/route_entry"
android:entryValues="@array/route_value"
android:summary="@string/route_list_summary"
app:entries="@array/route_entry"
app:entryValues="@array/route_value"
app:summary="%s"
android:title="@string/route_list">
</com.github.shadowsocks.preferences.SummaryListPreference>
<CheckBoxPreference
</com.github.shadowsocks.preferences.DropDownPreference>
<SwitchPreference
android:key="isIpv6"
android:defaultValue="false"
android:summary="@string/ipv6_summary"
android:title="@string/ipv6">
</CheckBoxPreference>
<CheckBoxPreference
</SwitchPreference>
<SwitchPreference
android:defaultValue="true"
android:key="isGlobalProxy"
android:disableDependentsState="true"
android:summary="@string/auto_set_proxy_summary"
android:title="@string/auto_set_proxy">
</CheckBoxPreference>
</SwitchPreference>
<Preference
android:key="proxyedApps"
android:dependency="isGlobalProxy"
......@@ -78,23 +80,23 @@
android:title="@string/proxied_apps">
<intent android:action="com.github.shadowsocks.AppManager"/>
</Preference>
<CheckBoxPreference
<SwitchPreference
android:key="isUdpDns"
android:defaultValue="false"
android:summary="@string/udp_dns_summary"
android:title="@string/udp_dns">
</CheckBoxPreference>
<CheckBoxPreference
</SwitchPreference>
<SwitchPreference
android:key="isAuth"
android:defaultValue="false"
android:summary="@string/onetime_auth_summary"
android:title="@string/onetime_auth">
</CheckBoxPreference>
<CheckBoxPreference
</SwitchPreference>
<SwitchPreference
android:key="isAutoConnect"
android:summary="@string/auto_connect_summary"
android:title="@string/auto_connect">
</CheckBoxPreference>
</SwitchPreference>
</PreferenceCategory>
......
......@@ -78,7 +78,7 @@ class ObjectArrayTools[T <: AnyRef](a: Array[T]) {
}
}
case class ListEntry(box: CheckBox, text: TextView, icon: ImageView)
case class ListEntry(switch: Switch, text: TextView, icon: ImageView)
object AppManager {
......@@ -162,13 +162,13 @@ class AppManager extends Activity with OnCheckedChangeListener with OnClickListe
if (convertView == null) {
convertView = getLayoutInflater.inflate(R.layout.layout_apps_item, parent, false)
val icon = convertView.findViewById(R.id.itemicon).asInstanceOf[ImageView]
val box = convertView.findViewById(R.id.itemcheck).asInstanceOf[CheckBox]
val switch = convertView.findViewById(R.id.itemcheck).asInstanceOf[Switch]
val text = convertView.findViewById(R.id.itemtext).asInstanceOf[TextView]
entry = new ListEntry(box, text, icon)
entry = new ListEntry(switch, text, icon)
entry.text.setOnClickListener(AppManager.this)
entry.text.setOnClickListener(AppManager.this)
convertView.setTag(entry)
entry.box.setOnCheckedChangeListener(AppManager.this)
entry.switch.setOnCheckedChangeListener(AppManager.this)
} else {
entry = convertView.getTag.asInstanceOf[ListEntry]
}
......@@ -187,10 +187,10 @@ class AppManager extends Activity with OnCheckedChangeListener with OnClickListe
ImageLoader.getInstance().displayImage(Scheme.APP + app.packageName, entry.icon, options)
entry.text.setText(app.name)
val box: CheckBox = entry.box
box.setTag(app)
box.setChecked(app.proxied)
entry.text.setTag(box)
val switch = entry.switch
switch.setTag(app)
switch.setChecked(app.proxied)
entry.text.setTag(switch)
convertView
}
}
......@@ -207,11 +207,11 @@ class AppManager extends Activity with OnCheckedChangeListener with OnClickListe
}
def onClick(v: View) {
val cbox: CheckBox = v.getTag.asInstanceOf[CheckBox]
val app: ProxiedApp = cbox.getTag.asInstanceOf[ProxiedApp]
val switch = v.getTag.asInstanceOf[Switch]
val app: ProxiedApp = switch.getTag.asInstanceOf[ProxiedApp]
if (app != null) {
app.proxied = !app.proxied
cbox.setChecked(app.proxied)
switch.setChecked(app.proxied)
}
saveAppSettings(this)
}
......@@ -247,8 +247,8 @@ class AppManager extends Activity with OnCheckedChangeListener with OnClickListe
val array = proxiedAppString.split(" ")
val bypass = array(0).toBoolean
val apps = if (array.size > 1) array(1) else ""
prefs.edit.putBoolean(Key.isBypassApps, bypass).commit()
prefs.edit.putString(Key.proxied, apps).commit()
prefs.edit.putBoolean(Key.isBypassApps, bypass).apply()
prefs.edit.putString(Key.proxied, apps).apply()
Toast.makeText(this, R.string.action_import_msg, Toast.LENGTH_SHORT).show()
// Restart activity
val intent = getIntent
......@@ -299,7 +299,7 @@ class AppManager extends Activity with OnCheckedChangeListener with OnClickListe
val bypassSwitch = findViewById(R.id.bypassSwitch).asInstanceOf[Switch]
val prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext)
bypassSwitch.setOnCheckedChangeListener((button: CompoundButton, checked: Boolean) =>
prefs.edit().putBoolean(Key.isBypassApps, checked).commit())
prefs.edit().putBoolean(Key.isBypassApps, checked).apply())
bypassSwitch.setChecked(prefs.getBoolean(Key.isBypassApps, false))
appListView = findViewById(R.id.applistview).asInstanceOf[ListView]
......@@ -325,7 +325,7 @@ class AppManager extends Activity with OnCheckedChangeListener with OnClickListe
})
val edit: SharedPreferences.Editor = prefs.edit
edit.putString(Key.proxied, proxiedApps.toString())
edit.commit
edit.apply
}
val loadStartRunnable = new Runnable {
......
......@@ -50,6 +50,7 @@ import android.content.pm.{PackageInfo, PackageManager}
import android.net.{Network, ConnectivityManager}
import android.os._
import android.support.v4.app.NotificationCompat
import android.support.v4.content.ContextCompat
import android.util.{SparseArray, Log}
import android.widget.Toast
import com.github.shadowsocks.aidl.Config
......@@ -319,6 +320,7 @@ class ShadowsocksNatService extends Service with BaseService {
builder
.setWhen(0)
.setColor(ContextCompat.getColor(this, R.color.material_accent_500))
.setTicker(title)
.setContentTitle(getString(R.string.app_name))
.setContentText(info)
......
......@@ -65,19 +65,13 @@ class ShadowsocksRunnerActivity extends Activity {
}
// Variables
var vpnEnabled = -1
var bgService: IShadowsocksService = null
var receiver:BroadcastReceiver = null
var isRoot: Option[Boolean] = None
var bgService: IShadowsocksService = _
var receiver:BroadcastReceiver = _
def isVpnEnabled: Boolean = {
if (vpnEnabled < 0) {
vpnEnabled = if ((!Utils.isLollipopOrAbove || status.getBoolean(Key.isNAT, false)) && Console.isRoot) {
0
} else {
1
}
}
if (vpnEnabled == 1) true else false
if (isRoot.isEmpty) isRoot = Some(Console.isRoot)
!(isRoot.get && status.getBoolean(Key.isNAT, !Utils.isLollipopOrAbove))
}
......@@ -127,6 +121,7 @@ class ShadowsocksRunnerActivity extends Activity {
} else {
attachService()
}
finish
}
override def onDestroy() {
......
......@@ -64,18 +64,12 @@ class ShadowsocksRunnerService extends Service {
}
// Variables
var vpnEnabled = -1
var bgService: IShadowsocksService = null
var isRoot: Option[Boolean] = None
var bgService: IShadowsocksService = _
def isVpnEnabled: Boolean = {
if (vpnEnabled < 0) {
vpnEnabled = if ((!Utils.isLollipopOrAbove || status.getBoolean(Key.isNAT, false)) && Console.isRoot) {
0
} else {
1
}
}
if (vpnEnabled == 1) true else false
if (isRoot.isEmpty) isRoot = Some(Console.isRoot)
!(isRoot.get && status.getBoolean(Key.isNAT, !Utils.isLollipopOrAbove))
}
override def onBind(intent: Intent): IBinder = {
......
......@@ -48,6 +48,7 @@ import android.content.pm.{PackageInfo, PackageManager}
import android.net.VpnService
import android.os._
import android.support.v4.app.NotificationCompat
import android.support.v4.content.ContextCompat
import android.util.Log
import android.widget.Toast
import com.github.shadowsocks.aidl.Config
......@@ -113,6 +114,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
builder
.setWhen(0)
.setColor(ContextCompat.getColor(this, R.color.material_accent_500))
.setTicker(title)
.setContentTitle(getString(R.string.app_name))
.setContentText(info)
......
......@@ -133,7 +133,7 @@ class ProfileManager(settings: SharedPreferences, dbHelper: DBHelper) {
edit.putString(Key.proxied, profile.individual)
edit.putInt(Key.profileId, profile.id)
edit.putString(Key.route, profile.route)
edit.commit()
edit.apply()
profile
}
......
package com.github.shadowsocks.preferences
import android.annotation.TargetApi
import android.content.Context
import android.content.res.TypedArray
import android.os.Build
import android.preference.Preference
import android.support.annotation.NonNull
import android.support.v7.widget.AppCompatSpinner
import android.util.AttributeSet
import android.view.{View, ViewGroup}
import android.widget.AdapterView.OnItemSelectedListener
import android.widget.{AdapterView, ArrayAdapter}
import com.github.shadowsocks.R
/**
* Based on: https://github.com/Mygod/mygod-lib-android/blob/683c41cb86d10e99ab0f457d41a218c6915fccc8/lib/src/main/scala/tk/mygod/preference/DropDownPreference.scala
* @author Mygod
*/
final class DropDownPreference(private val mContext: Context, attrs: AttributeSet = null)
extends Preference(mContext, attrs) with SummaryPreference {
private val mAdapter = new ArrayAdapter[String](mContext, android.R.layout.simple_spinner_dropdown_item)
private val mSpinner = new AppCompatSpinner(mContext)
private var mEntries: Array[CharSequence] = _
private var mEntryValues: Array[CharSequence] = _
private var mSelectedIndex: Int = _
private var mValueSet: Boolean = _
mSpinner.setVisibility(View.INVISIBLE)
mSpinner.setAdapter(mAdapter)
mSpinner.setOnItemSelectedListener(new OnItemSelectedListener {
override def onNothingSelected(parent: AdapterView[_]) { }
override def onItemSelected(parent: AdapterView[_], view: View, position: Int, id: Long) = setValueIndex(position)
})
setOnPreferenceClickListener((preference: Preference) => {
// TODO: not working with scrolling
// mSpinner.setDropDownVerticalOffset(Utils.dpToPx(getContext, -48 * mSelectedIndex).toInt)
mSpinner.performClick
true
})
initSummary(mContext, attrs)
val a: TypedArray = mContext.obtainStyledAttributes(attrs, R.styleable.DropDownPreference)
setEntries(a.getTextArray(R.styleable.DropDownPreference_entries))
mEntryValues = a.getTextArray(R.styleable.DropDownPreference_entryValues)
a.recycle
protected override def getSummaryValue = getEntry
/**
* Sets the human-readable entries to be shown in the list. This will be shown in subsequent dialogs.
*
* Each entry must have a corresponding index in [[setEntryValues(CharSequence[])]].
*
* @param entries The entries.
* @see [[setEntryValues(CharSequence[])]]
*/
def setEntries(entries: Array[CharSequence]) {
mEntries = entries
mAdapter.clear
if (entries != null) for (entry <- entries) mAdapter.add(entry.toString)
}
/**
* @see #setEntries(CharSequence[])
* @param entriesResId The entries array as a resource.
*/
def setEntries(entriesResId: Int): Unit = setEntries(getContext.getResources.getTextArray(entriesResId))
/**
* The list of entries to be shown in the list in subsequent dialogs.
*
* @return The list as an array.
*/
def getEntries = mEntries
/**
* The array to find the value to save for a preference when an entry from
* entries is selected. If a user clicks on the second item in entries, the
* second item in this array will be saved to the preference.
*
* @param entryValues The array to be used as values to save for the preference.
*/
def setEntryValues(entryValues: Array[CharSequence]) = mEntryValues = entryValues
/**
* @see #setEntryValues(CharSequence[])
* @param entryValuesResId The entry values array as a resource.
*/
def setEntryValues(entryValuesResId: Int): Unit =
setEntryValues(getContext.getResources.getTextArray(entryValuesResId))
/**
* Returns the array of values to be saved for the preference.
*
* @return The array of values.
*/
def getEntryValues: Array[CharSequence] = mEntryValues
/**
* Sets the value of the key. This should be one of the entries in [[getEntryValues]].
*
* @param value The value to set for the key.
*/
def setValue(value: String) {
val i = findIndexOfValue(value)
if (i > -1) setValueIndex(i)
}
/**
* Sets the value to the given index from the entry values.
*
* @param index The index of the value to set.
*/
def setValueIndex(index: Int) {
val changed = mSelectedIndex != index
if (changed || !mValueSet) {
mSelectedIndex = index
mValueSet = true
if (mEntryValues != null) {
val value = mEntryValues(index).toString
persistString(value)
setValue(value)
}
if (changed) notifyChanged
}
mSpinner.setSelection(index)
}
/**
* Returns the value of the key. This should be one of the entries in [[getEntryValues]].
*
* @return The value of the key.
*/
def getValue = if (mEntryValues == null) null else mEntryValues(mSelectedIndex).toString
/**
* Returns the entry corresponding to the current value.
*
* @return The entry corresponding to the current value, or null.
*/
def getEntry = {
val index = getValueIndex
if (index >= 0 && mEntries != null) mEntries(index) else null
}
/**
* Returns the index of the given value (in the entry values array).
*
* @param value The value whose index should be returned.
* @return The index of the value, or -1 if not found.
*/
def findIndexOfValue(value: String): Int = {
if (value != null && mEntryValues != null) {
var i = mEntryValues.length - 1
while (i >= 0) {
if (mEntryValues(i) == value) return i
i -= 1
}
}
-1
}
def getValueIndex = mSelectedIndex
protected override def onGetDefaultValue(a: TypedArray, index: Int) = a.getString(index)
protected override def onSetInitialValue (restoreValue: Boolean, defaultValue: AnyRef) =
setValue(if (restoreValue) getPersistedString(getValue) else defaultValue.asInstanceOf[String])
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
def setDropDownWidth(dimenResId: Int) =
mSpinner.setDropDownWidth(mContext.getResources.getDimensionPixelSize(dimenResId))
protected override def onBindView(@NonNull view: View) {
super.onBindView(view)
val parent = mSpinner.getParent.asInstanceOf[ViewGroup]
if (view eq parent) return
if (parent != null) parent.removeView(mSpinner)
view.asInstanceOf[ViewGroup].addView(mSpinner, 0)
val lp = mSpinner.getLayoutParams
lp.width = 0
mSpinner.setLayoutParams(lp)
}
}
/*
* Shadowsocks - A shadowsocks client for Android
* Copyright (C) 2014 <max.c.lv@gmail.com>
*
* 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/>.
*
*
* ___====-_ _-====___
* _--^^^#####// \\#####^^^--_
* _-^##########// ( ) \\##########^-_
* -############// |\^^/| \\############-
* _/############// (@::@) \\############\_
* /#############(( \\// ))#############\
* -###############\\ (oo) //###############-
* -#################\\ / VV \ //#################-
* -###################\\/ \//###################-
* _#/|##########/\######( /\ )######/\##########|\#_
* |/ |#/\#/\#/\/ \#/\##\ | | /##/\#/ \/\#/\#/\#| \|
* ` |/ V V ` V \#\| | | |/#/ V ' V V \| '
* ` ` ` ` / | | | | \ ' ' ' '
* ( | | | | )
* __\ | | | | /__
* (vvv(VVV)(VVV)vvv)
*
* HERE BE DRAGONS
*
*/
package com.github.shadowsocks.preferences
import android.preference.ListPreference
import android.content.Context
import android.util.AttributeSet
class SummaryListPreference(context: Context, attrs: AttributeSet)
extends ListPreference(context, attrs) {
override def setValue(text: String) {
super.setValue(text)
val entry = getEntry
if (entry != null)
setSummary(entry)
else
setSummary(text)
}
override def setSummary(summary: CharSequence) {
if (summary == null || summary.toString.isEmpty) {
super.setSummary("")
} else {
super.setSummary(summary)
}
}
}
package com.github.shadowsocks.preferences
import android.content.Context
import android.preference.Preference
import android.util.AttributeSet
import com.github.shadowsocks.R
/**
* Make your preference support %s in summary. Override getSummaryValue to customize what to put in.
* Based on: https://github.com/Mygod/mygod-lib-android/blob/eb20c57a0da70bb5dbc27104002f20fb1eb31d76/lib/src/main/scala/tk/mygod/preference/SummaryPreference.scala
* @author Mygod
*/
trait SummaryPreference extends Preference {
private var mSummary: String = _
protected def initSummary(context: Context, attrs: AttributeSet) {
val a = context.obtainStyledAttributes(attrs, R.styleable.SummaryPreference)
mSummary = a.getString(R.styleable.SummaryPreference_summary)
a.recycle
}
protected def getSummaryValue: AnyRef
/**
* Returns the summary of this SummaryPreference. If the summary has a String formatting marker in it
* (i.e. "%s" or "%1$s"), then the current entry value will be substituted in its place.
*
* @return the summary with appropriate string substitution
*/
override def getSummary = {
val entry = getSummaryValue
if (mSummary == null || entry == null) super.getSummary else String.format(mSummary, entry)
}
/**
* Sets the summary for this Preference with a CharSequence. If the summary has a String formatting marker in it
* (i.e. "%s" or "%1$s"), then the current entry value will be substituted in its place when it's retrieved.
*
* @param summary The summary for the preference.
*/
override def setSummary(summary: CharSequence) {
super.setSummary(summary)
if (summary == null && mSummary != null) mSummary = null
else if (summary != null && summary != mSummary) mSummary = summary.toString
}
}
......@@ -66,7 +66,6 @@ object Key {
val proxied = "Proxyed"
val isNAT = "isNAT"
val isRoot = "isRoot"
val status = "status"
val proxyedApps = "proxyedApps"
val route = "route"
......
......@@ -50,7 +50,10 @@ import android.graphics.drawable.{BitmapDrawable, Drawable}
import android.os.Build
import android.provider.Settings
import android.support.v4.content.ContextCompat
import android.util.{Base64, Log}
import android.util.{DisplayMetrics, Base64, Log}
import android.view.View.MeasureSpec
import android.view.{Gravity, View, Window}
import android.widget.Toast
import com.github.shadowsocks.BuildConfig
import org.xbill.DNS._
......@@ -72,20 +75,13 @@ object Utils {
val TIME_OUT: Int = -99
var initialized: Boolean = false
var hasRedirectSupport: Int = -1
var isRoot: Int = -1
var shell: String = null
var root_shell: String = null
var iptables: String = null
var data_path: String = null
var rootTries = 0
def isLollipopOrAbove: Boolean = {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
true
} else {
false
}
}
def isLollipopOrAbove: Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
def getSignature(context: Context): String = {
val info = context
......@@ -126,6 +122,23 @@ object Utils {
bitmap
}
// Based on: http://stackoverflow.com/a/21026866/2245107
def positionToast(toast: Toast, view: View, window: Window, offsetX: Int = 0, offsetY: Int = 0) = {
val rect = new Rect
window.getDecorView.getWindowVisibleDisplayFrame(rect)
val viewLocation = new Array[Int](2)
view.getLocationInWindow(viewLocation)
val metrics = new DisplayMetrics
window.getWindowManager.getDefaultDisplay.getMetrics(metrics)
val toastView = toast.getView
toastView.measure(MeasureSpec.makeMeasureSpec(metrics.widthPixels, MeasureSpec.UNSPECIFIED),
MeasureSpec.makeMeasureSpec(metrics.heightPixels, MeasureSpec.UNSPECIFIED))
toast.setGravity(Gravity.LEFT | Gravity.TOP,
viewLocation(0) - rect.left + (view.getWidth - toast.getView.getMeasuredWidth) / 2 + offsetX,
viewLocation(1) - rect.top + view.getHeight + offsetY)
toast
}
// Blocked > 3 seconds
def toggleAirplaneMode(context: Context) {
if (Build.VERSION.SDK_INT >= 17) {
......
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