clean up
This commit is contained in:
@@ -21,388 +21,362 @@ import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.*;
|
||||
import android.widget.AbsListView;
|
||||
import android.widget.AbsListView.OnScrollListener;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.CompoundButton.OnCheckedChangeListener;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
import com.actionbarsherlock.app.SherlockActivity;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.Vector;
|
||||
|
||||
import java.util.*;
|
||||
public class AppManager extends SherlockActivity
|
||||
implements OnCheckedChangeListener, OnClickListener {
|
||||
|
||||
public class AppManager extends SherlockActivity implements OnCheckedChangeListener,
|
||||
OnClickListener {
|
||||
public final static String PREFS_KEY_PROXYED = "Proxyed";
|
||||
private static final int MSG_LOAD_START = 1;
|
||||
private static final int MSG_LOAD_FINISH = 2;
|
||||
final Handler handler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_LOAD_START:
|
||||
pd = ProgressDialog.show(AppManager.this, "", getString(R.string.loading), true, true);
|
||||
break;
|
||||
case MSG_LOAD_FINISH:
|
||||
|
||||
public final static String PREFS_KEY_PROXYED = "Proxyed";
|
||||
private static final int MSG_LOAD_START = 1;
|
||||
private static final int MSG_LOAD_FINISH = 2;
|
||||
final Handler handler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_LOAD_START:
|
||||
pd = ProgressDialog.show(AppManager.this, "",
|
||||
getString(R.string.loading), true, true);
|
||||
break;
|
||||
case MSG_LOAD_FINISH:
|
||||
listApps.setAdapter(adapter);
|
||||
|
||||
listApps.setAdapter(adapter);
|
||||
listApps.setOnScrollListener(new OnScrollListener() {
|
||||
|
||||
listApps.setOnScrollListener(new OnScrollListener() {
|
||||
boolean visible;
|
||||
|
||||
boolean visible;
|
||||
|
||||
@Override
|
||||
public void onScroll(AbsListView view,
|
||||
int firstVisibleItem, int visibleItemCount,
|
||||
int totalItemCount) {
|
||||
if (visible) {
|
||||
String name = apps[firstVisibleItem].getName();
|
||||
if (name != null && name.length() > 1)
|
||||
overlay.setText(apps[firstVisibleItem]
|
||||
.getName().substring(0, 1));
|
||||
else
|
||||
overlay.setText("*");
|
||||
overlay.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScrollStateChanged(AbsListView view,
|
||||
int scrollState) {
|
||||
visible = true;
|
||||
if (scrollState == OnScrollListener.SCROLL_STATE_IDLE) {
|
||||
overlay.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (pd != null) {
|
||||
pd.dismiss();
|
||||
pd = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
super.handleMessage(msg);
|
||||
}
|
||||
};
|
||||
private ProxyedApp[] apps = null;
|
||||
private ListView listApps;
|
||||
private AppManager mAppManager;
|
||||
private TextView overlay;
|
||||
private ProgressDialog pd = null;
|
||||
private ListAdapter adapter;
|
||||
private ImageLoader dm;
|
||||
private boolean appsLoaded = false;
|
||||
|
||||
public static ProxyedApp[] getProxyedApps(Context context) {
|
||||
|
||||
SharedPreferences prefs = PreferenceManager
|
||||
.getDefaultSharedPreferences(context);
|
||||
|
||||
String tordAppString = prefs.getString(PREFS_KEY_PROXYED, "");
|
||||
String[] tordApps;
|
||||
|
||||
StringTokenizer st = new StringTokenizer(tordAppString, "|");
|
||||
tordApps = new String[st.countTokens()];
|
||||
int tordIdx = 0;
|
||||
while (st.hasMoreTokens()) {
|
||||
tordApps[tordIdx++] = st.nextToken();
|
||||
}
|
||||
|
||||
Arrays.sort(tordApps);
|
||||
|
||||
// else load the apps up
|
||||
PackageManager pMgr = context.getPackageManager();
|
||||
|
||||
List<ApplicationInfo> lAppInfo = pMgr.getInstalledApplications(0);
|
||||
|
||||
Iterator<ApplicationInfo> itAppInfo = lAppInfo.iterator();
|
||||
|
||||
Vector<ProxyedApp> vectorApps = new Vector<ProxyedApp>();
|
||||
|
||||
ApplicationInfo aInfo = null;
|
||||
|
||||
while (itAppInfo.hasNext()) {
|
||||
aInfo = itAppInfo.next();
|
||||
|
||||
// ignore system apps
|
||||
if (aInfo.uid < 10000)
|
||||
continue;
|
||||
|
||||
ProxyedApp app = new ProxyedApp();
|
||||
|
||||
app.setUid(aInfo.uid);
|
||||
|
||||
app.setUsername(pMgr.getNameForUid(app.getUid()));
|
||||
|
||||
// check if this application is allowed
|
||||
if (aInfo.packageName != null
|
||||
&& aInfo.packageName.equals("com.github.shadowsocks")) {
|
||||
app.setProxyed(true);
|
||||
} else if (Arrays.binarySearch(tordApps, app.getUsername()) >= 0) {
|
||||
app.setProxyed(true);
|
||||
} else {
|
||||
app.setProxyed(false);
|
||||
}
|
||||
|
||||
vectorApps.add(app);
|
||||
|
||||
}
|
||||
|
||||
ProxyedApp[] apps = new ProxyedApp[vectorApps.size()];
|
||||
vectorApps.toArray(apps);
|
||||
return apps;
|
||||
}
|
||||
|
||||
public void getApps(Context context) {
|
||||
|
||||
SharedPreferences prefs = PreferenceManager
|
||||
.getDefaultSharedPreferences(context);
|
||||
|
||||
String tordAppString = prefs.getString(PREFS_KEY_PROXYED, "");
|
||||
String[] tordApps;
|
||||
|
||||
StringTokenizer st = new StringTokenizer(tordAppString, "|");
|
||||
tordApps = new String[st.countTokens()];
|
||||
int tordIdx = 0;
|
||||
while (st.hasMoreTokens()) {
|
||||
tordApps[tordIdx++] = st.nextToken();
|
||||
}
|
||||
|
||||
Arrays.sort(tordApps);
|
||||
|
||||
Vector<ProxyedApp> vectorApps = new Vector<ProxyedApp>();
|
||||
|
||||
// else load the apps up
|
||||
PackageManager pMgr = context.getPackageManager();
|
||||
|
||||
List<ApplicationInfo> lAppInfo = pMgr.getInstalledApplications(0);
|
||||
|
||||
Iterator<ApplicationInfo> itAppInfo = lAppInfo.iterator();
|
||||
|
||||
ApplicationInfo aInfo = null;
|
||||
|
||||
while (itAppInfo.hasNext()) {
|
||||
aInfo = itAppInfo.next();
|
||||
|
||||
// ignore system apps
|
||||
if (aInfo.uid < 10000)
|
||||
continue;
|
||||
|
||||
if (aInfo.processName == null)
|
||||
continue;
|
||||
if (pMgr.getApplicationLabel(aInfo) == null
|
||||
|| pMgr.getApplicationLabel(aInfo).toString().equals(""))
|
||||
continue;
|
||||
if (pMgr.getApplicationIcon(aInfo) == null)
|
||||
continue;
|
||||
|
||||
ProxyedApp tApp = new ProxyedApp();
|
||||
|
||||
tApp.setEnabled(aInfo.enabled);
|
||||
tApp.setUid(aInfo.uid);
|
||||
tApp.setUsername(pMgr.getNameForUid(tApp.getUid()));
|
||||
tApp.setProcname(aInfo.processName);
|
||||
tApp.setName(pMgr.getApplicationLabel(aInfo).toString());
|
||||
|
||||
// check if this application is allowed
|
||||
if (Arrays.binarySearch(tordApps, tApp.getUsername()) >= 0) {
|
||||
tApp.setProxyed(true);
|
||||
} else {
|
||||
tApp.setProxyed(false);
|
||||
}
|
||||
|
||||
vectorApps.add(tApp);
|
||||
}
|
||||
|
||||
apps = new ProxyedApp[vectorApps.size()];
|
||||
vectorApps.toArray(apps);
|
||||
|
||||
}
|
||||
|
||||
private void loadApps() {
|
||||
getApps(this);
|
||||
|
||||
Arrays.sort(apps, new Comparator<ProxyedApp>() {
|
||||
@Override
|
||||
public int compare(ProxyedApp o1, ProxyedApp o2) {
|
||||
if (o1 == null || o2 == null || o1.getName() == null
|
||||
|| o2.getName() == null)
|
||||
return 1;
|
||||
if (o1.isProxyed() == o2.isProxyed())
|
||||
return o1.getName().compareTo(o2.getName());
|
||||
if (o1.isProxyed())
|
||||
return -1;
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
|
||||
final LayoutInflater inflater = getLayoutInflater();
|
||||
|
||||
adapter = new ArrayAdapter<ProxyedApp>(this, R.layout.layout_apps_item,
|
||||
R.id.itemtext, apps) {
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
ListEntry entry;
|
||||
if (convertView == null) {
|
||||
// Inflate a new view
|
||||
convertView = inflater.inflate(R.layout.layout_apps_item,
|
||||
parent, false);
|
||||
entry = new ListEntry();
|
||||
entry.icon = (ImageView) convertView
|
||||
.findViewById(R.id.itemicon);
|
||||
entry.box = (CheckBox) convertView
|
||||
.findViewById(R.id.itemcheck);
|
||||
entry.text = (TextView) convertView
|
||||
.findViewById(R.id.itemtext);
|
||||
|
||||
entry.text.setOnClickListener(mAppManager);
|
||||
entry.text.setOnClickListener(mAppManager);
|
||||
|
||||
convertView.setTag(entry);
|
||||
|
||||
entry.box.setOnCheckedChangeListener(mAppManager);
|
||||
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
|
||||
int totalItemCount) {
|
||||
if (visible) {
|
||||
String name = apps[firstVisibleItem].getName();
|
||||
if (name != null && name.length() > 1) {
|
||||
overlay.setText(apps[firstVisibleItem].getName().substring(0, 1));
|
||||
} else {
|
||||
// Convert an existing view
|
||||
entry = (ListEntry) convertView.getTag();
|
||||
overlay.setText("*");
|
||||
}
|
||||
|
||||
final ProxyedApp app = apps[position];
|
||||
|
||||
entry.icon.setTag(app.getUid());
|
||||
|
||||
dm.DisplayImage(app.getUid(),
|
||||
(Activity) convertView.getContext(), entry.icon);
|
||||
|
||||
entry.text.setText(app.getName());
|
||||
|
||||
final CheckBox box = entry.box;
|
||||
box.setTag(app);
|
||||
box.setChecked(app.isProxyed());
|
||||
|
||||
entry.text.setTag(box);
|
||||
|
||||
return convertView;
|
||||
overlay.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
appsLoaded = true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Called an application is check/unchecked
|
||||
*/
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
final ProxyedApp app = (ProxyedApp) buttonView.getTag();
|
||||
if (app != null) {
|
||||
app.setProxyed(isChecked);
|
||||
}
|
||||
|
||||
saveAppSettings(this);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
|
||||
CheckBox cbox = (CheckBox) v.getTag();
|
||||
|
||||
final ProxyedApp app = (ProxyedApp) cbox.getTag();
|
||||
if (app != null) {
|
||||
app.setProxyed(!app.isProxyed());
|
||||
cbox.setChecked(app.isProxyed());
|
||||
}
|
||||
|
||||
saveAppSettings(this);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
this.setContentView(R.layout.layout_apps);
|
||||
|
||||
this.dm = ImageLoaderFactory.getImageLoader(this);
|
||||
|
||||
this.overlay = (TextView) View.inflate(this, R.layout.overlay, null);
|
||||
getWindowManager()
|
||||
.addView(
|
||||
overlay,
|
||||
new WindowManager.LayoutParams(
|
||||
LayoutParams.WRAP_CONTENT,
|
||||
LayoutParams.WRAP_CONTENT,
|
||||
WindowManager.LayoutParams.TYPE_APPLICATION,
|
||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
|
||||
PixelFormat.TRANSLUCENT));
|
||||
|
||||
mAppManager = this;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
new Thread() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
handler.sendEmptyMessage(MSG_LOAD_START);
|
||||
|
||||
listApps = (ListView) findViewById(R.id.applistview);
|
||||
|
||||
if (!appsLoaded)
|
||||
loadApps();
|
||||
handler.sendEmptyMessage(MSG_LOAD_FINISH);
|
||||
public void onScrollStateChanged(AbsListView view, int scrollState) {
|
||||
visible = true;
|
||||
if (scrollState == OnScrollListener.SCROLL_STATE_IDLE) {
|
||||
overlay.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
});
|
||||
|
||||
if (pd != null) {
|
||||
pd.dismiss();
|
||||
pd = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
super.handleMessage(msg);
|
||||
}
|
||||
};
|
||||
private ProxyedApp[] apps = null;
|
||||
private ListView listApps;
|
||||
private AppManager mAppManager;
|
||||
private TextView overlay;
|
||||
private ProgressDialog pd = null;
|
||||
private ListAdapter adapter;
|
||||
private ImageLoader dm;
|
||||
private boolean appsLoaded = false;
|
||||
|
||||
public static ProxyedApp[] getProxyedApps(Context context) {
|
||||
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
|
||||
String tordAppString = prefs.getString(PREFS_KEY_PROXYED, "");
|
||||
String[] tordApps;
|
||||
|
||||
StringTokenizer st = new StringTokenizer(tordAppString, "|");
|
||||
tordApps = new String[st.countTokens()];
|
||||
int tordIdx = 0;
|
||||
while (st.hasMoreTokens()) {
|
||||
tordApps[tordIdx++] = st.nextToken();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see android.app.Activity#onStop()
|
||||
*/
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
Arrays.sort(tordApps);
|
||||
|
||||
// Log.d(getClass().getName(),"Exiting Preferences");
|
||||
// else load the apps up
|
||||
PackageManager pMgr = context.getPackageManager();
|
||||
|
||||
List<ApplicationInfo> lAppInfo = pMgr.getInstalledApplications(0);
|
||||
|
||||
Iterator<ApplicationInfo> itAppInfo = lAppInfo.iterator();
|
||||
|
||||
Vector<ProxyedApp> vectorApps = new Vector<ProxyedApp>();
|
||||
|
||||
ApplicationInfo aInfo = null;
|
||||
|
||||
while (itAppInfo.hasNext()) {
|
||||
aInfo = itAppInfo.next();
|
||||
|
||||
// ignore system apps
|
||||
if (aInfo.uid < 10000) continue;
|
||||
|
||||
ProxyedApp app = new ProxyedApp();
|
||||
|
||||
app.setUid(aInfo.uid);
|
||||
|
||||
app.setUsername(pMgr.getNameForUid(app.getUid()));
|
||||
|
||||
// check if this application is allowed
|
||||
if (aInfo.packageName != null && aInfo.packageName.equals("com.github.shadowsocks")) {
|
||||
app.setProxyed(true);
|
||||
} else if (Arrays.binarySearch(tordApps, app.getUsername()) >= 0) {
|
||||
app.setProxyed(true);
|
||||
} else {
|
||||
app.setProxyed(false);
|
||||
}
|
||||
|
||||
vectorApps.add(app);
|
||||
}
|
||||
|
||||
public void saveAppSettings(Context context) {
|
||||
if (apps == null)
|
||||
return;
|
||||
ProxyedApp[] apps = new ProxyedApp[vectorApps.size()];
|
||||
vectorApps.toArray(apps);
|
||||
return apps;
|
||||
}
|
||||
|
||||
SharedPreferences prefs = PreferenceManager
|
||||
.getDefaultSharedPreferences(this);
|
||||
public void getApps(Context context) {
|
||||
|
||||
// final SharedPreferences prefs =
|
||||
// context.getSharedPreferences(PREFS_KEY, 0);
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
|
||||
StringBuilder tordApps = new StringBuilder();
|
||||
String tordAppString = prefs.getString(PREFS_KEY_PROXYED, "");
|
||||
String[] tordApps;
|
||||
|
||||
for (int i = 0; i < apps.length; i++) {
|
||||
if (apps[i].isProxyed()) {
|
||||
tordApps.append(apps[i].getUsername());
|
||||
tordApps.append("|");
|
||||
}
|
||||
StringTokenizer st = new StringTokenizer(tordAppString, "|");
|
||||
tordApps = new String[st.countTokens()];
|
||||
int tordIdx = 0;
|
||||
while (st.hasMoreTokens()) {
|
||||
tordApps[tordIdx++] = st.nextToken();
|
||||
}
|
||||
|
||||
Arrays.sort(tordApps);
|
||||
|
||||
Vector<ProxyedApp> vectorApps = new Vector<ProxyedApp>();
|
||||
|
||||
// else load the apps up
|
||||
PackageManager pMgr = context.getPackageManager();
|
||||
|
||||
List<ApplicationInfo> lAppInfo = pMgr.getInstalledApplications(0);
|
||||
|
||||
Iterator<ApplicationInfo> itAppInfo = lAppInfo.iterator();
|
||||
|
||||
ApplicationInfo aInfo = null;
|
||||
|
||||
while (itAppInfo.hasNext()) {
|
||||
aInfo = itAppInfo.next();
|
||||
|
||||
// ignore system apps
|
||||
if (aInfo.uid < 10000) continue;
|
||||
|
||||
if (aInfo.processName == null) continue;
|
||||
if (pMgr.getApplicationLabel(aInfo) == null || pMgr.getApplicationLabel(aInfo)
|
||||
.toString()
|
||||
.equals("")) {
|
||||
continue;
|
||||
}
|
||||
if (pMgr.getApplicationIcon(aInfo) == null) continue;
|
||||
|
||||
ProxyedApp tApp = new ProxyedApp();
|
||||
|
||||
tApp.setEnabled(aInfo.enabled);
|
||||
tApp.setUid(aInfo.uid);
|
||||
tApp.setUsername(pMgr.getNameForUid(tApp.getUid()));
|
||||
tApp.setProcname(aInfo.processName);
|
||||
tApp.setName(pMgr.getApplicationLabel(aInfo).toString());
|
||||
|
||||
// check if this application is allowed
|
||||
if (Arrays.binarySearch(tordApps, tApp.getUsername()) >= 0) {
|
||||
tApp.setProxyed(true);
|
||||
} else {
|
||||
tApp.setProxyed(false);
|
||||
}
|
||||
|
||||
vectorApps.add(tApp);
|
||||
}
|
||||
|
||||
apps = new ProxyedApp[vectorApps.size()];
|
||||
vectorApps.toArray(apps);
|
||||
}
|
||||
|
||||
private void loadApps() {
|
||||
getApps(this);
|
||||
|
||||
Arrays.sort(apps, new Comparator<ProxyedApp>() {
|
||||
@Override
|
||||
public int compare(ProxyedApp o1, ProxyedApp o2) {
|
||||
if (o1 == null || o2 == null || o1.getName() == null || o2.getName() == null) return 1;
|
||||
if (o1.isProxyed() == o2.isProxyed()) return o1.getName().compareTo(o2.getName());
|
||||
if (o1.isProxyed()) return -1;
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
|
||||
final LayoutInflater inflater = getLayoutInflater();
|
||||
|
||||
adapter = new ArrayAdapter<ProxyedApp>(this, R.layout.layout_apps_item, R.id.itemtext, apps) {
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
ListEntry entry;
|
||||
if (convertView == null) {
|
||||
// Inflate a new view
|
||||
convertView = inflater.inflate(R.layout.layout_apps_item, parent, false);
|
||||
entry = new ListEntry();
|
||||
entry.icon = (ImageView) convertView.findViewById(R.id.itemicon);
|
||||
entry.box = (CheckBox) convertView.findViewById(R.id.itemcheck);
|
||||
entry.text = (TextView) convertView.findViewById(R.id.itemtext);
|
||||
|
||||
entry.text.setOnClickListener(mAppManager);
|
||||
entry.text.setOnClickListener(mAppManager);
|
||||
|
||||
convertView.setTag(entry);
|
||||
|
||||
entry.box.setOnCheckedChangeListener(mAppManager);
|
||||
} else {
|
||||
// Convert an existing view
|
||||
entry = (ListEntry) convertView.getTag();
|
||||
}
|
||||
|
||||
Editor edit = prefs.edit();
|
||||
edit.putString(PREFS_KEY_PROXYED, tordApps.toString());
|
||||
edit.commit();
|
||||
final ProxyedApp app = apps[position];
|
||||
|
||||
entry.icon.setTag(app.getUid());
|
||||
|
||||
dm.DisplayImage(app.getUid(), (Activity) convertView.getContext(), entry.icon);
|
||||
|
||||
entry.text.setText(app.getName());
|
||||
|
||||
final CheckBox box = entry.box;
|
||||
box.setTag(app);
|
||||
box.setChecked(app.isProxyed());
|
||||
|
||||
entry.text.setTag(box);
|
||||
|
||||
return convertView;
|
||||
}
|
||||
};
|
||||
|
||||
appsLoaded = true;
|
||||
}
|
||||
|
||||
/** Called an application is check/unchecked */
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
final ProxyedApp app = (ProxyedApp) buttonView.getTag();
|
||||
if (app != null) {
|
||||
app.setProxyed(isChecked);
|
||||
}
|
||||
|
||||
private static class ListEntry {
|
||||
private CheckBox box;
|
||||
private TextView text;
|
||||
private ImageView icon;
|
||||
saveAppSettings(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
|
||||
CheckBox cbox = (CheckBox) v.getTag();
|
||||
|
||||
final ProxyedApp app = (ProxyedApp) cbox.getTag();
|
||||
if (app != null) {
|
||||
app.setProxyed(!app.isProxyed());
|
||||
cbox.setChecked(app.isProxyed());
|
||||
}
|
||||
|
||||
saveAppSettings(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
this.setContentView(R.layout.layout_apps);
|
||||
|
||||
this.dm = ImageLoaderFactory.getImageLoader(this);
|
||||
|
||||
this.overlay = (TextView) View.inflate(this, R.layout.overlay, null);
|
||||
getWindowManager().addView(overlay,
|
||||
new WindowManager.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT,
|
||||
WindowManager.LayoutParams.TYPE_APPLICATION,
|
||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT));
|
||||
|
||||
mAppManager = this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
new Thread() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
handler.sendEmptyMessage(MSG_LOAD_START);
|
||||
|
||||
listApps = (ListView) findViewById(R.id.applistview);
|
||||
|
||||
if (!appsLoaded) loadApps();
|
||||
handler.sendEmptyMessage(MSG_LOAD_FINISH);
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see android.app.Activity#onStop()
|
||||
*/
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
|
||||
// Log.d(getClass().getName(),"Exiting Preferences");
|
||||
}
|
||||
|
||||
public void saveAppSettings(Context context) {
|
||||
if (apps == null) return;
|
||||
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
|
||||
// final SharedPreferences prefs =
|
||||
// context.getSharedPreferences(PREFS_KEY, 0);
|
||||
|
||||
StringBuilder tordApps = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < apps.length; i++) {
|
||||
if (apps[i].isProxyed()) {
|
||||
tordApps.append(apps[i].getUsername());
|
||||
tordApps.append("|");
|
||||
}
|
||||
}
|
||||
|
||||
Editor edit = prefs.edit();
|
||||
edit.putString(PREFS_KEY_PROXYED, tordApps.toString());
|
||||
edit.commit();
|
||||
}
|
||||
|
||||
private static class ListEntry {
|
||||
private CheckBox box;
|
||||
private TextView text;
|
||||
private ImageView icon;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,46 +27,41 @@ import java.io.FileDescriptor;
|
||||
*/
|
||||
|
||||
public class Exec {
|
||||
static {
|
||||
java.lang.System.loadLibrary("exec");
|
||||
}
|
||||
static {
|
||||
java.lang.System.loadLibrary("exec");
|
||||
}
|
||||
|
||||
/**
|
||||
* Close a given file descriptor.
|
||||
*/
|
||||
public static native void close(FileDescriptor fd);
|
||||
/** Close a given file descriptor. */
|
||||
public static native void close(FileDescriptor fd);
|
||||
|
||||
/**
|
||||
* Create a subprocess. Differs from java.lang.ProcessBuilder in that a pty
|
||||
* is used to communicate with the subprocess.
|
||||
* <p/>
|
||||
* Callers are responsible for calling Exec.close() on the returned file
|
||||
* descriptor.
|
||||
*
|
||||
* @param rdt Whether redirect stdout and stderr
|
||||
* @param cmd The command to execute.
|
||||
* @param args An array of arguments to the command.
|
||||
* @param envVars An array of strings of the form "VAR=value" to be added to the
|
||||
* environment of the process.
|
||||
* @param scripts The scripts to execute.
|
||||
* @param processId A one-element array to which the process ID of the started
|
||||
* process will be written.
|
||||
* @return File descriptor
|
||||
*/
|
||||
public static native FileDescriptor createSubprocess(int rdt, String cmd,
|
||||
String[] args, String[] envVars,
|
||||
String scripts, int[] processId);
|
||||
/**
|
||||
* Create a subprocess. Differs from java.lang.ProcessBuilder in that a pty
|
||||
* is used to communicate with the subprocess.
|
||||
* <p/>
|
||||
* Callers are responsible for calling Exec.close() on the returned file
|
||||
* descriptor.
|
||||
*
|
||||
* @param rdt Whether redirect stdout and stderr
|
||||
* @param cmd The command to execute.
|
||||
* @param args An array of arguments to the command.
|
||||
* @param envVars An array of strings of the form "VAR=value" to be added to the
|
||||
* environment of the process.
|
||||
* @param scripts The scripts to execute.
|
||||
* @param processId A one-element array to which the process ID of the started
|
||||
* process will be written.
|
||||
* @return File descriptor
|
||||
*/
|
||||
public static native FileDescriptor createSubprocess(int rdt, String cmd, String[] args,
|
||||
String[] envVars, String scripts, int[] processId);
|
||||
|
||||
/**
|
||||
* Send SIGHUP to a process group.
|
||||
*/
|
||||
public static native void hangupProcessGroup(int processId);
|
||||
/** Send SIGHUP to a process group. */
|
||||
public static native void hangupProcessGroup(int processId);
|
||||
|
||||
/**
|
||||
* Causes the calling thread to wait for the process associated with the
|
||||
* receiver to finish executing.
|
||||
*
|
||||
* @return The exit value of the Process being waited on
|
||||
*/
|
||||
public static native int waitFor(int processId);
|
||||
/**
|
||||
* Causes the calling thread to wait for the process associated with the
|
||||
* receiver to finish executing.
|
||||
*
|
||||
* @return The exit value of the Process being waited on
|
||||
*/
|
||||
public static native int waitFor(int processId);
|
||||
}
|
||||
|
||||
@@ -45,7 +45,6 @@ import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
@@ -54,195 +53,190 @@ import java.util.Stack;
|
||||
|
||||
public class ImageLoader {
|
||||
|
||||
final int stub_id = R.drawable.sym_def_app_icon;
|
||||
PhotosQueue photosQueue = new PhotosQueue();
|
||||
PhotosLoader photoLoaderThread = new PhotosLoader();
|
||||
// the simplest in-memory cache implementation. This should be replaced with
|
||||
// something like SoftReference or BitmapOptions.inPurgeable(since 1.6)
|
||||
private HashMap<Integer, Bitmap> cache = new HashMap<Integer, Bitmap>();
|
||||
private File cacheDir;
|
||||
private Context context;
|
||||
final int stub_id = R.drawable.sym_def_app_icon;
|
||||
PhotosQueue photosQueue = new PhotosQueue();
|
||||
PhotosLoader photoLoaderThread = new PhotosLoader();
|
||||
// the simplest in-memory cache implementation. This should be replaced with
|
||||
// something like SoftReference or BitmapOptions.inPurgeable(since 1.6)
|
||||
private HashMap<Integer, Bitmap> cache = new HashMap<Integer, Bitmap>();
|
||||
private File cacheDir;
|
||||
private Context context;
|
||||
|
||||
public ImageLoader(Context c) {
|
||||
// Make the background thead low priority. This way it will not affect
|
||||
// the UI performance
|
||||
photoLoaderThread.setPriority(Thread.NORM_PRIORITY - 1);
|
||||
public ImageLoader(Context c) {
|
||||
// Make the background thead low priority. This way it will not affect
|
||||
// the UI performance
|
||||
photoLoaderThread.setPriority(Thread.NORM_PRIORITY - 1);
|
||||
|
||||
context = c;
|
||||
context = c;
|
||||
|
||||
// Find the dir to save cached images
|
||||
cacheDir = context.getCacheDir();
|
||||
// Find the dir to save cached images
|
||||
cacheDir = context.getCacheDir();
|
||||
}
|
||||
|
||||
public void clearCache() {
|
||||
// clear memory cache
|
||||
cache.clear();
|
||||
|
||||
// clear SD cache
|
||||
File[] files = cacheDir.listFiles();
|
||||
for (File f : files)
|
||||
f.delete();
|
||||
}
|
||||
|
||||
// decodes image and scales it to reduce memory consumption
|
||||
private Bitmap decodeFile(File f) {
|
||||
try {
|
||||
// decode image size
|
||||
BitmapFactory.Options o = new BitmapFactory.Options();
|
||||
o.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeStream(new FileInputStream(f), null, o);
|
||||
|
||||
// Find the correct scale value. It should be the power of 2.
|
||||
final int REQUIRED_SIZE = 70;
|
||||
int width_tmp = o.outWidth, height_tmp = o.outHeight;
|
||||
int scale = 1;
|
||||
while (true) {
|
||||
if (width_tmp / 2 < REQUIRED_SIZE || height_tmp / 2 < REQUIRED_SIZE) break;
|
||||
width_tmp /= 2;
|
||||
height_tmp /= 2;
|
||||
scale *= 2;
|
||||
}
|
||||
|
||||
// decode with inSampleSize
|
||||
BitmapFactory.Options o2 = new BitmapFactory.Options();
|
||||
o2.inSampleSize = scale;
|
||||
return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
|
||||
} catch (FileNotFoundException ignored) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void DisplayImage(int uid, Activity activity, ImageView imageView) {
|
||||
if (cache.containsKey(uid)) {
|
||||
imageView.setImageBitmap(cache.get(uid));
|
||||
} else {
|
||||
queuePhoto(uid, activity, imageView);
|
||||
imageView.setImageResource(stub_id);
|
||||
}
|
||||
}
|
||||
|
||||
private Bitmap getBitmap(int uid) {
|
||||
// I identify images by hashcode. Not a perfect solution, good for the
|
||||
// demo.
|
||||
String filename = String.valueOf(uid);
|
||||
File f = new File(cacheDir, filename);
|
||||
|
||||
// from SD cache
|
||||
Bitmap b = decodeFile(f);
|
||||
if (b != null) return b;
|
||||
|
||||
// from web
|
||||
try {
|
||||
BitmapDrawable icon = (BitmapDrawable) Utils.getAppIcon(context, uid);
|
||||
return icon.getBitmap();
|
||||
} catch (Exception ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void queuePhoto(int uid, Activity activity, ImageView imageView) {
|
||||
// This ImageView may be used for other images before. So there may be
|
||||
// some old tasks in the queue. We need to discard them.
|
||||
photosQueue.Clean(imageView);
|
||||
PhotoToLoad p = new PhotoToLoad(uid, imageView);
|
||||
synchronized (photosQueue.photosToLoad) {
|
||||
photosQueue.photosToLoad.push(p);
|
||||
photosQueue.photosToLoad.notifyAll();
|
||||
}
|
||||
|
||||
public void clearCache() {
|
||||
// clear memory cache
|
||||
cache.clear();
|
||||
// start thread if it's not started yet
|
||||
if (photoLoaderThread.getState() == Thread.State.NEW) photoLoaderThread.start();
|
||||
}
|
||||
|
||||
// clear SD cache
|
||||
File[] files = cacheDir.listFiles();
|
||||
for (File f : files)
|
||||
f.delete();
|
||||
public void stopThread() {
|
||||
photoLoaderThread.interrupt();
|
||||
}
|
||||
|
||||
// Used to display bitmap in the UI thread
|
||||
class BitmapDisplayer implements Runnable {
|
||||
Bitmap bitmap;
|
||||
ImageView imageView;
|
||||
|
||||
public BitmapDisplayer(Bitmap b, ImageView i) {
|
||||
bitmap = b;
|
||||
imageView = i;
|
||||
}
|
||||
|
||||
// decodes image and scales it to reduce memory consumption
|
||||
private Bitmap decodeFile(File f) {
|
||||
try {
|
||||
// decode image size
|
||||
BitmapFactory.Options o = new BitmapFactory.Options();
|
||||
o.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeStream(new FileInputStream(f), null, o);
|
||||
@Override
|
||||
public void run() {
|
||||
if (bitmap != null) {
|
||||
imageView.setImageBitmap(bitmap);
|
||||
} else {
|
||||
imageView.setImageResource(stub_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find the correct scale value. It should be the power of 2.
|
||||
final int REQUIRED_SIZE = 70;
|
||||
int width_tmp = o.outWidth, height_tmp = o.outHeight;
|
||||
int scale = 1;
|
||||
while (true) {
|
||||
if (width_tmp / 2 < REQUIRED_SIZE
|
||||
|| height_tmp / 2 < REQUIRED_SIZE)
|
||||
break;
|
||||
width_tmp /= 2;
|
||||
height_tmp /= 2;
|
||||
scale *= 2;
|
||||
class PhotosLoader extends Thread {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
while (true) {
|
||||
// thread waits until there are any images to load in the
|
||||
// queue
|
||||
if (photosQueue.photosToLoad.size() == 0) {
|
||||
synchronized (photosQueue.photosToLoad) {
|
||||
photosQueue.photosToLoad.wait();
|
||||
}
|
||||
|
||||
// decode with inSampleSize
|
||||
BitmapFactory.Options o2 = new BitmapFactory.Options();
|
||||
o2.inSampleSize = scale;
|
||||
return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
|
||||
} catch (FileNotFoundException ignored) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void DisplayImage(int uid, Activity activity, ImageView imageView) {
|
||||
if (cache.containsKey(uid))
|
||||
imageView.setImageBitmap(cache.get(uid));
|
||||
else {
|
||||
queuePhoto(uid, activity, imageView);
|
||||
imageView.setImageResource(stub_id);
|
||||
}
|
||||
}
|
||||
|
||||
private Bitmap getBitmap(int uid) {
|
||||
// I identify images by hashcode. Not a perfect solution, good for the
|
||||
// demo.
|
||||
String filename = String.valueOf(uid);
|
||||
File f = new File(cacheDir, filename);
|
||||
|
||||
// from SD cache
|
||||
Bitmap b = decodeFile(f);
|
||||
if (b != null)
|
||||
return b;
|
||||
|
||||
// from web
|
||||
try {
|
||||
BitmapDrawable icon = (BitmapDrawable) Utils.getAppIcon(context,
|
||||
uid);
|
||||
return icon.getBitmap();
|
||||
} catch (Exception ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void queuePhoto(int uid, Activity activity, ImageView imageView) {
|
||||
// This ImageView may be used for other images before. So there may be
|
||||
// some old tasks in the queue. We need to discard them.
|
||||
photosQueue.Clean(imageView);
|
||||
PhotoToLoad p = new PhotoToLoad(uid, imageView);
|
||||
synchronized (photosQueue.photosToLoad) {
|
||||
photosQueue.photosToLoad.push(p);
|
||||
photosQueue.photosToLoad.notifyAll();
|
||||
}
|
||||
|
||||
// start thread if it's not started yet
|
||||
if (photoLoaderThread.getState() == Thread.State.NEW)
|
||||
photoLoaderThread.start();
|
||||
}
|
||||
|
||||
public void stopThread() {
|
||||
photoLoaderThread.interrupt();
|
||||
}
|
||||
|
||||
// Used to display bitmap in the UI thread
|
||||
class BitmapDisplayer implements Runnable {
|
||||
Bitmap bitmap;
|
||||
ImageView imageView;
|
||||
|
||||
public BitmapDisplayer(Bitmap b, ImageView i) {
|
||||
bitmap = b;
|
||||
imageView = i;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (bitmap != null)
|
||||
imageView.setImageBitmap(bitmap);
|
||||
else
|
||||
imageView.setImageResource(stub_id);
|
||||
}
|
||||
}
|
||||
|
||||
class PhotosLoader extends Thread {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
while (true) {
|
||||
// thread waits until there are any images to load in the
|
||||
// queue
|
||||
if (photosQueue.photosToLoad.size() == 0)
|
||||
synchronized (photosQueue.photosToLoad) {
|
||||
photosQueue.photosToLoad.wait();
|
||||
}
|
||||
if (photosQueue.photosToLoad.size() != 0) {
|
||||
PhotoToLoad photoToLoad;
|
||||
synchronized (photosQueue.photosToLoad) {
|
||||
photoToLoad = photosQueue.photosToLoad.pop();
|
||||
}
|
||||
Bitmap bmp = getBitmap(photoToLoad.uid);
|
||||
cache.put(photoToLoad.uid, bmp);
|
||||
Object tag = photoToLoad.imageView.getTag();
|
||||
if (tag != null && ((Integer) tag) == photoToLoad.uid) {
|
||||
BitmapDisplayer bd = new BitmapDisplayer(bmp,
|
||||
photoToLoad.imageView);
|
||||
Activity a = (Activity) photoToLoad.imageView
|
||||
.getContext();
|
||||
a.runOnUiThread(bd);
|
||||
}
|
||||
}
|
||||
if (Thread.interrupted())
|
||||
break;
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
// allow thread to exit
|
||||
}
|
||||
if (photosQueue.photosToLoad.size() != 0) {
|
||||
PhotoToLoad photoToLoad;
|
||||
synchronized (photosQueue.photosToLoad) {
|
||||
photoToLoad = photosQueue.photosToLoad.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// stores list of photos to download
|
||||
class PhotosQueue {
|
||||
private final Stack<PhotoToLoad> photosToLoad = new Stack<PhotoToLoad>();
|
||||
|
||||
// removes all instances of this ImageView
|
||||
public void Clean(ImageView image) {
|
||||
synchronized (photosToLoad) {
|
||||
for (int j = 0; j < photosToLoad.size(); ) {
|
||||
if (photosToLoad.get(j).imageView == image) photosToLoad.remove(j);
|
||||
else ++j;
|
||||
}
|
||||
Bitmap bmp = getBitmap(photoToLoad.uid);
|
||||
cache.put(photoToLoad.uid, bmp);
|
||||
Object tag = photoToLoad.imageView.getTag();
|
||||
if (tag != null && ((Integer) tag) == photoToLoad.uid) {
|
||||
BitmapDisplayer bd = new BitmapDisplayer(bmp, photoToLoad.imageView);
|
||||
Activity a = (Activity) photoToLoad.imageView.getContext();
|
||||
a.runOnUiThread(bd);
|
||||
}
|
||||
}
|
||||
if (Thread.interrupted()) break;
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
// allow thread to exit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Task for the queue
|
||||
private class PhotoToLoad {
|
||||
public int uid;
|
||||
public ImageView imageView;
|
||||
// stores list of photos to download
|
||||
class PhotosQueue {
|
||||
private final Stack<PhotoToLoad> photosToLoad = new Stack<PhotoToLoad>();
|
||||
|
||||
public PhotoToLoad(int u, ImageView i) {
|
||||
uid = u;
|
||||
imageView = i;
|
||||
// removes all instances of this ImageView
|
||||
public void Clean(ImageView image) {
|
||||
synchronized (photosToLoad) {
|
||||
for (int j = 0; j < photosToLoad.size(); ) {
|
||||
if (photosToLoad.get(j).imageView == image) {
|
||||
photosToLoad.remove(j);
|
||||
} else {
|
||||
++j;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Task for the queue
|
||||
private class PhotoToLoad {
|
||||
public int uid;
|
||||
public ImageView imageView;
|
||||
|
||||
public PhotoToLoad(int u, ImageView i) {
|
||||
uid = u;
|
||||
imageView = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,12 +42,12 @@ import android.content.Context;
|
||||
|
||||
public class ImageLoaderFactory {
|
||||
|
||||
private static ImageLoader il = null;
|
||||
private static ImageLoader il = null;
|
||||
|
||||
public static ImageLoader getImageLoader(Context context) {
|
||||
if (il == null) {
|
||||
il = new ImageLoader(context);
|
||||
}
|
||||
return il;
|
||||
public static ImageLoader getImageLoader(Context context) {
|
||||
if (il == null) {
|
||||
il = new ImageLoader(context);
|
||||
}
|
||||
return il;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,94 +40,70 @@ package com.github.shadowsocks;
|
||||
|
||||
public class ProxyedApp {
|
||||
|
||||
private boolean enabled;
|
||||
private int uid;
|
||||
private String username;
|
||||
private String procname;
|
||||
private String name;
|
||||
private boolean proxyed = false;
|
||||
private boolean enabled;
|
||||
private int uid;
|
||||
private String username;
|
||||
private String procname;
|
||||
private String name;
|
||||
private boolean proxyed = false;
|
||||
|
||||
/**
|
||||
* @return the name
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
/** @return the name */
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name the name to set
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
/** @param name the name to set */
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the procname
|
||||
*/
|
||||
public String getProcname() {
|
||||
return procname;
|
||||
}
|
||||
/** @return the procname */
|
||||
public String getProcname() {
|
||||
return procname;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param procname the procname to set
|
||||
*/
|
||||
public void setProcname(String procname) {
|
||||
this.procname = procname;
|
||||
}
|
||||
/** @param procname the procname to set */
|
||||
public void setProcname(String procname) {
|
||||
this.procname = procname;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the uid
|
||||
*/
|
||||
public int getUid() {
|
||||
return uid;
|
||||
}
|
||||
/** @return the uid */
|
||||
public int getUid() {
|
||||
return uid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uid the uid to set
|
||||
*/
|
||||
public void setUid(int uid) {
|
||||
this.uid = uid;
|
||||
}
|
||||
/** @param uid the uid to set */
|
||||
public void setUid(int uid) {
|
||||
this.uid = uid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the username
|
||||
*/
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
/** @return the username */
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param username the username to set
|
||||
*/
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
/** @param username the username to set */
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the enabled
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
/** @return the enabled */
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param enabled the enabled to set
|
||||
*/
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
/** @param enabled the enabled to set */
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the proxyed
|
||||
*/
|
||||
public boolean isProxyed() {
|
||||
return proxyed;
|
||||
}
|
||||
/** @return the proxyed */
|
||||
public boolean isProxyed() {
|
||||
return proxyed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param proxyed the proxyed to set
|
||||
*/
|
||||
public void setProxyed(boolean proxyed) {
|
||||
this.proxyed = proxyed;
|
||||
}
|
||||
/** @param proxyed the proxyed to set */
|
||||
public void setProxyed(boolean proxyed) {
|
||||
this.proxyed = proxyed;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -43,16 +43,15 @@ import com.google.analytics.tracking.android.EasyTracker;
|
||||
|
||||
public class ShadowsocksApplication extends Application {
|
||||
|
||||
private static String sTmpDir;
|
||||
private static String sTmpDir;
|
||||
|
||||
public static String getTmpDir() {
|
||||
return sTmpDir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
EasyTracker.getInstance().setContext(this);
|
||||
sTmpDir = getCacheDir().getAbsolutePath();
|
||||
}
|
||||
public static String getTmpDir() {
|
||||
return sTmpDir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
EasyTracker.getInstance().setContext(this);
|
||||
sTmpDir = getCacheDir().getAbsolutePath();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,47 +47,47 @@ import android.preference.PreferenceManager;
|
||||
|
||||
public class ShadowsocksReceiver extends BroadcastReceiver {
|
||||
|
||||
private static final String TAG = "Shadowsocks";
|
||||
private static final String TAG = "Shadowsocks";
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
|
||||
if (intent != null) {
|
||||
final String action = intent.getAction();
|
||||
if (action.equals(Intent.ACTION_SHUTDOWN)) {
|
||||
context.stopService(new Intent(context, ShadowsocksService.class));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
String versionName;
|
||||
try {
|
||||
versionName = context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
versionName = "NONE";
|
||||
}
|
||||
|
||||
final boolean isAutoConnect = settings.getBoolean("isAutoConnect", false);
|
||||
final boolean isInstalled = settings.getBoolean(versionName, false);
|
||||
|
||||
if (isAutoConnect && isInstalled) {
|
||||
final String portText = settings.getString("port", "");
|
||||
if (portText == null || portText.length() <= 0) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
int port = Integer.valueOf(portText);
|
||||
if (port <= 1024) {
|
||||
return;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return;
|
||||
}
|
||||
|
||||
Intent it = new Intent(context, ShadowsocksService.class);
|
||||
context.startService(it);
|
||||
}
|
||||
if (intent != null) {
|
||||
final String action = intent.getAction();
|
||||
if (action.equals(Intent.ACTION_SHUTDOWN)) {
|
||||
context.stopService(new Intent(context, ShadowsocksService.class));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
String versionName;
|
||||
try {
|
||||
versionName =
|
||||
context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
versionName = "NONE";
|
||||
}
|
||||
|
||||
final boolean isAutoConnect = settings.getBoolean("isAutoConnect", false);
|
||||
final boolean isInstalled = settings.getBoolean(versionName, false);
|
||||
|
||||
if (isAutoConnect && isInstalled) {
|
||||
final String portText = settings.getString("port", "");
|
||||
if (portText == null || portText.length() <= 0) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
int port = Integer.valueOf(portText);
|
||||
if (port <= 1024) {
|
||||
return;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return;
|
||||
}
|
||||
|
||||
Intent it = new Intent(context, ShadowsocksService.class);
|
||||
context.startService(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -39,9 +39,9 @@
|
||||
package com.github.shadowsocks;
|
||||
|
||||
public class System {
|
||||
static {
|
||||
java.lang.System.loadLibrary("system");
|
||||
}
|
||||
static {
|
||||
java.lang.System.loadLibrary("system");
|
||||
}
|
||||
|
||||
public static native void exec(String cmd);
|
||||
public static native void exec(String cmd);
|
||||
}
|
||||
|
||||
@@ -45,471 +45,451 @@ import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
import org.apache.http.conn.util.InetAddressUtils;
|
||||
|
||||
import java.io.*;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.apache.http.conn.util.InetAddressUtils;
|
||||
|
||||
public class Utils {
|
||||
|
||||
public final static String TAG = "Shadowsocks";
|
||||
public final static String ABI_PROP = "ro.product.cpu.abi";
|
||||
public final static String ABI2_PROP = "ro.product.cpu.abi2";
|
||||
public final static String ARM_ABI = "arm";
|
||||
public final static String X86_ABI = "x86";
|
||||
public final static String MIPS_ABI = "mips";
|
||||
public final static String DEFAULT_SHELL = "/system/bin/sh";
|
||||
public final static String DEFAULT_ROOT = "/system/bin/su";
|
||||
public final static String ALTERNATIVE_ROOT = "/system/xbin/su";
|
||||
public final static String DEFAULT_IPTABLES = "/data/data/com.github.shadowsocks/iptables";
|
||||
public final static String ALTERNATIVE_IPTABLES = "/system/bin/iptables";
|
||||
public final static int TIME_OUT = -99;
|
||||
private static boolean initialized = false;
|
||||
private static int hasRedirectSupport = -1;
|
||||
private static int isRoot = -1;
|
||||
private static String shell = null;
|
||||
private static String root_shell = null;
|
||||
private static String iptables = null;
|
||||
private static String data_path = null;
|
||||
public final static String TAG = "Shadowsocks";
|
||||
public final static String ABI_PROP = "ro.product.cpu.abi";
|
||||
public final static String ABI2_PROP = "ro.product.cpu.abi2";
|
||||
public final static String ARM_ABI = "arm";
|
||||
public final static String X86_ABI = "x86";
|
||||
public final static String MIPS_ABI = "mips";
|
||||
public final static String DEFAULT_SHELL = "/system/bin/sh";
|
||||
public final static String DEFAULT_ROOT = "/system/bin/su";
|
||||
public final static String ALTERNATIVE_ROOT = "/system/xbin/su";
|
||||
public final static String DEFAULT_IPTABLES = "/data/data/com.github.shadowsocks/iptables";
|
||||
public final static String ALTERNATIVE_IPTABLES = "/system/bin/iptables";
|
||||
public final static int TIME_OUT = -99;
|
||||
private static boolean initialized = false;
|
||||
private static int hasRedirectSupport = -1;
|
||||
private static int isRoot = -1;
|
||||
private static String shell = null;
|
||||
private static String root_shell = null;
|
||||
private static String iptables = null;
|
||||
private static String data_path = null;
|
||||
|
||||
/**
|
||||
* Get IP address from first non-localhost interface
|
||||
*/
|
||||
public static boolean isIPv6Support() {
|
||||
try {
|
||||
List<NetworkInterface> interfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
|
||||
for (NetworkInterface intf : interfaces) {
|
||||
List<InetAddress> addrs = Collections.list(intf.getInetAddresses());
|
||||
for (InetAddress addr : addrs) {
|
||||
if (!addr.isLoopbackAddress() && !addr.isLinkLocalAddress()) {
|
||||
String sAddr = addr.getHostAddress().toUpperCase();
|
||||
boolean isIPv6 = InetAddressUtils.isIPv6Address(sAddr);
|
||||
if (isIPv6) {
|
||||
Log.d(TAG, "IPv6 address detected");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
// Ignore
|
||||
} // for now eat exceptions
|
||||
return false;
|
||||
}
|
||||
|
||||
public static String getABI() {
|
||||
String abi = getSystemProperty(ABI_PROP);
|
||||
if (abi != null) {
|
||||
if (abi.toLowerCase().contains(ARM_ABI)) {
|
||||
return ARM_ABI;
|
||||
} else if (abi.toLowerCase().contains(X86_ABI)) {
|
||||
return X86_ABI;
|
||||
/** Get IP address from first non-localhost interface */
|
||||
public static boolean isIPv6Support() {
|
||||
try {
|
||||
List<NetworkInterface> interfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
|
||||
for (NetworkInterface intf : interfaces) {
|
||||
List<InetAddress> addrs = Collections.list(intf.getInetAddresses());
|
||||
for (InetAddress addr : addrs) {
|
||||
if (!addr.isLoopbackAddress() && !addr.isLinkLocalAddress()) {
|
||||
String sAddr = addr.getHostAddress().toUpperCase();
|
||||
boolean isIPv6 = InetAddressUtils.isIPv6Address(sAddr);
|
||||
if (isIPv6) {
|
||||
Log.d(TAG, "IPv6 address detected");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
// Ignore
|
||||
} // for now eat exceptions
|
||||
return false;
|
||||
}
|
||||
|
||||
public static String getABI() {
|
||||
String abi = getSystemProperty(ABI_PROP);
|
||||
if (abi != null) {
|
||||
if (abi.toLowerCase().contains(ARM_ABI)) {
|
||||
return ARM_ABI;
|
||||
} else if (abi.toLowerCase().contains(X86_ABI)) {
|
||||
return X86_ABI;
|
||||
}
|
||||
}
|
||||
return ARM_ABI;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a SystemProperty
|
||||
*
|
||||
* @param propName The Property to retrieve
|
||||
* @return The Property, or NULL if not found
|
||||
*/
|
||||
private static String getSystemProperty(String propName) {
|
||||
String line;
|
||||
BufferedReader input = null;
|
||||
try {
|
||||
Process p = Runtime.getRuntime().exec("getprop " + propName);
|
||||
input = new BufferedReader(new InputStreamReader(p.getInputStream()), 1024);
|
||||
line = input.readLine();
|
||||
input.close();
|
||||
} catch (IOException ex) {
|
||||
Log.e(TAG, "Unable to read sysprop " + propName, ex);
|
||||
return null;
|
||||
} finally {
|
||||
if (input != null) {
|
||||
try {
|
||||
input.close();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Exception while closing InputStream", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
private static void checkIptables() {
|
||||
|
||||
if (!isRoot()) {
|
||||
iptables = DEFAULT_IPTABLES;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check iptables binary
|
||||
iptables = DEFAULT_IPTABLES;
|
||||
|
||||
String lines = null;
|
||||
|
||||
boolean compatible = false;
|
||||
boolean version = false;
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String command = iptables + " --version\n" + iptables + " -L -t nat -n\n" + "exit\n";
|
||||
|
||||
int exitcode = runScript(command, sb, 10 * 1000, true);
|
||||
|
||||
if (exitcode == TIME_OUT) return;
|
||||
|
||||
lines = sb.toString();
|
||||
|
||||
if (lines.contains("OUTPUT")) {
|
||||
compatible = true;
|
||||
}
|
||||
if (lines.contains("v1.4.")) {
|
||||
version = true;
|
||||
}
|
||||
|
||||
if (!compatible || !version) {
|
||||
iptables = ALTERNATIVE_IPTABLES;
|
||||
if (!new File(iptables).exists()) iptables = "iptables";
|
||||
}
|
||||
}
|
||||
|
||||
public static Drawable getAppIcon(Context c, int uid) {
|
||||
PackageManager pm = c.getPackageManager();
|
||||
Drawable appIcon = c.getResources().getDrawable(android.R.drawable.sym_def_app_icon);
|
||||
String[] packages = pm.getPackagesForUid(uid);
|
||||
|
||||
if (packages != null) {
|
||||
if (packages.length == 1) {
|
||||
try {
|
||||
ApplicationInfo appInfo = pm.getApplicationInfo(packages[0], 0);
|
||||
appIcon = pm.getApplicationIcon(appInfo);
|
||||
} catch (NameNotFoundException e) {
|
||||
Log.e(c.getPackageName(), "No package found matching with the uid " + uid);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.e(c.getPackageName(), "Package not found for uid " + uid);
|
||||
}
|
||||
|
||||
return appIcon;
|
||||
}
|
||||
|
||||
public static String getDataPath(Context ctx) {
|
||||
|
||||
if (data_path == null) {
|
||||
|
||||
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
|
||||
data_path = Environment.getExternalStorageDirectory().getAbsolutePath();
|
||||
} else {
|
||||
data_path = ctx.getFilesDir().getAbsolutePath();
|
||||
}
|
||||
|
||||
Log.d(TAG, "Python Data Path: " + data_path);
|
||||
}
|
||||
|
||||
return data_path;
|
||||
}
|
||||
|
||||
public static boolean getHasRedirectSupport() {
|
||||
if (hasRedirectSupport == -1) initHasRedirectSupported();
|
||||
return hasRedirectSupport == 1;
|
||||
}
|
||||
|
||||
public static String getIptables() {
|
||||
if (iptables == null) checkIptables();
|
||||
return iptables;
|
||||
}
|
||||
|
||||
private static String getShell() {
|
||||
if (shell == null) {
|
||||
shell = DEFAULT_SHELL;
|
||||
if (!new File(shell).exists()) shell = "sh";
|
||||
}
|
||||
return shell;
|
||||
}
|
||||
|
||||
public static void initHasRedirectSupported() {
|
||||
|
||||
if (!Utils.isRoot()) return;
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String command =
|
||||
Utils.getIptables() + " -t nat -A OUTPUT -p udp --dport 54 -j REDIRECT --to 8154";
|
||||
|
||||
int exitcode = runScript(command, sb, 10 * 1000, true);
|
||||
|
||||
String lines = sb.toString();
|
||||
|
||||
hasRedirectSupport = 1;
|
||||
|
||||
// flush the check command
|
||||
Utils.runRootCommand(command.replace("-A", "-D"));
|
||||
|
||||
if (exitcode == TIME_OUT) return;
|
||||
|
||||
if (lines.contains("No chain/target/match")) {
|
||||
hasRedirectSupport = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isInitialized() {
|
||||
if (initialized) {
|
||||
return true;
|
||||
} else {
|
||||
initialized = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isRoot() {
|
||||
|
||||
if (isRoot != -1) return isRoot == 1;
|
||||
|
||||
// switch between binaries
|
||||
if (new File(DEFAULT_ROOT).exists()) {
|
||||
root_shell = DEFAULT_ROOT;
|
||||
} else if (new File(ALTERNATIVE_ROOT).exists()) {
|
||||
root_shell = ALTERNATIVE_ROOT;
|
||||
} else {
|
||||
root_shell = "su";
|
||||
}
|
||||
|
||||
String lines = null;
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String command = "ls /\n exit\n";
|
||||
|
||||
int exitcode = runScript(command, sb, 10 * 1000, true);
|
||||
|
||||
if (exitcode == TIME_OUT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
lines = sb.toString();
|
||||
|
||||
if (lines.contains("system")) {
|
||||
isRoot = 1;
|
||||
}
|
||||
|
||||
return isRoot == 1;
|
||||
}
|
||||
|
||||
public static boolean runCommand(String command) {
|
||||
|
||||
return runCommand(command, 10 * 1000);
|
||||
}
|
||||
|
||||
public static boolean runCommand(String command, int timeout) {
|
||||
|
||||
Log.d(TAG, command);
|
||||
|
||||
runScript(command, null, timeout, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean runRootCommand(String command) {
|
||||
|
||||
return runRootCommand(command, 10 * 1000);
|
||||
}
|
||||
|
||||
public static boolean runRootCommand(String command, int timeout) {
|
||||
|
||||
if (!isRoot()) {
|
||||
Log.e(TAG, "Cannot get ROOT permission: " + root_shell);
|
||||
return false;
|
||||
}
|
||||
|
||||
Log.d(TAG, command);
|
||||
|
||||
runScript(command, null, timeout, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private synchronized static int runScript(String script, StringBuilder result, long timeout,
|
||||
boolean asroot) {
|
||||
final ScriptRunner runner = new ScriptRunner(script, result, asroot);
|
||||
runner.start();
|
||||
try {
|
||||
if (timeout > 0) {
|
||||
runner.join(timeout);
|
||||
} else {
|
||||
runner.join();
|
||||
}
|
||||
if (runner.isAlive()) {
|
||||
// Timed-out
|
||||
runner.destroy();
|
||||
runner.join(1000);
|
||||
return TIME_OUT;
|
||||
}
|
||||
} catch (InterruptedException ex) {
|
||||
return TIME_OUT;
|
||||
}
|
||||
return runner.exitcode;
|
||||
}
|
||||
|
||||
/** Internal thread used to execute scripts (as root or not). */
|
||||
private static final class ScriptRunner extends Thread {
|
||||
private final String scripts;
|
||||
private final StringBuilder result;
|
||||
private final boolean asroot;
|
||||
public int exitcode = -1;
|
||||
|
||||
private int[] pid = new int[1];
|
||||
private FileDescriptor pipe;
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
if (pid[0] != -1) {
|
||||
Exec.hangupProcessGroup(pid[0]);
|
||||
pid[0] = -1;
|
||||
}
|
||||
if (pipe != null) {
|
||||
Exec.close(pipe);
|
||||
pipe = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a SystemProperty
|
||||
* Creates a new script runner.
|
||||
*
|
||||
* @param propName The Property to retrieve
|
||||
* @return The Property, or NULL if not found
|
||||
* @param script script to run
|
||||
* @param result result output
|
||||
* @param asroot if true, executes the script as root
|
||||
*/
|
||||
private static String getSystemProperty(String propName) {
|
||||
String line;
|
||||
BufferedReader input = null;
|
||||
try {
|
||||
Process p = Runtime.getRuntime().exec("getprop " + propName);
|
||||
input = new BufferedReader(new InputStreamReader(p.getInputStream()), 1024);
|
||||
line = input.readLine();
|
||||
input.close();
|
||||
} catch (IOException ex) {
|
||||
Log.e(TAG, "Unable to read sysprop " + propName, ex);
|
||||
return null;
|
||||
} finally {
|
||||
if (input != null) {
|
||||
try {
|
||||
input.close();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Exception while closing InputStream", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return line;
|
||||
public ScriptRunner(String script, StringBuilder result, boolean asroot) {
|
||||
this.scripts = script;
|
||||
this.result = result;
|
||||
this.asroot = asroot;
|
||||
}
|
||||
|
||||
private static void checkIptables() {
|
||||
|
||||
if (!isRoot()) {
|
||||
iptables = DEFAULT_IPTABLES;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check iptables binary
|
||||
iptables = DEFAULT_IPTABLES;
|
||||
|
||||
String lines = null;
|
||||
|
||||
boolean compatible = false;
|
||||
boolean version = false;
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String command = iptables + " --version\n" + iptables
|
||||
+ " -L -t nat -n\n" + "exit\n";
|
||||
|
||||
int exitcode = runScript(command, sb, 10 * 1000, true);
|
||||
|
||||
if (exitcode == TIME_OUT)
|
||||
return;
|
||||
|
||||
lines = sb.toString();
|
||||
|
||||
if (lines.contains("OUTPUT")) {
|
||||
compatible = true;
|
||||
}
|
||||
if (lines.contains("v1.4.")) {
|
||||
version = true;
|
||||
}
|
||||
|
||||
if (!compatible || !version) {
|
||||
iptables = ALTERNATIVE_IPTABLES;
|
||||
if (!new File(iptables).exists())
|
||||
iptables = "iptables";
|
||||
}
|
||||
private FileDescriptor createSubprocess(int[] processId, String cmd) {
|
||||
ArrayList<String> argList = parse(cmd);
|
||||
String arg0 = argList.get(0);
|
||||
String[] args = argList.toArray(new String[1]);
|
||||
|
||||
return Exec.createSubprocess(result != null ? 1 : 0, arg0, args, null, scripts + "\nexit\n",
|
||||
processId);
|
||||
}
|
||||
|
||||
public static Drawable getAppIcon(Context c, int uid) {
|
||||
PackageManager pm = c.getPackageManager();
|
||||
Drawable appIcon = c.getResources().getDrawable(
|
||||
android.R.drawable.sym_def_app_icon);
|
||||
String[] packages = pm.getPackagesForUid(uid);
|
||||
|
||||
if (packages != null) {
|
||||
if (packages.length == 1) {
|
||||
try {
|
||||
ApplicationInfo appInfo = pm.getApplicationInfo(
|
||||
packages[0], 0);
|
||||
appIcon = pm.getApplicationIcon(appInfo);
|
||||
} catch (NameNotFoundException e) {
|
||||
Log.e(c.getPackageName(),
|
||||
"No package found matching with the uid " + uid);
|
||||
}
|
||||
private ArrayList<String> parse(String cmd) {
|
||||
final int PLAIN = 0;
|
||||
final int WHITESPACE = 1;
|
||||
final int INQUOTE = 2;
|
||||
int state = WHITESPACE;
|
||||
ArrayList<String> result = new ArrayList<String>();
|
||||
int cmdLen = cmd.length();
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (int i = 0; i < cmdLen; i++) {
|
||||
char c = cmd.charAt(i);
|
||||
if (state == PLAIN) {
|
||||
if (Character.isWhitespace(c)) {
|
||||
result.add(builder.toString());
|
||||
builder.delete(0, builder.length());
|
||||
state = WHITESPACE;
|
||||
} else if (c == '"') {
|
||||
state = INQUOTE;
|
||||
} else {
|
||||
builder.append(c);
|
||||
}
|
||||
} else if (state == WHITESPACE) {
|
||||
if (Character.isWhitespace(c)) {
|
||||
// do nothing
|
||||
} else if (c == '"') {
|
||||
state = INQUOTE;
|
||||
} else {
|
||||
state = PLAIN;
|
||||
builder.append(c);
|
||||
}
|
||||
} else if (state == INQUOTE) {
|
||||
if (c == '\\') {
|
||||
if (i + 1 < cmdLen) {
|
||||
i += 1;
|
||||
builder.append(cmd.charAt(i));
|
||||
}
|
||||
} else if (c == '"') {
|
||||
state = PLAIN;
|
||||
} else {
|
||||
builder.append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (builder.length() > 0) {
|
||||
result.add(builder.toString());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
pid[0] = -1;
|
||||
|
||||
try {
|
||||
|
||||
if (this.asroot) {
|
||||
// Create the "su" request to run the script
|
||||
pipe = createSubprocess(pid, root_shell);
|
||||
} else {
|
||||
Log.e(c.getPackageName(), "Package not found for uid " + uid);
|
||||
// Create the "sh" request to run the script
|
||||
pipe = createSubprocess(pid, getShell());
|
||||
}
|
||||
|
||||
return appIcon;
|
||||
}
|
||||
|
||||
public static String getDataPath(Context ctx) {
|
||||
|
||||
if (data_path == null) {
|
||||
|
||||
if (Environment.MEDIA_MOUNTED.equals(Environment
|
||||
.getExternalStorageState())) {
|
||||
data_path = Environment.getExternalStorageDirectory()
|
||||
.getAbsolutePath();
|
||||
} else {
|
||||
data_path = ctx.getFilesDir().getAbsolutePath();
|
||||
}
|
||||
|
||||
Log.d(TAG, "Python Data Path: " + data_path);
|
||||
}
|
||||
|
||||
return data_path;
|
||||
}
|
||||
|
||||
public static boolean getHasRedirectSupport() {
|
||||
if (hasRedirectSupport == -1)
|
||||
initHasRedirectSupported();
|
||||
return hasRedirectSupport == 1;
|
||||
}
|
||||
|
||||
public static String getIptables() {
|
||||
if (iptables == null)
|
||||
checkIptables();
|
||||
return iptables;
|
||||
}
|
||||
|
||||
private static String getShell() {
|
||||
if (shell == null) {
|
||||
shell = DEFAULT_SHELL;
|
||||
if (!new File(shell).exists())
|
||||
shell = "sh";
|
||||
}
|
||||
return shell;
|
||||
}
|
||||
|
||||
public static void initHasRedirectSupported() {
|
||||
|
||||
if (!Utils.isRoot())
|
||||
return;
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String command = Utils.getIptables()
|
||||
+ " -t nat -A OUTPUT -p udp --dport 54 -j REDIRECT --to 8154";
|
||||
|
||||
int exitcode = runScript(command, sb, 10 * 1000, true);
|
||||
|
||||
String lines = sb.toString();
|
||||
|
||||
hasRedirectSupport = 1;
|
||||
|
||||
// flush the check command
|
||||
Utils.runRootCommand(command.replace("-A", "-D"));
|
||||
|
||||
if (exitcode == TIME_OUT)
|
||||
return;
|
||||
|
||||
if (lines.contains("No chain/target/match")) {
|
||||
hasRedirectSupport = 0;
|
||||
if (pid[0] != -1) {
|
||||
exitcode = Exec.waitFor(pid[0]);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isInitialized() {
|
||||
if (initialized)
|
||||
return true;
|
||||
else {
|
||||
initialized = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static boolean isRoot() {
|
||||
if (result == null || pipe == null) return;
|
||||
|
||||
if (isRoot != -1)
|
||||
return isRoot == 1;
|
||||
final InputStream stdout = new FileInputStream(pipe);
|
||||
final byte buf[] = new byte[8192];
|
||||
int read = 0;
|
||||
|
||||
// switch between binaries
|
||||
if (new File(DEFAULT_ROOT).exists()) {
|
||||
root_shell = DEFAULT_ROOT;
|
||||
} else if (new File(ALTERNATIVE_ROOT).exists()) {
|
||||
root_shell = ALTERNATIVE_ROOT;
|
||||
} else {
|
||||
root_shell = "su";
|
||||
// Read stdout
|
||||
while (stdout.available() > 0) {
|
||||
read = stdout.read(buf);
|
||||
result.append(new String(buf, 0, read));
|
||||
}
|
||||
|
||||
String lines = null;
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String command = "ls /\n exit\n";
|
||||
|
||||
int exitcode = runScript(command, sb, 10 * 1000, true);
|
||||
|
||||
if (exitcode == TIME_OUT) {
|
||||
return false;
|
||||
} catch (Exception ex) {
|
||||
Log.e(TAG, "Cannot execute command", ex);
|
||||
if (result != null) result.append("\n").append(ex);
|
||||
} finally {
|
||||
if (pipe != null) {
|
||||
Exec.close(pipe);
|
||||
}
|
||||
|
||||
lines = sb.toString();
|
||||
|
||||
if (lines.contains("system")) {
|
||||
isRoot = 1;
|
||||
}
|
||||
|
||||
return isRoot == 1;
|
||||
}
|
||||
|
||||
public static boolean runCommand(String command) {
|
||||
|
||||
return runCommand(command, 10 * 1000);
|
||||
|
||||
}
|
||||
|
||||
public static boolean runCommand(String command, int timeout) {
|
||||
|
||||
Log.d(TAG, command);
|
||||
|
||||
runScript(command, null, timeout, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean runRootCommand(String command) {
|
||||
|
||||
return runRootCommand(command, 10 * 1000);
|
||||
|
||||
}
|
||||
|
||||
public static boolean runRootCommand(String command, int timeout) {
|
||||
|
||||
if (!isRoot()) {
|
||||
Log.e(TAG, "Cannot get ROOT permission: " + root_shell);
|
||||
return false;
|
||||
}
|
||||
|
||||
Log.d(TAG, command);
|
||||
|
||||
runScript(command, null, timeout, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private synchronized static int runScript(String script, StringBuilder result,
|
||||
long timeout, boolean asroot) {
|
||||
final ScriptRunner runner = new ScriptRunner(script, result, asroot);
|
||||
runner.start();
|
||||
try {
|
||||
if (timeout > 0) {
|
||||
runner.join(timeout);
|
||||
} else {
|
||||
runner.join();
|
||||
}
|
||||
if (runner.isAlive()) {
|
||||
// Timed-out
|
||||
runner.destroy();
|
||||
runner.join(1000);
|
||||
return TIME_OUT;
|
||||
}
|
||||
} catch (InterruptedException ex) {
|
||||
return TIME_OUT;
|
||||
}
|
||||
return runner.exitcode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal thread used to execute scripts (as root or not).
|
||||
*/
|
||||
private static final class ScriptRunner extends Thread {
|
||||
private final String scripts;
|
||||
private final StringBuilder result;
|
||||
private final boolean asroot;
|
||||
public int exitcode = -1;
|
||||
|
||||
private int[] pid = new int[1];
|
||||
private FileDescriptor pipe;
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
if (pid[0] != -1) {
|
||||
Exec.hangupProcessGroup(pid[0]);
|
||||
pid[0] = -1;
|
||||
}
|
||||
if (pipe != null) {
|
||||
Exec.close(pipe);
|
||||
pipe = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new script runner.
|
||||
*
|
||||
* @param script script to run
|
||||
* @param result result output
|
||||
* @param asroot if true, executes the script as root
|
||||
*/
|
||||
public ScriptRunner(String script, StringBuilder result,
|
||||
boolean asroot) {
|
||||
this.scripts = script;
|
||||
this.result = result;
|
||||
this.asroot = asroot;
|
||||
}
|
||||
|
||||
private FileDescriptor createSubprocess(int[] processId, String cmd) {
|
||||
ArrayList<String> argList = parse(cmd);
|
||||
String arg0 = argList.get(0);
|
||||
String[] args = argList.toArray(new String[1]);
|
||||
|
||||
return Exec.createSubprocess(result != null ? 1 : 0, arg0, args, null,
|
||||
scripts + "\nexit\n", processId);
|
||||
}
|
||||
|
||||
private ArrayList<String> parse(String cmd) {
|
||||
final int PLAIN = 0;
|
||||
final int WHITESPACE = 1;
|
||||
final int INQUOTE = 2;
|
||||
int state = WHITESPACE;
|
||||
ArrayList<String> result = new ArrayList<String>();
|
||||
int cmdLen = cmd.length();
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (int i = 0; i < cmdLen; i++) {
|
||||
char c = cmd.charAt(i);
|
||||
if (state == PLAIN) {
|
||||
if (Character.isWhitespace(c)) {
|
||||
result.add(builder.toString());
|
||||
builder.delete(0, builder.length());
|
||||
state = WHITESPACE;
|
||||
} else if (c == '"') {
|
||||
state = INQUOTE;
|
||||
} else {
|
||||
builder.append(c);
|
||||
}
|
||||
} else if (state == WHITESPACE) {
|
||||
if (Character.isWhitespace(c)) {
|
||||
// do nothing
|
||||
} else if (c == '"') {
|
||||
state = INQUOTE;
|
||||
} else {
|
||||
state = PLAIN;
|
||||
builder.append(c);
|
||||
}
|
||||
} else if (state == INQUOTE) {
|
||||
if (c == '\\') {
|
||||
if (i + 1 < cmdLen) {
|
||||
i += 1;
|
||||
builder.append(cmd.charAt(i));
|
||||
}
|
||||
} else if (c == '"') {
|
||||
state = PLAIN;
|
||||
} else {
|
||||
builder.append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (builder.length() > 0) {
|
||||
result.add(builder.toString());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
pid[0] = -1;
|
||||
|
||||
try {
|
||||
|
||||
if (this.asroot) {
|
||||
// Create the "su" request to run the script
|
||||
pipe = createSubprocess(pid, root_shell);
|
||||
} else {
|
||||
// Create the "sh" request to run the script
|
||||
pipe = createSubprocess(pid, getShell());
|
||||
}
|
||||
|
||||
if (pid[0] != -1) {
|
||||
exitcode = Exec.waitFor(pid[0]);
|
||||
}
|
||||
|
||||
if (result == null || pipe == null) return;
|
||||
|
||||
final InputStream stdout = new FileInputStream(pipe);
|
||||
final byte buf[] = new byte[8192];
|
||||
int read = 0;
|
||||
|
||||
// Read stdout
|
||||
while (stdout.available() > 0) {
|
||||
read = stdout.read(buf);
|
||||
result.append(new String(buf, 0, read));
|
||||
}
|
||||
|
||||
} catch (Exception ex) {
|
||||
Log.e(TAG, "Cannot execute command", ex);
|
||||
if (result != null)
|
||||
result.append("\n").append(ex);
|
||||
} finally {
|
||||
if (pipe != null) {
|
||||
Exec.close(pipe);
|
||||
}
|
||||
if (pid[0] != -1) {
|
||||
Exec.hangupProcessGroup(pid[0]);
|
||||
}
|
||||
}
|
||||
if (pid[0] != -1) {
|
||||
Exec.hangupProcessGroup(pid[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,36 +3,31 @@ 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;
|
||||
private CharSequence mDefaultSummary;
|
||||
|
||||
public SummaryEditTextPreference(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
mDefaultSummary = getSummary();
|
||||
}
|
||||
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, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mDefaultSummary = getSummary();
|
||||
}
|
||||
|
||||
public SummaryEditTextPreference(Context context) {
|
||||
super(context);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void setSummary(CharSequence summary) {
|
||||
if (summary.toString().isEmpty()) {
|
||||
super.setSummary(mDefaultSummary);
|
||||
} else {
|
||||
super.setSummary(summary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user