package de.fun2code.android.piratebox;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import de.fun2code.android.pawserver.PawServerActivity;
import de.fun2code.android.piratebox.util.NetworkUtil;
import de.fun2code.android.piratebox.util.PirateUtil;
import de.fun2code.android.piratebox.util.NetworkUtil.WrapResult;
import de.fun2code.android.piratebox.util.ShellUtil;
/**
* Main PirateBox Activity
*
*
*/
public class PirateBoxActivity extends PawServerActivity implements StateChangedListener {
@SuppressWarnings("unused")
private Handler handler;
// View that displays the server URL
private TextView txtVersion, txtInfo;
private ImageView imgServer, imgAp, imgNetwork;
private ImageButton btnSwitch;
private String version = null;
private SharedPreferences preferences;
private AlphaAnimation blinkAnimation;
private boolean supportedDevice;
private Activity activity;
// private boolean externalServer = false;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
preferences = PreferenceManager.getDefaultSharedPreferences(this);
INSTALL_DIR = Constants.getInstallDir(this.getApplicationContext());
activity = this;
/*
* Turn the PawServerActivity into runtime mode.
* Otherwise an error may occur if some things special to the
* original PAW server are not available.
*/
calledFromRuntime = true;
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
handler = new Handler();
txtVersion = (TextView) findViewById(R.id.version);
txtInfo = (TextView) findViewById(R.id.info);
imgServer = (ImageView) findViewById(R.id.imageServer);
imgAp = (ImageView) findViewById(R.id.imageAp);
imgNetwork = (ImageView) findViewById(R.id.imageNetwork);
btnSwitch = (ImageButton) findViewById(R.id.buttonSwitch);
try {
version = getPackageManager().getPackageInfo(getPackageName(), 0).versionName;
txtVersion.setText(version);
} catch (NameNotFoundException e) {
e.printStackTrace();
version = null;
}
// Handle on/off button
btnSwitch.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if(PirateBoxService.isRunning()) {
btnSwitch.setEnabled(false);
btnSwitch.setImageResource(R.drawable.switch_off);
stopService();
}
else {
btnSwitch.setEnabled(false);
btnSwitch.setImageResource(R.drawable.switch_on);
txtInfo.setText(getText(R.string.msg_setting_up_networking));
btnSwitch.startAnimation(blinkAnimation);
startService();
}
}
});
/* Check installation and extract ZIP if necessary */
checkInstallation();
/*
* Register handler This is needed in order to get dialogs etc. to work.
*/
messageHandler = new MessageHandler(this);
PirateBoxService.setActivityHandler(messageHandler);
/*
* Register activity with service.
*/
PirateBoxService.setActivity(this);
/*
* Blink animation
*/
blinkAnimation = new AlphaAnimation(1, 0.1F);
blinkAnimation.setDuration(1000);
blinkAnimation.setRepeatCount(Animation.INFINITE);
blinkAnimation.setRepeatMode(Animation.REVERSE);
}
@Override
public void onResume() {
super.onResume();
// Set views to correct state
imgServer.setVisibility(PirateBoxService.isRunning() ? View.VISIBLE : View.INVISIBLE);
imgNetwork.setVisibility(PirateBoxService.isNetworkRunning() ? View.VISIBLE : View.INVISIBLE);
imgAp.setVisibility(PirateBoxService.isApRunning() ? View.VISIBLE : View.INVISIBLE);
imgAp.setAlpha(!PirateBoxService.autoApStartup ? 50 : 255);
imgServer.setAlpha(PirateBoxService.externalServerRunning ? 50 : 255);
if(!PirateBoxService.isStartingUp()) {
btnSwitch.setImageResource(PirateBoxService.isRunning() ? R.drawable.switch_on : R.drawable.switch_off);
checkStatus();
}
else {
btnSwitch.setImageResource(R.drawable.switch_on);
btnSwitch.startAnimation(blinkAnimation);
txtInfo.setText(R.string.msg_setting_up_networking);
}
NetworkUtil netUtil = new NetworkUtil(this);
netUtil.createDnsMasqBackup();
checkPreRequisites();
}
@Override
public void onPause() {
super.onPause();
}
@Override
public void onStart() {
super.onStart();
PirateBoxService.registerChangeListener(this);
}
@Override
public void onStop() {
super.onStop();
PirateBoxService.unregisterChangeListener(this);
}
@Override
public void onDestroy() {
super.onDestroy();
/*
* Set INSTALL_DIR to null to force a new check if the
* Activity is restarted.
* This might be necessary if user has toggled the "Content to SD"
* setting.
*/
Constants.resetInstallDir();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main_menu, menu);
return true;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
return supportedDevice ? super.onPrepareOptionsMenu(menu) : false;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
switch (item.getItemId()) {
case R.id.menu_preferences:
Intent preferencesActivity = new Intent(getBaseContext(),
PreferencesActivity.class);
startActivity(preferencesActivity);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
/**
* Stops the service
*/
@Override
public void stopService() {
Intent serviceIntent = new Intent(this.getApplicationContext(),
PirateBoxService.class);
stopService(serviceIntent);
}
/**
* Starts the service
*/
@Override
public void startService() {
/*
* Do nothing, if service is already running.
*/
if (PirateBoxService.isRunning()) {
return;
}
Intent serviceIntent = new Intent(PirateBoxActivity.this,
PirateBoxService.class);
startService(serviceIntent);
}
/**
* Checks the installation and extracts the content.zip file
* to INSTALL_DIR if needed
*/
private void checkInstallation() {
String pref_version = preferences.getString(Constants.PREF_VERSION, null);
boolean update = preferences.getBoolean(Constants.PREF_UPDATE, true);
/*
* If installation directory exists and updates should not
* be installed automatically -> return
*/
if(new File(INSTALL_DIR).exists() && !update) {
return;
}
if(!new File(INSTALL_DIR).exists() || (version == null || pref_version == null) || !pref_version.equals(version)) {
// Create directories
new File(INSTALL_DIR).mkdirs();
final ProgressDialog progress = new ProgressDialog(this);
progress.setMessage(getString(R.string.please_wait));
progress.setCancelable(false);
progress.setCanceledOnTouchOutside(false);
progress.show();
new Thread() {
public void run() {
// Files not to overwrite
HashMap<String, Integer> keepFiles = new HashMap<String, Integer>();
// Extract ZIP file form assets
try {
extractZip(getAssets().open("content.zip"),
INSTALL_DIR, keepFiles);
// Write new version to preferences
Editor edit = preferences.edit();
edit.putString(Constants.PREF_VERSION, version);
edit.commit();
} catch (IOException e) {
Log.e(Constants.TAG, e.getMessage());
}
finally {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
progress.dismiss();
}
});
}
}
}.start();
}
}
@Override
public void apEnabled(boolean autoStartup) {
System.out.println("apEnabled");
imgAp.setVisibility(View.VISIBLE);
imgAp.setAlpha(autoStartup ? 255 : 50);
txtInfo.setText(getText(R.string.msg_ap_up));
checkStatus();
}
@Override
public void apDisabled(boolean autoStartup) {
System.out.println("apDisabled");
imgAp.setVisibility(View.INVISIBLE);
txtInfo.setText(getText(R.string.msg_ap_down));
checkStatus();
}
@Override
public void dnsMasqWrapped(WrapResult result) {
// Show warning dialogue if the wrapper did not work properly
if(result != WrapResult.OK) {
new AlertDialog.Builder(this)
.setTitle(getText(R.string.dialog_title_warning))
.setMessage(R.string.dialog_msg_wrap_error)
.setPositiveButton(getText(android.R.string.ok), null)
.show();
}
}
@Override
public void dnsMasqUnWrapped() {
}
@Override
public void networkUp() {
imgNetwork.setVisibility(View.VISIBLE);
txtInfo.setText(getText(R.string.msg_networking_up));
checkStatus();
}
@Override
public void serverUp(boolean success) {
imgServer.setVisibility(View.VISIBLE);
imgServer.setAlpha(!PirateBoxService.externalServerRunning ? 255 : 50);
txtInfo.setText(getText(R.string.msg_webserver_down));
checkStatus();
}
@Override
public void serverDown(boolean success) {
imgServer.setVisibility(View.INVISIBLE);
txtInfo.setText(getText(R.string.msg_webserver_down));
checkStatus();
}
@Override
public void networkDown() {
System.out.println("networkDown");
imgNetwork.setVisibility(View.INVISIBLE);
//btnSwitch.setEnabled(true);
txtInfo.setText(getText(R.string.msg_networking_down));
checkStatus();
}
/**
* Checks the PirateBox status and disables/enables the on/off switch
* accordingly
*/
private void checkStatus() {
int status = imgNetwork.getVisibility() + imgAp.getVisibility()
+ imgServer.getVisibility();
switch (status) {
case 3 * View.VISIBLE:
txtInfo.setText(getText(R.string.msg_piratebox_up));
btnSwitch.setImageResource(R.drawable.switch_on);
btnSwitch.clearAnimation();
btnSwitch.setEnabled(true);
break;
case 3 * View.INVISIBLE:
txtInfo.setText(getText(R.string.msg_piratebox_down));
btnSwitch.setImageResource(R.drawable.switch_off);
btnSwitch.setEnabled(true);
break;
}
// Need by Android 5 to make images invisible if needed
activity.findViewById(R.id.layoutImages).postInvalidate();
}
/**
* Checks if all prerequisites are met.
* Prerequisites are: rooted device, dnsmasq available and iptables available.
* If prerequisites are not me a message is displayed and buttons and menu
* are disabled.
*/
private void checkPreRequisites() {
Map<String, String> checks = new HashMap<String, String>();
String message = "";
checks.put(ShellUtil.SU_BIN, getString(R.string.chk_not_rooted));
// Android 5 fix: "su -c" is needed
checks.put(ShellUtil.SU_BIN + " -c " + NetworkUtil.DNSMASQ_BIN, getString(R.string.chk_missing_dnsmasq));
checks.put(NetworkUtil.DNSMASQ_BIN_BACKUP, getString(R.string.chk_missing_dnsmasq_backup));
checks.put(NetworkUtil.IPTABLES_BIN, getString(R.string.chk_missing_iptables));
ShellUtil shellUtil = new ShellUtil();
for(String cmd : checks.keySet()) {
if(!shellUtil.isCommandAvailable(cmd)) {
message += "- " + checks.get(cmd) + "\n";
}
}
if(message.length() > 0) {
message = activity.getText(R.string.chk_unsupported_device) + "\n" + message;
supportedDevice = false;
btnSwitch.setVisibility(View.GONE);
txtInfo.setTextColor(Color.RED);
txtInfo.setText(message);
}
else {
supportedDevice = true;
}
}
}