package se.leap.bitmaskclient; import se.leap.bitmaskclient.R; import de.blinkt.openvpn.activities.LogWindow; import de.blinkt.openvpn.core.VpnStatus; import de.blinkt.openvpn.core.VpnStatus.ConnectionStatus; import de.blinkt.openvpn.core.VpnStatus.StateListener; import android.app.Activity; import android.app.AlertDialog; import android.app.Fragment; import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.ResultReceiver; import android.util.Log; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.CompoundButton; import android.widget.RelativeLayout; import android.widget.Switch; import android.widget.TextView; public class EipServiceFragment extends Fragment implements StateListener, OnCheckedChangeListener { protected static final String IS_EIP_PENDING = "is_eip_pending"; public static final String START_ON_BOOT = "start on boot"; private View eipFragment; private static Switch eipSwitch; private View eipDetail; private TextView eipStatus; private boolean eipAutoSwitched = true; private boolean mEipStartPending = false; private boolean set_switch_off = false; private static EIPReceiver mEIPReceiver; public static String TAG = "se.leap.bitmask.EipServiceFragment"; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { eipFragment = inflater.inflate(R.layout.eip_service_fragment, container, false); eipDetail = ((RelativeLayout) eipFragment.findViewById(R.id.eipDetail)); eipDetail.setVisibility(View.VISIBLE); View eipSettings = eipFragment.findViewById(R.id.eipSettings); eipSettings.setVisibility(View.GONE); // FIXME too! if (mEipStartPending) eipFragment.findViewById(R.id.eipProgress).setVisibility(View.VISIBLE); eipStatus = (TextView) eipFragment.findViewById(R.id.eipStatus); eipSwitch = (Switch) eipFragment.findViewById(R.id.eipSwitch); eipSwitch.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { eipAutoSwitched = false; return false; } }); eipSwitch.setOnCheckedChangeListener(this); if(getArguments() != null && getArguments().containsKey(START_ON_BOOT) && getArguments().getBoolean(START_ON_BOOT)) startEipFromScratch(); return eipFragment; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mEIPReceiver = new EIPReceiver(new Handler()); if (savedInstanceState != null) mEipStartPending = savedInstanceState.getBoolean(IS_EIP_PENDING); } @Override public void onResume() { super.onResume(); VpnStatus.addStateListener(this); if(set_switch_off) { eipSwitch.setChecked(false); set_switch_off = false; } } protected void setSwitchOff(boolean value) { set_switch_off = value; } @Override public void onPause() { super.onPause(); VpnStatus.removeStateListener(this); } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putBoolean(IS_EIP_PENDING, mEipStartPending); } protected void saveEipStatus() { boolean eip_is_on = false; Log.d("bitmask", "saveEipStatus"); if(eipSwitch.isChecked()) { eip_is_on = true; } if(getActivity() != null) getActivity().getSharedPreferences(Dashboard.SHARED_PREFERENCES, Activity.MODE_PRIVATE).edit().putBoolean(Dashboard.START_ON_BOOT, eip_is_on).commit(); } @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { Log.d("bitmask", "onCheckChanged"); if (buttonView.equals(eipSwitch) && !eipAutoSwitched){ boolean allowed_anon = getActivity().getSharedPreferences(Dashboard.SHARED_PREFERENCES, Activity.MODE_PRIVATE).getBoolean(EIP.ALLOWED_ANON, false); String certificate = getActivity().getSharedPreferences(Dashboard.SHARED_PREFERENCES, Activity.MODE_PRIVATE).getString(EIP.CERTIFICATE, ""); if(allowed_anon || !certificate.isEmpty()) { if (isChecked){ startEipFromScratch(); } else { if (mEipStartPending){ AlertDialog.Builder alertBuilder = new AlertDialog.Builder(getActivity()); alertBuilder.setTitle(getResources().getString(R.string.eip_cancel_connect_title)); alertBuilder .setMessage(getResources().getString(R.string.eip_cancel_connect_text)) .setPositiveButton(getResources().getString(R.string.eip_cancel_connect_cancel), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { eipCommand(EIP.ACTION_STOP_EIP); mEipStartPending = false; } }) .setNegativeButton(getResources().getString(R.string.eip_cancel_connect_false), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { eipAutoSwitched = true; eipSwitch.setChecked(true); eipAutoSwitched = false; } }) .show(); } else { eipCommand(EIP.ACTION_STOP_EIP); } } } else { Dashboard dashboard = (Dashboard)getActivity(); Bundle waiting_on_login = new Bundle(); waiting_on_login.putBoolean(IS_EIP_PENDING, true); dashboard.logInDialog(getActivity().getCurrentFocus(), waiting_on_login); } } else { if(!eipSwitch.isChecked()) eipStatus.setText(R.string.state_noprocess); } eipAutoSwitched = true; saveEipStatus(); } public void startEipFromScratch() { mEipStartPending = true; eipFragment.findViewById(R.id.eipProgress).setVisibility(View.VISIBLE); ((TextView) eipFragment.findViewById(R.id.eipStatus)).setText(R.string.eip_status_start_pending); eipSwitch.setChecked(true); saveEipStatus(); eipCommand(EIP.ACTION_START_EIP); } /** * Send a command to EIP * * @param action A valid String constant from EIP class representing an Intent * filter for the EIP class */ private void eipCommand(String action){ // TODO validate "action"...how do we get the list of intent-filters for a class via Android API? Intent vpnIntent = new Intent(action); vpnIntent.putExtra(EIP.RECEIVER_TAG, mEIPReceiver); getActivity().startService(vpnIntent); } @Override public void updateState(final String state, final String logmessage, final int localizedResId, final ConnectionStatus level) { // Note: "states" are not organized anywhere...collected state strings: // NOPROCESS,NONETWORK,BYTECOUNT,AUTH_FAILED + some parsing thing ( WAIT(?),AUTH,GET_CONFIG,ASSIGN_IP,CONNECTED,SIGINT ) getActivity().runOnUiThread(new Runnable() { @Override public void run() { if (eipStatus != null) { boolean switchState = true; String statusMessage = ""; String prefix = getString(localizedResId); if (level == ConnectionStatus.LEVEL_CONNECTED){ statusMessage = getString(R.string.eip_state_connected); getActivity().findViewById(R.id.eipProgress).setVisibility(View.GONE); mEipStartPending = false; } else if ( level == ConnectionStatus.LEVEL_NONETWORK || level == ConnectionStatus.LEVEL_NOTCONNECTED || level == ConnectionStatus.LEVEL_AUTH_FAILED) { statusMessage = getString(R.string.eip_state_not_connected); if(getActivity() != null && getActivity().findViewById(R.id.eipProgress) != null) getActivity().findViewById(R.id.eipProgress).setVisibility(View.GONE); mEipStartPending = false; switchState = false; } else if (level == ConnectionStatus.LEVEL_CONNECTING_SERVER_REPLIED) { if(state.equals("AUTH") || state.equals("GET_CONFIG")) statusMessage = prefix + " " + logmessage; } else if (level == ConnectionStatus.LEVEL_CONNECTING_NO_SERVER_REPLY_YET) { statusMessage = prefix + " " + logmessage; } eipAutoSwitched = true; eipSwitch.setChecked(switchState); eipAutoSwitched = false; eipStatus.setText(statusMessage); } } }); } /** * Inner class for handling messages related to EIP status and control requests * * @author Sean Leonard */ protected class EIPReceiver extends ResultReceiver { protected EIPReceiver(Handler handler){ super(handler); } @Override protected void onReceiveResult(int resultCode, Bundle resultData) { super.onReceiveResult(resultCode, resultData); String request = resultData.getString(EIP.REQUEST_TAG); boolean checked = false; if (request == EIP.ACTION_IS_EIP_RUNNING) { switch (resultCode){ case Activity.RESULT_OK: checked = true; break; case Activity.RESULT_CANCELED: checked = false; break; } } else if (request == EIP.ACTION_START_EIP) { switch (resultCode){ case Activity.RESULT_OK: checked = true; eipFragment.findViewById(R.id.eipProgress).setVisibility(View.VISIBLE); break; case Activity.RESULT_CANCELED: checked = false; eipFragment.findViewById(R.id.eipProgress).setVisibility(View.GONE); break; } } else if (request == EIP.ACTION_STOP_EIP) { switch (resultCode){ case Activity.RESULT_OK: checked = false; break; case Activity.RESULT_CANCELED: checked = true; break; } } else if (request == EIP.EIP_NOTIFICATION) { switch (resultCode){ case Activity.RESULT_OK: checked = true; break; case Activity.RESULT_CANCELED: checked = false; break; } } eipAutoSwitched = true; eipSwitch.setChecked(checked); eipAutoSwitched = false; } } public static EIPReceiver getReceiver() { return mEIPReceiver; } public static boolean isEipSwitchChecked() { return eipSwitch.isChecked(); } public void checkEipSwitch(boolean checked) { eipSwitch.setChecked(checked); onCheckedChanged(eipSwitch, checked); } }