diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 9c210f56..388b0a33 100755
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -37,7 +37,12 @@
+ android:label="@string/app_name">
+
+
+
+
+
-
4.0.0
com.github.shadowsocks
@@ -35,6 +35,13 @@
provided
+
+ com.actionbarsherlock
+ actionbarsherlock
+ 4.3.0
+ apklib
+
+
com.google.android.analytics
analytics
@@ -47,6 +54,20 @@
1.5
+
+ net.saik0.android.unifiedpreference
+ unifiedpreference
+ 0.0.2
+ apklib
+
+
+
+ org.jraf
+ android-switch-backport
+ 1.0
+ apklib
+
+
${project.artifactId}
diff --git a/res/layout/layout_apps.xml b/res/layout/layout_apps.xml
index e9ce741c..a35c5974 100644
--- a/res/layout/layout_apps.xml
+++ b/res/layout/layout_apps.xml
@@ -1,10 +1,10 @@
-
-
+ android:layout_height="fill_parent" xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical" android:duplicateParentState="false">
+
+
\ No newline at end of file
diff --git a/res/layout/layout_apps_item.xml b/res/layout/layout_apps_item.xml
index ad821a9e..bef7f510 100644
--- a/res/layout/layout_apps_item.xml
+++ b/res/layout/layout_apps_item.xml
@@ -13,6 +13,5 @@
-
diff --git a/res/layout/layout_switch.xml b/res/layout/layout_switch.xml
new file mode 100644
index 00000000..973d96cb
--- /dev/null
+++ b/res/layout/layout_switch.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/values-v11/styles.xml b/res/values-v11/styles.xml
deleted file mode 100755
index ec5c08e5..00000000
--- a/res/values-v11/styles.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 3db2e333..af0d6f4d 100755
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -29,7 +29,7 @@
- Feature Settings
+ Feature Settings
HTTP Proxy
Local HTTP Proxy
DNS Proxy
@@ -40,10 +40,10 @@
Global Proxy
Set up system wide proxy
- Individual Proxy
- Set individual proxy for apps
+ Individual Proxy
+ Set individual proxy for apps
- Select apps to use with Shadowsocks:
+ Select apps to use with Shadowsocks:
Bypass Mode
Enable this option to bypass selected apps
Auto Connect
diff --git a/res/values/styles.xml b/res/values/styles.xml
old mode 100755
new mode 100644
index 5d3d3562..07165fca
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -17,6 +17,9 @@
-
+
+
\ No newline at end of file
diff --git a/res/xml/pref_feature.xml b/res/xml/pref_feature.xml
new file mode 100755
index 00000000..f6312ba7
--- /dev/null
+++ b/res/xml/pref_feature.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/xml/pref_proxy.xml b/res/xml/pref_proxy.xml
new file mode 100755
index 00000000..67d3ef1b
--- /dev/null
+++ b/res/xml/pref_proxy.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/xml/shadowsocks_headers.xml b/res/xml/shadowsocks_headers.xml
new file mode 100644
index 00000000..dca94452
--- /dev/null
+++ b/res/xml/shadowsocks_headers.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/xml/shadowsocks_preference.xml b/res/xml/shadowsocks_preference.xml
deleted file mode 100755
index dbaee4e5..00000000
--- a/res/xml/shadowsocks_preference.xml
+++ /dev/null
@@ -1,102 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/com/github/shadowsocks/Shadowsocks.java b/src/com/github/shadowsocks/Shadowsocks.java
index 67d71d85..04c2e4f5 100755
--- a/src/com/github/shadowsocks/Shadowsocks.java
+++ b/src/com/github/shadowsocks/Shadowsocks.java
@@ -53,20 +53,29 @@ import android.os.Message;
import android.preference.*;
import android.util.Log;
import android.view.KeyEvent;
-import android.view.Menu;
-import android.view.MenuItem;
+import android.widget.CompoundButton;
+import android.widget.RelativeLayout;
import android.widget.Toast;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
import com.google.analytics.tracking.android.EasyTracker;
+import net.saik0.android.unifiedpreference.UnifiedPreferenceFragment;
+import net.saik0.android.unifiedpreference.UnifiedSherlockPreferenceActivity;
+import org.jraf.android.backport.switchwidget.Switch;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-public class Shadowsocks extends PreferenceActivity implements
- OnSharedPreferenceChangeListener {
+public class Shadowsocks extends UnifiedSherlockPreferenceActivity
+ implements CompoundButton.OnCheckedChangeListener, OnSharedPreferenceChangeListener {
public static final String PREFS_NAME = "Shadowsocks";
+ public static final String[] PROXY_PREFS = {"proxy", "remotePort", "port"
+ , "sitekey", "encMethod"};
+ public static final String[] FEATRUE_PREFS = {"isHTTPProxy", "isDNSProxy", "isGFWList", "isGlobalProxy", "isBypassApps"
+ , "proxyedApps", "isAutoConnect"};
private static final String TAG = "Shadowsocks";
private static final int MSG_CRASH_RECOVER = 1;
private static final int MSG_INITIAL_FINISH = 2;
@@ -94,14 +103,7 @@ public class Shadowsocks extends PreferenceActivity implements
super.handleMessage(msg);
}
};
- private EditTextPreference proxyText;
- private EditTextPreference portText;
- private EditTextPreference remotePortText;
- private EditTextPreference sitekeyText;
- private ListPreference encMethodText;
- private Preference proxyedApps;
- private CheckBoxPreference isBypassAppsCheck;
- private CheckBoxPreference isRunningCheck;
+ private Switch switchButton;
private void copyAssets(String path) {
@@ -161,67 +163,43 @@ public class Shadowsocks extends PreferenceActivity implements
return false;
}
+ @Override
+ public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
+ if (compoundButton == switchButton) {
+ if (!serviceStart()) {
+ switchButton.setChecked(false);
+ }
+ }
+ }
+
/**
* Called when the activity is first created.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
+ setHeaderRes(R.xml.shadowsocks_headers);
super.onCreate(savedInstanceState);
- addPreferencesFromResource(R.xml.shadowsocks_preference);
+ RelativeLayout switchLayout = (RelativeLayout)
+ getLayoutInflater().inflate(R.layout.layout_switch, null);
- proxyText = (EditTextPreference) findPreference("proxy");
- portText = (EditTextPreference) findPreference("port");
- remotePortText = (EditTextPreference) findPreference("remotePort");
- sitekeyText = (EditTextPreference) findPreference("sitekey");
- encMethodText = (ListPreference) findPreference("encMethod");
- proxyedApps = findPreference("proxyedApps");
+ getSupportActionBar().setCustomView(switchLayout);
+ getSupportActionBar().setDisplayShowTitleEnabled(false);
+ getSupportActionBar().setDisplayShowCustomEnabled(true);
- isBypassAppsCheck = (CheckBoxPreference) findPreference("isBypassApps");
- isRunningCheck = (CheckBoxPreference) findPreference("isRunning");
+ switchButton = (Switch) switchLayout.findViewById(R.id.switchButton);
- if (mProgressDialog == null) {
- mProgressDialog = ProgressDialog.show(this, "",
- getString(R.string.initializing), true, true);
- }
-
- new Thread() {
- @Override
- public void run() {
-
- Utils.isRoot();
-
- String versionName;
- try {
- versionName = getPackageManager().getPackageInfo(
- getPackageName(), 0).versionName;
- } catch (NameNotFoundException e) {
- versionName = "NONE";
- }
-
- final SharedPreferences settings = PreferenceManager
- .getDefaultSharedPreferences(Shadowsocks.this);
-
- if (!settings.getBoolean(versionName, false)) {
- Editor edit = settings.edit();
- edit.putBoolean(versionName, true);
- edit.commit();
- reset();
- }
-
- handler.sendEmptyMessage(MSG_INITIAL_FINISH);
- }
- }.start();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
- menu.add(Menu.NONE, Menu.FIRST + 1, 1, getString(R.string.recovery))
- .setIcon(android.R.drawable.ic_menu_delete);
- menu.add(Menu.NONE, Menu.FIRST + 2, 2, getString(R.string.about))
- .setIcon(android.R.drawable.ic_menu_info_details);
+ menu.add(0, 0, 0, R.string.recovery)
+ .setIcon(android.R.drawable.ic_menu_revert)
+ .setShowAsAction(MenuItem.SHOW_AS_ACTION_WITH_TEXT);
+ menu.add(0, 1, 1, R.string.about)
+ .setIcon(android.R.drawable.ic_menu_info_details)
+ .setShowAsAction(MenuItem.SHOW_AS_ACTION_WITH_TEXT);
return true;
-
}
/**
@@ -235,11 +213,6 @@ public class Shadowsocks extends PreferenceActivity implements
editor.putBoolean("isConnected", ShadowsocksService.isServiceStarted());
editor.commit();
- if (mProgressDialog != null) {
- mProgressDialog.dismiss();
- mProgressDialog = null;
- }
-
super.onDestroy();
}
@@ -256,14 +229,13 @@ public class Shadowsocks extends PreferenceActivity implements
return super.onKeyDown(keyCode, event);
}
- // 菜单项被选择事件
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
- case Menu.FIRST + 1:
+ case 0:
recovery();
break;
- case Menu.FIRST + 2:
+ case 1:
String versionName = "";
try {
versionName = getPackageManager().getPackageInfo(
@@ -276,57 +248,27 @@ public class Shadowsocks extends PreferenceActivity implements
break;
}
- return true;
+ return super.onOptionsItemSelected(item);
}
@Override
protected void onPause() {
super.onPause();
-
- // Unregister the listener whenever a key changes
- getPreferenceScreen().getSharedPreferences()
+ PreferenceManager.getDefaultSharedPreferences(this)
.unregisterOnSharedPreferenceChangeListener(this);
}
- @Override
- public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
- Preference preference) {
- SharedPreferences settings = PreferenceManager
- .getDefaultSharedPreferences(this);
-
- if (preference.getKey() != null
- && preference.getKey().equals("proxyedApps")) {
- Intent intent = new Intent(this, AppManager.class);
- startActivity(intent);
- } else if (preference.getKey() != null
- && preference.getKey().equals("isRunning")) {
- if (!serviceStart()) {
- Editor edit = settings.edit();
- edit.putBoolean("isRunning", false);
- edit.commit();
- }
- }
- return super.onPreferenceTreeClick(preferenceScreen, preference);
- }
-
@Override
protected void onResume() {
super.onResume();
SharedPreferences settings = PreferenceManager
.getDefaultSharedPreferences(this);
- if (settings.getBoolean("isGlobalProxy", false)) {
- proxyedApps.setEnabled(false);
- isBypassAppsCheck.setEnabled(false);
- } else {
- proxyedApps.setEnabled(true);
- isBypassAppsCheck.setEnabled(true);
- }
-
Editor edit = settings.edit();
if (ShadowsocksService.isServiceStarted()) {
edit.putBoolean("isRunning", true);
+ switchButton.setChecked(true);
} else {
if (settings.getBoolean("isRunning", false)) {
new Thread() {
@@ -338,33 +280,44 @@ public class Shadowsocks extends PreferenceActivity implements
}.start();
}
edit.putBoolean("isRunning", false);
+ switchButton.setChecked(false);
}
edit.commit();
- // Setup the initial values
+ setPreferenceEnabled();
- if (!settings.getString("sitekey", "").equals(""))
- sitekeyText.setSummary(settings.getString("sitekey", ""));
-
- if (!settings.getString("encMethod", "").equals(""))
- encMethodText.setSummary(settings.getString("encMethod", ""));
-
- if (!settings.getString("port", "").equals(""))
- portText.setSummary(settings.getString("port",
- getString(R.string.port_summary)));
-
- if (!settings.getString("remotePort", "").equals(""))
- remotePortText.setSummary(settings.getString("remotePort",
- getString(R.string.remote_port_summary)));
-
- if (!settings.getString("proxy", "").equals(""))
- proxyText.setSummary(settings.getString("proxy",
- getString(R.string.proxy_summary)));
-
- // Set up a listener whenever a key changes
- getPreferenceScreen().getSharedPreferences()
+ switchButton.setOnCheckedChangeListener(this);
+ PreferenceManager.getDefaultSharedPreferences(this)
.registerOnSharedPreferenceChangeListener(this);
+
+ }
+
+ private void setPreferenceEnabled() {
+ SharedPreferences settings = PreferenceManager
+ .getDefaultSharedPreferences(Shadowsocks.this);
+
+ boolean enabled = !settings.getBoolean("isRunning", false)
+ && !settings.getBoolean("isConnecting", false);
+
+ for (String name : PROXY_PREFS) {
+ Preference pref = findPreference(name);
+ if (pref != null) {
+ pref.setEnabled(enabled);
+ }
+ }
+ for (String name : FEATRUE_PREFS) {
+ Preference pref = findPreference(name);
+ if (pref != null) {
+ if (name.equals("isBypassApps")
+ || name.equals("proxyedApps")) {
+ boolean isGlobalProxy = settings.getBoolean("isGlobalProxy", false);
+ pref.setEnabled(enabled && !isGlobalProxy);
+ } else {
+ pref.setEnabled(enabled);
+ }
+ }
+ }
}
@Override
@@ -376,9 +329,9 @@ public class Shadowsocks extends PreferenceActivity implements
if (key.equals("isRunning")) {
if (settings.getBoolean("isRunning", false)) {
- isRunningCheck.setChecked(true);
+ switchButton.setChecked(true);
} else {
- isRunningCheck.setChecked(false);
+ switchButton.setChecked(false);
}
}
@@ -397,44 +350,8 @@ public class Shadowsocks extends PreferenceActivity implements
}
}
- if (key.equals("isGlobalProxy")) {
- if (settings.getBoolean("isGlobalProxy", false)) {
- proxyedApps.setEnabled(false);
- isBypassAppsCheck.setEnabled(false);
- } else {
- proxyedApps.setEnabled(true);
- isBypassAppsCheck.setEnabled(true);
- }
- }
-
- if (key.equals("remotePort")) {
- if (settings.getString("remotePort", "").equals("")) {
- remotePortText.setSummary(getString(R.string.remote_port_summary));
- } else {
- remotePortText.setSummary(settings.getString("remotePort", ""));
- }
- } else if (key.equals("port")) {
- if (settings.getString("port", "").equals("")) {
- portText.setSummary(getString(R.string.port_summary));
- } else {
- portText.setSummary(settings.getString("port", ""));
- }
- } else if (key.equals("sitekey")) {
- if (settings.getString("sitekey", "").equals("")) {
- sitekeyText.setSummary(getString(R.string.sitekey_summary));
- } else {
- sitekeyText.setSummary(settings.getString("sitekey", ""));
- }
- } else if (key.equals("encMethod")) {
- if (!settings.getString("encMethod", "").equals("")) {
- encMethodText.setSummary(settings.getString("encMethod", ""));
- }
- } else if (key.equals("proxy")) {
- if (settings.getString("proxy", "").equals("")) {
- proxyText.setSummary(getString(R.string.proxy_summary));
- } else {
- proxyText.setSummary(settings.getString("proxy", ""));
- }
+ if (key.equals("isRunning") || key.equals("isGlobalProxy")) {
+ setPreferenceEnabled();
}
}
@@ -442,12 +359,56 @@ public class Shadowsocks extends PreferenceActivity implements
public void onStart() {
super.onStart();
EasyTracker.getInstance().activityStart(this);
+
+ final SharedPreferences settings = PreferenceManager
+ .getDefaultSharedPreferences(Shadowsocks.this);
+
+ boolean init = !settings.getBoolean("isRunning", false)
+ && !settings.getBoolean("isConnecting", false);
+
+ if (init) {
+ if (mProgressDialog == null) {
+ mProgressDialog = ProgressDialog.show(this, "",
+ getString(R.string.initializing), true, true);
+ }
+
+ new Thread() {
+ @Override
+ public void run() {
+
+ Utils.isRoot();
+
+ String versionName;
+ try {
+ versionName = getPackageManager().getPackageInfo(
+ getPackageName(), 0).versionName;
+ } catch (NameNotFoundException e) {
+ versionName = "NONE";
+ }
+
+
+ if (!settings.getBoolean(versionName, false)) {
+ Editor edit = settings.edit();
+ edit.putBoolean(versionName, true);
+ edit.commit();
+ reset();
+ }
+
+ handler.sendEmptyMessage(MSG_INITIAL_FINISH);
+ }
+ }.start();
+ }
}
@Override
public void onStop() {
super.onStop();
EasyTracker.getInstance().activityStop(this);
+
+ if (mProgressDialog != null) {
+ mProgressDialog.dismiss();
+ mProgressDialog = null;
+ }
}
public void reset() {
@@ -560,4 +521,97 @@ public class Shadowsocks extends PreferenceActivity implements
alert.show();
}
+ public static class ProxyFragment extends UnifiedPreferenceFragment
+ implements OnSharedPreferenceChangeListener {
+
+ private void setPreferenceEnabled() {
+ SharedPreferences settings = PreferenceManager
+ .getDefaultSharedPreferences(getActivity());
+
+ boolean enabled = !settings.getBoolean("isRunning", false)
+ && !settings.getBoolean("isConnecting", false);
+
+ for (String name : PROXY_PREFS) {
+ Preference pref = findPreference(name);
+ if (pref != null) {
+ pref.setEnabled(enabled);
+ }
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ setPreferenceEnabled();
+
+ // Set up a listener whenever a key changes
+ getPreferenceScreen().getSharedPreferences()
+ .registerOnSharedPreferenceChangeListener(this);
+
+ }
+
+ @Override
+ public void onPause() {
+ // Unregister the listener whenever a key changes
+ getPreferenceScreen().getSharedPreferences()
+ .unregisterOnSharedPreferenceChangeListener(this);
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ if (key.equals("isRunning") || key.equals("isGlobalProxy")) {
+ setPreferenceEnabled();
+ }
+ }
+ }
+
+ public static class FeatureFragment extends UnifiedPreferenceFragment
+ implements OnSharedPreferenceChangeListener {
+
+ private void setPreferenceEnabled() {
+ SharedPreferences settings = PreferenceManager
+ .getDefaultSharedPreferences(getActivity());
+
+ boolean enabled = !settings.getBoolean("isRunning", false)
+ && !settings.getBoolean("isConnecting", false);
+
+ for (String name : FEATRUE_PREFS) {
+ Preference pref = findPreference(name);
+ if (pref != null) {
+ if (name.equals("isBypassApps")
+ || name.equals("proxyedApps")) {
+ boolean isGlobalProxy = settings.getBoolean("isGlobalProxy", false);
+ pref.setEnabled(enabled && !isGlobalProxy);
+ } else {
+ pref.setEnabled(enabled);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ setPreferenceEnabled();
+
+ // Set up a listener whenever a key changes
+ getPreferenceScreen().getSharedPreferences()
+ .registerOnSharedPreferenceChangeListener(this);
+ }
+
+ @Override
+ public void onPause() {
+ // Unregister the listener whenever a key changes
+ getPreferenceScreen().getSharedPreferences()
+ .unregisterOnSharedPreferenceChangeListener(this);
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ if (key.equals("isRunning") || key.equals("isGlobalProxy")) {
+ setPreferenceEnabled();
+ }
+ }
+ }
+
}
diff --git a/src/com/github/shadowsocks/preferences/SummaryEditTextPreference.java b/src/com/github/shadowsocks/preferences/SummaryEditTextPreference.java
new file mode 100644
index 00000000..6a7ae974
--- /dev/null
+++ b/src/com/github/shadowsocks/preferences/SummaryEditTextPreference.java
@@ -0,0 +1,38 @@
+package com.github.shadowsocks.preferences;
+
+import android.content.Context;
+import android.preference.EditTextPreference;
+import android.util.AttributeSet;
+import com.github.shadowsocks.R;
+
+public class SummaryEditTextPreference extends EditTextPreference {
+
+ private CharSequence mDefaultSummary;
+
+ public SummaryEditTextPreference(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ mDefaultSummary = getSummary();
+ }
+
+ public SummaryEditTextPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mDefaultSummary = getSummary();
+ }
+
+ public SummaryEditTextPreference(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onDialogClosed(boolean positiveResult) {
+ super.onDialogClosed(positiveResult);
+ if (positiveResult) {
+ String value = getEditText().getText().toString();
+ if (value.isEmpty()) {
+ setSummary(mDefaultSummary);
+ } else {
+ setSummary(value);
+ }
+ }
+ }
+}