package com.zte.appopscontrol.firewall;
import java.io.File;
import java.io.IOException;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import com.zte.appopscontrol.R;
import com.zte.appopscontrol.Utils;
import android.app.Activity;
import android.app.ListFragment;
import android.app.LoaderManager;
import android.content.AsyncTaskLoader;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.Loader;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.SearchView;
import android.widget.TextView;
import android.widget.SearchView.OnQueryTextListener;
import android.widget.Toast;
public class FireWallFragment extends ListFragment implements
OnQueryTextListener, LoaderManager.LoaderCallbacks<List<FireWallFragment.AppEntry>> {
// This is the Adapter being used to display the list's data.
AppListAdapter mAdapter;
// If non-null, this is the current filter the user has provided.
String mCurFilter;
TextView mFirewallInfo;
static int wifiRes[] = {R.drawable.ic_firewall_wifi_allow, R.drawable.ic_firewall_wifi_deny};
static int mobileRes[] = {R.drawable.ic_firewall_mobile_allow, R.drawable.ic_firewall_mobile_deny};
private static SharedPreferences mMobilePreferences;
private static SharedPreferences mWifiPreferences;
private static SharedPreferences mPreferences;
public static final String FIREWALL_SHARED_PREFERENCES_NAME = "firewall_control";
public static final String ACTION_REFRESH_UI = "com.zte.appops.firewall_refreshui";
public static final String EXTRA_FIREWALL = "firewall";
public static final String EXTRA_ENABLE = "enable";
private static Context ctx;
private static BatchSetting mSetting;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Give some text to display if there is no data. In a real
// application this would come from a resource.
setEmptyText("No applications");
// We have a menu item to show in action bar.
setHasOptionsMenu(true);
// Create an empty adapter we will use to display the loaded data.
mAdapter = new AppListAdapter(getActivity());
setListAdapter(mAdapter);
// Start out with a progress indicator.
setListShown(false);
//get shared preference
mMobilePreferences = getActivity().getSharedPreferences("mobile", Activity.MODE_PRIVATE);
mWifiPreferences = getActivity().getSharedPreferences("wifi", Activity.MODE_PRIVATE);
mPreferences = getActivity().getSharedPreferences(FIREWALL_SHARED_PREFERENCES_NAME, Activity.MODE_PRIVATE);
mSetting = new BatchSetting();
mSetting.which = null;
mSetting.enable = false;
ctx = getActivity();
// Prepare the loader. Either re-connect with an existing one,
// or start a new one.
getLoaderManager().initLoader(0, null, this);
// it is too slow here
//MyApi.initIptables(getActivity());
// below is to init iptables
final Handler toaster = new Handler() {
public void handleMessage(Message msg) {
if (msg.arg1 != 0) Toast.makeText(ctx, msg.arg1, Toast.LENGTH_SHORT).show();
}
};
new Thread() {
@Override
public void run() {
Log.e("ZQL", "in run");
if (!MyApi.initIptables(ctx)) {
// Error enabling firewall on boot
final Message msg = new Message();
msg.arg1 = R.string.toast_error_enabling;
toaster.sendMessage(msg);
//Api.setEnabled(context, false);
}
MyApi.applySavedIptablesRules(ctx, true);
}
}.start();
}
private void refreshUi() {
Intent intent = new Intent();
intent.setAction(ACTION_REFRESH_UI);
getActivity().sendBroadcast(intent);
}
private static boolean shouldShowSystemApps() {
return mPreferences.getBoolean("show_system_apps", false);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// Place an action bar item for searching.
MenuItem item = menu.add("Search");
item.setIcon(android.R.drawable.ic_menu_search);
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM
| MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW);
SearchView sv = new SearchView(getActivity());
sv.setOnQueryTextListener(this);
item.setActionView(sv);
inflater.inflate(R.menu.firewall_control, menu);
menu.findItem(R.id.show_system_apps).setChecked(shouldShowSystemApps());
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.show_system_apps:
final String prefName = "show_system_apps";
item.setChecked(!item.isChecked());
mPreferences.edit().putBoolean(prefName, item.isChecked()).commit();
refreshUi();
return true;
default:
return super.onContextItemSelected(item);
}
}
@Override
public boolean onQueryTextChange(String newText) {
// Called when the action bar search text has changed. Since this
// is a simple array adapter, we can just have it do the filtering.
mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
mAdapter.getFilter().filter(mCurFilter);
return true;
}
@Override
public boolean onQueryTextSubmit(String query) {
// Don't care about this.
return true;
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
// Insert desired behavior here.
Log.i("LoaderCustom", "Item clicked: " + id);
}
@Override
public Loader<List<AppEntry>> onCreateLoader(int id, Bundle args) {
// This is called when a new Loader needs to be created. This
// sample only has one Loader with no arguments, so it is simple.
return new AppListLoader(getActivity());
}
@Override
public void onLoadFinished(Loader<List<AppEntry>> loader,
List<AppEntry> data) {
// Set the new data in the adapter.
mAdapter.setData(data);
// The list should now be shown.
if (isResumed()) {
setListShown(true);
} else {
setListShownNoAnimation(true);
}
}
@Override
public void onLoaderReset(Loader<List<AppEntry>> loader) {
// Clear the data in the adapter.
mAdapter.setData(null);
}
private static int getWifiRes(boolean enable){
if(enable) return wifiRes[0];
else return wifiRes[1];
}
private static int getMobileRes(boolean enable){
if(enable) return mobileRes[0];
else return mobileRes[1];
}
/**
* This class holds the per-item data in our Loader.
*/
public static class AppEntry {
public AppEntry(AppListLoader loader, ApplicationInfo info) {
mLoader = loader;
mInfo = info;
mApkFile = new File(info.sourceDir);
}
public ApplicationInfo getApplicationInfo() {
return mInfo;
}
public String getLabel() {
return mLabel;
}
public Drawable getIcon() {
if (mIcon == null) {
if (mApkFile.exists()) {
mIcon = mInfo.loadIcon(mLoader.mPm);
return mIcon;
} else {
mMounted = false;
}
} else if (!mMounted) {
// If the app wasn't mounted but is now mounted, reload
// its icon.
if (mApkFile.exists()) {
mMounted = true;
mIcon = mInfo.loadIcon(mLoader.mPm);
return mIcon;
}
} else {
return mIcon;
}
return mLoader.getContext().getResources().getDrawable(
android.R.drawable.sym_def_app_icon);
}
@Override public String toString() {
return mLabel;
}
public boolean isSystemApp() {
if(mInfo != null) {
if((mInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0)
return true;
}
return false;
}
void loadLabel(Context context) {
if (mLabel == null || !mMounted) {
if (!mApkFile.exists()) {
mMounted = false;
mLabel = mInfo.packageName;
} else {
mMounted = true;
CharSequence label = mInfo.loadLabel(context.getPackageManager());
mLabel = label != null ? label.toString() : mInfo.packageName;
}
}
}
void loadSettings(Context context){
mMobileEnable = mMobilePreferences.getBoolean(mInfo.uid+"", true);
mWifiEnable = mWifiPreferences.getBoolean(mInfo.uid+"", true);
}
//boolean isSystemApp()
public boolean isMobileEnable() {
return mMobileEnable;
}
public void setMobileEnable(boolean MobileEnable) {
Editor editor = mMobilePreferences.edit();
editor.putBoolean(mInfo.uid+"", MobileEnable);
editor.commit();
this.mMobileEnable = MobileEnable;
}
public boolean isWifiEnable() {
return mWifiEnable;
}
public void setWifiEnable(boolean WifiEnable) {
Editor editor = mWifiPreferences.edit();
editor.putBoolean(mInfo.uid+"", WifiEnable);
editor.commit();
this.mWifiEnable = WifiEnable;
}
private final AppListLoader mLoader;
private final ApplicationInfo mInfo;
private final File mApkFile;
private String mLabel;
private Drawable mIcon;
private boolean mMounted;
private boolean mMobileEnable;
private boolean mWifiEnable;
}
public static class AppListAdapter extends ArrayAdapter<AppEntry> {
private final LayoutInflater mInflater;
public AppListAdapter(Context context) {
super(context, android.R.layout.simple_list_item_2);
mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public void setData(List<AppEntry> data) {
clear();
if (data != null) {
addAll(data);
}
}
/**
* Populate new items in the list.
*/
@Override public View getView(int position, View convertView, ViewGroup parent) {
View view;
final ImageButton mobileBtn;
final ImageButton wifiBtn;
if (convertView == null) {
view = mInflater.inflate(R.layout.list_item_icon_text, parent, false);
} else {
view = convertView;
}
final AppEntry item = getItem(position);
mobileBtn = (ImageButton)view.findViewById(R.id.button_mobile);
wifiBtn = (ImageButton)view.findViewById(R.id.button_wifi);
mobileBtn.setImageResource(getMobileRes(item.isMobileEnable()));
wifiBtn.setImageResource(getWifiRes(item.isWifiEnable()));
mobileBtn.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
item.setMobileEnable(!item.isMobileEnable());
MyApi.modifyByUID(ctx, item.getApplicationInfo().uid, false, !item.isMobileEnable());
mobileBtn.setImageResource(getMobileRes(item.isMobileEnable()));
}
});
wifiBtn.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
item.setWifiEnable(!item.isWifiEnable());
// try {
// testScript.testRun(ctx);
// } catch (IOException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
MyApi.modifyByUID(ctx, item.getApplicationInfo().uid, true, !item.isWifiEnable());
wifiBtn.setImageResource(getWifiRes(item.isWifiEnable()));
}
});
//Bitmap photo = BitmapFactory.decodeResource(ctx.getResources(), R.drawable.arrow_down);
// set a flag to display a "sys " icon
if( item.isSystemApp()) {
ImageView iv = (ImageView)view.findViewById(R.id.icon);
LayoutParams lp = iv.getLayoutParams();
Rect dst = new Rect(0,0, lp.width, lp.height);
Bitmap photo = Utils.drawableToBitmap(item.getIcon(),dst);
Bitmap mark = BitmapFactory.decodeResource(ctx.getResources(), R.drawable.tag_system);
dst = new Rect(lp.width/3, lp.height-lp.height/3, lp.width, lp.height);
Bitmap newbm = Utils.createBitmap(photo, dst,mark,ctx.getString(R.string.app_system));
((ImageView)view.findViewById(R.id.icon)).setImageBitmap(newbm);
} else {
((ImageView)view.findViewById(R.id.icon)).setImageDrawable(item.getIcon());
}
((TextView)view.findViewById(R.id.text)).setText(item.getLabel());
return view;
}
}
/**
* A custom Loader that loads all of the installed applications.
*/
public static class AppListLoader extends AsyncTaskLoader<List<AppEntry>> {
final InterestingConfigChanges mLastConfig = new InterestingConfigChanges();
final PackageManager mPm;
List<AppEntry> mApps;
PackageIntentReceiver mPackageObserver;
public AppListLoader(Context context) {
super(context);
// Retrieve the package manager for later use; note we don't
// use 'context' directly but instead the save global application
// context returned by getContext().
mPm = getContext().getPackageManager();
}
/**
* This is where the bulk of our work is done. This function is
* called in a background thread and should generate a new set of
* data to be published by the loader.
*/
@Override public List<AppEntry> loadInBackground() {
// Retrieve all known applications.
List<ApplicationInfo> apps = mPm.getInstalledApplications(
PackageManager.GET_UNINSTALLED_PACKAGES |
PackageManager.GET_DISABLED_COMPONENTS);
if (apps == null) {
apps = new ArrayList<ApplicationInfo>();
}
final Context context = getContext();
boolean showSystemApps = shouldShowSystemApps();
// Create corresponding array of entries and load their labels.
final List<AppEntry> entries = new ArrayList<AppEntry>(apps.size());
for (int i=0; i<apps.size(); i++) {
if(apps.get(i).uid < 10000 && apps.get(i).uid != 1000)
continue;
// skip all system apps if they shall not be included
if (!showSystemApps && (apps.get(i).flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
continue;
}
/*
ApplicationInfo appInfo = apps.get(i);
try {
PackageInfo pkgInfo = mPm.getPackageInfo(appInfo.packageName, 0);
String shareUid = pkgInfo.sharedUserId;
} catch (NameNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
*/
AppEntry entry = new AppEntry(this, apps.get(i));
String name = mPm.getNameForUid(apps.get(i).uid);
entry.loadLabel(context);
entry.loadSettings(context);
entries.add(entry);
}
new Thread() {
@Override
public void run() {
Log.e("ZQL", "in run");
if(mSetting.which != null) {
for(AppEntry app: entries) {
if( mSetting.which.equals("mobile")) {
app.setMobileEnable(mSetting.enable);
MyApi.modifyByUID(ctx, app.getApplicationInfo().uid, false, mSetting.enable);
} else {
app.setWifiEnable(mSetting.enable);
MyApi.modifyByUID(ctx, app.getApplicationInfo().uid, true, mSetting.enable);
}
}
};
}
}.start();
// Sort the list.
Collections.sort(entries, ALPHA_COMPARATOR);
//MyApi.initIptables(context);
// Done!
return entries;
}
/**
* Called when there is new data to deliver to the client. The
* super class will take care of delivering it; the implementation
* here just adds a little more logic.
*/
@Override public void deliverResult(List<AppEntry> apps) {
if (isReset()) {
// An async query came in while the loader is stopped. We
// don't need the result.
if (apps != null) {
onReleaseResources(apps);
}
}
List<AppEntry> oldApps = apps;
mApps = apps;
if (isStarted()) {
// If the Loader is currently started, we can immediately
// deliver its results.
super.deliverResult(apps);
}
// At this point we can release the resources associated with
// 'oldApps' if needed; now that the new result is delivered we
// know that it is no longer in use.
if (oldApps != null) {
onReleaseResources(oldApps);
}
}
/**
* Handles a request to start the Loader.
*/
@Override protected void onStartLoading() {
if (mApps != null) {
// If we currently have a result available, deliver it
// immediately.
deliverResult(mApps);
}
// Start watching for changes in the app data.
if (mPackageObserver == null) {
mPackageObserver = new PackageIntentReceiver(this);
}
// Has something interesting in the configuration changed since we
// last built the app list?
boolean configChange = mLastConfig.applyNewConfig(getContext().getResources());
if (takeContentChanged() || mApps == null || configChange) {
// If the data has changed since the last time it was loaded
// or is not currently available, start a load.
forceLoad();
}
}
/**
* Handles a request to stop the Loader.
*/
@Override protected void onStopLoading() {
// Attempt to cancel the current load task if possible.
cancelLoad();
}
/**
* Handles a request to cancel a load.
*/
@Override public void onCanceled(List<AppEntry> apps) {
super.onCanceled(apps);
// At this point we can release the resources associated with 'apps'
// if needed.
onReleaseResources(apps);
}
/**
* Handles a request to completely reset the Loader.
*/
@Override protected void onReset() {
super.onReset();
// Ensure the loader is stopped
onStopLoading();
// At this point we can release the resources associated with 'apps'
// if needed.
if (mApps != null) {
onReleaseResources(mApps);
mApps = null;
}
// Stop monitoring for changes.
if (mPackageObserver != null) {
getContext().unregisterReceiver(mPackageObserver);
mPackageObserver = null;
}
}
/**
* Helper function to take care of releasing resources associated
* with an actively loaded data set.
*/
protected void onReleaseResources(List<AppEntry> apps) {
// For a simple List<> there is nothing to do. For something
// like a Cursor, we would close it here.
}
}
/**
* Helper class to look for interesting changes to the installed apps
* so that the loader can be updated.
*/
public static class PackageIntentReceiver extends BroadcastReceiver {
final AppListLoader mLoader;
public PackageIntentReceiver(AppListLoader loader) {
mLoader = loader;
IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
filter.addDataScheme("package");
mLoader.getContext().registerReceiver(this, filter);
// Register for events related to sdcard installation.
IntentFilter sdFilter = new IntentFilter();
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
mLoader.getContext().registerReceiver(this, sdFilter);
IntentFilter refreshFilter = new IntentFilter();
refreshFilter.addAction(ACTION_REFRESH_UI);
mLoader.getContext().registerReceiver(this, refreshFilter);
}
@Override public void onReceive(Context context, Intent intent) {
// Tell the loader about the change.
if(intent.getAction().equals(ACTION_REFRESH_UI)) {
String which = intent.getStringExtra(EXTRA_FIREWALL);
boolean enable = intent.getBooleanExtra(EXTRA_ENABLE , false);
if(which!=null) {
mSetting.enable = enable;
mSetting.which = which;
} else
mSetting.which = null;
}
mLoader.onContentChanged();
}
}
/**
* Helper for determining if the configuration has changed in an interesting
* way so we need to rebuild the app list.
*/
public static class InterestingConfigChanges {
final Configuration mLastConfiguration = new Configuration();
int mLastDensity;
boolean applyNewConfig(Resources res) {
int configChanges = mLastConfiguration.updateFrom(res.getConfiguration());
boolean densityChanged = mLastDensity != res.getDisplayMetrics().densityDpi;
if (densityChanged || (configChanges&(ActivityInfo.CONFIG_LOCALE
|ActivityInfo.CONFIG_UI_MODE|ActivityInfo.CONFIG_SCREEN_LAYOUT)) != 0) {
mLastDensity = res.getDisplayMetrics().densityDpi;
return true;
}
return false;
}
}
/**
* Perform alphabetical comparison of application entry objects.
*/
public static final Comparator<AppEntry> ALPHA_COMPARATOR = new Comparator<AppEntry>() {
private final Collator sCollator = Collator.getInstance();
@Override
public int compare(AppEntry object1, AppEntry object2) {
return sCollator.compare(object1.getLabel(), object2.getLabel());
}
};
public static class testScript{
public static void testRun(Context context) throws IOException{
int code;
final StringBuilder script = new StringBuilder();
StringBuilder res = new StringBuilder();
script.append("iptables -L");
code = MyApi.runScriptAsRoot(context, script.toString(), res);
String msg = res.toString();
Log.e("ZQL", msg);
}
}
class BatchSetting {
String which;
boolean enable;
}
}