/* Copyright (c) 2009, Nathan Freitas, Orbot / The Guardian Project - http://openideals.com/guardian */ /* See LICENSE for licensing information */ package org.shadowsocks; 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 android.app.Activity; import android.app.ProgressDialog; import android.content.Context; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.graphics.PixelFormat; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.preference.PreferenceManager; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.view.WindowManager; 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; public class AppManager extends Activity implements OnCheckedChangeListener, OnClickListener { private static class ListEntry { private CheckBox box; private TextView text; private ImageView icon; } 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("org.proxydroid")) { 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; } private ProxyedApp[] apps = null; private ListView listApps; private AppManager mAppManager; private TextView overlay; private ProgressDialog pd = null; private ListAdapter adapter; private ImageLoader dm; private static final int MSG_LOAD_START = 1; private static final int MSG_LOAD_FINISH = 2; public final static String PREFS_KEY_PROXYED = "Proxyed"; private boolean appsLoaded = false; 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.setOnScrollListener(new OnScrollListener() { 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 == ListView.OnScrollListener.SCROLL_STATE_IDLE) { overlay.setVisibility(View.INVISIBLE); } } }); if (pd != null) { pd.dismiss(); pd = null; } break; } super.handleMessage(msg); } }; 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); } else { // Convert an existing view entry = (ListEntry) convertView.getTag(); } 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); } 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(); } }