diff options
Diffstat (limited to 'app/src/fil/libre/repwifiapp/activities')
14 files changed, 1611 insertions, 639 deletions
diff --git a/app/src/fil/libre/repwifiapp/activities/ConnectionBoundActivity.java b/app/src/fil/libre/repwifiapp/activities/ConnectionBoundActivity.java new file mode 100644 index 0000000..4996024 --- /dev/null +++ b/app/src/fil/libre/repwifiapp/activities/ConnectionBoundActivity.java @@ -0,0 +1,282 @@ +// +// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> +// +// This file is part of RepWifiApp. +// +// RepWifiApp 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. +// +// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>. +// +// ******************************************************************** + +package fil.libre.repwifiapp.activities; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.content.SharedPreferences; +import android.content.SharedPreferences.OnSharedPreferenceChangeListener; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.Messenger; +import android.preference.PreferenceManager; +import fil.libre.repwifiapp.Prefs; +import fil.libre.repwifiapp.helpers.Logger; +import fil.libre.repwifiapp.network.AccessPointInfo; +import fil.libre.repwifiapp.network.ConnectionResult; +import fil.libre.repwifiapp.network.ConnectionStatus; +import fil.libre.repwifiapp.service.Channel; +import fil.libre.repwifiapp.service.ConnectionManagementService; + +/** + * Provides a base class for all activities that need to bind to the + * ConnectionManagementService. + */ +public class ConnectionBoundActivity extends MenuEnabledActivity implements + OnSharedPreferenceChangeListener { + + private IBinder svcBinder = null; + private Channel channel = null; + private ServiceConnection svcConn = null; + + + @Override + public void onStart() { + super.onStart(); + startConnectionService(); + registerPreferenceChangeListener(); + } + + protected boolean isServiceBound() { + return (channel != null); + } + + private void startConnectionService() { + + Intent startIntent = new Intent(this, ConnectionManagementService.class); + startIntent.setAction(ConnectionManagementService.ACTION_VOID); + startService(startIntent); + + if (channel != null) { + Logger.logDebug("ConnectionManagementService already bound"); + return; + } + + try { + + if (svcConn == null) { + + svcConn = new ServiceConnection() { + + public void onServiceConnected(ComponentName className, + android.os.IBinder service) { + ConnectionBoundActivity.this.initChannel(service); + } + + public void onServiceDisconnected(ComponentName className) { + ConnectionBoundActivity.this.channel = null; + ConnectionBoundActivity.this.svcBinder = null; + } + + }; + + } + + Intent intentGetService = new Intent(this, ConnectionManagementService.class); + if (!this.bindService(intentGetService, svcConn, Context.BIND_AUTO_CREATE)) { + Logger.logError("Failed to bind to ConnectionManagementService (bindService returned false)"); + return; + } + + } catch (Exception ex) { + Logger.logError("Exception while bounding to inner connectivity management service.", + ex); + } + + } + + private void initChannel(IBinder service) { + ConnectionBoundActivity.this.svcBinder = service; + ConnectionBoundActivity.this.channel = new Channel(this, new Messenger(service), + new Messenger(new ResponseHandler())); + ConnectionBoundActivity.this.onManagementServiceConnected(); + } + + protected void onManagementServiceConnected() { + sendCmdPrefChanged(Prefs.PREF_MONITOR_NET_STATE); + } + + protected boolean sendCmdStartConnect(AccessPointInfo network) { + if (channel == null) { + return false; + } + return channel.sendMsg(ConnectionManagementService.CMD_START_CONNECT, network, + Channel.PAYLOAD_APINFO); + } + + protected boolean sendCmdAbortConnection() { + return sendMsgIfChannelConnected(ConnectionManagementService.CMD_ABORT_CONNECTION); + } + + protected boolean sendCmdDisconnect() { + return sendMsgIfChannelConnected(ConnectionManagementService.CMD_DISCONNECT); + } + + protected boolean sendCmdGetAvailableNetworks() { + return sendMsgIfChannelConnected(ConnectionManagementService.CMD_GET_AVAILABLE_NETWORKS); + } + + protected boolean sendCmdStartMonitoringConnectionStatus() { + return sendMsgIfChannelConnected(ConnectionManagementService.CMD_START_MONITOR_CONNECTION_STATUS); + } + + protected boolean sendCmdStopMonitoringConnectionStatus() { + return sendMsgIfChannelConnected(ConnectionManagementService.CMD_STOP_MONITOR_CONNECTION_STATUS); + } + + protected boolean requestStatusUpdate() { + return sendMsgIfChannelConnected(ConnectionManagementService.CMD_STATUS_UPDATE); + } + + protected boolean sendCmdAutoconnect() { + return sendMsgIfChannelConnected(ConnectionManagementService.CMD_AUTOCONNECT); + } + + private boolean sendCmdPrefChanged(String prefname){ + if (channel == null){ + return false; + } + Bundle b = new Bundle(); + b.putString(Channel.PAYLOAD_PREFKEY, prefname); + return channel.sendMsg(ConnectionManagementService.CMD_PREF_CHANGED, b, 0); + } + + private void sendCmdUnbind() { + sendMsgIfChannelConnected(ConnectionManagementService.CMD_CLIENT_UNBINDING); + } + + private boolean sendMsgIfChannelConnected(int what) { + return sendMsgIfChannelConnected(what, 0); + } + + private boolean sendMsgIfChannelConnected(int what, int arg1) { + if (channel == null) { + return false; + } + return channel.sendMsg(what, null, arg1); + } + + protected void onMsgStatusChange(ConnectionStatus status) { + } + + protected void onMsgConnectionResult(ConnectionResult connres) { + } + + protected void onMsgAvailableNetworks(AccessPointInfo[] infos) { + } + + protected void onMsgAutoconnectReport(AccessPointInfo[] infos) { + } + + protected void onMsgDisconnectReport(ConnectionStatus status) { + } + + private class ResponseHandler extends Handler { + + public void handleMessage(Message msg) { + switch (msg.what) { + + case ConnectionManagementService.MSG_STATUS_CHANGE: + Logger.logDebug("Received status update from the management service"); + ConnectionStatus c = channel.getConnectionStatusPayload(msg); + onMsgStatusChange(c); + break; + + case ConnectionManagementService.MSG_CONNECTION_RESULT: + Logger.logDebug("Received connection result from management service"); + ConnectionResult r = channel.getConnectionResultPayload(msg); + onMsgConnectionResult(r); + break; + + case ConnectionManagementService.MSG_AVAILABLE_NETWORKS: + Logger.logDebug("Received available networks from management service"); + onMsgAvailableNetworks(channel.getApinfoArrayPayload(msg)); + break; + + case ConnectionManagementService.MSG_AUTOCONNECT_REPORT: + Logger.logDebug("Received autoconnect report from management service"); + onMsgAutoconnectReport(channel.getApinfoArrayPayload(msg)); + break; + + case ConnectionManagementService.MSG_DISCONNECT_REPORT: + Logger.logDebug("Received disconnect report from management service"); + onMsgDisconnectReport(channel.getConnectionStatusPayload(msg)); + break; + + default: + Logger.logError("Received response from connection management service with unknown what: " + + msg.what); + } + } + + } + + private void registerPreferenceChangeListener() { + + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(getApplicationContext()); + prefs.registerOnSharedPreferenceChangeListener(this); + + } + + private void unregisterPreferenceChangeListener() { + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(getApplicationContext()); + prefs.unregisterOnSharedPreferenceChangeListener(this); + } + + + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + + if (sharedPreferences == null) { + return; + } + + sharedPreferences.edit().commit(); + Prefs.commit(getApplicationContext()); + sendCmdPrefChanged(key); + + } + + protected void unbindFromService() { + sendCmdUnbind(); + if (svcBinder != null && svcConn != null && channel != null) { + unbindService(svcConn); + } + } + + @Override + protected void onStop() { + super.onStop(); + } + + @Override + public void onDestroy() { + unregisterPreferenceChangeListener(); + unbindFromService(); + super.onDestroy(); + } +} diff --git a/app/src/fil/libre/repwifiapp/activities/CreditsActivity.java b/app/src/fil/libre/repwifiapp/activities/CreditsActivity.java index 80c15e6..cc520a9 100644 --- a/app/src/fil/libre/repwifiapp/activities/CreditsActivity.java +++ b/app/src/fil/libre/repwifiapp/activities/CreditsActivity.java @@ -1,5 +1,5 @@ // -// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> +// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> // // This file is part of RepWifiApp. // @@ -20,14 +20,14 @@ package fil.libre.repwifiapp.activities; -import fil.libre.repwifiapp.R; -import android.os.Bundle; import android.app.Activity; +import android.os.Bundle; import android.text.Html; import android.text.method.LinkMovementMethod; import android.text.method.ScrollingMovementMethod; import android.view.Menu; import android.widget.TextView; +import fil.libre.repwifiapp.R; public class CreditsActivity extends Activity { diff --git a/app/src/fil/libre/repwifiapp/activities/InputPasswordActivity.java b/app/src/fil/libre/repwifiapp/activities/InputPasswordActivity.java index 32922c6..7578ec3 100644 --- a/app/src/fil/libre/repwifiapp/activities/InputPasswordActivity.java +++ b/app/src/fil/libre/repwifiapp/activities/InputPasswordActivity.java @@ -1,5 +1,5 @@ // -// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> +// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> // // This file is part of RepWifiApp. // @@ -17,13 +17,11 @@ // along with RepWifiApp. If not, see <http://www.gnu.org/licenses/>. // // ******************************************************************** +// +// ******************************************************************** package fil.libre.repwifiapp.activities; -import fil.libre.repwifiapp.ActivityLauncher; -import fil.libre.repwifiapp.Commons; -import fil.libre.repwifiapp.R; -import fil.libre.repwifiapp.helpers.AccessPointInfo; import android.app.Activity; import android.content.Intent; import android.os.Bundle; @@ -35,10 +33,15 @@ import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.EditText; import android.widget.TextView; +import fil.libre.repwifiapp.ActivityLauncher; +import fil.libre.repwifiapp.R; +import fil.libre.repwifiapp.Utils; +import fil.libre.repwifiapp.network.AccessPointInfo; public class InputPasswordActivity extends Activity implements OnCheckedChangeListener { AccessPointInfo apinfo = null; + String message = null; @Override public void onCreate(Bundle icicle) { @@ -54,11 +57,27 @@ public class InputPasswordActivity extends Activity implements OnCheckedChangeLi TextView v = (TextView) findViewById(R.id.txt_insert_pass); // get the network to set password to: - this.apinfo = (AccessPointInfo) getIntent().getSerializableExtra( + this.apinfo = (AccessPointInfo) getIntent().getParcelableExtra( ActivityLauncher.EXTRA_APINFO); + + if (getIntent().hasExtra(ActivityLauncher.EXTRA_MESSAGE)){ + this.message = getIntent().getStringExtra(ActivityLauncher.EXTRA_MESSAGE); + } + v.append(" " + apinfo.getSsid()); } + + @Override + public void onStart(){ + super.onStart(); + + if (this.message != null){ + Utils.showMessage(message, this); + this.message = null; + } + + } @Override public boolean onCreateOptionsMenu(Menu menu) { @@ -81,7 +100,7 @@ public class InputPasswordActivity extends Activity implements OnCheckedChangeLi String pass = txpass.getText().toString(); if (pass.length() == 0) { - Commons.showMessage(getString(R.string.msg_password_empty), this); + Utils.showMessage(getString(R.string.msg_password_empty), this); } this.apinfo.setPassword(pass); diff --git a/app/src/fil/libre/repwifiapp/activities/InputSsidActivity.java b/app/src/fil/libre/repwifiapp/activities/InputSsidActivity.java index 8b7c60c..8ff1100 100644 --- a/app/src/fil/libre/repwifiapp/activities/InputSsidActivity.java +++ b/app/src/fil/libre/repwifiapp/activities/InputSsidActivity.java @@ -1,5 +1,5 @@ // -// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> +// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> // // This file is part of RepWifiApp. // @@ -17,20 +17,20 @@ // along with RepWifiApp. If not, see <http://www.gnu.org/licenses/>. // // ******************************************************************** - package fil.libre.repwifiapp.activities; -import fil.libre.repwifiapp.ActivityLauncher; -import fil.libre.repwifiapp.ActivityLauncher.RequestCode; -import fil.libre.repwifiapp.Commons; -import fil.libre.repwifiapp.R; -import fil.libre.repwifiapp.helpers.AccessPointInfo; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.Menu; import android.view.View; import android.widget.EditText; +import fil.libre.repwifiapp.ActivityLauncher; +import fil.libre.repwifiapp.ActivityLauncher.RequestCode; +import fil.libre.repwifiapp.Commons; +import fil.libre.repwifiapp.R; +import fil.libre.repwifiapp.Utils; +import fil.libre.repwifiapp.network.AccessPointInfo; public class InputSsidActivity extends Activity { @@ -66,8 +66,7 @@ public class InputSsidActivity extends Activity { if (intent.hasExtra(ActivityLauncher.EXTRA_APINFO)) { Bundle xtras = intent.getExtras(); - AccessPointInfo i = (AccessPointInfo) xtras - .getSerializable(ActivityLauncher.EXTRA_APINFO); + AccessPointInfo i = (AccessPointInfo) xtras.getParcelable(ActivityLauncher.EXTRA_APINFO); returnAccessPointInfo(i); } @@ -85,7 +84,7 @@ public class InputSsidActivity extends Activity { String ssid = txssid.getText().toString(); if (ssid.length() == 0) { - Commons.showMessage(getString(R.string.msg_network_name_empty), this); + Utils.showMessage(getString(R.string.msg_network_name_empty), this); return; } diff --git a/app/src/fil/libre/repwifiapp/activities/Ipv4SettingsActivity.java b/app/src/fil/libre/repwifiapp/activities/Ipv4SettingsActivity.java index 1f15252..eba62ac 100644 --- a/app/src/fil/libre/repwifiapp/activities/Ipv4SettingsActivity.java +++ b/app/src/fil/libre/repwifiapp/activities/Ipv4SettingsActivity.java @@ -1,5 +1,5 @@ // -// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> +// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> // // This file is part of RepWifiApp. // @@ -20,21 +20,22 @@ package fil.libre.repwifiapp.activities; -import fil.libre.repwifiapp.ActivityLauncher; -import fil.libre.repwifiapp.Commons; -import fil.libre.repwifiapp.R; -import fil.libre.repwifiapp.helpers.AccessPointInfo; -import fil.libre.repwifiapp.helpers.DhcpSettings; -import android.nfc.FormatException; -import android.os.Bundle; import android.app.Activity; import android.content.Intent; +import android.nfc.FormatException; +import android.os.Bundle; import android.view.Menu; import android.view.View; import android.widget.CheckBox; import android.widget.CompoundButton; -import android.widget.EditText; import android.widget.CompoundButton.OnCheckedChangeListener; +import android.widget.EditText; +import fil.libre.repwifiapp.ActivityLauncher; +import fil.libre.repwifiapp.R; +import fil.libre.repwifiapp.Utils; +import fil.libre.repwifiapp.network.AccessPointInfo; +import fil.libre.repwifiapp.network.DhcpSettings; +import fil.libre.repwifiapp.network.NetworkManager; public class Ipv4SettingsActivity extends Activity implements OnCheckedChangeListener { @@ -65,15 +66,14 @@ public class Ipv4SettingsActivity extends Activity implements OnCheckedChangeLis return; } - this.currentNetwork = (AccessPointInfo) intent.getExtras().getSerializable( - ActivityLauncher.EXTRA_APINFO); + this.currentNetwork = (AccessPointInfo) intent.getExtras().getParcelable(ActivityLauncher.EXTRA_APINFO); if (this.currentNetwork == null) { this.setResult(RESULT_CANCELED); this.finish(); return; } - this.currentNetwork = Commons.storage.getSavedNetwork(currentNetwork); + this.currentNetwork = NetworkManager.getSavedNetwork(currentNetwork); loadNetwork(); @@ -88,7 +88,7 @@ public class Ipv4SettingsActivity extends Activity implements OnCheckedChangeLis private void loadNetwork() { - setTitle(this.currentNetwork.getSsid()); + setTitle(getString(R.string.text_ipv4_settings) + " " + this.currentNetwork.getSsid()); currentSettings = this.currentNetwork.getDhcpConfiguration(); loadSettings(); @@ -147,17 +147,17 @@ public class Ipv4SettingsActivity extends Activity implements OnCheckedChangeLis String gw = txtGw.getText().toString(); if (!DhcpSettings.isValidAddress(ip)) { - Commons.showMessage(getString(R.string.msg_invalid_ip),this); + Utils.showMessage(getString(R.string.msg_invalid_ip),this); return; } if (!DhcpSettings.isValidMaks(mask)) { - Commons.showMessage(getString(R.string.msg_invalid_netmask),this); + Utils.showMessage(getString(R.string.msg_invalid_netmask),this); return; } if (!DhcpSettings.isValidAddress(gw)) { - Commons.showMessage(getString(R.string.msg_invalid_gateway),this); + Utils.showMessage(getString(R.string.msg_invalid_gateway),this); return; } @@ -166,10 +166,10 @@ public class Ipv4SettingsActivity extends Activity implements OnCheckedChangeLis } currentNetwork.setDhcpConfiguration(currentSettings); - if (Commons.storage.save(currentNetwork)) { - Commons.showMessage(getString(R.string.msg_network_saved),this); + if (NetworkManager.save(currentNetwork)) { + Utils.showMessage(getString(R.string.msg_network_saved),this); } else { - Commons.showMessage(getString(R.string.msg_network_save_fail),this); + Utils.showMessage(getString(R.string.msg_network_save_fail),this); } } diff --git a/app/src/fil/libre/repwifiapp/activities/LongTaskActivity.java b/app/src/fil/libre/repwifiapp/activities/LongTaskActivity.java index a5292d2..940d547 100644 --- a/app/src/fil/libre/repwifiapp/activities/LongTaskActivity.java +++ b/app/src/fil/libre/repwifiapp/activities/LongTaskActivity.java @@ -1,5 +1,5 @@ // -// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> +// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> // // This file is part of RepWifiApp. // @@ -18,83 +18,47 @@ // // ******************************************************************** - package fil.libre.repwifiapp.activities; -import fil.libre.repwifiapp.ActivityLauncher; -import fil.libre.repwifiapp.Commons; -import fil.libre.repwifiapp.R; -import fil.libre.repwifiapp.helpers.AccessPointInfo; -import fil.libre.repwifiapp.helpers.ConnectionStatus; -import fil.libre.repwifiapp.helpers.Utils; -import android.os.AsyncTask; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.Intent; import android.os.Bundle; import android.view.Menu; import android.view.View; import android.widget.ProgressBar; import android.widget.TextView; -import android.app.Activity; -import android.content.Intent; - -public class LongTaskActivity extends Activity { - - private class Task extends AsyncTask<Object, Object, Object> { - - private int REQ_CODE; - - public Task(int reqCode, Object input) { - this.REQ_CODE = reqCode; - } - - @Override - protected Object doInBackground(Object... params) { - - Object ret = null; - - switch (this.REQ_CODE) { - - case ActivityLauncher.RequestCode.CONNECT: - - ret = Commons.connectionEngine.connect((AccessPointInfo) params[0]); - break; - - case ActivityLauncher.RequestCode.NETWORKS_GET: - - ret = Commons.connectionEngine.getAvailableNetworks(); - break; - - case ActivityLauncher.RequestCode.STATUS_GET: - - ret = Commons.connectionEngine.getConnectionStatus(); - break; - - default: - - break; - - } - - return ret; - - } - - @Override - protected void onPostExecute(Object result) { - taskCompleted(result, this.REQ_CODE); - } +import android.widget.Toast; +import fil.libre.repwifiapp.ActivityLauncher; +import fil.libre.repwifiapp.ActivityLauncher.RequestCode; +import fil.libre.repwifiapp.Prefs; +import fil.libre.repwifiapp.R; +import fil.libre.repwifiapp.helpers.Logger; +import fil.libre.repwifiapp.network.AccessPointInfo; +import fil.libre.repwifiapp.network.ConnectionResult; - } +public class LongTaskActivity extends ConnectionBoundActivity { private AccessPointInfo currentNetwork = null; + // private Task currentTask = null; + + private int currentReqCode = -1; + private boolean isCancelled = false; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_long_task); - - toggleProgbar(Commons.isProgbarEnabled()); - startTask(); - + toggleProgbar(isProgbarEnabled()); + + } + + @Override + public void onStart(){ + super.onStart(); + if (isServiceBound()){ + startTask(); + } } @Override @@ -102,6 +66,21 @@ public class LongTaskActivity extends Activity { return true; } + @Override + protected void onManagementServiceConnected() { + startTask(); + } + + @Override + protected void onMsgConnectionResult(ConnectionResult connres) { + taskCompleted(connres, currentReqCode); + } + + @Override + protected void onMsgAvailableNetworks(AccessPointInfo[] infos) { + taskCompleted(infos, currentReqCode); + } + private void startTask() { // retrieve the request code: @@ -112,40 +91,56 @@ public class LongTaskActivity extends Activity { } Object input = null; - int reqCode = intent.getExtras().getInt(ActivityLauncher.EXTRA_REQCODE); - - switch (reqCode) { + currentReqCode = intent.getExtras().getInt(ActivityLauncher.EXTRA_REQCODE); + + switch (currentReqCode) { case ActivityLauncher.RequestCode.CONNECT: // Extract AccessPointInfo - input = intent.getExtras().getSerializable(ActivityLauncher.EXTRA_APINFO); + input = intent.getExtras().getParcelable(ActivityLauncher.EXTRA_APINFO); currentNetwork = (AccessPointInfo) input; setTitle(getString(R.string.msg_connecting_to) + " " + currentNetwork.getSsid() + "..."); - setMessage(getString(R.string.msg_connecting_to) + " " + currentNetwork.getSsid() + "..."); + setMessage(getString(R.string.msg_connecting_to) + " " + currentNetwork.getSsid() + + "..."); + + sendCmdStartConnect(currentNetwork); + break; case ActivityLauncher.RequestCode.NETWORKS_GET: setTitle(getString(R.string.title_scanning)); setMessage(getString(R.string.msg_scanning_for_nets)); - - case ActivityLauncher.RequestCode.STATUS_GET: + + sendCmdGetAvailableNetworks(); + + break; + +/* case ActivityLauncher.RequestCode.STATUS_GET: setTitle(getString(R.string.msg_checking_status)); setMessage(getString(R.string.msg_checking_status)); - + break;*/ + default: setTitle(getString(R.string.msg_please_wait)); setMessage(getString(R.string.msg_please_wait)); break; } - Task task = new Task(reqCode, input); - task.execute(input); + +/* currentTask = new Task(reqCode, input); + currentTask.execute(input);*/ } private void taskCompleted(Object result, int reqCode) { - Utils.logDebug("Finished long task reqCode: " + reqCode, 1); + Logger.logDebug("Finished long task reqCode: " + reqCode); + + if (isCancelled){ + Logger.logDebug("Received result but activity has been canceled"); + this.setResult(RESULT_CANCELED); + finish(); + } // Return to caller: Intent intent = this.getIntent(); @@ -154,7 +149,7 @@ public class LongTaskActivity extends Activity { case ActivityLauncher.RequestCode.CONNECT: - intent.putExtra(ActivityLauncher.EXTRA_BOOLEAN, (Boolean) result); + intent.putExtra(ActivityLauncher.EXTRA_CONNRES, (ConnectionResult)result); intent.putExtra(ActivityLauncher.EXTRA_APINFO, this.currentNetwork); break; @@ -162,15 +157,15 @@ public class LongTaskActivity extends Activity { intent.putExtra(ActivityLauncher.EXTRA_APINFO_ARR, (AccessPointInfo[]) result); break; - +/* case ActivityLauncher.RequestCode.STATUS_GET: intent.putExtra(ActivityLauncher.EXTRA_CONSTATUS, (ConnectionStatus) result); - break; + break;*/ default: - Utils.logDebug("Task terminating in null: ", 1); + Logger.logDebug("Task terminating in null: ", 1); break; } @@ -199,7 +194,71 @@ public class LongTaskActivity extends Activity { @Override public void onBackPressed() { - // suppress back button + promtpCancelJob(); + } + + private void promtpCancelJob() { + + String msg = getString(R.string.msg_confirm_stop_job); + AlertDialog.Builder dlgAlert = new AlertDialog.Builder(this, + R.style.Theme_RepWifiDialogTheme); + dlgAlert.setMessage(msg); + dlgAlert.setPositiveButton(getString(android.R.string.ok), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int whichButton) { + confirmCancelJob(); + return; + } + }); + dlgAlert.setNegativeButton(getString(android.R.string.cancel), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int whichButton) { + return; + } + }); + + dlgAlert.setCancelable(false); + AlertDialog diag = dlgAlert.create(); + + diag.show(); + + } + + private boolean cancelConnection() { + return sendCmdAbortConnection(); + } + + private void confirmCancelJob() { + /* + * if (currentTask != null){ + * if (currentReqCode == RequestCode.CONNECT){ + * Commons.connectionEngine.abortConnection(); + * } + * + * currentTask.cancel(true); + * } + */ + + if (currentReqCode == RequestCode.CONNECT) { + if (cancelConnection()){ + + super.onBackPressed(); + } else { + Toast.makeText(getApplicationContext(), getString(R.string.msg_error_generic), Toast.LENGTH_LONG).show(); + } + + } else { + isCancelled = true; + super.onBackPressed(); + } + + + } + + private boolean isProgbarEnabled() { + return Prefs.getBoolean(getApplicationContext(), Prefs.PREF_PROGBAR, true); } } diff --git a/app/src/fil/libre/repwifiapp/activities/MainActivity.java b/app/src/fil/libre/repwifiapp/activities/MainActivity.java index 7a22416..14f33e9 100644 --- a/app/src/fil/libre/repwifiapp/activities/MainActivity.java +++ b/app/src/fil/libre/repwifiapp/activities/MainActivity.java @@ -1,5 +1,5 @@ // -// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> +// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> // // This file is part of RepWifiApp. // @@ -20,64 +20,80 @@ package fil.libre.repwifiapp.activities; -import java.net.SocketException; -import fil.libre.repwifiapp.ActivityLauncher; -import fil.libre.repwifiapp.Commons; -import fil.libre.repwifiapp.R; -import fil.libre.repwifiapp.ActivityLauncher.RequestCode; -import fil.libre.repwifiapp.helpers.AccessPointInfo; -import fil.libre.repwifiapp.helpers.ConnectionStatus; -import fil.libre.repwifiapp.helpers.NetworkManager; -import fil.libre.repwifiapp.helpers.OpenVpnManager; -import fil.libre.repwifiapp.helpers.RootCommand; -import fil.libre.repwifiapp.helpers.Utils; -import fil.libre.repwifiapp.helpers.WpaSupplicant; -import android.os.Bundle; +import android.app.AlertDialog; import android.content.BroadcastReceiver; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.AssetManager; +import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.hardware.usb.UsbManager; +import android.os.Bundle; +import android.os.Parcelable; import android.util.Log; import android.view.View; +import android.widget.Button; import android.widget.ImageView; +import android.widget.RelativeLayout; +import android.widget.RelativeLayout.LayoutParams; +import android.widget.TextView; import android.widget.Toast; +import fil.libre.repwifiapp.ActivityLauncher; +import fil.libre.repwifiapp.ActivityLauncher.RequestCode; +import fil.libre.repwifiapp.Commons; +import fil.libre.repwifiapp.Prefs; +import fil.libre.repwifiapp.R; +import fil.libre.repwifiapp.Utils; +import fil.libre.repwifiapp.helpers.Logger; +import fil.libre.repwifiapp.helpers.RootCommand; +import fil.libre.repwifiapp.network.AccessPointInfo; +import fil.libre.repwifiapp.network.ConnectionResult; +import fil.libre.repwifiapp.network.ConnectionStatus; +import fil.libre.repwifiapp.network.Engine; +import fil.libre.repwifiapp.network.NetworkManager; +import fil.libre.repwifiapp.network.WpaCli; +import fil.libre.repwifiapp.network.WpaSupplicant; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.SocketException; -public class MainActivity extends MenuEnabledActivity { +public class MainActivity extends VpnAndConnectionBoundActivity{ private ActivityLauncher launcher = new ActivityLauncher(this); private BroadcastReceiver detachReceiver; - + private ConnectionStatus status = null; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); - if (!Commons.init(this)) { - Utils.logDebug("Failed to initialize Commons. Aborting."); - finish(); - return; - } + Commons.init(getApplicationContext()); - if (! Commons.storage.updateStorageVersion()){ - Utils.logError("Failed to convert storage file to new version!"); + if (!NetworkManager.updateStorageVersion()) { + Logger.logError("Failed to convert storage file to new version!"); } - - setImage(); + + toggleStatusAppearance(false); setUsbDeviceMonitor(); setVersionOnTitle(); - - OpenVpnManager.initialize(this); + + if (isSystemApp()) { + setTitle(getTitle() + " (sysapp)"); + } } @Override public void onStart() { super.onStart(); - Utils.logDebug("Main onStart()"); - - Commons.updateNotification(this); + Logger.logDebug("Main onStart()"); if (handleIntent()) { // app called for a specific intent. @@ -88,14 +104,17 @@ public class MainActivity extends MenuEnabledActivity { checkConditions(); - ConnectionStatus status = Commons.connectionEngine.getConnectionStatus(); - if (status != null && status.isConnected()) { - Utils.logDebug("Main about to launch status activity..."); - launcher.launchStatusActivity(status); + if (isServiceBound()) { + requestStatusUpdate(); } - Utils.logDebug("Main onStart() returning."); + Logger.logDebug("Main onStart() returning."); + + } + @Override + public void onSaveInstanceState(Bundle savedInstanceState) { + super.onSaveInstanceState(savedInstanceState); } private boolean handleIntent() { @@ -126,53 +145,62 @@ public class MainActivity extends MenuEnabledActivity { @Override public void onActivityResult(int requestCode, int resultCode, Intent intent) { - Utils.logDebug("Main onActivityResult(): ", 1); + Logger.logDebug("Main onActivityResult(): "); if (intent == null) { return; } - if (resultCode != RESULT_OK) { + if (resultCode != RESULT_OK && requestCode != RequestCode.VPN_PERMISSION_CONN) { return; } AccessPointInfo i = null; if (intent.hasExtra(ActivityLauncher.EXTRA_APINFO)) { Bundle xtras = intent.getExtras(); - i = (AccessPointInfo) xtras.getSerializable(ActivityLauncher.EXTRA_APINFO); + i = (AccessPointInfo) xtras.getParcelable(ActivityLauncher.EXTRA_APINFO); } switch (requestCode) { case RequestCode.PASS_INPUT: + Logger.logDebug("ReqCode: PASS_INPUT"); handleResultSetPass(i); break; case RequestCode.SELECT_CONN: + Logger.logDebug("ReqCode: SELECT_CONN"); boolean rescan = intent.getExtras().getBoolean(ActivityLauncher.EXTRA_RESCAN); handleResultSelect(i, rescan); break; case RequestCode.CONNECT: - boolean conres = intent.getExtras().getBoolean(ActivityLauncher.EXTRA_BOOLEAN); + Logger.logDebug("ReqCode: CONNECT"); + ConnectionResult conres = (ConnectionResult) intent.getExtras().getParcelable( + ActivityLauncher.EXTRA_CONNRES); handleFinishedConnecting(conres, i); break; - case RequestCode.NETWORKS_GET: - AccessPointInfo[] nets = (AccessPointInfo[]) intent.getExtras().getSerializable( + Logger.logDebug("ReqCode: NETWORKS_GET"); + Parcelable[] p = intent.getExtras().getParcelableArray( ActivityLauncher.EXTRA_APINFO_ARR); + AccessPointInfo[] nets = AccessPointInfo.fromParcellableArray(p); launcher.launchSelectActivity(nets, true, false); + break; case RequestCode.STATUS_SHOW: + Logger.logDebug("ReqCode: STATUS_SHOW"); // do nothing break; case RequestCode.SELECT_DETAILS: + Logger.logDebug("ReqCode: SELECT_DETAILS"); launcher.launchDetailsActivity(i); break; case RequestCode.DETAILS_SHOW: + Logger.logDebug("ReqCode: DETAILS_SHOW"); boolean del = intent.getExtras().getBoolean(ActivityLauncher.EXTRA_DELETE); if (del) { deleteNetwork(i); @@ -180,13 +208,14 @@ public class MainActivity extends MenuEnabledActivity { break; case RequestCode.CONNECT_HIDDEN: + Logger.logDebug("ReqCode: CONNECT_HIDDEN"); if (i != null) { + Logger.logDebug("NetworkInfo NOT null, handling result"); handleResultSelect(i, false); } break; - default: break; @@ -207,22 +236,118 @@ public class MainActivity extends MenuEnabledActivity { setTitle(getTitle() + " - v." + vers); } catch (Exception e) { - Utils.logError("Error while setting version on MainActivity's title.", e); + Logger.logError("Error while setting version on MainActivity's title.", e); } } - private void setImage() { + private void showStatus(ConnectionStatus status) { + + Logger.logDebug("MainActivity.showStatus()"); - ImageView img = (ImageView) findViewById(R.id.img_logo); + String msg = ""; + this.status = status; try { - Drawable d = Drawable.createFromStream(getAssets().open("repwifi-logo-0.png"), null); - img.setImageDrawable(d); + + if (status == null) { + msg = getString(R.string.text_status) + ": [NULL]"; + toggleStatusAppearance(false); + + } else if (this.status.isConnected()) { + Logger.logDebug("showStatus isConnected,showing buttons"); + msg = getString(R.string.msg_connected_to) + " " + status.SSID; + toggleStatusAppearance(true); + + } else { + Logger.logDebug("showStatus status Else"); + msg = getString(R.string.msg_disconnected); + toggleStatusAppearance(false); + } + } catch (Exception e) { - Utils.logError("Error while loading logo image", e); + Logger.logError("Exception on showStatus", e); + msg = "[ERORR]"; } + TextView view = (TextView) findViewById(R.id.txt_status); + view.setText(msg); + + } + + private void toggleStatusAppearance(boolean connected) { + + try { + Button b = (Button) findViewById(R.id.btn_disconnect); + Button i = (Button) findViewById(R.id.btn_info); + ImageView img = (ImageView) findViewById(R.id.img_logo); + + LayoutParams lp = (LayoutParams) img.getLayoutParams(); + + b.setEnabled(connected); + i.setEnabled(connected); + + if (connected) { + b.setVisibility(View.VISIBLE); + i.setVisibility(View.VISIBLE); + lp.removeRule(RelativeLayout.CENTER_HORIZONTAL); + + } else { + b.setVisibility(View.INVISIBLE); + i.setVisibility(View.INVISIBLE); + lp.addRule(RelativeLayout.CENTER_HORIZONTAL); + } + + setLogoDrawable(img, connected); + img.setLayoutParams(lp); + + } catch (Exception e) { + Logger.logError("Error while setting status appearance", e); + + } + + } + + BitmapDrawable logoConn; + BitmapDrawable logoDisc; + + public void setLogoDrawable(ImageView img, boolean connected) { + + img.setImageDrawable(null); + System.gc(); + + try { + + String res = "repwifi-logo-0-small.png"; + BitmapDrawable logo = logoConn; + if (!connected) { + logo = logoDisc; + res = "repwifi-logo-1-small.png"; + } + + AssetManager am = getAssets(); + InputStream s = am.open(res); + + if (logo == null) { + logo = (BitmapDrawable) Drawable.createFromStream(s, res); + } + img.setImageDrawable(logo); + + s.close(); + } catch (IOException e) { + } + + } + + public void onBtnDisconnectClick(View v) { + + disconnectVpn(); + sendCmdDisconnect(); + + } + + public void onBtnInfoClick(View v) { + launcher.launchStatusActivity(this.status); } private void setUsbDeviceMonitor() { @@ -245,9 +370,17 @@ public class MainActivity extends MenuEnabledActivity { registerReceiver(detachReceiver, filt2); } - private boolean checkConditions() { + private boolean checkConditions(boolean serviceStarted) { + // second chance to have the service bound: + boolean conds = (checkRootEnabled() && checkIsSystemApp() && checkInterface(true)); + if (serviceStarted) { + conds = conds && isServiceBound(); + } + return conds; + } - return (checkRootEnabled() && checkInterface(true)); + private boolean checkConditions() { + return checkConditions(false); } private boolean checkInterface(boolean alert) { @@ -256,16 +389,16 @@ public class MainActivity extends MenuEnabledActivity { String msg = ""; try { - res = Commons.connectionEngine.isInterfaceAvailable(WpaSupplicant.INTERFACE_NAME); + res = Engine.isInterfaceAvailable(WpaSupplicant.INTERFACE_NAME); } catch (SocketException e) { - Utils.logError("SocketException during isInterfaceAvailable()", e); + Logger.logError("SocketException during isInterfaceAvailable()", e); msg = "Error while retrieving interface list!"; res = false; } if (res == false && alert) { msg = getResources().getString(R.string.msg_interface_not_found); - Commons.showMessage(msg, this); + Utils.showMessage(msg, this); } return res; @@ -283,7 +416,7 @@ public class MainActivity extends MenuEnabledActivity { try { excode = su.testRootAccess(); } catch (Exception e) { - Utils.logError("Error while trying to get first Super User access.", e); + Logger.logError("Error while trying to get first Super User access.", e); excode = -1; result = false; } @@ -310,7 +443,7 @@ public class MainActivity extends MenuEnabledActivity { } if (!result) { - Commons.showMessage(msg, this); + Utils.showMessage(msg, this); } return result; @@ -328,7 +461,7 @@ public class MainActivity extends MenuEnabledActivity { if (i.needsPassword()) { // try to fetch network's configuration from storage - AccessPointInfo fromStorage = Commons.storage.getSavedNetwork(i); + AccessPointInfo fromStorage = NetworkManager.getSavedNetwork(i); if (fromStorage == null) { launcher.launchPasswordActivity(i); @@ -341,6 +474,8 @@ public class MainActivity extends MenuEnabledActivity { } + // disconnect any vpn before connecting to a new network + disconnectVpn(); launcher.launchLongTaskActivityConnect(i); } @@ -350,44 +485,70 @@ public class MainActivity extends MenuEnabledActivity { launcher.launchLongTaskActivityConnect(i); } - private void handleFinishedConnecting(boolean connectionResult, AccessPointInfo info) { + private void handleFinishedConnecting(ConnectionResult connectionResult, AccessPointInfo info) { - if (connectionResult && info.needsPassword()) { + Logger.logDebug("handleFinishedConnecting"); - ConnectionStatus status = Commons.connectionEngine.getConnectionStatus(); - if (status != null) { - // update APinfo with the right BSSID - info.setBssid(status.BSSID); - } + String toastText = null; + int res = connectionResult.getResult(); + ConnectionStatus status = connectionResult.getStatus(); - // Save network - if (Commons.storage.save(info)) { - Toast toast2 = Toast.makeText(getApplicationContext(), - getString(R.string.msg_network_saved), Toast.LENGTH_LONG); - toast2.show(); + switch (res) { - } else { - Toast toast2 = Toast.makeText(getApplicationContext(), - getString(R.string.msg_network_save_fail), Toast.LENGTH_LONG); - toast2.show(); - } + case ConnectionResult.CONN_OK: - // show status - launcher.launchStatusActivity(status); + Logger.logDebug("About to launch VPN on successful connection result."); + beginConnectVpn(status.getNetworkDetails()); + + + break; - } else { - // alert that connection failed - Toast toast = Toast.makeText(getApplicationContext(), - getString(R.string.msg_connect_fail), Toast.LENGTH_LONG); + case ConnectionResult.CONN_GW_FAIL: + Logger.logDebug("Result code CONN_GW_FAIL"); + toastText = getString(R.string.msg_gw_failed); + break; + + case ConnectionResult.CONN_TIMEOUT: + // probable wrong password: + handleConnectionTimeout(info); + break; + + default: + Logger.logDebug("Result code: " + res); + toastText = getString(R.string.msg_connect_fail); + break; + + } + + if (toastText != null) { + Toast toast = Toast.makeText(getApplicationContext(), toastText, Toast.LENGTH_LONG); toast.show(); } + + showStatus(status); + + } + + private void handleConnectionTimeout(AccessPointInfo info) { + // reset wpa_supplicant's state + WpaCli.disconnect(); + if (!WpaCli.terminateSupplicant()) { + WpaSupplicant.kill(); + } + + if (info.needsPassword()) { + // prompt user for password retry: + launcher.launchPasswordActivity(info, getString(R.string.msg_connection_timeout)); + } else { + Utils.showMessage(getString(R.string.msg_connection_timeout_nopass), this); + } + } private void deleteNetwork(AccessPointInfo info) { - NetworkManager manager = new NetworkManager(Commons.getNetworkStorageFile()); String msg = ""; - if (manager.remove(info)) { + if (NetworkManager.remove(info)) { msg = getString(R.string.msg_netinfo_deleted); } else { msg = getString(R.string.msg_netinfo_delete_fail); @@ -401,10 +562,11 @@ public class MainActivity extends MenuEnabledActivity { private void handleUsbEvent(boolean detached) { if (detached && !checkInterface(false)) { - // device disconnected, update the status bar: - Commons.updateNotification(this); + // device disconnected: + // clear back-end state and update status. + disconnectVpn(); - } else if (Commons.isAutoConnectEnabled()) { + } else if (isAutoConnectEnabled()) { try { @@ -417,7 +579,7 @@ public class MainActivity extends MenuEnabledActivity { msWaited += 100; if (checkInterface(false)) { - autoConnect(); + sendCmdAutoconnect(); return; } } @@ -429,56 +591,213 @@ public class MainActivity extends MenuEnabledActivity { } - } + requestStatusUpdate(); - private void autoConnect() { + } - try { + private boolean isAutoConnectEnabled() { + return Prefs.getBoolean(getApplicationContext(), Prefs.PREF_AUTOCONNECT, false); + } - AccessPointInfo[] nets = Commons.connectionEngine.getAvailableNetworks(); - if (nets == null || nets.length == 0) { - return; - } + private void doScan() { + if (checkConditions(true)) { + launcher.launchLongTaskActivityScan(); + } + } - for (AccessPointInfo i : nets) { + public void btnScanClick(View v) { + doScan(); + } - if (Commons.storage.isKnown(i)) { - launcher.launchLongTaskActivityConnect(i); - return; - } + public void btnHiddenClick(View v) { + if (checkConditions(true)) { + launcher.launchInputSsidActivity(); + } + } - } + public void btnManageClick(View v) { + launcher.launchSelectActivity(null, false, true); + } - // if no network is known, shows available networks to the user. - launcher.launchSelectActivity(nets, true, false); + private boolean checkIsSystemApp() { - } catch (Exception e) { - Utils.logError("Error while autoconnecting", e); - Commons.showMessage(getString(R.string.msg_autoconnect_error), this); + if (isSystemApp()) { + return true; + } else { + promtpMakeSystemApp(); + return false; } + } + + private void promtpMakeSystemApp() { + + String msg = getString(R.string.msg_make_system_app); + AlertDialog.Builder dlgAlert = new AlertDialog.Builder(this, + R.style.Theme_RepWifiDialogTheme); + dlgAlert.setMessage(msg); + dlgAlert.setPositiveButton(getString(android.R.string.ok), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int whichButton) { + confirmMakeSystemApp(); + return; + } + }); + dlgAlert.setNegativeButton(getString(android.R.string.cancel), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int whichButton) { + return; + } + }); + + dlgAlert.setCancelable(false); + AlertDialog diag = dlgAlert.create(); + + diag.show(); } - private void doScan() { - if (checkConditions()) { - launcher.launchLongTaskActivityScan(); + private void confirmMakeSystemApp() { + String msg = getString(R.string.msg_confirm_make_system_app); + AlertDialog.Builder dlgAlert = new AlertDialog.Builder(this, + R.style.Theme_RepWifiDialogTheme); + dlgAlert.setMessage(msg); + dlgAlert.setPositiveButton(getString(android.R.string.yes), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int whichButton) { + makeSystemApp(); + return; + } + }); + dlgAlert.setNegativeButton(getString(android.R.string.no), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int whichButton) { + return; + } + }); + + dlgAlert.setCancelable(false); + AlertDialog diag = dlgAlert.create(); + + diag.show(); + } + + private boolean isSystemApp() { + + PackageManager pm = getPackageManager(); + boolean isSystem = false; + try { + ApplicationInfo ai = pm.getApplicationInfo(getPackageName(), 0); + + isSystem = ((ai.flags & ApplicationInfo.FLAG_SYSTEM) == 1); + + } catch (NameNotFoundException e) { + // always succeeds, as we use our own package } + return isSystem; + } - public void btnScanClick(View v) { - doScan(); + public static final String SYS_FOLDER = "/system"; + public static final String SYS_APP_FOLDER = "/system/priv-app"; + private static final String SYSAPP_SCRIPT_FNAME = "make-system-app.sh"; + + private boolean makeSystemApp() { + + PackageManager pm = getPackageManager(); + String pkgName = getPackageName(); + File currentSourceDir = null; + try { + ApplicationInfo ai = pm.getApplicationInfo(pkgName, 0); + currentSourceDir = new File(ai.sourceDir); + + } catch (NameNotFoundException e) { + }// always succeeds, as we use our own package + + String parentFolder = currentSourceDir.getParentFile().getName(); + if (!parentFolder.contains(pkgName)) { + // use the plain package name as a parent folder for the apk + parentFolder = pkgName; + } + + String targetDir = SYS_APP_FOLDER + "/" + parentFolder; + + // get own data directory: + File ownDir = getFilesDir(); + File scriptFile = new File(ownDir, SYSAPP_SCRIPT_FNAME); + String conts = Utils.rawResourceAsString(getApplicationContext(), R.raw.make_system_app); + + if (conts == null) { + Logger.logError("Error while opening script from raw resources."); + return false; + } + + if (!Utils.writeFile(scriptFile.getAbsolutePath(), conts, true)) { + Logger.logError("Failed to write script contents to file."); + return false; + } + + Logger.logDebug("Starting script to make myself a system app..."); + + String cmd = "bash \"" + scriptFile.getAbsolutePath() + "\"" + " \"" + targetDir + "\"" + + " \"" + currentSourceDir.getAbsolutePath() + "\"" + " \"" + SYS_FOLDER + + "\""; + + return RootCommand.executeRootCmd(cmd); + } + + + @Override + protected void onMsgDisconnectReport(ConnectionStatus status) { + String msg = ""; - public void btnHiddenClick(View v) { + if (status != null && !status.isConnected()) { + msg = getString(R.string.msg_disconnected); + } else { + msg = getString(R.string.msg_disconnect_fail); + } - if (checkConditions()) { - launcher.launchInputSsidActivity(); + Toast.makeText(this, msg, Toast.LENGTH_LONG).show(); + showStatus(status); + + } + + @Override + protected void onMsgAutoconnectReport(AccessPointInfo[] infos) { + if (infos != null && infos.length > 0) { + launcher.launchSelectActivity(infos, true, false); } + } + @Override + protected void onMsgStatusChange(ConnectionStatus status) { + Logger.logDebug("Received status update from service."); + showStatus(status); } - public void btnManageClick(View v) { - launcher.launchSelectActivity(null, false, true); + @Override + protected void onManagementServiceConnected() { + super.onManagementServiceConnected(); + requestStatusUpdate(); + } + + @Override + protected void onStop() { + super.onStop(); + } + + @Override + public void onDestroy() { + + if (detachReceiver != null) { + unregisterReceiver(detachReceiver); + } + + super.onDestroy(); } } diff --git a/app/src/fil/libre/repwifiapp/activities/MenuEnabledActivity.java b/app/src/fil/libre/repwifiapp/activities/MenuEnabledActivity.java index f5c8516..55afd5a 100644 --- a/app/src/fil/libre/repwifiapp/activities/MenuEnabledActivity.java +++ b/app/src/fil/libre/repwifiapp/activities/MenuEnabledActivity.java @@ -1,5 +1,5 @@ // -// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> +// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> // // This file is part of RepWifiApp. // @@ -20,10 +20,6 @@ package fil.libre.repwifiapp.activities; -import java.lang.reflect.Field; -import fil.libre.repwifiapp.Commons; -import fil.libre.repwifiapp.R; -import fil.libre.repwifiapp.helpers.RootCommand; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; @@ -31,6 +27,10 @@ import android.content.Intent; import android.view.Menu; import android.view.MenuItem; import android.view.ViewConfiguration; +import fil.libre.repwifiapp.R; +import fil.libre.repwifiapp.Utils; +import fil.libre.repwifiapp.helpers.RootCommand; +import java.lang.reflect.Field; public class MenuEnabledActivity extends Activity { @@ -95,8 +95,7 @@ public class MenuEnabledActivity extends Activity { if (silent) { - Commons.connectionEngine.disconnect(); - Commons.killBackEnd(this, true); + Utils.killBackEnd(this, true); super.onDestroy(); RootCommand.executeRootCmd("am force-stop " + getPackageName()); diff --git a/app/src/fil/libre/repwifiapp/activities/NetworkDetailsActivity.java b/app/src/fil/libre/repwifiapp/activities/NetworkDetailsActivity.java index f2a8944..7b12efc 100644 --- a/app/src/fil/libre/repwifiapp/activities/NetworkDetailsActivity.java +++ b/app/src/fil/libre/repwifiapp/activities/NetworkDetailsActivity.java @@ -1,5 +1,5 @@ // -// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> +// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> // // This file is part of RepWifiApp. // @@ -20,23 +20,23 @@ package fil.libre.repwifiapp.activities; -import java.util.Date; -import fil.libre.repwifiapp.ActivityLauncher; -import fil.libre.repwifiapp.Commons; -import fil.libre.repwifiapp.R; -import fil.libre.repwifiapp.helpers.AccessPointInfo; -import android.os.Bundle; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; +import android.os.Bundle; import android.text.format.DateFormat; import android.view.Menu; import android.view.View; import android.widget.CheckBox; import android.widget.CompoundButton; -import android.widget.TextView; import android.widget.CompoundButton.OnCheckedChangeListener; +import android.widget.TextView; +import fil.libre.repwifiapp.ActivityLauncher; +import fil.libre.repwifiapp.R; +import fil.libre.repwifiapp.network.AccessPointInfo; +import fil.libre.repwifiapp.network.NetworkManager; +import java.util.Date; public class NetworkDetailsActivity extends Activity implements OnCheckedChangeListener { @@ -57,7 +57,7 @@ public class NetworkDetailsActivity extends Activity implements OnCheckedChangeL return; } - this.currentNetwork = (AccessPointInfo) intent.getExtras().getSerializable( + this.currentNetwork = (AccessPointInfo) intent.getExtras().getParcelable( ActivityLauncher.EXTRA_APINFO); if (this.currentNetwork == null) { this.setResult(RESULT_CANCELED); @@ -72,9 +72,9 @@ public class NetworkDetailsActivity extends Activity implements OnCheckedChangeL @Override protected void onStart() { super.onStart(); - currentNetwork = Commons.storage.getSavedNetwork(currentNetwork); + currentNetwork = NetworkManager.getSavedNetwork(currentNetwork); }; - + @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. @@ -100,21 +100,22 @@ public class NetworkDetailsActivity extends Activity implements OnCheckedChangeL } if (showPassword) { - v.append("\n\n" + getString(R.string.text_password) + ":\n" + this.currentNetwork.getPassword()); + v.append("\n\n" + getString(R.string.text_password) + ":\n" + + this.currentNetwork.getPassword()); } else { v.append("\n\n\n"); } } - public void btnIpSettingsClick(View v){ + public void btnIpSettingsClick(View v) { new ActivityLauncher(this).launchIpSettingsActivity(currentNetwork); } - - public void btnVpnSettingsClick(View v){ + + public void btnVpnSettingsClick(View v) { new ActivityLauncher(this).launchVpnSettingsActivity(currentNetwork); } - + public void btnDeleteClick(View v) { String msg = getResources().getString(R.string.msg_confirm_delete_network); diff --git a/app/src/fil/libre/repwifiapp/activities/SelectNetworkActivity.java b/app/src/fil/libre/repwifiapp/activities/SelectNetworkActivity.java index 51baed8..f1e1a96 100644 --- a/app/src/fil/libre/repwifiapp/activities/SelectNetworkActivity.java +++ b/app/src/fil/libre/repwifiapp/activities/SelectNetworkActivity.java @@ -1,5 +1,5 @@ // -// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> +// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> // // This file is part of RepWifiApp. // @@ -20,15 +20,9 @@ package fil.libre.repwifiapp.activities; -import fil.libre.repwifiapp.ActivityLauncher; -import fil.libre.repwifiapp.Commons; -import fil.libre.repwifiapp.R; -import fil.libre.repwifiapp.helpers.AccessPointInfo; -import fil.libre.repwifiapp.helpers.NetworkButton; -import fil.libre.repwifiapp.helpers.Utils; -import android.os.Bundle; import android.app.Activity; import android.content.Intent; +import android.os.Bundle; import android.view.Gravity; import android.view.Menu; import android.view.View; @@ -38,6 +32,11 @@ import android.widget.Button; import android.widget.TableLayout; import android.widget.TableRow; import android.widget.TextView; +import fil.libre.repwifiapp.ActivityLauncher; +import fil.libre.repwifiapp.R; +import fil.libre.repwifiapp.helpers.Logger; +import fil.libre.repwifiapp.network.AccessPointInfo; +import fil.libre.repwifiapp.network.NetworkButton; public class SelectNetworkActivity extends Activity implements OnClickListener { @@ -75,8 +74,8 @@ public class SelectNetworkActivity extends Activity implements OnClickListener { finish(); return; } - AccessPointInfo[] nets = (AccessPointInfo[]) intent.getExtras().getSerializable( - ActivityLauncher.EXTRA_APINFO_ARR); + AccessPointInfo[] nets = AccessPointInfo.fromParcellableArray(intent.getExtras().getParcelableArray( + ActivityLauncher.EXTRA_APINFO_ARR)); if (nets == null) { this.setResult(RESULT_CANCELED); finish(); @@ -134,7 +133,7 @@ public class SelectNetworkActivity extends Activity implements OnClickListener { private void showNetworksForConnection(AccessPointInfo[] info) { if (info == null) { - Utils.logError("Unable to retrieve network list!"); + Logger.logError("Unable to retrieve network list!"); writeOut(getString(R.string.msg_network_list_fail)); return; } @@ -203,7 +202,7 @@ public class SelectNetworkActivity extends Activity implements OnClickListener { LayoutParams.WRAP_CONTENT); button.setLayoutParams(params); button.setBackground(getResources().getDrawable(R.drawable.repwifi_button)); - button.setTextColor(Commons.colorThemeLight); + button.setTextColor(getResources().getColor(R.color.ThemeLight)); button.setTextSize(20); button.setPadding(25, 10, 25, 10); button.setGravity(Gravity.CENTER_HORIZONTAL); diff --git a/app/src/fil/libre/repwifiapp/activities/SettingsActivity.java b/app/src/fil/libre/repwifiapp/activities/SettingsActivity.java index 36e0e1f..c50524a 100644 --- a/app/src/fil/libre/repwifiapp/activities/SettingsActivity.java +++ b/app/src/fil/libre/repwifiapp/activities/SettingsActivity.java @@ -1,5 +1,5 @@ // -// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> +// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> // // This file is part of RepWifiApp. // @@ -18,19 +18,28 @@ // // ******************************************************************** - package fil.libre.repwifiapp.activities; -import org.apache.http.conn.util.InetAddressUtils; -import java.util.List; -import fil.libre.repwifiapp.Commons; -import fil.libre.repwifiapp.R; -import fil.libre.repwifiapp.helpers.Utils; +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.SharedPreferences.Editor; import android.os.Bundle; +import android.preference.CheckBoxPreference; import android.preference.EditTextPreference; +import android.preference.ListPreference; import android.preference.Preference; import android.preference.PreferenceActivity; import android.preference.PreferenceFragment; +import android.preference.PreferenceManager; +import fil.libre.repwifiapp.Prefs; +import fil.libre.repwifiapp.R; +import fil.libre.repwifiapp.R.string; +import fil.libre.repwifiapp.Utils; +import fil.libre.repwifiapp.helpers.Logger; +import fil.libre.repwifiapp.service.ConnectionManagementService; +import org.apache.http.conn.util.InetAddressUtils; +import java.util.List; public class SettingsActivity extends PreferenceActivity { @@ -38,6 +47,7 @@ public class SettingsActivity extends PreferenceActivity { protected void onCreate(Bundle savedInstanceState) { setTheme(R.style.Theme_SettingsTheme); super.onCreate(savedInstanceState); + } @Override @@ -54,7 +64,7 @@ public class SettingsActivity extends PreferenceActivity { setConfirmKillBackend(); setDumpFileClick(); - + setDebugPriorityChangeListener(); } private void setConfirmKillBackend() { @@ -64,7 +74,7 @@ public class SettingsActivity extends PreferenceActivity { @Override public boolean onPreferenceClick(Preference p) { - Commons.killBackEnd(getActivity(), false); + Utils.killBackEnd(getActivity(), false); return true; } }); @@ -77,18 +87,38 @@ public class SettingsActivity extends PreferenceActivity { @Override public boolean onPreferenceClick(Preference p) { - if (Utils.dumpLogcatToFile(Commons.getLogDumpFile())) { - Commons.showMessage(getString(R.string.msg_log_saved) + ": \n" + Commons.getLogDumpFile(), - getActivity()); + if (Logger.dumpLogcatToFile(getActivity())) { + Utils.showMessage( + getString(R.string.msg_log_saved) + ": \n" + + Logger.getLogDumpFile(), getActivity()); } else { - Commons.showMessage(getString(R.string.msg_log_save_fail), getActivity()); + Utils.showMessage(getString(R.string.msg_log_save_fail), getActivity()); } return true; } }); } + /** + * Monitors changes in the "log priority" setting, and reflects them + * onto the Logger class. + */ + private void setDebugPriorityChangeListener() { + + ListPreference logPrioPref = (ListPreference) getPreferenceScreen().findPreference( + "debug_priority"); + + logPrioPref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + Logger.setLogPriority(Integer.parseInt((String) newValue)); + return true; + } + }); + } + } public static class GeneralSettingFragment extends PreferenceFragment { @@ -98,26 +128,37 @@ public class SettingsActivity extends PreferenceActivity { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.general_settings); - setValidationListener("dns1"); - setValidationListener("dns2"); + setIpFormatValidationListener(Prefs.PREF_DNS_1); + setIpFormatValidationListener(Prefs.PREF_DNS_2); setConfirmRestore(); + + formatSummaryMonitoryConnection(); + + } + + private void formatSummaryMonitoryConnection() { + + CheckBoxPreference chkMonitor = (CheckBoxPreference) findPreference("monitor_connection"); + chkMonitor.setSummary(getString(string.summary_monitor_connection_state).replace( + ConnectionManagementService.PLACEHOLDER_CHECK_STATUS_INTERVAL, + String.valueOf(ConnectionManagementService.CHECK_STATUS_INTERVAL_SECS))); } - private void setValidationListener(String prefName) { - EditTextPreference edit_Pref = (EditTextPreference) getPreferenceScreen() + private void setIpFormatValidationListener(String prefName) { + EditTextPreference editPref = (EditTextPreference) getPreferenceScreen() .findPreference(prefName); - edit_Pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + editPref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newValue) { - // put validation here.. + if (((String) newValue).isEmpty() || InetAddressUtils.isIPv4Address((String) newValue)) { return true; } else { - Commons.showMessage(getString(R.string.msg_error_ip_format), getActivity()); + Utils.showMessage(getString(R.string.msg_error_ip_format), getActivity()); return false; } } @@ -131,12 +172,55 @@ public class SettingsActivity extends PreferenceActivity { @Override public boolean onPreferenceClick(Preference p) { - Commons.resetSettingsDefault(getActivity(), false); + resetSettingsDefault(false); return true; } }); } + + public void resetSettingsDefault(boolean silent) { + + Context context = getActivity(); + + if (silent) { + + Editor e = PreferenceManager.getDefaultSharedPreferences(context).edit(); + e.clear(); + e.commit(); + + } else { + + String msg = context.getString(R.string.confirm_reset_settings); + AlertDialog.Builder dlgAlert = new AlertDialog.Builder(context, + R.style.Theme_RepWifiDialogTheme); + dlgAlert.setMessage(msg); + dlgAlert.setPositiveButton(context.getString(android.R.string.ok), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int whichButton) { + resetSettingsDefault(true); + return; + } + }); + dlgAlert.setNegativeButton(context.getString(android.R.string.cancel), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int whichButton) { + return; + } + }); + + dlgAlert.setCancelable(false); + AlertDialog diag = dlgAlert.create(); + + diag.show(); + + return; + } + + } + } } diff --git a/app/src/fil/libre/repwifiapp/activities/ShowStatusActivity.java b/app/src/fil/libre/repwifiapp/activities/ShowStatusActivity.java index 21bfc4c..7076556 100644 --- a/app/src/fil/libre/repwifiapp/activities/ShowStatusActivity.java +++ b/app/src/fil/libre/repwifiapp/activities/ShowStatusActivity.java @@ -1,5 +1,5 @@ // -// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> +// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> // // This file is part of RepWifiApp. // @@ -20,274 +20,67 @@ package fil.libre.repwifiapp.activities; -import fil.libre.repwifiapp.ActivityLauncher; -import fil.libre.repwifiapp.Commons; -import fil.libre.repwifiapp.R; -import fil.libre.repwifiapp.ActivityLauncher.RequestCode; -import fil.libre.repwifiapp.helpers.AccessPointInfo; -import fil.libre.repwifiapp.helpers.ConnectionStatus; -import fil.libre.repwifiapp.helpers.OpenVpnManager; -import fil.libre.repwifiapp.helpers.Utils; -import android.content.Intent; import android.os.Bundle; -import android.os.RemoteException; import android.view.View; -import android.widget.Button; import android.widget.TextView; -import android.widget.Toast; +import fil.libre.repwifiapp.ActivityLauncher; +import fil.libre.repwifiapp.R; +import fil.libre.repwifiapp.helpers.Logger; +import fil.libre.repwifiapp.network.ConnectionStatus; public class ShowStatusActivity extends MenuEnabledActivity { private ConnectionStatus status; - // private AccessPointInfo info; - @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_show_status); if (getIntent().hasExtra(ActivityLauncher.EXTRA_CONSTATUS)) { - this.status = (ConnectionStatus) getIntent().getSerializableExtra( + this.status = (ConnectionStatus) getIntent().getParcelableExtra( ActivityLauncher.EXTRA_CONSTATUS); } try { - showStatus(false); + showStatus(); } catch (Exception e) { - Utils.logError("Exception on showStatus", e); + Logger.logError("Exception on showStatus", e); } } - @Override - public void onRestart() { - super.onRestart(); - try { - showStatus(true); - } catch (Exception e) { - Utils.logError("Exception on showStatus", e); - } - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent intent) { - - switch (requestCode) { + private void showStatus() { - case RequestCode.VPN_PERMISSION: - - if (resultCode == RESULT_OK) { - endLaunchVpn(true); - } else { - endLaunchVpn(false); - } + if (this.status != null) { - break; + String txt = getPrintableConnectionStatus(status); - default: - - break; + TextView t = (TextView) findViewById(R.id.txt_status); + t.setText(txt); } } - private void setMessage(String msg) { - TextView view = (TextView) findViewById(R.id.txt_status); - view.setText(msg); + public void onBtnMainClick(View v) { + finish(); } - private void showStatus(boolean refresh) throws Exception { - - if (refresh || status == null) { - this.status = Commons.connectionEngine.getConnectionStatus(); - } + public String getPrintableConnectionStatus(ConnectionStatus status) { if (status == null) { - this.finish(); - - } else if (this.status.isConnected()) { - Utils.logDebug("StatusActivity isConnected,showing buttons"); - setMessage(getString(R.string.msg_connected_to) + " " + status.SSID + "\n\n" - + getString(R.string.text_ip_address) + ": " + status.IP + "\n"); - toggleBtnDisconnect(true); - beginLauncVpn(); - - } else { - Utils.logDebug("StatusActivity status Else"); - setMessage(getString(R.string.text_status) + ":\n" + status.status); - toggleBtnDisconnect(false); + return "[NULL]"; } - Commons.updateNotification(this); + String fmat = "%s: %s\n%s: %s\n%s: %s\n%s: %s\n%s: %s\n"; - } - - private void toggleBtnDisconnect(boolean enable) { - - Button b = (Button) findViewById(R.id.btn_disconnect); - Button bk = (Button) findViewById(R.id.btn_back); - b.setEnabled(enable); - bk.setEnabled(!enable); - - if (enable) { - b.setVisibility(View.VISIBLE); - bk.setVisibility(View.INVISIBLE); - } else { - b.setVisibility(View.INVISIBLE); - bk.setVisibility(View.VISIBLE); - } + return String.format(fmat, getString(R.string.text_ip_address), status.IP, + getString(R.string.text_subnet_mask), status.subnetMask, + getString(R.string.text_gateway), status.gateway, + getString(R.string.text_broadcast_address), status.broadcastAddress, + getString(R.string.text_hardware_address), status.hwAddress); } - public void onBtnDisconnectClick(View v) { - - disconnectVpn(); - - boolean res = Commons.connectionEngine.disconnect(); - String msg = ""; - if (res) { - msg = getString(R.string.msg_disconnected); - } else { - msg = getString(R.string.msg_disconnect_fail); - } - - Toast toast = Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT); - toast.show(); - - try { - showStatus(true); - } catch (Exception e) { - Utils.logError("Exception on showStatus", e); - } - - } - - private void disconnectVpn() { - - if (! OpenVpnManager.isVpnConnected()){ - return; - } - - if (OpenVpnManager.disconnect()) { - Toast t = Toast.makeText(this, getString(R.string.msg_vpn_disconnect), - Toast.LENGTH_LONG); - t.show(); - - } else { - Commons.showMessage(getString(R.string.msg_vpn_disconnect_error), this); - } - - } - - private void beginLauncVpn() throws Exception { - - if (OpenVpnManager.isVpnConnected()){ - // already connected; - return; - } - - if (!OpenVpnManager.isExternalAppInstalled(this)) { - Utils.logDebug("External VPN app not installed."); - return; - } - - if (getVpnNameIfAny() == null){ - Utils.logDebug("No vpn profile set. Exiting beginLaunchVpn()"); - return; - } - - // first, we make sure we have permission to use the vpn service. - Intent pi; - - try { - pi = OpenVpnManager.askApiPermissionsGetIntent(); - } catch (RemoteException e) { - Utils.logError("Exception while asking for VPN permission", e); - Toast t = Toast.makeText(getApplicationContext(), getString(R.string.msg_vpn_connect_error), Toast.LENGTH_LONG); - t.show(); - return; - - } - - if (pi == null){ - // no need to ask for permission - Utils.logDebug("No need for vpn permission: going to endLaunchVpn."); - endLaunchVpn(true); - - } else{ - // launch the intent to ask permission - Utils.logDebug("Need to ask for vpn permission. Starting intent.."); - startActivityForResult(pi, ActivityLauncher.RequestCode.VPN_PERMISSION); - } - - } - - private void endLaunchVpn(boolean permissionGranted) { - - try { - - if (!permissionGranted) { - // warn user that permission must be granted - Utils.logDebug("User rejected vpn permission."); - String msg = getString(R.string.msg_vpn_no_permission).replace( - OpenVpnManager.PLACEHOLDER_APPNAME, OpenVpnManager.APP_COMMON_NAME); - Commons.showMessage(msg, this); - return; - } - - String profname = getVpnNameIfAny(); - - // check if profile exists - String profUuid = OpenVpnManager.getUuidFromName(profname); - if (profUuid == null){ - // warn user that selected profile doesn't exist - Commons.showMessage(getString(R.string.msg_vpn_wrong_profile), this); - return; - } - - if (OpenVpnManager.startVpn(profUuid)){ - Toast t = Toast.makeText(this, getString(R.string.msg_vpn_launched), Toast.LENGTH_LONG); - t.show(); - } else { - Commons.showMessage(getString(R.string.msg_vpn_connect_error),this); - } - - } catch (Exception e) { - Utils.logError("Exception while endLaunchVpn", e); - - } - - } - - private String getVpnNameIfAny(){ - - if (status == null) { - return null; - } - - AccessPointInfo i = status.getNetworkDetails(); - if (i == null) { - return null; - } - - String profname = i.getVpnProfileName(); - if (profname == null || profname.isEmpty()) { - return null; - } else { - return profname; - } - - } - - public void onBtnMainClick(View v) { - finish(); - } - - @Override - public void onBackPressed() { - moveTaskToBack(true); - } - } diff --git a/app/src/fil/libre/repwifiapp/activities/VpnAndConnectionBoundActivity.java b/app/src/fil/libre/repwifiapp/activities/VpnAndConnectionBoundActivity.java new file mode 100644 index 0000000..4acf28a --- /dev/null +++ b/app/src/fil/libre/repwifiapp/activities/VpnAndConnectionBoundActivity.java @@ -0,0 +1,435 @@ +// +// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> +// +// This file is part of RepWifiApp. +// This file is based upon the example file included in +// de.blinkt.openvpn package by Arne Schwabe. +// +// RepWifiApp 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. +// +// RepWifiApp 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 RepWifiApp. If not, see <http://www.gnu.org/licenses/>. +// +// ******************************************************************** + +package fil.libre.repwifiapp.activities; + +import android.app.Activity; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager.NameNotFoundException; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; +import android.widget.Toast; +import de.blinkt.openvpn.api.APIVpnProfile; +import de.blinkt.openvpn.api.IOpenVPNAPIService; +import fil.libre.repwifiapp.ActivityLauncher; +import fil.libre.repwifiapp.R; +import fil.libre.repwifiapp.Utils; +import fil.libre.repwifiapp.helpers.Logger; +import fil.libre.repwifiapp.network.AccessPointInfo; +import java.util.ArrayList; +import java.util.List; + +public abstract class VpnAndConnectionBoundActivity extends ConnectionBoundActivity { + + public static final String SERVICE_PACKAGE_NAME = "de.blinkt.openvpn"; + public static final String APP_COMMON_NAME = "OpenVPN for Android"; + public static final String PLACEHOLDER_APPNAME = "[VPN_EXT_APP]"; + + private static final int ACTION_NONE = 0; + private static final int ACTION_GET_PROFILES = 1; + private static final int ACTION_CONNECT = 2; + + protected IOpenVPNAPIService _vpnSvc; + + private AccessPointInfo _lastInfo; + private int _lastAction = ACTION_NONE; + + private int lastRequestCode = ActivityLauncher.RequestCode.NONE; + private boolean permissionAsked = false; + + private ServiceConnection _svcConnection; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + bindVpnService(ACTION_NONE); + } + + private void bindVpnService(int action) { + + if (!isExternalAppInstalled()) { + Logger.logDebug("External VPN app is not installed. Skipping vpn service binding."); + return; + } + + _lastAction = action; + + this._svcConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + _vpnSvc = IOpenVPNAPIService.Stub.asInterface(service); + onVpnServiceConnected(); + } + + public void onServiceDisconnected(ComponentName className) { + _vpnSvc = null; + onVpnServiceDisconnected(); + } + }; + + Intent intentGetService = new Intent(IOpenVPNAPIService.class.getName()); + intentGetService.setPackage(SERVICE_PACKAGE_NAME); + if (!bindService(intentGetService, _svcConnection, Context.BIND_AUTO_CREATE)) { + Logger.logError("FAILED to bind to OpenVPN service!"); + } + + } + + private void unbindVpnService() { + if (_svcConnection != null && _vpnSvc != null) { + unbindService(_svcConnection); + } + } + + protected void onVpnServiceConnected() { + + switch (_lastAction) { + case ACTION_NONE: + return; + + case ACTION_GET_PROFILES: + beginGetExistingVpnProfiles(); + break; + + case ACTION_CONNECT: + beginConnectVpn(_lastInfo); + break; + + default: + break; + } + + } + + protected void onVpnServiceDisconnected() { + } + + protected void onVpnProfilesAvailable(List<String> vpnProfiles) { + } + + protected void onVpnPermissionDenied(){ + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent intent) { + + if (requestCode != ActivityLauncher.RequestCode.VPN_PERMISSION_CONN + && requestCode != ActivityLauncher.RequestCode.VPN_PERMISSION_LIST) { + // activity result is unrelated to our code. + return; + + } else if (resultCode != Activity.RESULT_OK) { + // warn user that permission is needed to manage VPN + + Logger.logDebug("User rejected vpn permission."); + + String msg = getString(R.string.msg_vpn_no_permission).replace( + VpnAndConnectionBoundActivity.PLACEHOLDER_APPNAME, + VpnAndConnectionBoundActivity.APP_COMMON_NAME); + Utils.showMessage(msg, this); + + onVpnPermissionDenied(); + return; + } + + switch (lastRequestCode) { + + case ActivityLauncher.RequestCode.VPN_PERMISSION_CONN: + + endConnectVpn(); + break; + + case ActivityLauncher.RequestCode.VPN_PERMISSION_LIST: + + endGetExistingVpnProfiles(); + break; + + default: + break; + } + + } + + protected void close() { + unbindVpnService(); + } + + protected boolean isExternalAppInstalled() { + + try { + + ApplicationInfo i; + i = getPackageManager().getApplicationInfo(SERVICE_PACKAGE_NAME, 0); + return (i != null); + + } catch (NameNotFoundException e) { + return false; + } + + } + + private boolean doStartVpn(String profileUuid) { + + if (profileUuid == null) { + Logger.logError("Invoked startVpn with null uuid"); + return false; + } + + if (_vpnSvc == null) { + Logger.logError("Invoked startVpn but inner service is null."); + return false; + } + + try { + + _vpnSvc.startProfile(profileUuid); + return true; + + } catch (RemoteException e) { + Logger.logError("Exception while starting vpn.", e); + return false; + } + + } + + private String getUuidFromName(String profileName) { + if (_vpnSvc == null) { + Logger.logError("Called getUuidFromName but inner service is null!"); + return null; + } + + try { + List<APIVpnProfile> list = _vpnSvc.getProfiles(); + + for (APIVpnProfile vp : list) { + if (vp.mName.equals(profileName)) { + return vp.mUUID; + } + } + + return null; + + } catch (RemoteException e) { + Logger.logError("Exception while retrieving profiles from vpn service.", e); + return null; + } + } + + /*** + * + * @return Returns 0 if no permission is needed, + * 1 if permission is needed and it was asked successfully, + * a negative number on error. + */ + private int askPermissionIfNeeded(int requestCode) { + + Logger.logDebug("Called OpenVpnManager.askPermissionIfNeeded()"); + + if (_vpnSvc == null) { + Logger.logError("Internal vpn service is null, but not supposed to be. Aborting."); + return -1; + } + + Intent pi; + + try { + pi = _vpnSvc.prepare(getPackageName()); + } catch (RemoteException e) { + Logger.logError("Exception while asking for VPN permission", e); + Toast t = Toast.makeText(getApplicationContext(), + getString(R.string.msg_vpn_service_error), Toast.LENGTH_LONG); + + t.show(); + + String msgerr = getString(R.string.msg_vpn_error_manual_open).replace( + PLACEHOLDER_APPNAME, APP_COMMON_NAME); + Utils.showMessage(msgerr, getApplicationContext()); + + return -1; + + } + + if (pi == null) { + // no need to ask for permission + Logger.logDebug("No need for vpn permission."); + return 0; + + } else if (!permissionAsked) { + // launch the intent to ask permission + Logger.logDebug("Need to ask for vpn permission. Starting intent.."); + permissionAsked = true; + startActivityForResult(pi, requestCode); + return 1; + + } else { + return 1; + + } + + } + + private String getVpnNameIfAny(AccessPointInfo i) { + + if (i == null) { + return null; + } + + String profname = i.getVpnProfileName(); + if (profname == null || profname.isEmpty()) { + return null; + } else { + return profname; + } + + } + + protected void beginConnectVpn(AccessPointInfo info) { + + if (!isExternalAppInstalled()) { + return; + } + + if (getVpnNameIfAny(info) == null) { + Logger.logDebug("No vpn profile set. Exiting beginConnectVpn()"); + return; + } + + _lastInfo = info; + + if (_vpnSvc == null) { + bindVpnService(ACTION_CONNECT); + return; + } + + // make sure we have permission to use the vpn service. + if (askPermissionIfNeeded(ActivityLauncher.RequestCode.VPN_PERMISSION_CONN) == 0) { + // no need for permission + Logger.logDebug("Going to endConnectVpn."); + endConnectVpn(); + } + + } + + private void endConnectVpn() { + + try { + + if (_lastInfo == null) { + Logger.logError("Called endConnectVpn, but last AccessPointInfo is null."); + return; + } + + String profname = getVpnNameIfAny(_lastInfo); + + // check if profile exists + String profUuid = getUuidFromName(profname); + if (profUuid == null) { + // warn user that selected profile doesn't exist + Utils.showMessage(getString(R.string.msg_vpn_wrong_profile), + getApplicationContext()); + return; + } + + if (doStartVpn(profUuid)) { + Toast t = Toast.makeText(getApplicationContext(), + getString(R.string.msg_vpn_launched), Toast.LENGTH_LONG); + t.show(); + } else { + Utils.showMessage(getString(R.string.msg_vpn_connect_error), + getApplicationContext()); + } + + } catch (Exception e) { + Logger.logError("Exception while endConnectVpn", e); + + } + + } + + protected void beginGetExistingVpnProfiles() { + + if (!isExternalAppInstalled()) { + return; + } + + if (_vpnSvc == null) { + bindVpnService(ACTION_GET_PROFILES); + return; + } + + // we make sure we have permission to use the vpn service. + if (askPermissionIfNeeded(ActivityLauncher.RequestCode.VPN_PERMISSION_LIST) == 0) { + // no need for permission + endGetExistingVpnProfiles(); + } + + } + + private void endGetExistingVpnProfiles() { + + try { + List<APIVpnProfile> list = _vpnSvc.getProfiles(); + + List<String> ret = new ArrayList<String>(); + for (APIVpnProfile vp : list) { + ret.add(vp.mName); + } + + onVpnProfilesAvailable(ret); + + } catch (RemoteException e) { + Logger.logError("Exception while retrieving profiles from vpn service.", e); + } + + } + + + + protected boolean disconnectVpn() { + + if (_vpnSvc == null) { + Logger.logDebug("Attempted to disconnect from VPN, but inner service is null"); + return true; + } + + try { + _vpnSvc.disconnect(); + return true; + } catch (Exception e) { + Logger.logError("Exception while disconnecting from vpn.", e); + return false; + } + + } + + @Override + public void onDestroy() { + super.onDestroy(); + this.close(); + } + +} diff --git a/app/src/fil/libre/repwifiapp/activities/VpnSettingsActivity.java b/app/src/fil/libre/repwifiapp/activities/VpnSettingsActivity.java index b9dab93..3c134d7 100644 --- a/app/src/fil/libre/repwifiapp/activities/VpnSettingsActivity.java +++ b/app/src/fil/libre/repwifiapp/activities/VpnSettingsActivity.java @@ -1,5 +1,5 @@ // -// Copyright 2017 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> +// Copyright 2017, 2018 Filippo "Fil" Bergamo <fil.bergamo@riseup.net> // // This file is part of RepWifiApp. // @@ -20,37 +20,35 @@ package fil.libre.repwifiapp.activities; -import java.util.List; -import fil.libre.repwifiapp.ActivityLauncher; -import fil.libre.repwifiapp.Commons; -import fil.libre.repwifiapp.R; -import fil.libre.repwifiapp.ActivityLauncher.RequestCode; -import fil.libre.repwifiapp.helpers.AccessPointInfo; -import fil.libre.repwifiapp.helpers.OpenVpnManager; -import fil.libre.repwifiapp.helpers.Utils; -import android.os.Bundle; -import android.app.Activity; import android.content.Intent; +import android.os.Bundle; +import android.view.Gravity; import android.view.Menu; import android.view.View; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.Spinner; import android.widget.TextView; +import fil.libre.repwifiapp.ActivityLauncher; +import fil.libre.repwifiapp.R; +import fil.libre.repwifiapp.Utils; +import fil.libre.repwifiapp.network.AccessPointInfo; +import fil.libre.repwifiapp.network.NetworkManager; +import java.util.List; -public class VpnSettingsActivity extends Activity { +public class VpnSettingsActivity extends VpnAndConnectionBoundActivity { private AccessPointInfo currentNetwork; private Spinner spinProfile; private TextView summaryView; - + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_vpn_settings); - + String title = getString(R.string.title_activity_vpn_settings); - + Intent intent = getIntent(); if (!intent.hasExtra(ActivityLauncher.EXTRA_APINFO)) { this.setResult(RESULT_CANCELED); @@ -58,144 +56,129 @@ public class VpnSettingsActivity extends Activity { return; } - this.currentNetwork = (AccessPointInfo) intent.getExtras().getSerializable( - ActivityLauncher.EXTRA_APINFO); - if (this.currentNetwork == null) { - this.setResult(RESULT_CANCELED); - this.finish(); - return; - } - - this.currentNetwork = Commons.storage.getSavedNetwork(currentNetwork); - this.spinProfile = (Spinner)findViewById(R.id.spin_vpn_profile); - this.summaryView = (TextView)findViewById(R.id.lbl_vpn_settings); - String summary = getString(R.string.summary_vpn_settings).replace(OpenVpnManager.PLACEHOLDER_APPNAME, OpenVpnManager.APP_COMMON_NAME); + this.spinProfile = (Spinner) findViewById(R.id.spin_vpn_profile); + this.summaryView = (TextView) findViewById(R.id.lbl_vpn_settings); + String summary = getString(R.string.summary_vpn_settings).replace(PLACEHOLDER_APPNAME, + APP_COMMON_NAME); summaryView.setText(summary); - title += " " + currentNetwork.getSsid(); - this.setTitle(title); - - if (!checkExternalApp()){ - toggleSettingsEnabled(false); - } else { - initVpnManager(); - } + this.currentNetwork = (AccessPointInfo) intent.getExtras().getParcelable( + ActivityLauncher.EXTRA_APINFO); + + this.currentNetwork = NetworkManager.getSavedNetwork(currentNetwork); + if (this.currentNetwork != null) { + title += " " + this.currentNetwork.getSsid(); + this.setTitle(title); + toggleSettingsEnabled(true); + } } - @Override - public void onActivityResult(int requestCode, int resultCode, Intent intent) { - - switch (requestCode) { - - case RequestCode.VPN_PERMISSION: - - if (resultCode != RESULT_OK) { - toggleSettingsEnabled(false); - Utils.logDebug("User rejected vpn permission."); - String msg = getString(R.string.msg_vpn_no_permission).replace( - OpenVpnManager.PLACEHOLDER_APPNAME, OpenVpnManager.APP_COMMON_NAME); - Commons.showMessage(msg, this); - return; - } - - break; - - default: - - break; - + public void onStart() { + + super.onStart(); + if (!checkExternalApp() || currentNetwork == null) { + toggleSettingsEnabled(false); + } else { + beginGetExistingVpnProfiles(); } } - @Override public boolean onCreateOptionsMenu(Menu menu) { return true; } - private void initVpnManager(){ - - try { - - Intent intentAllow = OpenVpnManager.askApiPermissionsGetIntent(); - if (intentAllow != null){ - startActivityForResult(intentAllow, ActivityLauncher.RequestCode.VPN_PERMISSION); - } - - List<String> profiles = OpenVpnManager.getExistingProfiles(); - if (profiles.size() == 0){ - String msg = getString(R.string.msg_vpn_no_profile).replace(OpenVpnManager.PLACEHOLDER_APPNAME, OpenVpnManager.APP_COMMON_NAME); - Commons.showMessage(msg, this); - toggleSettingsEnabled(false); - return; - } - Spinner spin = (Spinner)findViewById(R.id.spin_vpn_profile); - - ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,android.R.layout.simple_spinner_dropdown_item,profiles); - adapter.insert("",0); - spin.setAdapter(adapter); - spin.setSelection(adapter.getPosition(currentNetwork.getVpnProfileName())); - - } catch (Exception e) { - Utils.logError("Exception while creating openvpnmanager",e); - Commons.showMessage(getString(R.string.msg_vpn_connect_error)); - toggleSettingsEnabled(false); - } - - } - - private boolean checkExternalApp(){ - - if (! OpenVpnManager.isExternalAppInstalled(this)){ - String msg = getString(R.string.text_vpn_package_missing).replace(OpenVpnManager.PLACEHOLDER_APPNAME, OpenVpnManager.APP_COMMON_NAME); - Commons.showMessage(msg, this); + private boolean checkExternalApp() { + + if (!isExternalAppInstalled()) { + String msg = getString(R.string.text_vpn_package_missing).replace(PLACEHOLDER_APPNAME, + APP_COMMON_NAME); + Utils.showMessage(msg, this); toggleSettingsEnabled(false); return false; } else { return true; } - + } - - private void toggleSettingsEnabled(boolean enabled){ - + + private void toggleSettingsEnabled(boolean enabled) { + spinProfile.setEnabled(enabled); - - Button b = (Button)findViewById(R.id.btn_save_vpn_settings); + + Button b = (Button) findViewById(R.id.btn_save_vpn_settings); b.setEnabled(enabled); - + } - - public void btnSaveClick(View v){ - - String vpnProf = (String)spinProfile.getSelectedItem(); - - /*if (! vpnProf.isEmpty()){ - // check if profile name exists - if( OpenVpnManager.getUuidFromName(vpnProf) == null){ - Commons.showMessage(getString(R.string.msg_vpn_wrong_profile), this); - return; - } - - }*/ - + + public void btnSaveClick(View v) { + + String vpnProf = (String) spinProfile.getSelectedItem(); + // save profile currentNetwork.setVpnProfileName(vpnProf); - Commons.storage.save(currentNetwork); - + NetworkManager.save(currentNetwork); + terminate(); - + } - - public void btnBackClick(View v){ + + public void btnBackClick(View v) { terminate(); } - - private void terminate(){ + + private void terminate() { finish(); } + + @Override + protected void onVpnProfilesAvailable(List<String> profiles) { + + if (profiles.size() == 0) { + String msg = getString(R.string.msg_vpn_no_profile).replace(PLACEHOLDER_APPNAME, + APP_COMMON_NAME); + Utils.showMessage(msg, this); + toggleSettingsEnabled(false); + return; + } + Spinner spin = (Spinner) findViewById(R.id.spin_vpn_profile); + + ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, + android.R.layout.simple_spinner_dropdown_item, profiles){ + @Override + public View getView(int position, View convertView, android.view.ViewGroup parent) + { + return getStyledView(super.getView(position, convertView,parent)); + } + + private View getStyledView(View view) + { + TextView textView = (TextView)view.findViewById(android.R.id.text1); + textView.setGravity(Gravity.CENTER); + textView.setTextColor(R.color.ThemeLight); + return view; + } + + }; + adapter.insert(AccessPointInfo.DUMMY_VPN_PROFILE, 0); + spin.setAdapter(adapter); + + String pn = currentNetwork.getVpnProfileName(); + int pos = adapter.getPosition(pn); + if (pos < 0) { + spin.setSelection(adapter.getPosition(AccessPointInfo.DUMMY_VPN_PROFILE)); + } else { + spin.setSelection(pos); + } + + } + @Override + protected void onVpnPermissionDenied(){ + } + + } |