/**
* Main application activity.
* This is the screen displayed when you open the application
*
* Copyright (C) 2009-2011 Rodrigo Zechin Rosauro
* Copyright (C) 2011-2012 Umakanthan Chandran
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @author Rodrigo Zechin Rosauro, Umakanthan Chandran
* @version 1.1
*/
package dev.ukanth.ufirewall;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences.Editor;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.support.v4.view.MenuItemCompat;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.Editable;
import android.text.InputType;
import android.text.TextUtils.TruncateAt;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.RadioGroup;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import com.afollestad.materialdialogs.MaterialDialog;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import dev.ukanth.ufirewall.Api.PackageInfoData;
import dev.ukanth.ufirewall.activity.CustomScriptActivity;
import dev.ukanth.ufirewall.activity.HelpActivity;
import dev.ukanth.ufirewall.activity.LogActivity;
import dev.ukanth.ufirewall.activity.RulesActivity;
import dev.ukanth.ufirewall.preferences.PreferencesActivity;
import dev.ukanth.ufirewall.service.RootShell.RootCommand;
import dev.ukanth.ufirewall.util.AppListArrayAdapter;
import dev.ukanth.ufirewall.util.FileDialog;
import dev.ukanth.ufirewall.util.G;
import dev.ukanth.ufirewall.util.ImportApi;
import dev.ukanth.ufirewall.util.PackageComparator;
import eu.chainfire.libsuperuser.Shell;
import haibison.android.lockpattern.LockPatternActivity;
import haibison.android.lockpattern.util.AlpSettings;
import static haibison.android.lockpattern.LockPatternActivity.ACTION_COMPARE_PATTERN;
import static haibison.android.lockpattern.LockPatternActivity.EXTRA_PATTERN;
import static haibison.android.lockpattern.LockPatternActivity.RESULT_FAILED;
import static haibison.android.lockpattern.LockPatternActivity.RESULT_FORGOT_PATTERN;
public class MainActivity extends AppCompatActivity implements AdapterView.OnItemSelectedListener, OnClickListener, SwipeRefreshLayout.OnRefreshListener, RadioGroup.OnCheckedChangeListener {
//private TextView mSelected;
//private DrawerLayout mDrawerLayout;
//private ListView mDrawerList;
private Menu mainMenu;
public boolean isOnPause = false;
private ListView listview = null;
public static boolean dirty = false;
private MaterialDialog plsWait;
private ArrayAdapter<String> spinnerAdapter = null;
private SwipeRefreshLayout mSwipeLayout;
private int index;
private int top;
private List<String> mlocalList = new ArrayList<>(new LinkedHashSet<String>());
private int initDone=0;
private Spinner mSpinner;
private static final int REQ_ENTER_PATTERN = 9755;
private static final int SHOW_ABOUT_RESULT = 1200;
private static final int PREFERENCE_RESULT = 1205;
private static final int SHOW_CUSTOM_SCRIPT = 1201;
private static final int SHOW_RULES_ACTIVITY = 1202;
private static final int SHOW_LOGS_ACTIVITY = 1203;
public boolean isDirty() {
return dirty;
}
public void setDirty(boolean dirty) {
MainActivity.dirty = dirty;
}
/** Called when the activity is first created
* . */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
isOnPause = false;
try {
/* enable hardware acceleration on Android >= 3.0 */
final int FLAG_HARDWARE_ACCELERATED = WindowManager.LayoutParams.class
.getDeclaredField("FLAG_HARDWARE_ACCELERATED").getInt(null);
getWindow().setFlags(FLAG_HARDWARE_ACCELERATED,
FLAG_HARDWARE_ACCELERATED);
} catch (Exception e) {
}
setContentView(R.layout.main);
//CustomActivityOnCrash.install(this);
Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
getWindow().setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
setSupportActionBar(toolbar);
//set onclick listeners
this.findViewById(R.id.label_mode).setOnClickListener(this);
this.findViewById(R.id.img_wifi).setOnClickListener(this);
this.findViewById(R.id.img_reset).setOnClickListener(this);
this.findViewById(R.id.img_invert).setOnClickListener(this);
AlpSettings.Display.setStealthMode(getApplicationContext(), G.enableStealthPattern());
AlpSettings.Display.setMaxRetries(getApplicationContext(), G.getMaxPatternTry());
Api.assertBinaries(this, true);
mSwipeLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_container);
mSwipeLayout.setOnRefreshListener(this);
//one time migration of profiles to new logic
//migrateProfiles();
// Let's do some background stuff
(new Startup()).setContext(this).execute();
}
@Override
public void onRefresh() {
index = 0;
top = 0;
Api.applications = null;
showOrLoadApplications();
mSwipeLayout.setRefreshing(false);
}
private void updateRadioFilter() {
RadioGroup radioGroup = (RadioGroup) findViewById(R.id.appFilterGroup);
radioGroup.setOnCheckedChangeListener(this);
}
private void selectFilterGroup() {
RadioGroup radioGroup = (RadioGroup) findViewById(R.id.appFilterGroup);
switch (radioGroup.getCheckedRadioButtonId()) {
case R.id.rpkg_core:
showApplications(null, 0, false);
break;
case R.id.rpkg_sys:
showApplications(null, 1, false);
break;
case R.id.rpkg_user:
showApplications(null, 2, false);
break;
default:
showApplications("", 99 , true);
break;
}
}
/*private void selectFilterGroup() {
Spinner spinner1 = (Spinner) findViewById(R.id.filterGroup);
spinner1.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
switch (pos) {
case 0:
showApplications("", 99, true);
break;
case 1:
showApplications(null, 0, false);
break;
case 2:
showApplications(null, 1, false);
break;
case 3:
showApplications(null, 2, false);
break;
default:
showOrLoadApplications();
break;
}
}
public void onNothingSelected(AdapterView<?> parent) {
}
});
showApplications("", 99, true);
}
*/
private void updateIconStatus() {
if(Api.isEnabled(getApplicationContext())) {
getSupportActionBar().setIcon(R.drawable.notification);
} else {
getSupportActionBar().setIcon(R.drawable.notification_error);
}
}
private void startRootShell() {
//G.isRootAvail(true);
List<String> cmds = new ArrayList<String>();
cmds.add("true");
new RootCommand().setFailureToast(R.string.error_su)
.setReopenShell(true).run(getApplicationContext(), cmds);
//put up the notification
if(G.activeNotification()){
Api.showNotification(Api.isEnabled(getApplicationContext()), getApplicationContext());
}
}
@Override
public void onResume() {
super.onResume();
}
private void reloadPreferences() {
getSupportActionBar().setDisplayShowHomeEnabled(true);
G.reloadPrefs();
checkPreferences();
//language
Api.updateLanguage(getApplicationContext(), G.locale());
if (this.listview == null) {
this.listview = (ListView) this.findViewById(R.id.listview);
}
//verifyMultiProfile();
refreshHeader();
updateIconStatus();
NotificationManager mNotificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.cancel(Api.NOTIFICATION_ID);
if (G.disableIcons()) {
this.findViewById(R.id.imageHolder).setVisibility(View.GONE);
} else {
this.findViewById(R.id.imageHolder).setVisibility(View.VISIBLE);
}
if (G.showFilter()) {
this.findViewById(R.id.filerOption).setVisibility(View.VISIBLE);
} else {
this.findViewById(R.id.filerOption).setVisibility(View.GONE);
}
if (G.enableMultiProfile()) {
this.findViewById(R.id.profileOption).setVisibility(View.VISIBLE);
} else {
this.findViewById(R.id.profileOption).setVisibility(View.GONE);
}
if (G.enableRoam()) {
addColumns(R.id.img_roam);
} else {
hideColumns(R.id.img_roam);
}
if (G.enableVPN()) {
addColumns(R.id.img_vpn);
} else {
hideColumns(R.id.img_vpn);
}
if (!Api.isMobileNetworkSupported(getApplicationContext())) {
ImageView view = (ImageView) this.findViewById(R.id.img_3g);
view.setVisibility(View.GONE);
} else {
this.findViewById(R.id.img_3g).setOnClickListener(this);
}
if (G.enableLAN()) {
addColumns(R.id.img_lan);
} else {
hideColumns(R.id.img_lan);
}
updateRadioFilter();
if(G.enableMultiProfile()) {
setupMultiProfile(true);
}
selectFilterGroup();
}
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
switch (checkedId) {
case R.id.rpkg_all:
showOrLoadApplications();
break;
case R.id.rpkg_core:
showApplications(null, 0,false);
break;
case R.id.rpkg_sys:
showApplications(null, 1,false);
break;
case R.id.rpkg_user:
showApplications(null, 2,false);
break;
}
}
@Override
public void onStart() {
super.onStart();
initDone = 0;
//startRootShell();
reloadPreferences();
}
private void addColumns(int id) {
ImageView view = (ImageView)this.findViewById(id);
view.setVisibility(View.VISIBLE);
view.setOnClickListener(this);
}
private void hideColumns(int id) {
ImageView view = (ImageView)this.findViewById(id);
view.setVisibility(View.GONE);
view.setOnClickListener(this);
}
private void setupMultiProfile(boolean reset){
reloadLocalList(true);
mSpinner= (Spinner) findViewById(R.id.profileGroup);
spinnerAdapter = new ArrayAdapter<String>(this,android.R.layout.simple_spinner_item,
mlocalList);
spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mSpinner.setAdapter(spinnerAdapter);
String currentProfile = G.storedProfile();
switch(currentProfile){
case Api.DEFAULT_PREFS_NAME:
mSpinner.setSelection(0);
break;
case "AFWallProfile1":
mSpinner.setSelection(1);
break;
case "AFWallProfile2":
mSpinner.setSelection(2);
break;
case "AFWallProfile3":
mSpinner.setSelection(3);
break;
default:
if(currentProfile != null) {
mSpinner.setSelection(spinnerAdapter.getPosition(currentProfile));
}
}
mSpinner.setOnItemSelectedListener(this);
}
private void reloadLocalList(boolean reset) {
if(reset) {
mlocalList = new ArrayList<>(new LinkedHashSet<String>());
}
mlocalList.add(G.gPrefs.getString("default", getString(R.string.defaultProfile)));
mlocalList.add(G.gPrefs.getString("profile1", getString(R.string.profile1)));
mlocalList.add(G.gPrefs.getString("profile2", getString(R.string.profile2)));
mlocalList.add(G.gPrefs.getString("profile3", getString(R.string.profile3)));
boolean isAdditionalProfiles = false;
List<String> profilesList = G.getAdditionalProfiles();
for(String profiles : profilesList) {
if(profiles !=null && profiles.length() > 0) {
isAdditionalProfiles = true;
mlocalList.add(profiles);
}
}
}
private boolean passCheck(){
switch (G.protectionLevel()) {
case "p0":
return true;
case "p1":
final String oldpwd = G.profile_pwd();
if (oldpwd.length() == 0) {
return true;
} else {
// Check the password
requestPassword();
}
break;
case "p2":
final String pwd = G.sPrefs.getString("LockPassword", "");
if (pwd.length() == 0) {
return true;
} else {
requestPassword();
}
break;
}
return false;
}
@Override
protected void onPause() {
super.onPause();
//this.listview.setAdapter(null);
//mLastPause = Syst em.currentTimeMillis();
isOnPause = true;
//checkForProfile = true;
index = this.listview.getFirstVisiblePosition();
View v = this.listview.getChildAt(0);
top = (v == null) ? 0 : v.getTop();
}
/**
* Check if the stored preferences are OK
*/
private void checkPreferences() {
final Editor editor = G.pPrefs.edit();
boolean changed = false;
if (G.pPrefs.getString(Api.PREF_MODE, "").length() == 0) {
editor.putString(Api.PREF_MODE, Api.MODE_WHITELIST);
changed = true;
}
if (changed)
editor.commit();
}
/**
* Refresh informative header
*/
private void refreshHeader() {
final String mode = G.pPrefs.getString(Api.PREF_MODE, Api.MODE_WHITELIST);
final TextView labelmode = (TextView) this.findViewById(R.id.label_mode);
final Resources res = getResources();
int resid = (mode.equals(Api.MODE_WHITELIST) ? R.string.mode_whitelist: R.string.mode_blacklist);
labelmode.setText(res.getString(R.string.mode_header, res.getString(resid)));
}
/**
* Displays a dialog box to select the operation mode (black or white list)
*/
private void selectMode() {
final Resources res = getResources();
new MaterialDialog.Builder(this)
.title(R.string.selectMode)
.cancelable(true)
.items(new String[]{
res.getString(R.string.mode_whitelist),
res.getString(R.string.mode_blacklist)})
.itemsCallback(new MaterialDialog.ListCallback() {
@Override
public void onSelection(MaterialDialog dialog, View view, int which, CharSequence text) {
final String mode = (which == 0 ? Api.MODE_WHITELIST : Api.MODE_BLACKLIST);
final Editor editor = getSharedPreferences(Api.PREFS_NAME, 0).edit();
editor.putString(Api.PREF_MODE, mode);
editor.commit();
refreshHeader();
}
})
.show();
}
/**
* Request the password lock before displayed the main screen.
*/
private void requestPassword() {
switch(G.protectionLevel()) {
case "p1":
new MaterialDialog.Builder(MainActivity.this).cancelable(false)
.title(R.string.pass_titleget).autoDismiss(false)
.inputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD)
.positiveText(R.string.submit)
.negativeText(R.string.Cancel)
.callback(new MaterialDialog.ButtonCallback() {
@Override
public void onNegative(MaterialDialog dialog) {
MainActivity.this.finish();
android.os.Process.killProcess(android.os.Process.myPid());
}
})
.input(R.string.enterpass, R.string.password_empty, new MaterialDialog.InputCallback() {
@Override
public void onInput(MaterialDialog dialog, CharSequence input) {
String pass = input.toString();
boolean isAllowed = false;
if (G.isEnc()) {
String decrypt = Api.unhideCrypt("AFW@LL_P@SSWORD_PR0T3CTI0N", G.profile_pwd());
if (decrypt != null) {
if (decrypt.equals(pass)) {
isAllowed = true;
}
}
} else {
if (pass.equals(G.profile_pwd())) {
isAllowed = true;
}
}
if (isAllowed) {
showOrLoadApplications();
dialog.dismiss();
} else {
Api.toast(MainActivity.this, getString(R.string.wrong_password));
}
}
}).show();
break;
case "p2":
Intent intent = new Intent(ACTION_COMPARE_PATTERN, null, getApplicationContext(), LockPatternActivity.class);
String savedPattern = G.sPrefs.getString("LockPassword", "");
intent.putExtra(EXTRA_PATTERN, savedPattern.toCharArray());
startActivityForResult(intent, REQ_ENTER_PATTERN);
break;
}
}
/**
* If the applications are cached, just show them, otherwise load and show
*/
private void showOrLoadApplications() {
//nocache!!
(new GetAppList()).setContext(this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
initDone = initDone + 1;
if(initDone > 1) {
Spinner spinner = (Spinner) findViewById(R.id.profileGroup);
String profileName = spinner.getSelectedItem().toString();
switch (position) {
case 0:
G.setProfile(true, "AFWallPrefs");
break;
case 1:
G.setProfile(true, "AFWallProfile1");
break;
case 2:
G.setProfile(true, "AFWallProfile2");
break;
case 3:
G.setProfile(true, "AFWallProfile3");
break;
default:
G.setProfile(true, profileName);
}
G.reloadProfile();
showOrLoadApplications();
if (G.applyOnSwitchProfiles()) {
applyOrSaveRules();
}
}
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
public class GetAppList extends AsyncTask<Void, Integer, Void> {
boolean started = false;
Context context = null;
AsyncTask<Void,Integer,Void> myAsyncTaskInstance = null;
public GetAppList setContext(Context context) {
this.context = context;
return this;
}
@Override
protected void onPreExecute() {
plsWait = new MaterialDialog.Builder(context).cancelable(false).
title(getString(R.string.reading_apps)).progress(false, getPackageManager().getInstalledApplications(0)
.size(), true).show();
doProgress(0);
}
public void doProgress(int value) {
publishProgress(value);
}
public AsyncTask<Void, Integer, Void> getInstance() {
// if the current async task is already running, return null: no new
// async task
// shall be created if an instance is already running
if ((myAsyncTaskInstance != null)
&& myAsyncTaskInstance.getStatus() == Status.RUNNING) {
// it can be running but cancelled, in that case, return a new
// instance
if (myAsyncTaskInstance.isCancelled()) {
myAsyncTaskInstance = new GetAppList();
} else {
return null;
}
}
// if the current async task is pending, it can be executed return
// this instance
if ((myAsyncTaskInstance != null)
&& myAsyncTaskInstance.getStatus() == Status.PENDING) {
return myAsyncTaskInstance;
}
// if the current async task is finished, it can't be executed
// another time, so return a new instance
if ((myAsyncTaskInstance != null)
&& myAsyncTaskInstance.getStatus() == Status.FINISHED) {
myAsyncTaskInstance = new GetAppList();
}
// if the current async task is null, create a new instance
if (myAsyncTaskInstance == null) {
myAsyncTaskInstance = new GetAppList();
}
// return the current instance
return myAsyncTaskInstance;
}
@Override
protected Void doInBackground(Void... params) {
Api.getApps(MainActivity.this, this);
if( isCancelled() )
return null;
//publishProgress(-1);
return null;
}
@Override
protected void onPostExecute(Void result) {
selectFilterGroup();
doProgress(-1);
try {
started = false;
plsWait.dismiss();
mSwipeLayout.setRefreshing(false);
//plsWait.autoDismiss(true);
} catch (Exception e) {
// nothing
}
}
@Override
protected void onProgressUpdate(Integer... progress) {
if (progress[0] == 0 || progress[0] == -1) {
//do nothing
} else {
plsWait.incrementProgress(progress[0]);
}
}
};
/**
* Show the list of applications
*/
private void showApplications(final String searchStr, int flag, boolean showAll) {
setDirty(false);
List<PackageInfoData> searchApp = new ArrayList<PackageInfoData>();
final List<PackageInfoData> apps = Api.getApps(this,null);
boolean isResultsFound = false;
if(searchStr !=null && searchStr.length() > 1) {
for(PackageInfoData app:apps) {
for(String str: app.names) {
if(str.contains(searchStr.toLowerCase()) || str.toLowerCase().contains(searchStr.toLowerCase())
&& !searchApp.contains(app)) {
searchApp.add(app);
isResultsFound = true;
}
}
}
} else if (flag > -1){
switch(flag){
case 0:
for(PackageInfoData app:apps) {
if(app.pkgName.startsWith("dev.afwall.special")) {
searchApp.add(app);
}
}
break;
case 1:
for(PackageInfoData app: apps) {
if (app.appinfo != null && (app.appinfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
searchApp.add(app);
}
}
break;
case 2:
for(PackageInfoData app: apps) {
if (app.appinfo != null && (app.appinfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
searchApp.add(app);
}
}
break;
}
}
List<PackageInfoData> apps2;
if(showAll || (searchStr != null && searchStr.equals(""))) {
apps2 = apps;
} else if(isResultsFound || searchApp.size() > 0) {
apps2 = searchApp;
} else {
apps2 = new ArrayList<PackageInfoData>();
}
// Sort applications - selected first, then alphabetically
Collections.sort(apps2, new PackageComparator());
this.listview.setAdapter(new AppListArrayAdapter(this, getApplicationContext(), apps2));
// restore
this.listview.setSelectionFromTop(index, top);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
//language
Api.updateLanguage(getApplicationContext(), G.locale());
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.menu_bar, menu);
// Get widget's instance
mainMenu = menu;
return true;
}
public void menuSetApplyOrSave(final Menu menu, final boolean isEnabled) {
if (menu == null) {
return;
}
runOnUiThread(new Runnable() {
@Override
public void run() {
if (isEnabled) {
menu.findItem(R.id.menu_toggle).setTitle(R.string.fw_disabled).setIcon(R.drawable.notification_error);
menu.findItem(R.id.menu_apply).setTitle(R.string.applyrules);
getSupportActionBar().setIcon(R.drawable.notification);
} else {
menu.findItem(R.id.menu_toggle).setTitle(R.string.fw_enabled).setIcon(R.drawable.notification);
menu.findItem(R.id.menu_apply).setTitle(R.string.saverules);
getSupportActionBar().setIcon(R.drawable.notification_error);
}
}
});
}
@Override
public boolean onPrepareOptionsMenu(final Menu menu) {
//language
Api.updateLanguage(getApplicationContext(), G.locale());
menuSetApplyOrSave(menu, Api.isEnabled(MainActivity.this));
return true;
}
private void disableOrEnable() {
final boolean enabled = !Api.isEnabled(this);
Api.setEnabled(this, enabled, true);
if (enabled) {
applyOrSaveRules();
} else {
if (G.enableConfirm()) {
confirmDisable();
} else {
purgeRules();
}
}
refreshHeader();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
switch (item.getItemId()) {
/*case android.R.id.home:
disableOrEnable();
return true;*/
case R.id.menu_toggle:
disableOrEnable();
return true;
case R.id.menu_apply:
applyOrSaveRules();
return true;
case R.id.menu_exit:
finish();
System.exit(0);
return false;
case R.id.menu_help:
showAbout();
return true;
/*case R.id.menu_setpwd:
setPassword();
return true;*/
case R.id.menu_log:
showLog();
return true;
case R.id.menu_rules:
showRules();
return true;
case R.id.menu_setcustom:
setCustomScript();
return true;
case R.id.menu_preference:
showPreferences();
return true;
/*case R.id.menu_reload:
Api.applications = null;
showOrLoadApplications();
return true;*/
case R.id.menu_search:
item.setActionView(R.layout.searchbar);
final EditText filterText = (EditText) item.getActionView().findViewById(
R.id.searchApps);
filterText.addTextChangedListener(filterTextWatcher);
filterText.setEllipsize(TruncateAt.END);
filterText.setSingleLine();
MenuItemCompat.setOnActionExpandListener(item, new MenuItemCompat.OnActionExpandListener() {
@Override
public boolean onMenuItemActionCollapse(MenuItem item) {
// Do something when collapsed
return true; // Return true to collapse action view
}
@Override
public boolean onMenuItemActionExpand(MenuItem item) {
filterText.post(new Runnable() {
@Override
public void run() {
filterText.requestFocus();
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(filterText, InputMethodManager.SHOW_IMPLICIT);
}
});
return true; // Return true to expand action view
}
});
return true;
case R.id.menu_export:
new MaterialDialog.Builder(this)
.title(R.string.exports)
.cancelable(false)
.items(new String[]{
getString(R.string.export_rules),
getString(R.string.export_all)})
.itemsCallbackSingleChoice(-1, new MaterialDialog.ListCallbackSingleChoice() {
@Override
public boolean onSelection(MaterialDialog dialog, View view, int which, CharSequence text) {
switch (which) {
case 0:
Api.saveSharedPreferencesToFileConfirm(MainActivity.this);
break;
case 1:
Api.saveAllPreferencesToFileConfirm(MainActivity.this);
break;
}
return true;
}
}).positiveText(R.string.exports)
.negativeText(R.string.Cancel)
.show();
return true;
case R.id.menu_import:
new MaterialDialog.Builder(this)
.title(R.string.imports)
.cancelable(false)
.items(new String[]{
getString(R.string.import_rules),
getString(R.string.import_all),
getString(R.string.import_rules_droidwall)})
.itemsCallbackSingleChoice(-1, new MaterialDialog.ListCallbackSingleChoice() {
@Override
public boolean onSelection(MaterialDialog dialog, View view, int which, CharSequence text) {
switch (which) {
case 0:
//Intent intent = new Intent(MainActivity.this, FileChooserActivity.class);
//startActivityForResult(intent, FILE_CHOOSER_LOCAL);
File mPath = new File(Environment.getExternalStorageDirectory() + "//afwall//");
FileDialog fileDialog = new FileDialog(MainActivity.this,mPath,true);
//fileDialog.setFlag(true);
//fileDialog.setFileEndsWith(new String[] {"backup", "afwall-backup"}, "all");
fileDialog.addFileListener(new FileDialog.FileSelectedListener() {
public void fileSelected(File file) {
String fileSelected = file.toString();
StringBuilder builder = new StringBuilder();
if(Api.loadSharedPreferencesFromFile(MainActivity.this,builder,fileSelected)){
Api.applications = null;
showOrLoadApplications();
Api.toast(MainActivity.this, getString(R.string.import_rules_success) + fileSelected);
} else {
if (builder.toString().equals("")) {
Api.toast(MainActivity.this, getString(R.string.import_rules_fail));
} else {
Api.toast(MainActivity.this,builder.toString());
}
}
}
});
fileDialog.showDialog();
break;
case 1:
if (Api.getCurrentPackage(MainActivity.this).equals("dev.ukanth.ufirewall.donate") || G.isDo(getApplicationContext())) {
File mPath2 = new File(Environment.getExternalStorageDirectory() + "//afwall//");
FileDialog fileDialog2 = new FileDialog(MainActivity.this,mPath2,false);
//fileDialog2.setFlag(false);
//fileDialog2.setFileEndsWith(new String[] {"backup_all", "afwall-backup-all"}, "" );
fileDialog2.addFileListener(new FileDialog.FileSelectedListener() {
public void fileSelected(File file) {
String fileSelected = file.toString();
StringBuilder builder = new StringBuilder();
if(Api.loadAllPreferencesFromFile(MainActivity.this, builder, fileSelected)){
Api.applications = null;
showOrLoadApplications();
Api.toast(MainActivity.this, getString(R.string.import_rules_success) + fileSelected);
Intent intent = getIntent();
finish();
startActivity(intent);
} else {
if(builder.toString().equals("")) {
Api.toast(MainActivity.this, getString(R.string.import_rules_fail));
} else {
Api.toast(MainActivity.this,builder.toString());
}
}
}
});
fileDialog2.showDialog();
} else {
new MaterialDialog.Builder(MainActivity.this).cancelable(false)
.title(R.string.buy_donate)
.content(R.string.donate_only)
.positiveText(R.string.buy_donate)
.negativeText(R.string.close)
.icon(getResources().getDrawable(R.drawable.ic_launcher))
.callback(new MaterialDialog.ButtonCallback() {
@Override
public void onPositive(MaterialDialog dialog) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("market://search?q=pub:ukpriya"));
startActivity(intent);
}
@Override
public void onNegative(MaterialDialog dialog) {
dialog.cancel();
}
})
.show();
}
break;
case 2:
new MaterialDialog.Builder(MainActivity.this).cancelable(false)
.title(R.string.import_rules_droidwall)
.content(R.string.overrideRules)
.positiveText(R.string.Yes)
.negativeText(R.string.No)
.icon(getResources().getDrawable(R.drawable.ic_launcher))
.callback(new MaterialDialog.ButtonCallback() {
@Override
public void onPositive(MaterialDialog dialog) {
if (ImportApi.loadSharedPreferencesFromDroidWall(MainActivity.this)) {
Api.applications = null;
showOrLoadApplications();
Api.toast(MainActivity.this, getString(R.string.import_rules_success) + Environment.getExternalStorageDirectory().getAbsolutePath() + "/afwall/");
} else {
Api.toast(MainActivity.this, getString(R.string.import_rules_fail));
}
}
@Override
public void onNegative(MaterialDialog dialog) {
dialog.cancel();
}
})
.show();
break;
}
return true;
}
})
.positiveText(R.string.imports)
.negativeText(R.string.Cancel)
.show();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private TextWatcher filterTextWatcher = new TextWatcher() {
public void afterTextChanged(Editable s) {
showApplications(s.toString(),-1,false);
}
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
public void onTextChanged(CharSequence s, int start, int before,
int count) {
showApplications(s.toString(),-1,false);
}
};
private void showPreferences() {
Intent i = new Intent(this, PreferencesActivity.class);
//startActivity(i);
startActivityForResult(i,PREFERENCE_RESULT);
}
private void showAbout() {
Intent i = new Intent(this, HelpActivity.class);
startActivityForResult(i, SHOW_ABOUT_RESULT);
}
public void confirmDisable(){
new MaterialDialog.Builder(this)
.title(R.string.confirmMsg)
//.content(R.string.confirmMsg)
.cancelable(false)
.positiveText(R.string.Yes)
.negativeText(R.string.No)
.callback(new MaterialDialog.ButtonCallback() {
@Override
public void onPositive(MaterialDialog dialog) {
purgeRules();
if (G.activeNotification()) {
Api.showNotification(Api.isEnabled(getApplicationContext()), getApplicationContext());
}
dialog.dismiss();
}
@Override
public void onNegative(MaterialDialog dialog) {
Api.setEnabled(getApplicationContext(), true, true);
dialog.dismiss();
}
})
.show();
}
/**
* Set a new init script
*/
private void setCustomScript() {
Intent intent = new Intent();
intent.setClass(this, CustomScriptActivity.class);
startActivityForResult(intent, SHOW_CUSTOM_SCRIPT);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch(requestCode) {
case REQ_ENTER_PATTERN: {
switch (resultCode) {
case RESULT_OK:
//isPassVerify = true;
showOrLoadApplications();
break;
case RESULT_CANCELED:
MainActivity.this.finish();
android.os.Process.killProcess(android.os.Process.myPid());
break;
case RESULT_FAILED:
MainActivity.this.finish();
android.os.Process.killProcess(android.os.Process.myPid());
break;
case RESULT_FORGOT_PATTERN:
MainActivity.this.finish();
android.os.Process.killProcess(android.os.Process.myPid());
break;
default:
MainActivity.this.finish();
android.os.Process.killProcess(android.os.Process.myPid());
break;
}
}
break;
case PREFERENCE_RESULT: {
invalidateOptionsMenu();
}
break;
}
if (resultCode == RESULT_OK
&& data != null && Api.CUSTOM_SCRIPT_MSG.equals(data.getAction())) {
final String script = data.getStringExtra(Api.SCRIPT_EXTRA);
final String script2 = data.getStringExtra(Api.SCRIPT2_EXTRA);
setCustomScript(script, script2);
}
}
/**
* Set a new init script
*
* @param script
* new script (empty to remove)
* @param script2
* new "shutdown" script (empty to remove)
*/
private void setCustomScript(String script, String script2) {
final Editor editor = getSharedPreferences(Api.PREFS_NAME, 0).edit();
// Remove unnecessary white-spaces, also replace '\r\n' if necessary
script = script.trim().replace("\r\n", "\n");
script2 = script2.trim().replace("\r\n", "\n");
editor.putString(Api.PREF_CUSTOMSCRIPT, script);
editor.putString(Api.PREF_CUSTOMSCRIPT2, script2);
int msgid;
if (editor.commit()) {
if (script.length() > 0 || script2.length() > 0) {
msgid = R.string.custom_script_defined;
} else {
msgid = R.string.custom_script_removed;
}
} else {
msgid = R.string.custom_script_error;
}
Api.displayToasts(MainActivity.this, msgid, Toast.LENGTH_SHORT);
if (Api.isEnabled(this)) {
// If the firewall is enabled, re-apply the rules
applyOrSaveRules();
}
}
/**
* Show iptables rules on a dialog
*/
private void showRules() {
Intent i = new Intent(this, RulesActivity.class);
startActivityForResult(i, SHOW_RULES_ACTIVITY);
}
/**
* Show logs on a dialog
*/
private void showLog() {
Intent i = new Intent(this, LogActivity.class);
startActivityForResult(i, SHOW_LOGS_ACTIVITY);
}
/**
* Apply or save iptables rules, showing a visual indication
*/
private void applyOrSaveRules() {
final Resources res = getResources();
final boolean enabled = Api.isEnabled(this);
final Context ctx = getApplicationContext();
Api.saveRules(ctx);
if (!enabled) {
Api.setEnabled(ctx, false, true);
Api.displayToasts(ctx, R.string.rules_saved, Toast.LENGTH_SHORT);
setDirty(false);
return;
}
Api.showNotification(Api.isEnabled(getApplicationContext()), getApplicationContext());
final MaterialDialog progress = new MaterialDialog.Builder(this)
.title(R.string.working)
.cancelable(false)
.content(enabled ? R.string.applying_rules
: R.string.saving_rules)
.progress(true, 0)
.show();
Api.applySavedIptablesRules(ctx, true, new RootCommand()
.setSuccessToast(R.string.rules_applied)
.setFailureToast(R.string.error_apply)
.setReopenShell(true)
.setCallback(new RootCommand.Callback() {
public void cbFunc(RootCommand state) {
try {
progress.dismiss();
} catch (Exception ex) {
}
boolean result = enabled;
if (state.exitCode == 0) {
setDirty(false);
} else {
result = false;
}
menuSetApplyOrSave(MainActivity.this.mainMenu, result);
Api.setEnabled(ctx, result, true);
}
}));
}
/**
* Purge iptables rules, showing a visual indication
*/
private void purgeRules() {
final Context ctx = getApplicationContext();
Api.purgeIptables(ctx, true, new RootCommand()
.setSuccessToast(R.string.rules_deleted)
.setFailureToast(R.string.error_purge)
.setReopenShell(true)
.setCallback(new RootCommand.Callback() {
public void cbFunc(RootCommand state) {
// error exit -> assume the rules are still enabled
// we shouldn't wind up in this situation, but if we do, the user's
// best bet is to click Apply then toggle Enabled again
boolean nowEnabled = state.exitCode != 0;
Api.setEnabled(ctx, nowEnabled, true);
menuSetApplyOrSave(MainActivity.this.mainMenu, nowEnabled);
}
}));
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.label_mode:
selectMode();
break;
case R.id.img_wifi:
selectActionConfirmation(v.getId());
break;
case R.id.img_3g:
selectActionConfirmation(v.getId());
break;
case R.id.img_roam:
selectActionConfirmation(v.getId());
break;
case R.id.img_vpn:
selectActionConfirmation(v.getId());
break;
case R.id.img_lan:
selectActionConfirmation(v.getId());
break;
case R.id.img_invert:
selectActionConfirmation(getString(R.string.reverse_all), v.getId());
break;
case R.id.img_reset:
selectActionConfirmation(getString(R.string.unselect_all), v.getId());
break;
//case R.id.img_invert:
// revertApplications();
// break;
}
}
private void selectAllLAN(boolean flag) {
if (this.listview == null) {
this.listview = (ListView) this.findViewById(R.id.listview);
}
ListAdapter adapter = listview.getAdapter();
if(adapter !=null) {
int count = adapter.getCount(), item;
for (item = 0; item < count; item++) {
PackageInfoData data = (PackageInfoData) adapter.getItem(item);
if(data.uid != Api.SPECIAL_UID_ANY) {
data.selected_lan = flag;
}
setDirty(true);
}
((BaseAdapter) adapter).notifyDataSetChanged();
}
}
private void selectAllVPN(boolean flag) {
if (this.listview == null) {
this.listview = (ListView) this.findViewById(R.id.listview);
}
ListAdapter adapter = listview.getAdapter();
if(adapter !=null) {
int count = adapter.getCount(), item;
for (item = 0; item < count; item++) {
PackageInfoData data = (PackageInfoData) adapter.getItem(item);
if(data.uid != Api.SPECIAL_UID_ANY) {
data.selected_vpn = flag;
}
setDirty(true);
}
((BaseAdapter) adapter).notifyDataSetChanged();
}
}
private void selectRevert(int flag){
if (this.listview == null) {
this.listview = (ListView) this.findViewById(R.id.listview);
}
ListAdapter adapter = listview.getAdapter();
if (adapter != null) {
int count = adapter.getCount(), item;
for (item = 0; item < count; item++) {
PackageInfoData data = (PackageInfoData) adapter.getItem(item);
if(data.uid != Api.SPECIAL_UID_ANY) {
switch (flag) {
case R.id.img_wifi:
data.selected_wifi = !data.selected_wifi;
break;
case R.id.img_3g:
data.selected_3g = !data.selected_3g;
break;
case R.id.img_roam:
data.selected_roam = !data.selected_roam;
break;
case R.id.img_vpn:
data.selected_vpn = !data.selected_vpn;
break;
case R.id.img_lan:
data.selected_lan = !data.selected_lan;
break;
}
}
setDirty(true);
}
((BaseAdapter) adapter).notifyDataSetChanged();
}
}
private void selectRevert() {
if (this.listview == null) {
this.listview = (ListView) this.findViewById(R.id.listview);
}
ListAdapter adapter = listview.getAdapter();
if (adapter != null) {
int count = adapter.getCount(), item;
for (item = 0; item < count; item++) {
PackageInfoData data = (PackageInfoData) adapter.getItem(item);
if(data.uid != Api.SPECIAL_UID_ANY) {
data.selected_wifi = !data.selected_wifi;
data.selected_3g = !data.selected_3g;
data.selected_roam = !data.selected_roam;
data.selected_vpn = !data.selected_vpn;
data.selected_lan = !data.selected_lan;
}
setDirty(true);
}
((BaseAdapter) adapter).notifyDataSetChanged();
}
}
private void selectAllRoam(boolean flag){
if (this.listview == null) {
this.listview = (ListView) this.findViewById(R.id.listview);
}
ListAdapter adapter = listview.getAdapter();
if(adapter !=null) {
int count = adapter.getCount(), item;
for (item = 0; item < count; item++) {
PackageInfoData data = (PackageInfoData) adapter.getItem(item);
if(data.uid != Api.SPECIAL_UID_ANY) {
data.selected_roam = flag;
}
setDirty(true);
}
((BaseAdapter) adapter).notifyDataSetChanged();
}
}
private void clearAll(){
if (this.listview == null) {
this.listview = (ListView) this.findViewById(R.id.listview);
}
ListAdapter adapter = listview.getAdapter();
if(adapter !=null) {
int count = adapter.getCount(), item;
for (item = 0; item < count; item++) {
PackageInfoData data = (PackageInfoData) adapter.getItem(item);
data.selected_wifi = false;
data.selected_3g = false;
data.selected_roam = false;
data.selected_vpn = false;
data.selected_lan = false;
setDirty(true);
}
((BaseAdapter) adapter).notifyDataSetChanged();
}
}
private void selectAll3G(boolean flag) {
if (this.listview == null) {
this.listview = (ListView) this.findViewById(R.id.listview);
}
ListAdapter adapter = listview.getAdapter();
if(adapter !=null) {
int count = adapter.getCount(), item;
for (item = 0; item < count; item++) {
PackageInfoData data = (PackageInfoData) adapter.getItem(item);
if(data.uid != Api.SPECIAL_UID_ANY) {
data.selected_3g = flag;
}
setDirty(true);
}
((BaseAdapter) adapter).notifyDataSetChanged();
}
}
private void selectAllWifi(boolean flag) {
if (this.listview == null) {
this.listview = (ListView) this.findViewById(R.id.listview);
}
ListAdapter adapter = listview.getAdapter();
int count = adapter.getCount(), item;
if(adapter !=null) {
for (item = 0; item < count; item++) {
PackageInfoData data = (PackageInfoData) adapter.getItem(item);
if(data.uid != Api.SPECIAL_UID_ANY) {
data.selected_wifi = flag;
}
setDirty(true);
}
((BaseAdapter) adapter).notifyDataSetChanged();
}
}
@Override
public boolean onKeyUp(final int keyCode, final KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_UP)
{
switch (keyCode) {
case KeyEvent.KEYCODE_MENU:
if(mainMenu != null){
mainMenu.performIdentifierAction(R.id.menu_list_item, 0);
return true;
}
break;
case KeyEvent.KEYCODE_BACK:
if(isDirty()) {
new MaterialDialog.Builder(this)
.title(R.string.confirmation)
.cancelable(false)
.content(R.string.unsaved_changes_message)
.positiveText(R.string.apply)
.negativeText(R.string.discard)
.callback(new MaterialDialog.ButtonCallback() {
@Override
public void onPositive(MaterialDialog dialog) {
applyOrSaveRules();
dialog.dismiss();
}
@Override
public void onNegative(MaterialDialog dialog) {
setDirty(false);
Api.applications = null;
finish();
System.exit(0);
//force reload rules.
MainActivity.super.onKeyDown(keyCode, event);
dialog.dismiss();
}
})
.show();
return true;
} else {
setDirty(false);
finish();
System.exit(0);
}
}
}
return super.onKeyUp(keyCode, event);
}
/*@Override
public boolean onKeyDown(final int keyCode, final KeyEvent event) {
switch (keyCode){
case KeyEvent.KEYCODE_BACK:
}
break;
}
// Handle the back button when dirty
return super.onKeyDown(keyCode, event);
}*/
/**
*
* @param i
*/
private void selectActionConfirmation(String displayMessage, final int i){
new MaterialDialog.Builder(this)
.title(R.string.confirmation).content(displayMessage)
.cancelable(true)
.positiveText(R.string.OK)
.negativeText(R.string.Cancel)
.callback(new MaterialDialog.ButtonCallback() {
@Override
public void onPositive(MaterialDialog dialog) {
switch (i) {
case R.id.img_invert:
selectRevert();
break;
case R.id.img_reset:
clearAll();
}
dialog.dismiss();
}
@Override
public void onNegative(MaterialDialog dialog) {
dialog.dismiss();
}
})
.show();
}
private void selectActionConfirmation(final int i) {
new MaterialDialog.Builder(this)
.title(R.string.select_action)
.cancelable(true)
.items(new String[]{
getString(R.string.check_all),
getString(R.string.invert_all),
getString(R.string.uncheck_all)})
.itemsCallback(new MaterialDialog.ListCallback() {
@Override
public void onSelection(MaterialDialog dialog, View view, int which, CharSequence text) {
switch (which) {
case 0:
switch (i) {
case R.id.img_wifi:
dialog.setTitle(text + getString(R.string.wifi));
selectAllWifi(true);
break;
case R.id.img_3g:
dialog.setTitle(text + getString(R.string.data));
selectAll3G(true);
break;
case R.id.img_roam:
dialog.setTitle(text + getString(R.string.roam));
selectAllRoam(true);
break;
case R.id.img_vpn:
dialog.setTitle(text + getString(R.string.vpn));
selectAllVPN(true);
break;
case R.id.img_lan:
dialog.setTitle(text + getString(R.string.lan));
selectAllLAN(true);
break;
}
break;
case 1:
switch (i) {
case R.id.img_wifi:
dialog.setTitle(text + getString(R.string.wifi));
break;
case R.id.img_3g:
dialog.setTitle(text + getString(R.string.data));
break;
case R.id.img_roam:
dialog.setTitle(text + getString(R.string.roam));
break;
case R.id.img_vpn:
dialog.setTitle(text + getString(R.string.vpn));
break;
case R.id.img_lan:
dialog.setTitle(text + getString(R.string.lan));
break;
}
selectRevert(i);
dirty = true;
break;
case 2:
switch (i) {
case R.id.img_wifi:
dialog.setTitle(text + getString(R.string.wifi));
selectAllWifi(false);
break;
case R.id.img_3g:
dialog.setTitle(text + getString(R.string.data));
selectAll3G(false);
break;
case R.id.img_roam:
dialog.setTitle(text + getString(R.string.roam));
selectAllRoam(false);
break;
case R.id.img_vpn:
dialog.setTitle(text + getString(R.string.vpn));
selectAllVPN(false);
break;
case R.id.img_lan:
dialog.setTitle(text + getString(R.string.lan));
selectAllLAN(false);
break;
}
break;
}
}
}).show();
}
/*@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
switch (checkedId) {
case R.id.rpkg_all:
showOrLoadApplications();
break;
case R.id.rpkg_core:
showApplications(null, 0,false);
break;
case R.id.rpkg_sys:
showApplications(null, 1,false);
break;
case R.id.rpkg_user:
showApplications(null, 2,false);
break;
}
}*/
protected boolean isSuPackage(PackageManager pm, String suPackage) {
boolean found = false;
try {
PackageInfo info = pm.getPackageInfo(suPackage, 0);
if(info.applicationInfo != null) {
found = true;
}
//found = s + " v" + info.versionName;
} catch (PackageManager.NameNotFoundException e) {
} catch (Exception e) { }
return found;
}
private class Startup extends AsyncTask<Void, Void, Boolean> {
private MaterialDialog dialog = null;
private Context context = null;
//private boolean suAvailable = false;
public Startup setContext(Context context) {
this.context = context;
return this;
}
@Override
protected void onPreExecute() {
dialog = new MaterialDialog.Builder(context).
cancelable(false).
title(getString(R.string.su_check_title)).progress(true,0).content(context.getString(R.string.su_check_message)).show();
}
@Override
protected Boolean doInBackground(Void... params) {
// Let's do some SU stuff
boolean suAvailable = Shell.SU.available();
if (suAvailable) {
startRootShell();
}
return suAvailable;
}
@Override
protected void onPostExecute(Boolean rootGranted) {
super.onPostExecute(rootGranted);
dialog.dismiss();
if(!rootGranted && !isSuPackage(getPackageManager(), "com.kingouser.com")) {
new MaterialDialog.Builder(MainActivity.this).cancelable(false)
.title(R.string.error_common)
.content(R.string.error_su)
.positiveText(R.string.OK)
.callback(new MaterialDialog.ButtonCallback() {
@Override
public void onPositive(MaterialDialog dialog) {
MainActivity.this.finish();
android.os.Process.killProcess(android.os.Process.myPid());
dialog.dismiss();
}
})
.show();
} else {
passCheck();
}
}
}
}