diff --git a/README.md b/README.md index c1aef8f..3b94018 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ ## What is this? +[WiFiota](https://tobywoerthle.github.io/flashWiFiSite/) ## ToDos diff --git a/app/build.gradle b/app/build.gradle index ddf1ce7..8288297 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -5,8 +5,8 @@ android { buildToolsVersion '26.0.2' defaultConfig { applicationId "jenny.daniel.wifip2p" - minSdkVersion 22 - targetSdkVersion 22 + minSdkVersion 23 + targetSdkVersion 23 multiDexEnabled = true versionCode 1 versionName "1.0" @@ -48,5 +48,6 @@ dependencies { compile 'com.github.kenglxn.QRGen:android:2.4.0' compile 'me.dm7.barcodescanner:zxing:1.9.8' compile 'pl.droidsonroids.gif:android-gif-drawable:1.2.10' + compile 'com.google.api-client:google-api-client:1.23.0' testCompile 'junit:junit:4.12' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 96cd9cb..714f849 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,8 +1,9 @@ - + @@ -14,6 +15,10 @@ + + 0) { + boolean ok = true; + for (int grantResult: grantResults) { + if (grantResult != PackageManager.PERMISSION_GRANTED) { + ok = false; + Log.d(TAG, "onRequestPermissionsResult: denied: " + permissions[i]); + } + i++; + } + if (ok) { + Toast.makeText(HomeActivity.this, "Permissions granted", Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText(HomeActivity.this, "Permissions denied", Toast.LENGTH_SHORT).show(); + } + AppOpsManager appOps = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE); + int mode = appOps.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, + android.os.Process.myUid(), getPackageName()); + if (mode != AppOpsManager.MODE_ALLOWED) { + Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS); + startActivity(intent); + } + + int mode2 = appOps.checkOpNoThrow(AppOpsManager.OPSTR_WRITE_SETTINGS, + android.os.Process.myUid(), getPackageName()); + if (mode2 != AppOpsManager.MODE_ALLOWED) { + Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS); + startActivity(intent); + } + } else { + + // permission denied, boo! Disable the + // functionality that depends on this permission. + Toast.makeText(HomeActivity.this, "Permission denied to read your External storage", Toast.LENGTH_SHORT).show(); + } + } + + // other 'case' lines to check for other + // permissions this app might request + } + } + private void setProgressBar(int percentage) { ProgressBar prog = (ProgressBar) findViewById(R.id.progressbar); prog.setProgress(percentage); @@ -137,7 +211,10 @@ public class HomeActivity extends AppCompatActivity { } else { final EditText field = (EditText) findViewById(R.id.password); field.setText(""); - Snackbar.make(view, getString(R.string.wrong_password), Snackbar.LENGTH_LONG).setAction("Action", null).show(); + Toast toast= Toast.makeText(getApplicationContext(), + getString(R.string.wrong_password), Toast.LENGTH_SHORT); + toast.setGravity(Gravity.TOP|Gravity.CENTER_HORIZONTAL, 0, 0); + toast.show(); } } @@ -177,9 +254,17 @@ public class HomeActivity extends AppCompatActivity { button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View view) { - setProgressBar(50); - String password = new_password_field.getText().toString(); - storeNewSeed(seed, password); + if (new_password_field.getText().toString().length() > 0) { + setProgressBar(50); + String password = new_password_field.getText().toString(); + storeNewSeed(seed, password); + } else { + Toast toast= Toast.makeText(getApplicationContext(), + "No blank password allowed", Toast.LENGTH_SHORT); + toast.setGravity(Gravity.TOP|Gravity.CENTER_HORIZONTAL, 0, 0); + toast.show(); + } + } }); } diff --git a/app/src/main/java/com/flashwifi/wifip2p/HotspotFragment.java b/app/src/main/java/com/flashwifi/wifip2p/HotspotFragment.java index c3e1a97..2fe6000 100644 --- a/app/src/main/java/com/flashwifi/wifip2p/HotspotFragment.java +++ b/app/src/main/java/com/flashwifi/wifip2p/HotspotFragment.java @@ -19,6 +19,7 @@ import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.ProgressBar; import android.widget.TextView; import android.widget.ToggleButton; @@ -33,8 +34,6 @@ public class HotspotFragment extends Fragment { private int numberConnectedPeers = 0; - WiFiDirectBroadcastService mService; - boolean mBound = false; BroadcastReceiver updateUIReceiver; public HotspotFragment() { @@ -72,33 +71,34 @@ public class HotspotFragment extends Fragment { private void updateUi(Intent intent) { Log.d(TAG, "updateUi: Got some network data into the hotspot fragment"); - String numberAvailableDevices = Integer.toString(mService.getArrayList().size()); + String numberAvailableDevices = Integer.toString(getmService().getArrayList().size()); TextView text = (TextView) getActivity().findViewById(R.id.numberPeers); + text.setVisibility(View.VISIBLE); text.setText(String.format("%s peers", numberAvailableDevices)); final View activity_view = getActivity().findViewById(R.id.drawer_layout); //if (intent.hasExtra("what") && intent.getExtras().getString("what", "").equals("connectivity_changed")) { - NetworkInfo network_info = mService.getNetwork_info(); - WifiP2pInfo p2p_info = mService.getP2p_info(); - WifiP2pGroup wifiP2pGroup = mService.getP2p_group(); + NetworkInfo network_info = getmService().getNetwork_info(); + WifiP2pInfo p2p_info = getmService().getP2p_info(); + WifiP2pGroup wifiP2pGroup = getmService().getP2p_group(); - //if (intent.hasExtra("currentDeviceConnected")) { + if (intent.hasExtra("currentDeviceConnected")) { //String macAddress = intent.getExtras().getString("currentDeviceConnected"); if (network_info.getState() == NetworkInfo.State.CONNECTED) { // ToDo: look for the other device and make sure we are only two if (p2p_info.isGroupOwner) { Snackbar.make(activity_view, "You are the group owner", Snackbar.LENGTH_LONG).setAction("Action", null).show(); - mService.startNegotiationServer(false, null); + getmService().startNegotiationServer(false, null); } else { InetAddress groupOwnerAddress = p2p_info.groupOwnerAddress; Snackbar.make(activity_view, "You are only a member of the group", Snackbar.LENGTH_LONG).setAction("Action", null).show(); - mService.startNegotiationClient(groupOwnerAddress, false, null); + getmService().startNegotiationClient(groupOwnerAddress, false, null); } } - //} + } @@ -109,29 +109,58 @@ public class HotspotFragment extends Fragment { @Override public void onStop() { super.onStop(); - getActivity().unbindService(mConnection); getActivity().unregisterReceiver(updateUIReceiver); - mBound = false; } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); + initFragment(); + } + + @Override + public void onResume() { + super.onResume(); + initFragment(); + } + + public WiFiDirectBroadcastService getmService() { + MainActivity act = (MainActivity) getActivity(); + return act.getmService(); + } + + private void initFragment(){ IntentFilter filter = new IntentFilter(); filter.addAction("com.flashwifi.wifip2p.update_ui"); + filter.addAction("com.flashwifi.wifip2p.start_roaming"); + filter.addAction("com.flashwifi.wifip2p.stop_roaming"); updateUIReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - updateUi(intent); + if (getActivity() == null) { + Log.d(TAG, "onReceive: getActivity is null"); + return; + } + if (intent.getAction().equals("com.flashwifi.wifip2p.start_roaming")) { + String mac = intent.getStringExtra("peer_mac_address"); + ToggleButton toggle = (ToggleButton) getActivity().findViewById(R.id.startAPButton); + toggle.setChecked(false); + ProgressBar progressBar = (ProgressBar) getActivity().findViewById(R.id.progressbarAP); + progressBar.setVisibility(View.INVISIBLE); + } else if (intent.getAction().equals("com.flashwifi.wifip2p.stop_roaming")) { + ToggleButton toggle = (ToggleButton) getActivity().findViewById(R.id.startAPButton); + toggle.setChecked(false); + ProgressBar progressBar = (ProgressBar) getActivity().findViewById(R.id.progressbarAP); + progressBar.setVisibility(View.INVISIBLE); + } else { + updateUi(intent); + } } }; - getActivity().registerReceiver(updateUIReceiver, filter); - // Bind to Service - Intent intent = new Intent(getActivity(), WiFiDirectBroadcastService.class); - getActivity().bindService(intent, mConnection, Context.BIND_AUTO_CREATE); + getActivity().registerReceiver(updateUIReceiver, filter); initUI(); @@ -157,36 +186,25 @@ public class HotspotFragment extends Fragment { // ToDo: Move this to a async task //boolean status = (isNetworkConnected() && isInternetAvailable()); } - - private boolean isNetworkConnected() { - ConnectivityManager cm = (ConnectivityManager) getActivity().getSystemService(Context.CONNECTIVITY_SERVICE); - - return cm.getActiveNetworkInfo() != null; - } - - public boolean isInternetAvailable() { - try { - InetAddress ipAddr = InetAddress.getByName("google.com"); - return !ipAddr.getHostAddress().equals(""); - - } catch (Exception e) { - return false; - } - } private void startDiscovery() { final View activity_view = getActivity().findViewById(R.id.drawer_layout); - if (mBound) { - mService.setInRoleConsumer(false); - mService.setInRoleHotspot(true); - mService.getPeerList(new WifiP2pManager.ActionListener() { + if (getmService() != null) { + getmService().setInRoleConsumer(false); + getmService().setInRoleHotspot(true); + getmService().getPeerList(new WifiP2pManager.ActionListener() { @Override public void onSuccess() { + // show progress wheel + ProgressBar progressBar = (ProgressBar) getActivity().findViewById(R.id.progressbarAP); + progressBar.setVisibility(View.VISIBLE); } @Override public void onFailure(int reasonCode) { Snackbar.make(activity_view, "Aaaargh :( Searching problem!", Snackbar.LENGTH_LONG).setAction("Action", null).show(); + ProgressBar progressBar = (ProgressBar) getActivity().findViewById(R.id.progressbarAP); + progressBar.setVisibility(View.INVISIBLE); } }); } @@ -195,20 +213,24 @@ public class HotspotFragment extends Fragment { private void stopDiscovery() { final View activity_view = getActivity().findViewById(R.id.drawer_layout); - if (mBound) { - mService.stopDiscovery(new WifiP2pManager.ActionListener() { + if (getmService() != null) { + getmService().stopDiscovery(new WifiP2pManager.ActionListener() { @Override public void onSuccess() { - mService.setInRoleConsumer(false); - mService.setInRoleHotspot(false); + getmService().setInRoleConsumer(false); + getmService().setInRoleHotspot(false); Snackbar.make(activity_view, "Stopped Hotspot mode", Snackbar.LENGTH_LONG).setAction("Action", null).show(); + ProgressBar progressBar = (ProgressBar) getActivity().findViewById(R.id.progressbarAP); + progressBar.setVisibility(View.INVISIBLE); } @Override public void onFailure(int reasonCode) { - mService.setInRoleConsumer(false); - mService.setInRoleHotspot(false); + getmService().setInRoleConsumer(false); + getmService().setInRoleHotspot(false); Snackbar.make(activity_view, "Aaaargh :( Problem stopping discovery", Snackbar.LENGTH_LONG).setAction("Action", null).show(); + ProgressBar progressBar = (ProgressBar) getActivity().findViewById(R.id.progressbarAP); + progressBar.setVisibility(View.INVISIBLE); } }); } @@ -218,10 +240,16 @@ public class HotspotFragment extends Fragment { private void initUI() { final View activity_view = getActivity().findViewById(R.id.drawer_layout); final ToggleButton button = (ToggleButton) getActivity().findViewById(R.id.startAPButton); - //button.setChecked(mService.isInRoleHotspot()); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { + if (getmService() != null) { + if (!getmService().isSetup()) { + Snackbar.make(activity_view, "Please enable WiFi P2P", Snackbar.LENGTH_LONG).setAction("Action", null).show(); + button.setChecked(false); + return; + } + } if (button.isChecked()) { startDiscovery(); Snackbar.make(activity_view, "Start Discovery Mode", Snackbar.LENGTH_LONG).setAction("Action", null).show(); @@ -232,25 +260,4 @@ public class HotspotFragment extends Fragment { } }); } - - /** Defines callbacks for service binding, passed to bindService() */ - private ServiceConnection mConnection = new ServiceConnection() { - - @Override - public void onServiceConnected(ComponentName className, - IBinder service) { - // We've bound to LocalService, cast the IBinder and get LocalService instance - WiFiDirectBroadcastService.LocalBinder binder = (WiFiDirectBroadcastService.LocalBinder) service; - mService = binder.getService(); - mBound = true; - - final ToggleButton button = (ToggleButton) getActivity().findViewById(R.id.startAPButton); - button.setChecked(mService.isInRoleHotspot()); - } - - @Override - public void onServiceDisconnected(ComponentName arg0) { - mBound = false; - } - }; } \ No newline at end of file diff --git a/app/src/main/java/com/flashwifi/wifip2p/MainActivity.java b/app/src/main/java/com/flashwifi/wifip2p/MainActivity.java index 96ffd6d..83b1c05 100644 --- a/app/src/main/java/com/flashwifi/wifip2p/MainActivity.java +++ b/app/src/main/java/com/flashwifi/wifip2p/MainActivity.java @@ -1,5 +1,6 @@ package com.flashwifi.wifip2p; +import android.app.ActivityManager; import android.app.Fragment; import android.app.FragmentManager; import android.content.BroadcastReceiver; @@ -8,30 +9,42 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; +import android.content.SharedPreferences; +import android.net.wifi.p2p.WifiP2pManager; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.preference.PreferenceManager; import android.support.design.widget.NavigationView; import android.support.v4.view.GravityCompat; import android.support.v4.widget.DrawerLayout; import android.support.v7.app.ActionBarDrawerToggle; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; +import android.util.Log; import android.view.Menu; import android.view.MenuItem; -import android.widget.CompoundButton; +import android.view.View; import android.widget.ListView; -import android.widget.Switch; +import android.widget.ProgressBar; import android.widget.Toast; +import com.flashwifi.wifip2p.billing.Accountant; import com.flashwifi.wifip2p.broadcast.WiFiDirectBroadcastService; +import com.flashwifi.wifip2p.iotaAPI.Requests.WalletBalanceChecker; public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener { + private static final String TAG = "MainAct"; private String password; private String seed; + private static final int PREF_UPDATE = 2; + private static final int BALANCE_RETRIEVE_TASK_COMPLETE = 1; + private Handler balanceHandler; private DrawerLayout mDrawerLayout; private ListView mDrawerList; @@ -46,22 +59,75 @@ public class MainActivity extends AppCompatActivity private void subscribeToBroadcasts() { IntentFilter filter = new IntentFilter(); filter.addAction("com.flashwifi.wifip2p.start_roaming"); + filter.addAction("com.flashwifi.wifip2p.stop_roaming"); updateUIReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { + try { + // hide progress bar + ProgressBar progressConnection = (ProgressBar) findViewById(R.id.progressConnection); + progressConnection.setVisibility(View.VISIBLE); + } catch (Exception e) { + } if (intent.getAction().equals("com.flashwifi.wifip2p.start_roaming")) { startRoamingView(intent.getStringExtra("peer_mac_address"), intent.getStringExtra("ssid"), intent.getStringExtra("key")); + } else if (intent.getAction().equals("com.flashwifi.wifip2p.stop_roaming")) { + Log.d(TAG, "onReceive: Reset billing state"); + } } }; registerReceiver(updateUIReceiver, filter); } + public boolean isTheServiceRunning() { + return isMyServiceRunning(WiFiDirectBroadcastService.class); + } + + private boolean isMyServiceRunning(Class serviceClass) { + ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); + for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) { + if (serviceClass.getName().equals(service.service.getClassName())) { + return true; + } + } + return false; + } + + @Override + protected void onPause() { + super.onPause(); + //unbindWifiBroadcast(); + unsubscribeFromBroadcast(); + } + + private void unbindWifiBroadcast() { + try {unregisterReceiver(updateUIReceiver); + unbindService(mConnection); + } + catch(IllegalArgumentException e) { + } + } + + @Override + protected void onResume() { + super.onResume(); + initEverything(); + } + + private void unsubscribeFromBroadcast() { + try { + unregisterReceiver(updateUIReceiver); + updateUIReceiver = null; + } catch (Exception e){ + } + } + private void initUi() { - final Switch switch_ = (Switch) findViewById(R.id.wifiSwitch); + /*final Switch switch_ = (Switch) findViewById(R.id.wifiSwitch); switch_.setOnCheckedChangeListener(new Switch.OnCheckedChangeListener() { public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (isChecked) { @@ -75,16 +141,27 @@ public class MainActivity extends AppCompatActivity mService.disableService(); } } - }); + });*/ } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); + // get the secrets from the login screen + Intent intent = getIntent(); + password = intent.getStringExtra("password"); + seed = intent.getStringExtra("seed"); + + setBalanceHandler(); + updateBalance(); + + Accountant.getInstance().setSeed(seed); + DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); ActionBarDrawerToggle toggle = new ActionBarDrawerToggle( this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close); @@ -94,25 +171,46 @@ public class MainActivity extends AppCompatActivity NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view); navigationView.setNavigationItemSelectedListener(this); - // get the secrets from the login screen - Intent intent = getIntent(); - password = intent.getStringExtra("password"); - seed = intent.getStringExtra("seed"); + PreferenceManager.setDefaultValues(this, R.xml.preferences, false); + + beginForegroundService(); - // Bind to Service Intent intent2 = new Intent(this, WiFiDirectBroadcastService.class); bindService(intent2, mConnection, Context.BIND_AUTO_CREATE); + } + public WiFiDirectBroadcastService getmService() { + if (mBound) { + return mService; + } + return null; + } + + private void beginForegroundService() { + if (!isTheServiceRunning()) { + Intent startIntent = new Intent(MainActivity.this, WiFiDirectBroadcastService.class); + startIntent.setAction(Constants.ACTION.STARTFOREGROUND_ACTION); + startService(startIntent); + } else { + Log.d(TAG, "beginForegroundService: Service is already running"); + } + } + + private void initEverything() { subscribeToBroadcasts(); initUi(); - } @Override protected void onStop() { super.onStop(); - unregisterReceiver(updateUIReceiver); - unbindService(mConnection); + + try {unregisterReceiver(updateUIReceiver); + unbindService(mConnection); + } + catch(IllegalArgumentException e) { + System.out.println(e); + } } @Override @@ -148,16 +246,16 @@ public class MainActivity extends AppCompatActivity startSearchFragment(); } else if (id == R.id.nav_start) { startHotspotFragment(); - } else if (id == R.id.nav_itp) { - +// } else if (id == R.id.nav_itp) { +// } else if (id == R.id.nav_fund) { startFundWalletFragment(); } else if (id == R.id.nav_withdraw) { startWithdrawWalletFragment(); - } else if (id == R.id.nav_conditions) { +// } else if (id == R.id.nav_conditions) { } else if (id == R.id.nav_settings) { - + startSettingsFragment(); } DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); @@ -165,9 +263,37 @@ public class MainActivity extends AppCompatActivity return true; } + private boolean settingsAreReady(){ + // check whether all necessary settings were set by the user + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); + String[] intVariables = new String[]{ + "edit_text_sell_min_minutes", + "edit_text_sell_price", + "edit_text_sell_max_minutes", + "edit_text_buy_price", + "edit_text_client_minutes" + }; + String val; + for (String variable: intVariables) { + val = prefs.getString(variable, null); + if (val == null) { + Log.d("Variable not set: ", "settingsAreReady: " + variable); + return false; + } + } + return true; + } + + private void updateBalance() { + WalletBalanceChecker balanceChecker = new WalletBalanceChecker(this,this.getString(R.string.preference_file_key),seed, balanceHandler,PREF_UPDATE,true); + balanceChecker.execute(); + } + public void startSearchFragment() { if (mBound && mService.isInRoleHotspot()) { Toast.makeText(this, "Can't start search because you are hotspot", Toast.LENGTH_SHORT).show(); + } else if (!settingsAreReady()) { + Toast.makeText(this, "Navigate to settings and assign all variables", Toast.LENGTH_SHORT).show(); } else { Fragment fragment = new SearchFragment(); @@ -181,6 +307,21 @@ public class MainActivity extends AppCompatActivity } private void startRoamingView(String macAddress, String ssid, String key){ + // disable WiFi P2P + if (mBound) { + mService.stopDiscovery(new WifiP2pManager.ActionListener() { + @Override + public void onSuccess() { + Log.d("MainAct", "discovery stopped"); + } + + @Override + public void onFailure(int i) { + Log.d("MainAct", "discovery could not be stopped"); + } + }); + } + Intent intent = new Intent(this, RoamingActivity.class); intent.putExtra("address", macAddress); intent.putExtra("key", key); @@ -191,11 +332,11 @@ public class MainActivity extends AppCompatActivity public void startHotspotFragment() { if (mBound && mService.isInRoleConsumer()) { Toast.makeText(this, "Can't start hotspot because you are searching", Toast.LENGTH_SHORT).show(); + } else if (!settingsAreReady()) { + Toast.makeText(this, "Navigate to settings and assign all variables", Toast.LENGTH_SHORT).show(); } else { Fragment fragment = new HotspotFragment(); Bundle args = new Bundle(); - //args.putInt(PlanetFragment.ARG_PLANET_NUMBER, position); - //fragment.setArguments(args); // Insert the fragment by replacing any existing fragment FragmentManager fragmentManager = getFragmentManager(); @@ -235,6 +376,20 @@ public class MainActivity extends AppCompatActivity .commit(); } + private void startSettingsFragment() { + Fragment fragment = new SettingsFragment(); + Bundle bundle = new Bundle(); + bundle.putString("seed", seed); + fragment.setArguments(bundle); + fragment.setRetainInstance(true); + + // Insert the fragment by replacing any existing fragment + FragmentManager fragmentManager = getFragmentManager(); + fragmentManager.beginTransaction() + .replace(R.id.content_frame, fragment) + .commit(); + } + @Override public void onBackPressed() { DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); @@ -270,6 +425,7 @@ public class MainActivity extends AppCompatActivity WiFiDirectBroadcastService.LocalBinder binder = (WiFiDirectBroadcastService.LocalBinder) service; mService = binder.getService(); mBound = true; + mService.enableService(); } @Override @@ -277,4 +433,34 @@ public class MainActivity extends AppCompatActivity mBound = false; } }; + + private void setBalanceHandler() { + //Handle post-asynctask activities + balanceHandler = new Handler(Looper.getMainLooper()) { + @Override + public void handleMessage(Message inputMessage) { + switch (inputMessage.what) { + case BALANCE_RETRIEVE_TASK_COMPLETE: + AddressBalanceTransfer addressBalanceTransfer = (AddressBalanceTransfer) inputMessage.obj; + String returnStatus = addressBalanceTransfer.getMessage(); + if (returnStatus.equals("noError")) { + makeToastBalance("Balance updated"); + } else if (returnStatus.equals("hostError")) { + makeToastBalance("Unable to reach host (node)"); + } else if (returnStatus.equals("addressError")) { + makeToastBalance("Error getting address"); + } else if (returnStatus.equals("balanceError")) { + makeToastBalance("Error getting balance. May not be able to resolve host/node"); + } else { + makeToastBalance("Unknown error"); + } + } + } + }; + } + + private void makeToastBalance(String message) { + Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); + } + } \ No newline at end of file diff --git a/app/src/main/java/com/flashwifi/wifip2p/RoamingActivity.java b/app/src/main/java/com/flashwifi/wifip2p/RoamingActivity.java index dec0f2c..954ee06 100644 --- a/app/src/main/java/com/flashwifi/wifip2p/RoamingActivity.java +++ b/app/src/main/java/com/flashwifi/wifip2p/RoamingActivity.java @@ -1,5 +1,6 @@ package com.flashwifi.wifip2p; +import android.annotation.SuppressLint; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; @@ -26,10 +27,12 @@ import android.widget.Button; import android.widget.CheckBox; import android.widget.EditText; import android.widget.ListView; +import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; import java.net.InetAddress; +import java.net.UnknownHostException; import java.util.ArrayList; import com.flashwifi.wifip2p.billing.Accountant; @@ -37,6 +40,8 @@ import com.flashwifi.wifip2p.broadcast.WiFiDirectBroadcastService; import com.flashwifi.wifip2p.datastore.PeerStore; import com.flashwifi.wifip2p.protocol.NegotiationFinalization; +import org.w3c.dom.Text; + public class RoamingActivity extends AppCompatActivity { private static final String TAG = "RoamingActivity"; @@ -59,6 +64,7 @@ public class RoamingActivity extends AppCompatActivity { private Button stopButton; private boolean endRoamingFlag = false; + private boolean initiatedEnd; @Override protected void onStart() { @@ -93,20 +99,40 @@ public class RoamingActivity extends AppCompatActivity { Log.d(TAG, "updateUi: message=" + message); CheckBox apConnected = (CheckBox)findViewById(R.id.accessPointActive); CheckBox flashEstablished = (CheckBox)findViewById(R.id.flashEstablished); + CheckBox channelFunded = (CheckBox)findViewById(R.id.channelFunded); if (message.equals("AP SUCCESS")) { apConnected.setChecked(true); + mService.resetBillingState(); // when the AP is setup we can start the server startBillingProtocol(); } else if (message.equals("AP FAILED")) { apConnected.setChecked(false); Toast.makeText(getApplicationContext(), "Could not create Access point", Toast.LENGTH_LONG).show(); + exitRoaming(); } else if (message.equals("AP STOPPED")) { apConnected.setChecked(false); } else if (message.equals("Channel established")) { flashEstablished.setChecked(true); + } else if (message.equals("Start Channel funding")) { + // start the task + mService.fundChannel(address); + } else if (message.equals("Channel funded")) { + channelFunded.setChecked(true); } else if (message.equals("Billing")) { updateBillingCard(); + } else if (message.equals("Channel closed")) { + exitRoaming(); + if (mService.isInRoleHotspot()) { + showRetransferCard(); + } + } else if (message.equals("Socket exception")) { + //Toast.makeText(getApplicationContext(), "Socket exception", Toast.LENGTH_LONG).show(); + //exitRoaming(); + } else if (message.equals("Exit")) { + Toast.makeText(getApplicationContext(), "Can't connect", Toast.LENGTH_LONG).show(); + exitRoaming(); } + // ToDo: add a critical error that uses exitRoaming() } } @@ -118,25 +144,50 @@ public class RoamingActivity extends AppCompatActivity { } + private void stopRoamingBroadcast() { + Intent local = new Intent(); + local.setAction("com.flashwifi.wifip2p.stop_roaming"); + this.sendBroadcast(local); + } + + private void showRetransferCard() { + CardView cardView = (CardView) findViewById(R.id.card_view_tangle_attachment); + cardView.setVisibility(View.VISIBLE); + } + + @SuppressLint("DefaultLocale") private void updateBillingCard() { CardView summaryView = (CardView) findViewById(R.id.card_view_overview); if (summaryView.getVisibility() != View.VISIBLE) { summaryView.setVisibility(View.VISIBLE); } - String minutes = Integer.toString(Accountant.getInstance().getTotalDurance()); - String megabytes_max = Integer.toString(Accountant.getInstance().getBookedMegabytes()); - String megabytes_used = Integer.toString(Accountant.getInstance().getTotalMegabytes()); - String iotas_transferred = Integer.toString(Accountant.getInstance().getTotalIotaPrice()); + int minutes = Accountant.getInstance().getTotalDurance() / 60; + int minutes_max = Accountant.getInstance().getBookedMinutes(); + int bytes_max = Accountant.getInstance().getBookedBytes(); + int bytes_used = Accountant.getInstance().getTotalBytes(); + int iotas_transferred = Accountant.getInstance().getTotalIotaPrice(); + int iotas_max = Accountant.getInstance().getTotalIotaDeposit(); TextView summaryMinutes = (TextView) findViewById(R.id.summaryMinutes); - summaryMinutes.setText(String.format("%s minutes active", minutes)); + summaryMinutes.setText(String.format("%d/%d minutes active", minutes, minutes_max)); TextView summaryMegabytes = (TextView) findViewById(R.id.summaryMegabytes); - summaryMegabytes.setText(String.format("%s/%s Megabytes roamed", megabytes_used, megabytes_max)); - + summaryMegabytes.setText(String.format("%d/%d Megabytes roamed", bytes_used, bytes_max)); TextView summaryIota = (TextView) findViewById(R.id.summaryIota); - summaryMegabytes.setText(String.format("%s Iota transferred", iotas_transferred)); + summaryIota.setText(String.format("%d/%d Iota transferred", iotas_transferred, iotas_max)); + + ProgressBar progressMinutes = (ProgressBar) findViewById(R.id.progressbarDurance); + progressMinutes.setMax(minutes_max); + progressMinutes.setProgress(minutes); + + ProgressBar progressMegabytes = (ProgressBar) findViewById(R.id.progressbarMegabytes); + progressMegabytes.setMax(bytes_max); + progressMegabytes.setProgress(bytes_used); + + ProgressBar progressIota = (ProgressBar) findViewById(R.id.progressbarIota); + progressIota.setProgress(iotas_transferred); + progressIota.setMax(iotas_max); } @@ -144,9 +195,9 @@ public class RoamingActivity extends AppCompatActivity { private void startBillingProtocol() { // setup the flash channel etc... if (mService.isInRoleHotspot()) { - mService.startBillingServer(); + mService.startBillingServer(address); } else { - mService.startBillingClient(); + mService.startBillingClient(address); } } @@ -166,6 +217,8 @@ public class RoamingActivity extends AppCompatActivity { super.onCreate(savedInstanceState); setContentView(R.layout.activity_roaming); + Accountant.getInstance().reset(); + // Get the Intent that started this activity and extract the string Intent intent = getIntent(); name = intent.getStringExtra("name"); @@ -179,78 +232,12 @@ public class RoamingActivity extends AppCompatActivity { } private void cancelNotification() { - NotificationManager mNotificationManager = - (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - mNotificationManager.cancel(1); - } - - - private void showNotification() { - // The id of the channel. - String CHANNEL_ID = "com.flashwifi.wifip2p.roaming_1"; - - String contentText = mService.isInRoleHotspot() ? "You are providing" : "You are consuming"; - - // ToDo: make this work on high API version - NotificationCompat.Builder mBuilder = - new NotificationCompat.Builder(this) - .setSmallIcon(R.drawable.icon_tethering_on) - .setContentTitle("IOTA HOTSPOT") - .setContentText(contentText); - // Creates an explicit intent for an Activity in your app - Intent resultIntent = new Intent(this, RoamingActivity.class); - - // The stack builder object will contain an artificial back stack for the - // started Activity. - // This ensures that navigating backward from the Activity leads out of - // your app to the Home screen. - TaskStackBuilder stackBuilder = TaskStackBuilder.create(this); - // Adds the back stack for the Intent (but not the Intent itself) - stackBuilder.addParentStack(RoamingActivity.class); - // Adds the Intent that starts the Activity to the top of the stack - stackBuilder.addNextIntent(resultIntent); - PendingIntent resultPendingIntent = - stackBuilder.getPendingIntent( - 0, - PendingIntent.FLAG_UPDATE_CURRENT - ); - mBuilder.setContentIntent(resultPendingIntent); - NotificationManager mNotificationManager = - (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - - // mNotificationId is a unique integer your app uses to identify the - // notification. For example, to cancel the notification, you can pass its ID - // number to NotificationManager.cancel(). - mNotificationManager.notify(1, mBuilder.build()); } private void initUI() { - //final EditText input = (EditText) findViewById(R.id.chat_input); - //Button button = (Button) findViewById(R.id.btn_send); - /*button.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(final View view) { - addMessageRight(name, input.getText().toString()); - // send the message to the peer - //mService.sendMessageToSocketServer(groupOwnerAddress, input.getText().toString()); - } - });*/ - /*FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); - fab.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(final View view) { - toggleHotspot(); - } - }); - - listView = (ListView) findViewById(R.id.peer_list); - arrayList = new ArrayList<>(); - - listAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, arrayList); - listView.setAdapter(listAdapter);*/ } /** Defines callbacks for service binding, passed to bindService() */ @@ -294,10 +281,25 @@ public class RoamingActivity extends AppCompatActivity { } }); - showNotification(); + updateBillingCard(); + + // ToDo: update notification + //showNotification(); } private void endRoaming() { + if (!initiatedEnd) { + initiatedEnd = true; + Accountant.getInstance().setCloseAfterwards(true); + // the next bill will send the close request + // meanwhile show a loading icon + ProgressBar stopProgressBar = (ProgressBar) findViewById(R.id.stopProgressBar); + stopProgressBar.setVisibility(View.VISIBLE); + } + } + + private void exitRoaming() { + Accountant.getInstance().setCloseAfterwards(true); endRoamingFlag = true; cancelNotification(); if (mService.isInRoleHotspot()){ @@ -306,7 +308,26 @@ public class RoamingActivity extends AppCompatActivity { mService.disconnectAP(); } mService.setRoaming(false); + mService.resetBillingState(); + mService.setInRoleConsumer(false); + mService.setInRoleHotspot(false); + + PeerStore.getInstance().clear(); + + // hide the spinner and the stop button + ProgressBar stopProgressBar = (ProgressBar) findViewById(R.id.stopProgressBar); + stopProgressBar.setVisibility(View.GONE); + Button stopButton = (Button) findViewById(R.id.stopRoamingButton); + stopButton.setVisibility(View.GONE); + + TextView stopText = (TextView) findViewById(R.id.stopText); + stopText.setVisibility(View.VISIBLE); + + stopRoamingBroadcast(); + Toast.makeText(getApplicationContext(), "Press BACK now", Toast.LENGTH_LONG).show(); + initiatedEnd = false; + //finish(); } @Override diff --git a/app/src/main/java/com/flashwifi/wifip2p/SearchFragment.java b/app/src/main/java/com/flashwifi/wifip2p/SearchFragment.java index af6ee0e..2a45300 100644 --- a/app/src/main/java/com/flashwifi/wifip2p/SearchFragment.java +++ b/app/src/main/java/com/flashwifi/wifip2p/SearchFragment.java @@ -21,6 +21,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ListView; +import android.widget.ProgressBar; import android.widget.Toast; import android.widget.ToggleButton; @@ -32,6 +33,7 @@ import java.net.InetAddress; import java.util.ArrayList; import com.flashwifi.wifip2p.broadcast.WiFiDirectBroadcastService; +import com.flashwifi.wifip2p.iotaFlashWrapper.Main; /** * Fragment that appears in the "content_frame", shows a planet @@ -40,9 +42,7 @@ public class SearchFragment extends Fragment { public final static String TAG = "SearchActivity"; - WiFiDirectBroadcastService mService; - boolean mBound = false; - BroadcastReceiver updateUIReceiver; + BroadcastReceiver updateUIReceiver = null; ArrayList arrayList; PeerListAdapter peerListAdapter; @@ -63,6 +63,14 @@ public class SearchFragment extends Fragment { return rootView; } + public WiFiDirectBroadcastService getmService() { + MainActivity act = (MainActivity) getActivity(); + if (act != null) { + return act.getmService(); + } + return null; + } + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -86,8 +94,8 @@ public class SearchFragment extends Fragment { } private void updateList() { - peerListAdapter.notifyDataSetInvalidated(); peerListAdapter.clear(); + // peerListAdapter.notifyDataSetInvalidated(); peerListAdapter.addAll(PeerStore.getInstance().getPeerArrayList()); peerListAdapter.notifyDataSetChanged(); } @@ -96,20 +104,22 @@ public class SearchFragment extends Fragment { updateList(); String what = intent.getStringExtra("what"); - Log.d(">>>>>>>>>>>>", "updateUi: " + what); + String message = intent.getStringExtra("message"); - if (what == null) { - what = ""; - } - - if (what.equals("connectivity_changed")) { + if (what != null && what.equals("connectivity_changed")) { String currentDeviceConnected = intent.getStringExtra("currentDeviceConnected"); startNegotiationProtocol(currentDeviceConnected); } + + if (message != null && message.equals("error")) { + String snd_message = intent.getStringExtra("snd_message"); + Snackbar.make(view, snd_message, Snackbar.LENGTH_LONG).setAction("Action", null).show(); + updateList(); + } } private void startNegotiationProtocol(String macAddress){ - WifiP2pInfo p2p_info = mService.getP2p_info(); + WifiP2pInfo p2p_info = getmService().getP2p_info(); if (p2p_info.isGroupOwner) { startNegotiationProtocolServer(macAddress); @@ -120,13 +130,13 @@ public class SearchFragment extends Fragment { private void startNegotiationProtocolServer(String macAddress) { // starts the server if necessary - WifiP2pInfo p2p_info = mService.getP2p_info(); - NetworkInfo network_info = mService.getNetwork_info(); + WifiP2pInfo p2p_info = getmService().getP2p_info(); + NetworkInfo network_info = getmService().getNetwork_info(); if (network_info.getState() == NetworkInfo.State.CONNECTED) { if (p2p_info.isGroupOwner) { Log.d(TAG, "You are the group owner"); - mService.startNegotiationServer(true, macAddress); + getmService().startNegotiationServer(true, macAddress); Log.d(TAG, "SocketServer started"); } } @@ -134,8 +144,8 @@ public class SearchFragment extends Fragment { private void startNegotiationProtocolClient(String macAddress){ // starts the server if necessary - WifiP2pInfo p2p_info = mService.getP2p_info(); - NetworkInfo network_info = mService.getNetwork_info(); + WifiP2pInfo p2p_info = getmService().getP2p_info(); + NetworkInfo network_info = getmService().getNetwork_info(); if (network_info.getState() == NetworkInfo.State.CONNECTED) { if (!p2p_info.isGroupOwner) { @@ -143,40 +153,73 @@ public class SearchFragment extends Fragment { // groupOwnerAddress = p2p_info.groupOwnerAddress; InetAddress groupOwnerAddress = p2p_info.groupOwnerAddress; Log.d(TAG, "Group owner address: " + p2p_info.groupOwnerAddress.getHostAddress()); - mService.startNegotiationClient(groupOwnerAddress, true, macAddress); + getmService().startNegotiationClient(groupOwnerAddress, true, macAddress); Log.d(TAG, "Client Socket started"); } } } + @Override + public void onPause() { + super.onPause(); + if (updateUIReceiver != null) { + getActivity().unregisterReceiver(updateUIReceiver); + updateUIReceiver = null; + } + } + @Override public void onStop() { super.onStop(); - getActivity().unbindService(mConnection); - getActivity().unregisterReceiver(updateUIReceiver); - mBound = false; + if (updateUIReceiver != null) { + getActivity().unregisterReceiver(updateUIReceiver); + updateUIReceiver = null; + } } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); + initFragment(); + } + + @Override + public void onResume() { + super.onResume(); + initFragment(); + } + + private void initFragment() { IntentFilter filter = new IntentFilter(); filter.addAction("com.flashwifi.wifip2p.update_ui"); + filter.addAction("com.flashwifi.wifip2p.start_roaming"); + filter.addAction("com.flashwifi.wifip2p.stop_roaming"); updateUIReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { + if (getActivity() == null) { + Log.d(TAG, "onReceive: getActivity is null"); + return; + } Log.d("", "onReceive: FRAGMENT HAT WAS"); - updateUi(intent); + if (intent.getAction().equals("com.flashwifi.wifip2p.start_roaming")) { + String mac = intent.getStringExtra("peer_mac_address"); + ToggleButton toggle = (ToggleButton) getActivity().findViewById(R.id.startSearchButton); + toggle.setChecked(false); + } else if (intent.getAction().equals("com.flashwifi.wifip2p.stop_roaming")) { + PeerStore.getInstance().clear(); + busy = false; + ToggleButton toggle = (ToggleButton) getActivity().findViewById(R.id.startSearchButton); + toggle.setChecked(false); + } else { + updateUi(intent); + } } }; getActivity().registerReceiver(updateUIReceiver, filter); - // Bind to Service - Intent intent = new Intent(getActivity(), WiFiDirectBroadcastService.class); - getActivity().bindService(intent, mConnection, Context.BIND_AUTO_CREATE); - view = getActivity().findViewById(R.id.fragment_view); initUI(); @@ -187,23 +230,24 @@ public class SearchFragment extends Fragment { toolbar.setTitle("Discover Peers"); final ToggleButton toggle = (ToggleButton) getActivity().findViewById(R.id.startSearchButton); - //toggle.setChecked(mService.isInRoleConsumer()); + toggle.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View view) { + if (!getmService().isSetup()) { + Snackbar.make(view, "Please enable WiFi P2P", Snackbar.LENGTH_LONG).setAction("Action", null).show(); + toggle.setChecked(false); + return; + } updateList(); if (toggle.isChecked()) { - if (mBound) { - mService.setInRoleHotspot(false); - mService.setInRoleConsumer(true); - startSearching(); - } + getmService().setInRoleHotspot(false); + getmService().setInRoleConsumer(true); + startSearching(); } else { - if (mBound) { - mService.setInRoleHotspot(false); - mService.setInRoleConsumer(false); - stopSearching(); - } + getmService().setInRoleHotspot(false); + getmService().setInRoleConsumer(false); + stopSearching(); } } }); @@ -223,6 +267,11 @@ public class SearchFragment extends Fragment { public void onItemClick(AdapterView adapterView, View view, int i, long l) { if (!busy) { busy = true; + + // show progress bar + ProgressBar progressConnection = (ProgressBar) getActivity().findViewById(R.id.progressConnection); + progressConnection.setVisibility(View.VISIBLE); + PeerInformation peer = PeerStore.getInstance().getPeerArrayList().get(i); peer.setSelected(true); @@ -242,13 +291,13 @@ public class SearchFragment extends Fragment { public void startChat(final String address, String name) { - mService.connect(address, new WifiP2pManager.ActionListener() { + getmService().connect(address, new WifiP2pManager.ActionListener() { @Override public void onSuccess() { Toast.makeText(view.getContext(), "Connected to peer", Toast.LENGTH_SHORT).show(); // start the protocol startNegotiationProtocol(address); - busy = false; + busy = true; } @Override public void onFailure(int reason) { @@ -259,56 +308,31 @@ public class SearchFragment extends Fragment { } private void stopSearching() { - if (mBound) { - mService.stopDiscovery(new WifiP2pManager.ActionListener() { - @Override - public void onSuccess() { - Snackbar.make(view, "Stopped search", Snackbar.LENGTH_LONG).setAction("Action", null).show(); - } + getmService().stopDiscovery(new WifiP2pManager.ActionListener() { + @Override + public void onSuccess() { + Snackbar.make(view, "Stopped search", Snackbar.LENGTH_LONG).setAction("Action", null).show(); + } - @Override - public void onFailure(int reasonCode) { - Snackbar.make(view, "Aaaargh :( problem stopping search!", Snackbar.LENGTH_LONG).setAction("Action", null).show(); - } - }); - } + @Override + public void onFailure(int reasonCode) { + Snackbar.make(view, "Aaaargh :( problem stopping search!", Snackbar.LENGTH_LONG).setAction("Action", null).show(); + } + }); } public void startSearching() { + getmService().getPeerList(new WifiP2pManager.ActionListener() { + @Override + public void onSuccess() { + Snackbar.make(view, "Successfully searched for peers", Snackbar.LENGTH_LONG).setAction("Action", null).show(); + } - if (mBound) { - mService.getPeerList(new WifiP2pManager.ActionListener() { - @Override - public void onSuccess() { - Snackbar.make(view, "Successfully searched for peers", Snackbar.LENGTH_LONG).setAction("Action", null).show(); - } + @Override + public void onFailure(int reasonCode) { + Snackbar.make(view, "Aaaargh :( Peering problem!", Snackbar.LENGTH_LONG).setAction("Action", null).show(); + } + }); - @Override - public void onFailure(int reasonCode) { - Snackbar.make(view, "Aaaargh :( Peering problem!", Snackbar.LENGTH_LONG).setAction("Action", null).show(); - } - }); - } } - - /** Defines callbacks for service binding, passed to bindService() */ - private ServiceConnection mConnection = new ServiceConnection() { - - @Override - public void onServiceConnected(ComponentName className, - IBinder service) { - // We've bound to LocalService, cast the IBinder and get LocalService instance - WiFiDirectBroadcastService.LocalBinder binder = (WiFiDirectBroadcastService.LocalBinder) service; - mService = binder.getService(); - mBound = true; - - final ToggleButton toggle = (ToggleButton) getActivity().findViewById(R.id.startSearchButton); - toggle.setChecked(mService.isInRoleConsumer()); - } - - @Override - public void onServiceDisconnected(ComponentName arg0) { - mBound = false; - } - }; } \ No newline at end of file diff --git a/app/src/main/java/com/flashwifi/wifip2p/SettingsFragment.java b/app/src/main/java/com/flashwifi/wifip2p/SettingsFragment.java new file mode 100644 index 0000000..89029de --- /dev/null +++ b/app/src/main/java/com/flashwifi/wifip2p/SettingsFragment.java @@ -0,0 +1,286 @@ +package com.flashwifi.wifip2p; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.preference.Preference; +import android.preference.PreferenceCategory; +import android.preference.PreferenceFragment; +import android.preference.PreferenceManager; +import android.preference.PreferenceScreen; +import android.text.InputType; +import android.widget.EditText; +import android.widget.Toast; + +import com.flashwifi.wifip2p.iotaAPI.Requests.WalletTestnetTokenGen; +import com.pddstudio.preferences.encrypted.EncryptedPreferences; + +public class SettingsFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener { + + private String password = ""; + private String seed = ""; + + private static final int TOKEN_TESTNET_RETRIEVE_TASK_COMPLETE = 0; + private static final int TOKEN_TESTNET_STATUS_UPDATE = 1; + + private Handler mHandler; + private Preference testnetFundAddPref; + private Preference prefTestNetPrivate; + private PreferenceCategory prefCatIotaSettings; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Bundle bundle = this.getArguments(); + if (bundle != null) { + seed = bundle.getString("seed"); + } + + // Load the preferences from an XML resource + addPreferencesFromResource(R.xml.preferences); + + // Set version text + Preference version = findPreference("pref_key_reset_version"); + String curTitle = version.getTitle().toString(); + version.setTitle("Version: "+curTitle); + + prefTestNetPrivate = findPreference("pref_key_switch_testnet_private"); + prefCatIotaSettings = (PreferenceCategory) findPreference("pref_key_IOTA_settings"); + + SharedPreferences prefManager = PreferenceManager.getDefaultSharedPreferences(getActivity()); + Boolean testnet = prefManager.getBoolean("pref_key_switch_testnet",false); + + if(!testnet){ + //Hide the testnet private switch + hideTestNetPref(); + } + + //Set change listener + getPreferenceScreen().getSharedPreferences() + .registerOnSharedPreferenceChangeListener(this); + + //Handle post-asynctask activities of updating UI + mHandler = new Handler(Looper.getMainLooper()) { + @Override + public void handleMessage(Message inputMessage) { + String result = (String) inputMessage.obj; + switch (inputMessage.what) { + case TOKEN_TESTNET_STATUS_UPDATE: + if(result.equals("Sending")){ + testnetFundAddPref.setSummary("Sending..."); + } + break; + case TOKEN_TESTNET_RETRIEVE_TASK_COMPLETE: + if(result.equals("Sent")){ + makeToastSettingsFragment("2000i generated and added to testnet wallet. Check balance."); + testnetFundAddPref.setSummary("2000i added"); + } + else{ + makeToastSettingsFragment(result); + testnetFundAddPref.setSummary(result); + } + break; + } + } + }; + } + + @Override + public void onResume() { + super.onResume(); + if(testnetFundAddPref != null){ + testnetFundAddPref.setSummary(""); + } + } + + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + switch(key){ + case "pref_key_security": + //makeToastSettingsFragment("Security Changed"); + break; + case "pref_key_network_timeout": + //makeToastSettingsFragment("Network Timeout Changed"); + break; + case "pref_key_units": + makeToastSettingsFragment("Units Changed"); + //makeToastSettingsFragment("Units Changed"); + break; + case "pref_key_switch_testnet": + makeToastSettingsFragment("Testnet on/off Changed"); + SharedPreferences prefManager = PreferenceManager.getDefaultSharedPreferences(getActivity()); + Boolean testnet = prefManager.getBoolean("pref_key_switch_testnet",false); + if(testnet){ + showTestNetPref(); + } + else{ + hideTestNetPref(); + } + break; + case "edit_text_sell_price": + makeToastSettingsFragment("Hotspot Sell Price Changed"); + break; + case "edit_text_sell_min_minutes": + makeToastSettingsFragment("Hotspot Min sell duration changed"); + break; + case "edit_text_sell_max_minutes": + makeToastSettingsFragment("Hotspot Max sell duration changed"); + break; + case "edit_text_buy_price": + makeToastSettingsFragment("Buy Price Changed"); + break; + case "edit_text_client_minutes": + makeToastSettingsFragment("Client duration duration changed"); + break; + } + } + + @Override + public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { + String key = preference.getKey(); + + switch (key) { + case "pref_key_reset_data_usage": + makeToastSettingsFragment( "Reset Data Usage"); + break; + case "pref_key_testnet_fund_add": + SharedPreferences prefManager = PreferenceManager.getDefaultSharedPreferences(getActivity()); + Boolean testnet = prefManager.getBoolean("pref_key_switch_testnet",false); + + if(testnet){ + testnetFundAddPref = preference; + testnetFundAddPref.setSummary("Generating..."); + makeToastSettingsFragment("Testnet wallet token generation request sent."); + WalletTestnetTokenGen tokenGen = new WalletTestnetTokenGen(mHandler, getActivity(), getString(R.string.preference_file_key), seed); + tokenGen.execute(); + } + else{ + makeToastSettingsFragment("Please enable testnet first."); + } + break; + case "pref_key_reset_password": + makeToastSettingsFragment( "Reset Password"); + askPassword(); + break; + case "pref_key_reset_wallet": + makeToastSettingsFragment("Reset Wallet."); + break; + } + return super.onPreferenceTreeClick(preferenceScreen, preference); + } + + private void askPassword() { + Context context = getActivity(); + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle("Enter current password"); + + final EditText input = new EditText(context); + input.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); + builder.setView(input); + + builder.setPositiveButton("OK", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + password = input.getText().toString(); + updatePassword(); + } + }); + builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.cancel(); + password = ""; + } + }); + builder.show(); + } + + public void updatePassword(){ + + if(password.equals("")){ + //Blank Password + makeToastSettingsFragment("Current password cannot be blank."); + return; + } + + Context context = getActivity(); + EncryptedPreferences encryptedPreferences = new EncryptedPreferences.Builder(context).withEncryptionPassword(password).build(); + String seed = encryptedPreferences.getString(getString(R.string.encrypted_seed), null); + + if (seed != null && context != null) { + //Correct password, re-store seed with new input password + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle("Enter new password"); + + final EditText input = new EditText(context); + input.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); + builder.setView(input); + + builder.setPositiveButton("OK", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + String newPassword = input.getText().toString(); + if(!newPassword.equals("")){ + EncryptedPreferences encryptedPreferencesRemove = new EncryptedPreferences.Builder(context).withEncryptionPassword(password).build(); + encryptedPreferencesRemove.edit().remove(getString(R.string.encrypted_seed)).apply(); + + EncryptedPreferences encryptedPreferencesUpdated = new EncryptedPreferences.Builder(context).withEncryptionPassword(newPassword).build(); + encryptedPreferencesUpdated.edit().putString(getString(R.string.encrypted_seed), seed).apply(); + password = ""; + makeToastSettingsFragment("Password changed."); + } + else{ + password = ""; + makeToastSettingsFragment("New password cannot be blank."); + } + } + }); + builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.cancel(); + password = ""; + } + }); + builder.show(); + } + else{ + //Wrong Password + makeToastSettingsFragment("Wrong Password. Please try again."); + } + } + + private void makeToastSettingsFragment(String s) { + if(getActivity() != null){ + Toast.makeText(getActivity(), s, Toast.LENGTH_SHORT).show(); + } + } + + private void hidePreference(String prefKey, String catKey){ + Preference pref = findPreference(prefKey); + PreferenceCategory catPref = (PreferenceCategory) findPreference(catKey); + catPref.removePreference(pref); + } + + private void showPreference(String prefKey, String catKey){ + Preference pref = findPreference(prefKey); + PreferenceCategory catPref = (PreferenceCategory) findPreference(catKey); + catPref.addPreference(pref); + } + + private void hideTestNetPref() { + prefCatIotaSettings.removePreference(prefTestNetPrivate); + } + + private void showTestNetPref() { + prefCatIotaSettings.addPreference(prefTestNetPrivate); + } + + +} diff --git a/app/src/main/java/com/flashwifi/wifip2p/WithdrawWalletFragment.java b/app/src/main/java/com/flashwifi/wifip2p/WithdrawWalletFragment.java index bd4a511..5a18fe3 100644 --- a/app/src/main/java/com/flashwifi/wifip2p/WithdrawWalletFragment.java +++ b/app/src/main/java/com/flashwifi/wifip2p/WithdrawWalletFragment.java @@ -20,7 +20,7 @@ import android.widget.ImageButton; import android.widget.TextView; import android.widget.Toast; -import com.flashwifi.wifip2p.iotaAPI.Requests.WalletAddressAndBalanceChecker; +import com.flashwifi.wifip2p.iotaAPI.Requests.WalletBalanceChecker; import com.flashwifi.wifip2p.iotaAPI.Requests.WalletTransferRequest; import jota.error.ArgumentException; @@ -47,7 +47,7 @@ public class WithdrawWalletFragment extends Fragment { private EditText editTextAmount; private EditText editTextMessage; private EditText editTextTag; - private WalletAddressAndBalanceChecker addressAndBalanceChecker; + private WalletBalanceChecker addressAndBalanceChecker; private GifImageView loadingGifImageView; private SwipeRefreshLayout mSwipeRefreshLayout; @@ -94,22 +94,22 @@ public class WithdrawWalletFragment extends Fragment { hideLoadingGIF(); transactionInProgress = false; - if(returnStatus == "noError"){ - balanceTextView.setText(appWalletBalance + " i"); + if(returnStatus.equals("noError")){ + balanceTextView.setText(appWalletBalance); makeToastFundWalletFragment("Balance updated"); } - else if (returnStatus == "noErrorNoUpdateMessage"){ - balanceTextView.setText(appWalletBalance + " i"); + else if (returnStatus.equals("noErrorNoUpdateMessage")){ + balanceTextView.setText(appWalletBalance); clearAllTransferValues(); makeFieldsEditable(); } - else if (returnStatus == "hostError"){ + else if (returnStatus.equals("hostError")){ makeToastFundWalletFragment("Unable to reach host (node)"); } - else if (returnStatus == "addressError"){ + else if (returnStatus.equals("addressError")){ makeToastFundWalletFragment("Error getting address"); } - else if (returnStatus == "balanceError"){ + else if (returnStatus.equals("balanceError")){ makeToastFundWalletFragment("Error getting balance"); } else{ @@ -198,7 +198,7 @@ public class WithdrawWalletFragment extends Fragment { private void getBalance(Boolean updateMessage) { transactionInProgress = true; - addressAndBalanceChecker = new WalletAddressAndBalanceChecker(getActivity(),getActivity().getString(R.string.preference_file_key),appWalletSeed, mHandler,WITHDRAW_WALLET,updateMessage); + addressAndBalanceChecker = new WalletBalanceChecker(getActivity(),getActivity().getString(R.string.preference_file_key),appWalletSeed, mHandler,WITHDRAW_WALLET,updateMessage); addressAndBalanceChecker.execute(); } diff --git a/app/src/main/java/com/flashwifi/wifip2p/accesspoint/AccessPointTask.java b/app/src/main/java/com/flashwifi/wifip2p/accesspoint/AccessPointTask.java index 5844a4e..c159e7c 100644 --- a/app/src/main/java/com/flashwifi/wifip2p/accesspoint/AccessPointTask.java +++ b/app/src/main/java/com/flashwifi/wifip2p/accesspoint/AccessPointTask.java @@ -86,17 +86,20 @@ public class AccessPointTask extends AsyncTask { } catch (IllegalArgumentException e) { e.printStackTrace(); + sendUpdateUIBroadcastWithMessage("AP FAILED"); } catch (IllegalAccessException e) { e.printStackTrace(); + sendUpdateUIBroadcastWithMessage("AP FAILED"); } catch (InvocationTargetException e) { e.printStackTrace(); + sendUpdateUIBroadcastWithMessage("AP FAILED"); } } } if (!methodFound){ - // ToDo: implement this + sendUpdateUIBroadcastWithMessage("AP FAILED"); //statusView.setText("Your phone's API does not contain setWifiApEnabled method to configure an access point"); } diff --git a/app/src/main/java/com/flashwifi/wifip2p/billing/Accountant.java b/app/src/main/java/com/flashwifi/wifip2p/billing/Accountant.java index 619f1ac..e518379 100644 --- a/app/src/main/java/com/flashwifi/wifip2p/billing/Accountant.java +++ b/app/src/main/java/com/flashwifi/wifip2p/billing/Accountant.java @@ -8,15 +8,21 @@ public class Accountant { private ArrayList bills; - private int totalMegabytes; + private int totalBytes; private int totalIotaPrice; private int totalDurance; private int bookedMegabytes; + private int bookedMinutes; + private int totalIotaDeposit; private int timeoutMinutes; + private int iotaPerMegaByte; + private boolean closeAfterwards; + private long startTime; private FlashChannelHelper flashChannelHelper; private boolean closed = true; + private String seed; public static Accountant getInstance() { return ourInstance; @@ -25,19 +31,49 @@ public class Accountant { private Accountant() { } - public void start(int bookedMegabytes, int timeoutMinutes){ - if (closed) { - bills = new ArrayList(); - this.bookedMegabytes = bookedMegabytes; - this.timeoutMinutes = timeoutMinutes; - totalMegabytes = 0; - totalIotaPrice = 0; - totalDurance = 0; - flashChannelHelper = new FlashChannelHelper(); - closed = false; + public void start(int bookedMegabytes, int timeoutMinutes, int bookedMinutes, int totalIotaDeposit, int iotaPerMegaByte){ + bills = new ArrayList(); + this.bookedMegabytes = bookedMegabytes; + this.timeoutMinutes = timeoutMinutes; + this.totalIotaDeposit = totalIotaDeposit; + this.iotaPerMegaByte = iotaPerMegaByte; + totalBytes = 0; + totalIotaPrice = 0; + totalDurance = 0; + flashChannelHelper = new FlashChannelHelper(); + closeAfterwards = false; + startTime = System.currentTimeMillis() / 1000L; + this.bookedMinutes = bookedMinutes; + closed = false; + } + + public long getLastTime() { + if (bills.isEmpty()) { + return startTime; + } else { + return bills.get(bills.size()-1).getTime(); } } + public void reset() { + this.bookedMegabytes = 0; + this.timeoutMinutes = 0; + this.totalIotaDeposit = 0; + this.iotaPerMegaByte = 0; + totalBytes = 0; + totalIotaPrice = 0; + totalDurance = 0; + bookedMinutes = 0; + } + + public boolean isCloseAfterwards() { + return closeAfterwards; + } + + public void setCloseAfterwards(boolean closeAfterwards) { + this.closeAfterwards = closeAfterwards; + } + public int getNextBillNumber() { return bills.size() + 1; } @@ -54,22 +90,25 @@ public class Accountant { // ToDo: check bill - totalMegabytes += b.getMegabytesUsed(); + totalBytes += b.getBytesUsed(); totalIotaPrice += b.getPriceInIota(); - totalDurance += b.getDuranceInMinutes(); + totalDurance += b.getDuranceInSeconds(); // ToDo: apply transfer to flash channel return true; } - public Bill createBill(int megaByte, int priceInIota, int duranceMinutes){ + public Bill createBill(int bytes){ if (!closed) { - Bill b = new Bill(getNextBillNumber(), totalDurance, duranceMinutes, megaByte, priceInIota); + long now = System.currentTimeMillis() / 1000L; + long duranceInSeconds = now - getLastTime(); + int priceInIota = (int) (bytes * getIotaPerByte()); + Bill b = new Bill(getNextBillNumber(), now, duranceInSeconds, bytes, priceInIota); - totalMegabytes += megaByte; + totalBytes += bytes; totalIotaPrice += priceInIota; - totalDurance += duranceMinutes; + totalDurance += duranceInSeconds; // 1) modify flash channel applyTransferToFlashChannel(priceInIota); @@ -100,7 +139,11 @@ public class Accountant { } public int getTotalMegabytes() { - return totalMegabytes; + return totalBytes / (1024*1024); + } + + public int getTotalBytes() { + return totalBytes / (1024*1024); } public int getTotalIotaPrice() { @@ -114,4 +157,36 @@ public class Accountant { public int getBookedMegabytes() { return bookedMegabytes; } + + public int getBookedBytes() { + return bookedMegabytes; + } + + public int getTimeoutMinutes() { + return timeoutMinutes; + } + + public int getBookedMinutes() { + return bookedMinutes; + } + + public int getTotalIotaDeposit() { + return totalIotaDeposit; + } + + public int getIotaPerMegaByte() { + return iotaPerMegaByte; + } + + public double getIotaPerByte() { + return ((double)getIotaPerMegaByte()) / (1024.0d * 1024.0d); + } + + public void setSeed(String seed) { + this.seed = seed; + } + + public String getSeed() { + return seed; + } } diff --git a/app/src/main/java/com/flashwifi/wifip2p/billing/Bill.java b/app/src/main/java/com/flashwifi/wifip2p/billing/Bill.java index b3bb0cb..0b8df8e 100644 --- a/app/src/main/java/com/flashwifi/wifip2p/billing/Bill.java +++ b/app/src/main/java/com/flashwifi/wifip2p/billing/Bill.java @@ -3,18 +3,18 @@ package com.flashwifi.wifip2p.billing; public class Bill { private int index; - private int minuteStart; - private int duranceInMinutes = 1; - private int megabytesUsed; + private int bytesUsed; private int priceInIota; + private long duranceInSeconds; + private long time; private boolean acceptedByPeer; - public Bill(int index, int minuteStart, int duranceInMinutes, int megabytesUsed, int priceInIota) { + public Bill(int index, long time, long duranceInSeconds, int bytesUsed, int priceInIota) { this.index = index; - this.minuteStart = minuteStart; - this.duranceInMinutes = duranceInMinutes; - this.megabytesUsed = megabytesUsed; + this.time = time; + this.duranceInSeconds = duranceInSeconds; + this.bytesUsed = bytesUsed; this.priceInIota = priceInIota; } @@ -30,19 +30,24 @@ public class Bill { return index; } - public int getMinuteStart() { - return minuteStart; - } public int getDuranceInMinutes() { - return duranceInMinutes; + return (int) duranceInSeconds/60; } - public int getMegabytesUsed() { - return megabytesUsed; + public int getDuranceInSeconds() { + return (int) duranceInSeconds; + } + + public int getBytesUsed() { + return bytesUsed; } public int getPriceInIota() { return priceInIota; } + + public long getTime() { + return time; + } } diff --git a/app/src/main/java/com/flashwifi/wifip2p/billing/BillingClient.java b/app/src/main/java/com/flashwifi/wifip2p/billing/BillingClient.java index 7555740..8431659 100644 --- a/app/src/main/java/com/flashwifi/wifip2p/billing/BillingClient.java +++ b/app/src/main/java/com/flashwifi/wifip2p/billing/BillingClient.java @@ -1,9 +1,12 @@ package com.flashwifi.wifip2p.billing; +import android.accounts.Account; import android.content.Context; import android.content.Intent; import android.util.Log; +import com.flashwifi.wifip2p.datastore.PeerInformation; +import com.flashwifi.wifip2p.datastore.PeerStore; import com.flashwifi.wifip2p.negotiation.SocketWrapper; import com.flashwifi.wifip2p.protocol.BillMessage; import com.flashwifi.wifip2p.protocol.BillMessageAnswer; @@ -11,6 +14,9 @@ import com.flashwifi.wifip2p.protocol.BillingCloseChannel; import com.flashwifi.wifip2p.protocol.BillingCloseChannelAnswer; import com.flashwifi.wifip2p.protocol.BillingOpenChannel; import com.flashwifi.wifip2p.protocol.BillingOpenChannelAnswer; +import com.flashwifi.wifip2p.protocol.NegotiationFinalization; +import com.flashwifi.wifip2p.protocol.NegotiationOffer; +import com.flashwifi.wifip2p.protocol.NegotiationOfferAnswer; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -30,11 +36,13 @@ import java.net.UnknownHostException; public class BillingClient { private static final String TAG = "BillingClient"; private final Gson gson; + private final String mac; private State state = State.NOT_PAIRED; private Socket socket; private SocketWrapper socketWrapper; private static final int PORT = 9199; private static final int clientTimeoutMillis = 2 * 60 * 1000; + private static final int maxErrorCount = 5; private BillingOpenChannel billingOpenChannel; private BillingOpenChannelAnswer billingOpenChannelAnswer; @@ -49,15 +57,18 @@ public class BillingClient { context.sendBroadcast(local); } - public BillingClient(Context context){ + public BillingClient(String mac, Context context){ this.context = context; + this.mac = mac; gson = new GsonBuilder().create(); } public void start(String serverIPAddress) { - while (state != State.CLOSED) { + int error_count = 0; + + while (state != State.CLOSED && state != State.ERROR) { try { // create client socket that connects to server socket = new Socket(serverIPAddress, PORT); @@ -74,17 +85,39 @@ public class BillingClient { String hotspotStateLine = socketWrapper.getLineThrowing(); if (hotspotStateLine.contains("INITIAL") || hotspotStateLine.contains("NOT_PAIRED")) { // ask the hotspot to open the flash channel + + // get the negotiated data + NegotiationOffer offer = PeerStore.getInstance().getLatestNegotiationOffer(mac); + NegotiationOfferAnswer answer = PeerStore.getInstance().getLatestNegotiationOfferAnswer(mac); + NegotiationFinalization finalization = PeerStore.getInstance().getLatestFinalization(mac); + // get the necessary values + // ToDo: replace magic number with setting + int totalMegabytes = 100; + int treeDepth = 8; String[] digests = new String[]{"1234", "2345", "3456"}; - billingOpenChannel = new BillingOpenChannel(100, 100, "clientAddress", 8, digests, 20 * 60 * 1000); + int timeoutMinutesClient = 20 * 60 * 1000; + + int iotaPerMegabyte = offer.getIotaPerMegabyte(); + String clientRefundAddress = finalization.getClientRefundAddress(); + int totalMinutes = answer.getDuranceInMinutes(); + + billingOpenChannel = new BillingOpenChannel(totalMegabytes, iotaPerMegabyte, clientRefundAddress, treeDepth, digests, timeoutMinutesClient, totalMinutes); + PeerStore.getInstance().setLatestBillingOpenChannel(mac, billingOpenChannel); String billingOpenChannelString = gson.toJson(billingOpenChannel); socketWrapper.sendLine(billingOpenChannelString); // receive the hotspot details for the flash channel String billingOpenChannelAnswerString = socketWrapper.getLineThrowing(); billingOpenChannelAnswer = gson.fromJson(billingOpenChannelAnswerString, BillingOpenChannelAnswer.class); + PeerStore.getInstance().setLatestBillingOpenChannelAnswer(mac, billingOpenChannelAnswer); // now create the flash channel on our side - Accountant.getInstance().start(billingOpenChannel.getTotalMegabytes(), billingOpenChannel.getTimeoutMinutesClient()); + Accountant.getInstance().start(billingOpenChannel.getTotalMegabytes(), billingOpenChannel.getTimeoutMinutesClient(), billingOpenChannel.getTotalMinutes(), billingOpenChannelAnswer.getClientDepositIota(), + billingOpenChannel.getIotaPerMegabyte()); sendUpdateUIBroadcastWithMessage("Channel established"); state = State.ROAMING; + + // start the task to fund the channel + sendUpdateUIBroadcastWithMessage("Start Channel funding"); + } else { // what to do if the hotspot already created stuff and was in roaming mode // ToDo: ^^^^^ @@ -106,7 +139,7 @@ public class BillingClient { // ToDo: flash object -> diff() // ToDo: sign flash transaction sendUpdateUIBroadcastWithMessage("Billing"); - latestBillAnswer = new BillMessageAnswer("id", true, "", false); + latestBillAnswer = new BillMessageAnswer("id", true, "", Accountant.getInstance().isCloseAfterwards()); latestBillAnswerString = gson.toJson(latestBillAnswer); socketWrapper.sendLine(latestBillAnswerString); @@ -128,16 +161,47 @@ public class BillingClient { String billingCloseChannelAnswerString = gson.toJson(billingCloseChannelAnswer); socketWrapper.sendLine(billingCloseChannelAnswerString); + sendUpdateUIBroadcastWithMessage("Channel closed"); + state = State.CLOSED; } } catch (SocketException e) { e.printStackTrace(); + sendUpdateUIBroadcastWithMessage("Socket exception"); + error_count++; } catch (UnknownHostException e) { + sendUpdateUIBroadcastWithMessage("UnknownHostException exception"); e.printStackTrace(); + error_count++; } catch (IOException e) { + sendUpdateUIBroadcastWithMessage("IOException"); e.printStackTrace(); + error_count++; + } finally { + if (socket != null) { + try { + socketWrapper.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + // sleep in finally case 5 seconds + try { + Thread.sleep(1000*5); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + if (error_count >= maxErrorCount) { + // stop trying to connect + Log.d(TAG, "start: error count too high"); + state = State.ERROR; + sendUpdateUIBroadcastWithMessage("Exit"); + } + } } } @@ -146,6 +210,7 @@ public class BillingClient { NOT_PAIRED, ROAMING, CLOSE, - CLOSED + CLOSED, + ERROR } } diff --git a/app/src/main/java/com/flashwifi/wifip2p/billing/BillingServer.java b/app/src/main/java/com/flashwifi/wifip2p/billing/BillingServer.java index 1b21a17..1e85d1d 100644 --- a/app/src/main/java/com/flashwifi/wifip2p/billing/BillingServer.java +++ b/app/src/main/java/com/flashwifi/wifip2p/billing/BillingServer.java @@ -1,9 +1,16 @@ package com.flashwifi.wifip2p.billing; +import android.app.usage.NetworkStats; +import android.app.usage.NetworkStatsManager; import android.content.Context; import android.content.Intent; +import android.net.ConnectivityManager; +import android.net.wifi.WifiManager; +import android.os.RemoteException; +import android.text.format.Formatter; import android.util.Log; +import com.flashwifi.wifip2p.datastore.PeerStore; import com.flashwifi.wifip2p.negotiation.SocketWrapper; import com.flashwifi.wifip2p.protocol.BillMessage; import com.flashwifi.wifip2p.protocol.BillMessageAnswer; @@ -11,15 +18,21 @@ import com.flashwifi.wifip2p.protocol.BillingCloseChannel; import com.flashwifi.wifip2p.protocol.BillingCloseChannelAnswer; import com.flashwifi.wifip2p.protocol.BillingOpenChannel; import com.flashwifi.wifip2p.protocol.BillingOpenChannelAnswer; +import com.flashwifi.wifip2p.protocol.NegotiationFinalization; +import com.flashwifi.wifip2p.protocol.NegotiationOffer; +import com.flashwifi.wifip2p.protocol.NegotiationOfferAnswer; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import java.io.IOException; +import java.math.BigDecimal; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; import java.net.UnknownHostException; +import static android.content.Context.WIFI_SERVICE; + /** * 1) This class keeps the socket connection alive. * 2) It tracks the state of the communication. @@ -32,6 +45,10 @@ import java.net.UnknownHostException; public class BillingServer { private static final String TAG = "BillingServer"; private final Gson gson; + private final String mac; + private final String hotspotRefundAddress; + private final String channelRootAddress; + private final String[] digests; private State state = State.NOT_PAIRED; private ServerSocket serverSocket; private Socket socket; @@ -40,6 +57,7 @@ public class BillingServer { private static final int serverTimeoutMillis = 2 * 60 * 1000; Context context; + NetworkStatsManager networkStatsManager; private void sendUpdateUIBroadcastWithMessage(String message){ Intent local = new Intent(); @@ -48,10 +66,33 @@ public class BillingServer { context.sendBroadcast(local); } - public BillingServer(int bookedMegabytes, int timeoutMinutes, Context context){ + public BillingServer(String mac, Context context){ this.context = context; - Accountant.getInstance().start(bookedMegabytes,timeoutMinutes); + this.mac = mac; + + // get the negotiated data + NegotiationOffer offer = PeerStore.getInstance().getLatestNegotiationOffer(mac); + NegotiationOfferAnswer answer = PeerStore.getInstance().getLatestNegotiationOfferAnswer(mac); + NegotiationFinalization finalization = PeerStore.getInstance().getLatestFinalization(mac); + // get the necessary values + // ToDo: replace magic number with setting + int totalMegabytes = 100; + int treeDepth = 8; + this.digests = new String[]{"1234", "2345", "3456"}; + int timeoutMinutesServer = 20 * 60 * 1000; + + this.hotspotRefundAddress = finalization.getHotspotRefundAddress(); + this.channelRootAddress = finalization.getDepositAddressFlashChannel(); + + + int iotaPerMegabyte = offer.getIotaPerMegabyte(); + int iotaDepositClient = totalMegabytes * iotaPerMegabyte; + String clientRefundAddress = finalization.getClientRefundAddress(); + int totalMinutes = answer.getDuranceInMinutes(); + + Accountant.getInstance().start(totalMegabytes, timeoutMinutesServer, totalMinutes, iotaDepositClient, iotaPerMegabyte); gson = new GsonBuilder().create(); + networkStatsManager = (NetworkStatsManager) context.getSystemService(Context.NETWORK_STATS_SERVICE); } public void start() throws IOException { @@ -59,6 +100,10 @@ public class BillingServer { createDeadlineGuard(); // 1) create a socket + Log.d(TAG, "start: Billing server has been started"); + + // ToDo: receive end of roaming broadcast + while (state != State.CLOSED) { try { // create server socket @@ -86,11 +131,20 @@ public class BillingServer { // receive the BillingOpenChannel message String billingOpenChannelString = socketWrapper.getLineThrowing(); BillingOpenChannel billingOpenChannel = gson.fromJson(billingOpenChannelString, BillingOpenChannel.class); - + PeerStore.getInstance().setLatestBillingOpenChannel(mac, billingOpenChannel); // answer with billingOpenChannelAnswerString + + // ToDo: create the flash channel - String[] myDigests = new String[]{"1234", "2345", "3456"}; - BillingOpenChannelAnswer billingOpenChannelAnswer = new BillingOpenChannelAnswer(0, 0, "", "", myDigests); + + + BillingOpenChannelAnswer billingOpenChannelAnswer = new BillingOpenChannelAnswer( + Accountant.getInstance().getTotalIotaDeposit(), + Accountant.getInstance().getTotalIotaDeposit(), + hotspotRefundAddress, + channelRootAddress, + digests); + PeerStore.getInstance().setLatestBillingOpenChannelAnswer(mac, billingOpenChannelAnswer); String billingOpenChannelAnswerString = gson.toJson(billingOpenChannelAnswer); socketWrapper.sendLine(billingOpenChannelAnswerString); @@ -98,6 +152,9 @@ public class BillingServer { // OK state = State.ROAMING; + + // start funding + sendUpdateUIBroadcastWithMessage("Start Channel funding"); } if (state == State.ROAMING) { @@ -117,10 +174,23 @@ public class BillingServer { Log.d(TAG, "start: Good morning!"); // create new bill // ToDo: integrate real network data - // ToDo: calculate time correctly - b = Accountant.getInstance().createBill(0,0,1); + NetworkStats.Bucket bucket; + long total_bytes; + try { + bucket = networkStatsManager.querySummaryForDevice(ConnectivityManager.TYPE_WIFI, + "", + Accountant.getInstance().getLastTime() * 1000, + System.currentTimeMillis()); + long bytes_received = bucket.getRxBytes(); + long bytes_transmitted = bucket.getTxBytes(); + total_bytes = bytes_received + bytes_transmitted; + } catch (RemoteException e) { + Log.d(TAG, "start: Can't get the network traffic."); + total_bytes = 0; + } + b = Accountant.getInstance().createBill((int)total_bytes); // ToDo: integrate real flash channel - latestBill = new BillMessage(b, "", false); + latestBill = new BillMessage(b, "", Accountant.getInstance().isCloseAfterwards()); latestBillString = gson.toJson(latestBill); socketWrapper.sendLine(latestBillString); @@ -136,11 +206,14 @@ public class BillingServer { if (latestBill.isCloseAfterwards() || latestBillAnswer.isCloseAfterwards()) { state = State.CLOSE; } + + sendUpdateUIBroadcastWithMessage("Billing"); } } if (state == State.CLOSE) { + Log.d(TAG, "start: state is CLOSE now"); // ToDo: handle the final deposit of the flash channel // ToDo: sign the transaction BillingCloseChannel billingCloseChannel = new BillingCloseChannel(0,0,0,0,"", "", ""); @@ -150,6 +223,9 @@ public class BillingServer { String billingCloseChannelAnswerString = socketWrapper.getLineThrowing(); BillingCloseChannelAnswer billingCloseChannelAnswer = gson.fromJson(billingCloseChannelAnswerString, BillingCloseChannelAnswer.class); // ToDo: validate the signature + + // change the ui + sendUpdateUIBroadcastWithMessage("Channel closed"); state = State.CLOSED; } @@ -158,18 +234,35 @@ public class BillingServer { boolean attached = false; while (!attached) { Log.d(TAG, "start: Attach to tangle please"); + Thread.sleep(5000); } state = State.FULLY_ATTACHED; } } catch (IOException e) { - + e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); + } finally { + try { + if (socketWrapper != null) { + socketWrapper.close(); + } + } catch (Exception e) { + + } + try { + if (serverSocket != null) { + serverSocket.close(); + } + } catch (Exception e) { + + } } } } + private void createDeadlineGuard() { // this method measures the time and stops the connection if // nothing happened after diff --git a/app/src/main/java/com/flashwifi/wifip2p/broadcast/WiFiDirectBroadcastService.java b/app/src/main/java/com/flashwifi/wifip2p/broadcast/WiFiDirectBroadcastService.java index abdf621..a75792e 100644 --- a/app/src/main/java/com/flashwifi/wifip2p/broadcast/WiFiDirectBroadcastService.java +++ b/app/src/main/java/com/flashwifi/wifip2p/broadcast/WiFiDirectBroadcastService.java @@ -1,9 +1,16 @@ package com.flashwifi.wifip2p.broadcast; +import android.app.Activity; +import android.app.Notification; +import android.app.PendingIntent; import android.app.Service; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.SharedPreferences; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.DhcpInfo; import android.net.NetworkInfo; import android.net.wifi.WifiManager; import android.net.wifi.p2p.WifiP2pConfig; @@ -13,26 +20,52 @@ import android.net.wifi.p2p.WifiP2pInfo; import android.net.wifi.p2p.WifiP2pManager; import android.os.AsyncTask; import android.os.Binder; +import android.os.Handler; import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.preference.PreferenceManager; +import android.support.v4.app.NotificationCompat; +import android.text.format.Formatter; import android.util.Log; +import com.flashwifi.wifip2p.AddressBalanceTransfer; +import com.flashwifi.wifip2p.Constants; +import com.flashwifi.wifip2p.MainActivity; +import com.flashwifi.wifip2p.R; import com.flashwifi.wifip2p.accesspoint.AccessPointTask; import com.flashwifi.wifip2p.accesspoint.ConnectTask; import com.flashwifi.wifip2p.accesspoint.StopAccessPointTask; +import com.flashwifi.wifip2p.billing.Accountant; import com.flashwifi.wifip2p.billing.BillingClient; import com.flashwifi.wifip2p.billing.BillingServer; import com.flashwifi.wifip2p.datastore.PeerStore; +import com.flashwifi.wifip2p.iotaAPI.Requests.WalletTransferRequest; import com.flashwifi.wifip2p.negotiation.Negotiator; +import com.flashwifi.wifip2p.protocol.BillingOpenChannel; +import com.flashwifi.wifip2p.protocol.BillingOpenChannelAnswer; import com.flashwifi.wifip2p.protocol.NegotiationFinalization; +import com.flashwifi.wifip2p.protocol.NegotiationOffer; +import com.flashwifi.wifip2p.protocol.NegotiationOfferAnswer; + +import org.apache.commons.lang3.ArrayUtils; import java.io.IOException; import java.lang.reflect.Method; +import java.math.BigInteger; import java.net.InetAddress; import java.net.NetworkInterface; +import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import jota.IotaAPI; +import jota.dto.response.GetBalancesResponse; +import jota.dto.response.SendTransferResponse; +import jota.error.ArgumentException; +import jota.model.Transfer; + public class WiFiDirectBroadcastService extends Service { public final static String TAG = "WiFiService"; @@ -75,6 +108,62 @@ public class WiFiDirectBroadcastService extends Service { AccessPointTask apTask; boolean apRuns = false; ConnectTask connectTask; + private boolean negotiatorRunning = false; + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + if (intent.getAction().equals(Constants.ACTION.STARTFOREGROUND_ACTION)) { + Log.i(TAG, "Received Start Foreground Intent "); + Intent notificationIntent = new Intent(this, MainActivity.class); + notificationIntent.setAction(Constants.ACTION.MAIN_ACTION); + notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_CLEAR_TASK); + PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, + notificationIntent, 0); +/* + Intent previousIntent = new Intent(this, WiFiDirectBroadcastService.class); + previousIntent.setAction(Constants.ACTION.PREV_ACTION); + PendingIntent ppreviousIntent = PendingIntent.getService(this, 0, + previousIntent, 0); + + Intent playIntent = new Intent(this, WiFiDirectBroadcastService.class); + playIntent.setAction(Constants.ACTION.PLAY_ACTION); + PendingIntent pplayIntent = PendingIntent.getService(this, 0, + playIntent, 0);*/ + + Intent stopIntent = new Intent(this, WiFiDirectBroadcastService.class); + stopIntent.setAction(Constants.ACTION.STOPFOREGROUND_ACTION); + PendingIntent pstopIntent = PendingIntent.getService(this, 0, + stopIntent, 0); + + Bitmap icon = BitmapFactory.decodeResource(getResources(), + R.drawable.icon_tethering_on); + + Notification notification = new NotificationCompat.Builder(this) + .setContentTitle(getString(R.string.app_name)) + .setTicker(getString(R.string.app_name)) + .setContentText(getString(R.string.notification_doing_nothing)) + .setSmallIcon(R.drawable.icon_tethering_on) + .setLargeIcon( + Bitmap.createScaledBitmap(icon, 128, 128, false)) + .setContentIntent(pendingIntent) + .setOngoing(true) + .addAction(R.drawable.icon_tethering_off, "Stop", + pstopIntent).build(); + startForeground(Constants.NOTIFICATION_ID.FOREGROUND_SERVICE, + notification); + } else if (intent.getAction().equals( + Constants.ACTION.STOPFOREGROUND_ACTION)) { + Log.i(TAG, "Received Stop Foreground Intent"); + stopForeground(true); + stopSelf(); + } + return START_STICKY; + } + + public boolean isSetup() { + return setup; + } public void enableWiFi() { WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(WIFI_SERVICE); @@ -94,15 +183,21 @@ public class WiFiDirectBroadcastService extends Service { } } - public void startBillingServer(){ + public void resetBillingState() { + billingClientIsRunning = false; + billingServerIsRunning = false; + } + + public void startBillingServer(String mac){ if (!billingServerIsRunning) { billingServerIsRunning = true; Runnable task = new Runnable() { @Override public void run() { + Log.d(TAG, "run: instantiate billing server"); // ToDo: remove magic numbers - BillingServer billingServer = new BillingServer(100, 20, getApplicationContext()); + BillingServer billingServer = new BillingServer(mac, getApplicationContext()); try { billingServer.start(); @@ -115,13 +210,17 @@ public class WiFiDirectBroadcastService extends Service { }; Log.d(TAG, "startBillingServer"); Thread thread = new Thread(task); + //asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params); + //AsyncTask.execute(thread); threads.add(thread); - AsyncTask.execute(thread); + thread.start(); + } else { + Log.d(TAG, "startBillingServer: blocked"); } } - public void startBillingClient() { + public void startBillingClient(String mac) { if (!billingClientIsRunning) { billingClientIsRunning = true; @@ -130,18 +229,34 @@ public class WiFiDirectBroadcastService extends Service { Runnable task = new Runnable() { @Override public void run() { - // ToDo: remove magic numbers - BillingClient billingClient = new BillingClient(getApplicationContext()); - // ToDo: get the AP gateway ip address + BillingClient billingClient = new BillingClient(mac, getApplicationContext()); + // Gget the AP gateway ip address // https://stackoverflow.com/questions/9035784/how-to-know-ip-address-of-the-router-from-code-in-android - billingClient.start("192.168.43.1"); + final WifiManager manager = (WifiManager) getApplicationContext().getSystemService(WIFI_SERVICE); + final DhcpInfo dhcp = manager.getDhcpInfo(); + byte[] myIPAddress = BigInteger.valueOf(dhcp.gateway).toByteArray(); + ArrayUtils.reverse(myIPAddress); + InetAddress myInetIP = null; + String routerIP = null; + try { + myInetIP = InetAddress.getByAddress(myIPAddress); + routerIP = myInetIP.getHostAddress(); + } catch (UnknownHostException e) { + e.printStackTrace(); + routerIP = "192.168.43.1"; + } + Log.d(TAG, "DHCP gateway: " + routerIP); + billingClient.start(routerIP); // ToDo: handle billingServer EXIT CODES // -> close the roaming etc. } }; Thread thread = new Thread(task); threads.add(thread); - AsyncTask.execute(thread); + thread.start(); + //AsyncTask.execute(thread); + } else { + Log.d(TAG, "startBillingClient: blocked"); } } @@ -214,27 +329,45 @@ public class WiFiDirectBroadcastService extends Service { } } + public void startNegotiationServer(final boolean isClient, String macAddress) { Runnable task = new Runnable() { @Override public void run() { - Negotiator negotiator = new Negotiator(isClient, getWFDMacAddress()); + Negotiator negotiator = new Negotiator(isClient, + getWFDMacAddress(), + PreferenceManager.getDefaultSharedPreferences(getApplicationContext()), + getBaseContext() + ); + String peer_mac_address = null; - while (!isRoaming && enabled && peer_mac_address == null) { + boolean restartAfterwards = true; + int error_count = 0; + int max_error_count = 10; + Negotiator.NegotiationReturn ret = null; + while (!isRoaming && enabled && peer_mac_address == null && restartAfterwards && (error_count < max_error_count)) { + Log.d(TAG, String.format("%d/%d errors", error_count, max_error_count)); Log.d(TAG, "run: " + enabled); - peer_mac_address = negotiator.workAsServer(); - // ToDo: use other broadcast for this - // sendUpdateUIBroadcast(); + ret = negotiator.workAsServer(); + peer_mac_address = ret.mac; + restartAfterwards = ret.restartAfterwards; + if (ret.code != 0) { + sendUpdateUIBroadcastWithMessage(getString(ret.code)); + if (restartAfterwards) { + error_count++; + } + } try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); + error_count++; } if (peer_mac_address == null) { deletePersistentGroups(); } } - if (peer_mac_address != null) { + if (peer_mac_address != null && ret != null && ret.code == 0) { // block the discovery mode due to switch to roaming state setRoaming(true); // tell the UI @@ -242,15 +375,22 @@ public class WiFiDirectBroadcastService extends Service { String ssid = negFin.getHotspotName(); String key = negFin.getHotspotPassword(); sendStartRoamingBroadcast(peer_mac_address, ssid, key); + } else if (peer_mac_address != null) { + PeerStore.getInstance().unselectAll(); } + negotiatorRunning = false; } }; - if (!isRoaming()) { + if (!isRoaming() && !negotiatorRunning) { + negotiatorRunning = true; Thread thread = new Thread(task); threads.add(thread); - AsyncTask.execute(thread); + //AsyncTask.execute(thread); + stopAllTasks(); + thread.start(); + //stopDiscovery(null); } else { Log.d(TAG, "startNegServer: BLOCKED due to roaming state"); } @@ -269,12 +409,24 @@ public class WiFiDirectBroadcastService extends Service { Runnable task = new Runnable() { @Override public void run() { - Negotiator negotiator = new Negotiator(isClient, getWFDMacAddress()); + Negotiator negotiator = new Negotiator( + isClient, + getWFDMacAddress(), + PreferenceManager.getDefaultSharedPreferences(getApplicationContext()), + getBaseContext() + ); String peer_mac_address = null; - while (!isRoaming && enabled && peer_mac_address == null) { + boolean restartAfterwards = true; + Negotiator.NegotiationReturn negotiationReturn = null; + while (!isRoaming && enabled && peer_mac_address == null && restartAfterwards && negotiatorRunning) { Log.d(TAG, "run: " + enabled); System.out.println(" *******+ work as client *******"); - peer_mac_address = negotiator.workAsClient(address.getHostAddress()); + negotiationReturn = negotiator.workAsClient(address.getHostAddress()); + peer_mac_address = negotiationReturn.mac; + restartAfterwards = negotiationReturn.restartAfterwards; + if (negotiationReturn.code != 0) { + sendUpdateUIBroadcastWithMessage(getString(negotiationReturn.code)); + } try { Thread.sleep(2000); } catch (InterruptedException e) { @@ -284,7 +436,7 @@ public class WiFiDirectBroadcastService extends Service { deletePersistentGroups(); } } - if (peer_mac_address != null) { + if (peer_mac_address != null && negotiationReturn != null && negotiationReturn.code == 0) { // block the discovery mode due to switch to roaming state setRoaming(true); // tell the UI @@ -292,15 +444,21 @@ public class WiFiDirectBroadcastService extends Service { String ssid = negFin.getHotspotName(); String key = negFin.getHotspotPassword(); sendStartRoamingBroadcast(peer_mac_address, ssid, key); + } else { + Log.d(TAG, "run: could not start roaming"); } + PeerStore.getInstance().unselectAll(); + negotiatorRunning = false; } }; - if (!isRoaming()) { + if (!isRoaming() && !negotiatorRunning) { + negotiatorRunning = true; Thread thread = new Thread(task); threads.add(thread); - AsyncTask.execute(thread); + stopAllTasks(); + thread.start(); } else { - Log.d(TAG, "startNegotiationClient: BLOCKED due to roaming state"); + Log.d(TAG, "startNegotiationClient: BLOCKED due to roaming state or negotiator running"); } @@ -385,8 +543,10 @@ public class WiFiDirectBroadcastService extends Service { } public void startDiscoveryMode(WifiP2pManager.ActionListener action_listener) { - mManager.discoverPeers(mChannel, action_listener); - discoveryModeActive = true; + if (setup) { + mManager.discoverPeers(mChannel, action_listener); + discoveryModeActive = true; + } } public boolean isInRoleHotspot() { @@ -410,12 +570,181 @@ public class WiFiDirectBroadcastService extends Service { } public void setRoaming(boolean roaming) { - if (roaming) { + if (!roaming) { stopAllTasks(); } isRoaming = roaming; } + public void freezeWiFiP2P() { + + } + + public void fundChannel(String address) { + // get the necessary data + NegotiationOffer offer = PeerStore.getInstance().getLatestNegotiationOffer(address); + NegotiationOfferAnswer offerAnser = PeerStore.getInstance().getLatestNegotiationOfferAnswer(address); + NegotiationFinalization finalization = PeerStore.getInstance().getLatestFinalization(address); + BillingOpenChannel openChannel = PeerStore.getInstance().getPeer(address).getBillingOpenChannel(); + BillingOpenChannelAnswer openChannelAnswer = PeerStore.getInstance().getPeer(address).getBillingOpenChannelAnswer(); + + String multisigAddress = finalization.getDepositAddressFlashChannel(); + + int timeoutClientSeconds = openChannel.getTimeoutMinutesClient(); + int timeoutHotspotSeconds = openChannelAnswer.getTimeoutMinutesHotspot() * 60; + int timeout = (isInRoleHotspot()) ? timeoutHotspotSeconds : timeoutClientSeconds; + + int clientDeposit = finalization.getDepositClientFlashChannelInIota(); + int hotspotDeposit = finalization.getDepositServerFlashChannelInIota(); + int deposit = (isInRoleHotspot()) ? hotspotDeposit : clientDeposit; + + int depositTogether = clientDeposit + hotspotDeposit; + + String seed = Accountant.getInstance().getSeed(); + + Log.d(TAG, "fundChannel: Let's fund " + multisigAddress); + + + Runnable task = () -> { + Log.d(TAG, "run: fund multisig address"); + + boolean fundDone = false; + boolean fundIsThere = false; + + int fundErrorCount = 0; + int maxFundErrors = 10; + + SharedPreferences prefManager = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); + boolean testnet = prefManager.getBoolean("pref_key_switch_testnet",false); + + IotaAPI api; + + if(testnet){ + //Testnet node: + api = new IotaAPI.Builder() + .protocol("https") + .host("testnet140.tangle.works") + .port("443") + .build(); + } + else{ + //Mainnet node: + api = new IotaAPI.Builder() + .protocol("http") + .host("node.iotawallet.info") + .port("14265") + .build(); + } + + long start = System.currentTimeMillis() / 1000L; + + while (((System.currentTimeMillis()/1000L) - start < timeout || !fundDone || !fundIsThere) && fundErrorCount < maxFundErrors) { + SendTransferResponse sendTransferResponse = null; + if (!fundDone) { + Log.d(TAG, "fundChannel: DEPOSIT::" + Integer.toString(deposit)); + try { + List transfers = new ArrayList<>(); + transfers.add(new Transfer(multisigAddress, deposit, "", "")); + + if(testnet) { + Log.d(TAG, "fundChannel: fund on testnet"); + sendTransferResponse = api.sendTransfer(seed, 2, 4, 9, transfers, null, null, false); + } + else{ + Log.d(TAG, "fundChannel: fund on mainnet"); + sendTransferResponse = api.sendTransfer(seed, 2, 4, 18, transfers, null, null, false); + } + } catch (ArgumentException | IllegalAccessError | IllegalStateException e) { + String transferResult = ""; + if (e instanceof ArgumentException) { + if (e.getMessage().contains("Sending to a used address.") || e.getMessage().contains("Private key reuse detect!") || e.getMessage().contains("Send to inputs!")) { + transferResult = "Sending to a used address/Private key reuse detect. Error Occurred."; + } + else if(e.getMessage().contains("Failed to connect to node")){ + transferResult = "Failed to connect to node"; + } + else{ + transferResult = "Network Error: Attaching new address failed or Transaction failed"; + } + } + if (e instanceof IllegalAccessError) { + transferResult = "Local POW needs to be enabled"; + } + fundErrorCount++; + Log.d(TAG, "fundChannel: " + transferResult); + } + + if(sendTransferResponse != null){ + Boolean[] transferSuccess = sendTransferResponse.getSuccessfully(); + + Boolean success = true; + for (Boolean status : transferSuccess) { + if(status.equals(false)){ + success = false; + fundErrorCount++; + break; + } + } + if(success){ + Log.d(TAG, "fundChannel: successfully transferred my part"); + fundDone = true; + } + } + + try { + Thread.sleep(3000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + } else if (fundDone && !fundIsThere) { + // now check the balance until we get it + // check the funding + Log.d(TAG, "fundChannel: checking the balance of the root..."); + List addresses = new ArrayList(); + addresses.add(multisigAddress); + GetBalancesResponse response = null; + try { + response = api.getBalances(100, addresses); + } catch (ArgumentException e) { + e.printStackTrace(); + } + if (response != null) { + if (response.getBalances().length > 0) { + long balance = Integer.parseInt(response.getBalances()[0]); + Log.d(TAG, "fundChannel: Found balance " + response.getBalances()[0]); + if (balance > depositTogether) { + Log.d(TAG, "fundChannel: balance is enough on both sides"); + fundIsThere = true; + } + } + } + try { + Thread.sleep(3000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + } + + } + if (fundErrorCount < maxFundErrors) { + Log.d(TAG, "fundChannel: channel funded"); + sendUpdateRoamingBroadcastWithMessage("Channel funded"); + } else { + Log.d(TAG, "fundChannel: too many fund errors"); + } + + + }; + + Log.d(TAG, "startFunding"); + Thread thread = new Thread(task); + threads.add(thread); + thread.start(); + + } + /** * Class used for the client Binder. Because we know this service always @@ -454,7 +783,9 @@ public class WiFiDirectBroadcastService extends Service { } public void getPeerList(WifiP2pManager.ActionListener action_listener) { - mManager.discoverPeers(mChannel, action_listener); + if (setup) { + mManager.discoverPeers(mChannel, action_listener); + } } public void stopDiscovery(WifiP2pManager.ActionListener action_listener) { @@ -507,6 +838,13 @@ public class WiFiDirectBroadcastService extends Service { this.sendBroadcast(local); } + private void sendUpdateRoamingBroadcastWithMessage(String message){ + Intent local = new Intent(); + local.putExtra("message", message); + local.setAction("com.flashwifi.wifip2p.update_roaming"); + this.sendBroadcast(local); + } + public void connect(String address, WifiP2pManager.ActionListener actionListener) { WifiP2pDevice device; WifiP2pConfig config = new WifiP2pConfig(); diff --git a/app/src/main/java/com/flashwifi/wifip2p/datastore/PeerInformation.java b/app/src/main/java/com/flashwifi/wifip2p/datastore/PeerInformation.java index e25caf5..3d3f3f4 100644 --- a/app/src/main/java/com/flashwifi/wifip2p/datastore/PeerInformation.java +++ b/app/src/main/java/com/flashwifi/wifip2p/datastore/PeerInformation.java @@ -4,6 +4,10 @@ import android.net.wifi.p2p.WifiP2pDevice; import android.net.wifi.p2p.WifiP2pInfo; import android.support.annotation.NonNull; +import com.flashwifi.wifip2p.protocol.BillingCloseChannel; +import com.flashwifi.wifip2p.protocol.BillingCloseChannelAnswer; +import com.flashwifi.wifip2p.protocol.BillingOpenChannel; +import com.flashwifi.wifip2p.protocol.BillingOpenChannelAnswer; import com.flashwifi.wifip2p.protocol.NegotiationFinalization; import com.flashwifi.wifip2p.protocol.NegotiationOffer; import com.flashwifi.wifip2p.protocol.NegotiationOfferAnswer; @@ -17,6 +21,7 @@ public class PeerInformation { private WifiP2pDevice wifiP2pDevice; private Date lastUpdate; private boolean selected; + private String errorMessage = "info"; // age stores how long it has been since the last signal from this peer // it is not stored in seconds but in update cycles @@ -26,6 +31,11 @@ public class PeerInformation { private NegotiationOfferAnswer latestOfferAnswer; private NegotiationFinalization latestFinalization; + private BillingOpenChannel billingOpenChannel; + private BillingOpenChannelAnswer billingOpenChannelAnswer; + private BillingCloseChannel billingCloseChannel; + private BillingCloseChannelAnswer billingCloseChannelAnswer; + public PeerInformation() { selected = false; age = 0; @@ -55,6 +65,8 @@ public class PeerInformation { return latestNegotiationOffer; } + + public void setLatestOfferAnswer(NegotiationOfferAnswer latestOfferAnswer) { this.latestOfferAnswer = latestOfferAnswer; } @@ -86,4 +98,44 @@ public class PeerInformation { public void setSelected(boolean selected) { this.selected = selected; } + + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + } + + public String getErrorMessage() { + return errorMessage; + } + + public BillingOpenChannel getBillingOpenChannel() { + return billingOpenChannel; + } + + public void setBillingOpenChannel(BillingOpenChannel billingOpenChannel) { + this.billingOpenChannel = billingOpenChannel; + } + + public BillingOpenChannelAnswer getBillingOpenChannelAnswer() { + return billingOpenChannelAnswer; + } + + public void setBillingOpenChannelAnswer(BillingOpenChannelAnswer billingOpenChannelAnswer) { + this.billingOpenChannelAnswer = billingOpenChannelAnswer; + } + + public BillingCloseChannel getBillingCloseChannel() { + return billingCloseChannel; + } + + public void setBillingCloseChannel(BillingCloseChannel billingCloseChannel) { + this.billingCloseChannel = billingCloseChannel; + } + + public BillingCloseChannelAnswer getBillingCloseChannelAnswer() { + return billingCloseChannelAnswer; + } + + public void setBillingCloseChannelAnswer(BillingCloseChannelAnswer billingCloseChannelAnswer) { + this.billingCloseChannelAnswer = billingCloseChannelAnswer; + } } diff --git a/app/src/main/java/com/flashwifi/wifip2p/datastore/PeerListAdapter.java b/app/src/main/java/com/flashwifi/wifip2p/datastore/PeerListAdapter.java index 69e2364..9d998f7 100644 --- a/app/src/main/java/com/flashwifi/wifip2p/datastore/PeerListAdapter.java +++ b/app/src/main/java/com/flashwifi/wifip2p/datastore/PeerListAdapter.java @@ -6,6 +6,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; +import android.widget.ProgressBar; import android.widget.TableRow; import android.widget.TextView; @@ -41,8 +42,7 @@ public class PeerListAdapter extends ArrayAdapter { TextView tt1 = (TextView) v.findViewById(R.id.deviceName); TextView tt2 = (TextView) v.findViewById(R.id.categoryId); TextView tt3 = (TextView) v.findViewById(R.id.description); - TextView tt4 = (TextView) v.findViewById(R.id.ipAddr); - TextView tt5 = (TextView) v.findViewById(R.id.iotaPrice); + TextView tt4 = (TextView) v.findViewById(R.id.iotaPrice); WifiP2pDevice device = p.getWifiP2pDevice(); @@ -58,22 +58,22 @@ public class PeerListAdapter extends ArrayAdapter { NegotiationOffer offer = p.getLatestNegotiationOffer(); if (offer != null) { - tt5.setText(Integer.toString(offer.getIotaPerMegabyte())); + tt4.setText(Integer.toString(offer.getIotaPerMegabyte()) + "i"); } - if (p.getIpAddress() != null) { - tt4.setText(p.getIpAddress()); + if (!p.isSelected()) { + ProgressBar progress_bar = (ProgressBar) v.findViewById(R.id.progressConnection); + progress_bar.setVisibility(View.GONE); } - - if (tt3 != null) - tt3.setText(String.format("%s", Integer.toString(p.getAge()))); + if (tt3 != null) { + tt3.setText(p.getErrorMessage()); } - - + } return v; } + } \ No newline at end of file diff --git a/app/src/main/java/com/flashwifi/wifip2p/datastore/PeerStore.java b/app/src/main/java/com/flashwifi/wifip2p/datastore/PeerStore.java index 84b05bc..77db7b4 100644 --- a/app/src/main/java/com/flashwifi/wifip2p/datastore/PeerStore.java +++ b/app/src/main/java/com/flashwifi/wifip2p/datastore/PeerStore.java @@ -1,5 +1,9 @@ package com.flashwifi.wifip2p.datastore; +import com.flashwifi.wifip2p.protocol.BillingCloseChannel; +import com.flashwifi.wifip2p.protocol.BillingCloseChannelAnswer; +import com.flashwifi.wifip2p.protocol.BillingOpenChannel; +import com.flashwifi.wifip2p.protocol.BillingOpenChannelAnswer; import com.flashwifi.wifip2p.protocol.NegotiationFinalization; import com.flashwifi.wifip2p.protocol.NegotiationOffer; import com.flashwifi.wifip2p.protocol.NegotiationOfferAnswer; @@ -87,6 +91,20 @@ public class PeerStore { getOrCreatePeer(macAddress.toLowerCase()).setLatestOffer(offer); } + public void setErrorMessage(String macAddress, String msg) { + getOrCreatePeer(macAddress.toLowerCase()).setErrorMessage(msg); + } + + public void setSelected(String macAddress, boolean selected) { + getOrCreatePeer(macAddress.toLowerCase()).setSelected(selected); + } + + public void unselectAll() { + for (PeerInformation p: peers.values()) { + p.setSelected(false); + } + } + public void setLatestOfferAnswer(String macAddress, NegotiationOfferAnswer answer) { getOrCreatePeer(macAddress.toLowerCase()).setLatestOfferAnswer(answer); } @@ -102,7 +120,46 @@ public class PeerStore { return null; } + public NegotiationOffer getLatestNegotiationOffer(String macAddress) { + if (peers.containsKey(macAddress.toLowerCase())) { + return peers.get(macAddress.toLowerCase()).getLatestNegotiationOffer(); + } + return null; + } + + public NegotiationOfferAnswer getLatestNegotiationOfferAnswer(String macAddress) { + if (peers.containsKey(macAddress.toLowerCase())) { + return peers.get(macAddress.toLowerCase()).getLatestOfferAnswer(); + } + return null; + } + public void setIPAddress(String macAddress, InetAddress IPAddress) { getOrCreatePeer(macAddress.toLowerCase()).setIPAddress(IPAddress.getHostAddress()); } + + public void setLatestBillingOpenChannel(String macAddress, BillingOpenChannel o) { + getOrCreatePeer(macAddress.toLowerCase()).setBillingOpenChannel(o); + } + + public void setLatestBillingOpenChannelAnswer(String macAddress, BillingOpenChannelAnswer o) { + getOrCreatePeer(macAddress.toLowerCase()).setBillingOpenChannelAnswer(o); + } + + public void setLatestBillingCloseChannel(String macAddress, BillingCloseChannel o) { + getOrCreatePeer(macAddress.toLowerCase()).setBillingCloseChannel(o); + } + + public void setLatestBillingCloseChannelAnswer(String macAddress, BillingCloseChannelAnswer o) { + getOrCreatePeer(macAddress.toLowerCase()).setBillingCloseChannelAnswer(o); + } + + public PeerInformation getPeer(String address) { + address = address.toLowerCase(); + if (peers.containsKey(address)) { + return peers.get(address); + } + return null; + } + } diff --git a/app/src/main/java/com/flashwifi/wifip2p/iotaAPI/Requests/Model/TokenGenJSONResponse.java b/app/src/main/java/com/flashwifi/wifip2p/iotaAPI/Requests/Model/TokenGenJSONResponse.java new file mode 100644 index 0000000..069e11f --- /dev/null +++ b/app/src/main/java/com/flashwifi/wifip2p/iotaAPI/Requests/Model/TokenGenJSONResponse.java @@ -0,0 +1,48 @@ +package com.flashwifi.wifip2p.iotaAPI.Requests.Model; + +import com.google.api.client.json.GenericJson; +import com.google.api.client.util.Key; + +public class TokenGenJSONResponse extends GenericJson { + + @Key("seed") + private String seed; + @Key("address") + private String address; + @Key("amount") + private Integer amount; + + private String success; + + public Integer getAmount() { + return amount; + } + + public void setAmount(Integer amount) { + this.amount = amount; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getSeed() { + return seed; + } + + public void setSeed(String seed) { + this.seed = seed; + } + + public String getSuccess() { + return success; + } + + public void setSuccess(String success) { + this.success = success; + } +} diff --git a/app/src/main/java/com/flashwifi/wifip2p/iotaAPI/Requests/WalletAddressAndBalanceChecker.java b/app/src/main/java/com/flashwifi/wifip2p/iotaAPI/Requests/WalletAddressAndBalanceChecker.java deleted file mode 100644 index 751c004..0000000 --- a/app/src/main/java/com/flashwifi/wifip2p/iotaAPI/Requests/WalletAddressAndBalanceChecker.java +++ /dev/null @@ -1,199 +0,0 @@ -package com.flashwifi.wifip2p.iotaAPI.Requests; - -/** - * Created by Toby on 1/6/2018. - */ - -import android.content.Context; -import android.content.SharedPreferences; -import android.os.AsyncTask; -import android.os.Handler; -import android.os.Message; - -import com.flashwifi.wifip2p.AddressBalanceTransfer; - -import java.util.ArrayList; -import java.util.List; - -import jota.IotaAPI; -import jota.dto.response.GetBalancesResponse; -import jota.dto.response.GetNewAddressResponse; -import jota.error.ArgumentException; -import jota.model.Transaction; - -public class WalletAddressAndBalanceChecker extends AsyncTask { - - private static final int BALANCE_RETRIEVE_TASK_COMPLETE = 1; - - private static final int FUND_WALLET = 0; - private static final int WITHDRAW_WALLET = 1; - - private static IotaAPI api; - private static Context context; - private String prefFile; - private String seed; - private Handler mHandler; - private int type; - private Boolean updateMessage; - - //GetNodeInfoResponse response = api.getNodeInfo(); - - public WalletAddressAndBalanceChecker(Context inActivity, String inPrefFile, String inSeed, Handler inMHandler, int inType, Boolean inUpdateMessage) { - context = inActivity; - prefFile = inPrefFile; - seed = inSeed; - mHandler = inMHandler; - type = inType; - updateMessage = inUpdateMessage; - - //Mainnet node: - /* - api = new IotaAPI.Builder() - .protocol("http") - .host("node.iotawallet.info") - .port("14265") - .build(); - */ - - //Testnet node: - api = new IotaAPI.Builder() - .protocol("https") - .host("testnet140.tangle.works") - .port("443") - .build(); - - } - - @Override - protected Void doInBackground(Void... voids) { - if(context != null){ - List addressList = getAddress(seed); - - if(addressList != null && addressList.get(0) == "Unable to resolve host"){ - AddressBalanceTransfer addressBalanceTransfer = new AddressBalanceTransfer(null,null,"hostError"); - Message completeMessage = mHandler.obtainMessage(BALANCE_RETRIEVE_TASK_COMPLETE, addressBalanceTransfer); - completeMessage.sendToTarget(); - } - else if(addressList != null){ - - String depositAddress = addressList.get(addressList.size()-1); - String balance = getBalance(addressList); - - if(balance != null){ - if(type == WITHDRAW_WALLET && updateMessage == false){ - AddressBalanceTransfer addressBalanceTransfer = new AddressBalanceTransfer(depositAddress,balance,"noErrorNoUpdateMessage"); - Message completeMessage = mHandler.obtainMessage(BALANCE_RETRIEVE_TASK_COMPLETE,addressBalanceTransfer); - completeMessage.sendToTarget(); - } - else{ - AddressBalanceTransfer addressBalanceTransfer = new AddressBalanceTransfer(depositAddress,balance,"noError"); - Message completeMessage = mHandler.obtainMessage(BALANCE_RETRIEVE_TASK_COMPLETE,addressBalanceTransfer); - completeMessage.sendToTarget(); - } - } - else{ - //Balance Retrieval Error - AddressBalanceTransfer addressBalanceTransfer = new AddressBalanceTransfer(null,null,"balanceError"); - Message completeMessage = mHandler.obtainMessage(BALANCE_RETRIEVE_TASK_COMPLETE, addressBalanceTransfer); - completeMessage.sendToTarget(); - } - } - else{ - //Address Retrieval Error - AddressBalanceTransfer addressBalanceTransfer = new AddressBalanceTransfer(null,null,"addressError"); - Message completeMessage = mHandler.obtainMessage(BALANCE_RETRIEVE_TASK_COMPLETE, addressBalanceTransfer); - completeMessage.sendToTarget(); - } - } - return null; - } - - public String getBalance(List inAddresses){ - String[] balanceArray; - try { - GetBalancesResponse balanceResultResponse = api.getBalances(100, inAddresses); - balanceArray = balanceResultResponse.getBalances(); - } catch (ArgumentException | IllegalStateException e) { - e.printStackTrace(); - return null; - } - - if(balanceArray.length>1){ - return balanceArray[balanceArray.length-2]; - } - else{ - return balanceArray[balanceArray.length-1]; - } - - } - - public List getAddress(String seed) { - - Boolean foundAddress = false; - List addressList = new ArrayList<>(); - int keyIndex = getKeyIndex(); - - while(foundAddress == false){ - - GetNewAddressResponse addressResponse = null; - try { - addressResponse = api.getNewAddress(seed, 2, keyIndex, false, 1, false); - } catch (ArgumentException e) { - e.printStackTrace(); - } - - if(addressResponse != null) { - addressList.add(addressResponse.getAddresses().get(0)); - - String[] addressesCheckArray = new String[1]; - addressesCheckArray[0] = addressResponse.getAddresses().get(0); - - List transactionsForAddress = null; - try { - transactionsForAddress = api.findTransactionObjectsByAddresses(addressesCheckArray); - } catch (ArgumentException | IllegalStateException | IllegalAccessError e) { - e.printStackTrace(); - if(e.getMessage().contains("Unable to resolve host")){ - List errorStringList = new ArrayList<>(); - errorStringList.add("Unable to resolve host"); - return errorStringList; - } - } - - if(transactionsForAddress.isEmpty() || (transactionsForAddress.size() == 0 || transactionsForAddress.equals(null))){ - //Transactions not found, use this address - foundAddress = true; - } - else{ - //Found transactions, increment for new address - keyIndex = keyIndex + 1; - } - } - } - - if(keyIndex == 0){ - //Put the initial address to search. No transactions for the seed yet. - putKeyIndex(keyIndex); - } - else{ - //Put the second last address to search - putKeyIndex(keyIndex-1); - } - return addressList; - } - - private int getKeyIndex() { - SharedPreferences sharedPref = context.getSharedPreferences(prefFile, Context.MODE_PRIVATE); - int keyIndex = sharedPref.getInt("keyIndex",0); - return keyIndex; - } - - private void putKeyIndex(int inKeyIndex) { - SharedPreferences sharedPref = context.getSharedPreferences( - prefFile, Context.MODE_PRIVATE); - SharedPreferences.Editor editor = sharedPref.edit(); - editor.putInt("keyIndex", inKeyIndex); - editor.apply(); - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/flashwifi/wifip2p/iotaAPI/Requests/WalletAddressChecker.java b/app/src/main/java/com/flashwifi/wifip2p/iotaAPI/Requests/WalletAddressChecker.java new file mode 100644 index 0000000..617d633 --- /dev/null +++ b/app/src/main/java/com/flashwifi/wifip2p/iotaAPI/Requests/WalletAddressChecker.java @@ -0,0 +1,176 @@ +package com.flashwifi.wifip2p.iotaAPI.Requests; + +import android.content.Context; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; + +import com.flashwifi.wifip2p.R; + +import java.util.ArrayList; +import java.util.List; + +import jota.IotaAPI; +import jota.dto.response.GetInclusionStateResponse; +import jota.dto.response.GetNewAddressResponse; +import jota.error.ArgumentException; +import jota.model.Transaction; + +public class WalletAddressChecker { + + private static IotaAPI api; + private Context context; + private String prefFile; + + boolean containsPendingTransaction = false; + boolean keyIndexChanged = false; + + public WalletAddressChecker(Context inContext, String inPrefFile){ + + context = inContext; + prefFile = inPrefFile; + + SharedPreferences prefManager = PreferenceManager.getDefaultSharedPreferences(context); + Boolean testnet = prefManager.getBoolean("pref_key_switch_testnet",false); + Boolean testnetPrivate = prefManager.getBoolean("pref_key_switch_testnet_private",false); + + if(testnet){ + //Testnet node: + if(testnetPrivate){ + //Private test node + api = new IotaAPI.Builder() + .protocol(context.getResources().getString(R.string.protocolPrivateTestnetNode)) + .host(context.getResources().getString(R.string.hostPrivateTestnetNode)) + .port(context.getResources().getString(R.string.portPrivateTestnetNode)) + .build(); + } + else{ + //Public test node + api = new IotaAPI.Builder() + .protocol(context.getResources().getString(R.string.protocolPublicTestnetNode)) + .host(context.getResources().getString(R.string.hostPublicTestnetNode)) + .port(context.getResources().getString(R.string.portPublicTestnetNode)) + .build(); + } + } + else{ + //Mainnet node: + api = new IotaAPI.Builder() + .protocol(context.getResources().getString(R.string.protocolDefaultMainnetNode)) + .host(context.getResources().getString(R.string.hostDefaultMainnetNode)) + .port(context.getResources().getString(R.string.portDefaultMainnetNode)) + .build(); + } + } + + public List getAddress(String seed) { + + Boolean foundAddress = false; + List addressList = new ArrayList<>(); + int keyIndex = getKeyIndex(); + ArrayList hashStringList = new ArrayList<>(); + + while(foundAddress == false){ + + GetNewAddressResponse addressResponse = null; + try { + SharedPreferences prefManager = PreferenceManager.getDefaultSharedPreferences(context); + String security = prefManager.getString("pref_key_security","2"); + int securityInt = Integer.parseInt(security); + addressResponse = api.getNewAddress(seed, securityInt, keyIndex, true, 1, false); + } catch (ArgumentException e) { + e.printStackTrace(); + return addressList; + } + + if(addressResponse != null) { + addressList.add(addressResponse.getAddresses().get(0)); + + String[] addressesCheckArray = new String[1]; + addressesCheckArray[0] = addressResponse.getAddresses().get(0); + + List transactionsForAddress = null; + try { + transactionsForAddress = api.findTransactionObjectsByAddresses(addressesCheckArray); + } catch (ArgumentException | IllegalStateException | IllegalAccessError e) { + e.printStackTrace(); + if(e.getMessage().contains("Unable to resolve host")){ + List errorStringList = new ArrayList<>(); + errorStringList.add("Unable to resolve host"); + return errorStringList; + } + } + + if(transactionsForAddress == null || transactionsForAddress.isEmpty() || (transactionsForAddress.size() == 0)){ + //Transactions not found, use this address + foundAddress = true; + } + else{ + //Found transactions, increment for new address + + String curHash = transactionsForAddress.get(0).getHash(); + hashStringList.add(curHash); + keyIndex = keyIndex + 1; + } + } + } + String[] hashStringArray = new String[hashStringList.size()]; + int hashIndex = 0; + //Convert hash String List to String Array + for (String curHash : hashStringList) { + hashStringArray[hashIndex] = curHash; + hashIndex = hashIndex + 1; + } + + //Check whether pending transactions exist + containsPendingTransaction = false; + keyIndexChanged = false; + GetInclusionStateResponse inclusionResponse; + try { + inclusionResponse = api.getLatestInclusion(hashStringArray); + } catch (ArgumentException | IllegalStateException e) { + e.printStackTrace(); + return addressList; + } + + boolean[] states = inclusionResponse.getStates(); + + for (boolean state : states) { + if(!state){ + containsPendingTransaction = true; + } + } + + if(!containsPendingTransaction){ + //all confirmed transactions, ok to change keyIndex + if(keyIndex != getKeyIndex()){ + keyIndexChanged = true; + putKeyIndex(keyIndex); + } + } + + return addressList; + } + + private int getKeyIndex() { + SharedPreferences sharedPref = context.getSharedPreferences(prefFile, Context.MODE_PRIVATE); + int keyIndex = sharedPref.getInt("keyIndex",0); + return keyIndex; + } + + private void putKeyIndex(int inKeyIndex) { + SharedPreferences sharedPref = context.getSharedPreferences( + prefFile, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedPref.edit(); + editor.putInt("keyIndex", inKeyIndex); + editor.apply(); + } + + public boolean getContainsPendingTransaction() { + return containsPendingTransaction; + } + + public boolean getkeyIndexChanged() { + return keyIndexChanged; + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/flashwifi/wifip2p/iotaAPI/Requests/WalletBalanceChecker.java b/app/src/main/java/com/flashwifi/wifip2p/iotaAPI/Requests/WalletBalanceChecker.java new file mode 100644 index 0000000..c64d856 --- /dev/null +++ b/app/src/main/java/com/flashwifi/wifip2p/iotaAPI/Requests/WalletBalanceChecker.java @@ -0,0 +1,217 @@ +package com.flashwifi.wifip2p.iotaAPI.Requests; + +import android.content.Context; +import android.content.SharedPreferences; +import android.os.AsyncTask; +import android.os.Handler; +import android.os.Message; +import android.preference.PreferenceManager; + +import com.flashwifi.wifip2p.AddressBalanceTransfer; +import com.flashwifi.wifip2p.R; + +import java.text.DateFormat; +import java.util.Date; +import java.util.List; + +import jota.IotaAPI; +import jota.dto.response.GetBalancesAndFormatResponse; +import jota.dto.response.GetBalancesResponse; +import jota.error.ArgumentException; +import jota.utils.StopWatch; + +public class WalletBalanceChecker extends AsyncTask { + + private static final int BALANCE_RETRIEVE_TASK_COMPLETE = 1; + + private static final int FUND_WALLET = 0; + private static final int WITHDRAW_WALLET = 1; + private static final int PREF_UPDATE = 2; + + private static IotaAPI api; + private static Context context; + private String prefFile; + private String seed; + private Handler mHandler; + private int type; + private Boolean updateMessage; + + public WalletBalanceChecker(Context inActivity, String inPrefFile, String inSeed, Handler inMHandler, int inType, Boolean inUpdateMessage) { + context = inActivity; + prefFile = inPrefFile; + seed = inSeed; + mHandler = inMHandler; + type = inType; + updateMessage = inUpdateMessage; + + SharedPreferences prefManager = PreferenceManager.getDefaultSharedPreferences(context); + Boolean testnet = prefManager.getBoolean("pref_key_switch_testnet",false); + Boolean testnetPrivate = prefManager.getBoolean("pref_key_switch_testnet_private",false); + + if(testnet){ + //Testnet node: + if(testnetPrivate){ + //Private test node + api = new IotaAPI.Builder() + .protocol(context.getResources().getString(R.string.protocolPrivateTestnetNode)) + .host(context.getResources().getString(R.string.hostPrivateTestnetNode)) + .port(context.getResources().getString(R.string.portPrivateTestnetNode)) + .build(); + } + else{ + //Public test node + api = new IotaAPI.Builder() + .protocol(context.getResources().getString(R.string.protocolPublicTestnetNode)) + .host(context.getResources().getString(R.string.hostPublicTestnetNode)) + .port(context.getResources().getString(R.string.portPublicTestnetNode)) + .build(); + } + } + else{ + //Mainnet node: + api = new IotaAPI.Builder() + .protocol(context.getResources().getString(R.string.protocolDefaultMainnetNode)) + .host(context.getResources().getString(R.string.hostDefaultMainnetNode)) + .port(context.getResources().getString(R.string.portDefaultMainnetNode)) + .build(); + } + } + + @Override + protected Void doInBackground(Void... voids) { + + if(context != null){ + + WalletAddressChecker addressChecker = new WalletAddressChecker(context,prefFile); + List addressList = addressChecker.getAddress(seed); + boolean containsPendingTransactions = addressChecker.getContainsPendingTransaction(); + boolean keyIndexChanged = addressChecker.getkeyIndexChanged(); + + if(addressList != null && addressList.get(0).equals("Unable to resolve host")){ + AddressBalanceTransfer addressBalanceTransfer = new AddressBalanceTransfer(null,null,"hostError"); + Message completeMessage = mHandler.obtainMessage(BALANCE_RETRIEVE_TASK_COMPLETE, addressBalanceTransfer); + completeMessage.sendToTarget(); + } + else if(addressList != null){ + + String depositAddress = addressList.get(addressList.size()-1); + String balance = getBalance(addressList, containsPendingTransactions, keyIndexChanged); + + if(balance != null){ + if(type == WITHDRAW_WALLET && updateMessage == false){ + AddressBalanceTransfer addressBalanceTransfer = new AddressBalanceTransfer(depositAddress,balance,"noErrorNoUpdateMessage"); + Message completeMessage = mHandler.obtainMessage(BALANCE_RETRIEVE_TASK_COMPLETE,addressBalanceTransfer); + completeMessage.sendToTarget(); + } + else{ + AddressBalanceTransfer addressBalanceTransfer = new AddressBalanceTransfer(depositAddress,balance,"noError"); + Message completeMessage = mHandler.obtainMessage(BALANCE_RETRIEVE_TASK_COMPLETE,addressBalanceTransfer); + completeMessage.sendToTarget(); + } + } + else{ + //Balance Retrieval Error + AddressBalanceTransfer addressBalanceTransfer = new AddressBalanceTransfer(null,null,"balanceError"); + Message completeMessage = mHandler.obtainMessage(BALANCE_RETRIEVE_TASK_COMPLETE, addressBalanceTransfer); + completeMessage.sendToTarget(); + } + } + else{ + //Address Retrieval Error + AddressBalanceTransfer addressBalanceTransfer = new AddressBalanceTransfer(null,null,"addressError"); + Message completeMessage = mHandler.obtainMessage(BALANCE_RETRIEVE_TASK_COMPLETE, addressBalanceTransfer); + completeMessage.sendToTarget(); + } + } + return null; + } + + public String getBalance(List inAddresses, boolean containsPendingTransactions, boolean keyIndexChanged){ + + String updatedBalanceString; + try { + StopWatch stopWatch = new StopWatch(); + + SharedPreferences prefManager = PreferenceManager.getDefaultSharedPreferences(context); + String security = prefManager.getString("pref_key_security","2"); + int securityInt = Integer.parseInt(security); + + GetBalancesAndFormatResponse balanceResponse = api.getBalanceAndFormat(inAddresses, 0, 0, stopWatch, securityInt); + long total = balanceResponse.getTotalBalance(); + long storedBaseBalance = Long.parseLong(getBaseSharedPrefKeyBalance()); + long updatedBaseBalance = storedBaseBalance + total; + updatedBalanceString = Long.toString(updatedBaseBalance); + + //Pending Transaction, no new confirmed transactions + if(containsPendingTransactions && !keyIndexChanged){ + //No action required + } + //Pending Transaction, new confirmed transactions + else if(containsPendingTransactions && keyIndexChanged){ + putSharedPrefBalance(updatedBalanceString); + } + //No Pending Transactions, no new confirmed transactions + else if(!containsPendingTransactions && !keyIndexChanged){ + //No action required + } + //No Pending Transactions, new confirmed transactions + else if(!containsPendingTransactions && keyIndexChanged){ + putBaseSharedPrefBalance(updatedBalanceString); + updatedBalanceString = Long.toString(updatedBaseBalance); + putSharedPrefBalance(updatedBalanceString); + } + } catch (ArgumentException | IllegalStateException e) { + e.printStackTrace(); + return getSharedPrefKeyBalance()+"i (cached: "+getSharedPrefKeyBalanceDateUpdate()+")"; + } + String currentDateTimeString = DateFormat.getDateTimeInstance().format(new Date()); + putSharedPrefBalanceDateUpdate(currentDateTimeString); + return updatedBalanceString+"i"; + } + + private void putSharedPrefBalanceDateUpdate(String currentDateTimeString) { + SharedPreferences sharedPref = context.getSharedPreferences( + prefFile, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedPref.edit(); + editor.putString("balanceDateUpdate", currentDateTimeString); + editor.apply(); + } + + private String getSharedPrefKeyBalanceDateUpdate() { + SharedPreferences sharedPref = context.getSharedPreferences(prefFile, Context.MODE_PRIVATE); + String defaultValue = "0"; + String storedBalance = sharedPref.getString("balanceDateUpdate",defaultValue); + return storedBalance; + } + + private String getSharedPrefKeyBalance() { + SharedPreferences sharedPref = context.getSharedPreferences(prefFile, Context.MODE_PRIVATE); + String defaultValue = "0"; + String storedBalance = sharedPref.getString("balance",defaultValue); + return storedBalance; + } + + private void putSharedPrefBalance(String inBalance) { + SharedPreferences sharedPref = context.getSharedPreferences( + prefFile, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedPref.edit(); + editor.putString("balance", inBalance); + editor.apply(); + } + + private String getBaseSharedPrefKeyBalance() { + SharedPreferences sharedPref = context.getSharedPreferences(prefFile, Context.MODE_PRIVATE); + String defaultValue = "0"; + String storedBalance = sharedPref.getString("baseBalance",defaultValue); + return storedBalance; + } + + private void putBaseSharedPrefBalance(String inBalance) { + SharedPreferences sharedPref = context.getSharedPreferences( + prefFile, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedPref.edit(); + editor.putString("baseBalance", inBalance); + editor.apply(); + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/flashwifi/wifip2p/iotaAPI/Requests/WalletTestnetTokenGen.java b/app/src/main/java/com/flashwifi/wifip2p/iotaAPI/Requests/WalletTestnetTokenGen.java new file mode 100644 index 0000000..727268f --- /dev/null +++ b/app/src/main/java/com/flashwifi/wifip2p/iotaAPI/Requests/WalletTestnetTokenGen.java @@ -0,0 +1,171 @@ +package com.flashwifi.wifip2p.iotaAPI.Requests; + +import android.content.Context; +import android.content.SharedPreferences; +import android.os.AsyncTask; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.preference.PreferenceManager; + +import com.flashwifi.wifip2p.AddressBalanceTransfer; +import com.flashwifi.wifip2p.R; +import com.flashwifi.wifip2p.iotaAPI.Requests.Model.TokenGenJSONResponse; +import com.google.api.client.http.GenericUrl; +import com.google.api.client.http.HttpBackOffUnsuccessfulResponseHandler; +import com.google.api.client.http.HttpRequest; +import com.google.api.client.http.HttpRequestFactory; +import com.google.api.client.http.HttpTransport; +import com.google.api.client.http.javanet.NetHttpTransport; +import com.google.api.client.json.JsonFactory; +import com.google.api.client.json.JsonObjectParser; +import com.google.api.client.json.jackson2.JacksonFactory; +import com.google.api.client.util.ExponentialBackOff; +import com.google.gson.reflect.TypeToken; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.List; + +public class WalletTestnetTokenGen extends AsyncTask { + + private static final int TOKEN_TESTNET_RETRIEVE_TASK_COMPLETE = 0; + private static final int TOKEN_TESTNET_STATUS_UPDATE = 1; + private static final int TRANSFER_TASK_COMPLETE = 2; + private static String seed; + private Handler settingsFragmentHandler; + private Handler testnetTokenGenHandler; + private String prefFile; + static HttpTransport HTTP_TRANSPORT = new NetHttpTransport(); + static JsonFactory JSON_FACTORY = new JacksonFactory(); + private static Context context; + + public WalletTestnetTokenGen(Handler inMHandler, Context inContext, String inPrefFile, String inSeed){ + settingsFragmentHandler = inMHandler; + context = inContext; + prefFile = inPrefFile; + seed = inSeed; + + //Handle post-asynctask activities of updating UI + testnetTokenGenHandler = new Handler(Looper.getMainLooper()) { + @Override + public void handleMessage(Message inputMessage) { + switch (inputMessage.what) { + case TRANSFER_TASK_COMPLETE: + AddressBalanceTransfer addressBalanceTransfer = (AddressBalanceTransfer) inputMessage.obj; + String message = addressBalanceTransfer.getMessage(); + //Send completion of send funds to settings fragment + Message completeMessage = settingsFragmentHandler.obtainMessage(TOKEN_TESTNET_RETRIEVE_TASK_COMPLETE,message); + completeMessage.sendToTarget(); + } + } + }; + } + + public TokenGenJSONResponse genToken() { + + HttpRequestFactory requestFactory + = HTTP_TRANSPORT.createRequestFactory( + (HttpRequest request) -> { + request.setParser(new JsonObjectParser(JSON_FACTORY)); + }); + HttpRequest request = null; + try { + + SharedPreferences prefManager = PreferenceManager.getDefaultSharedPreferences(context); + Boolean testnetPrivate = prefManager.getBoolean("pref_key_switch_testnet_private",false); + + if(testnetPrivate){ + request = requestFactory.buildGetRequest(new GenericUrl(context.getResources().getString(R.string.generatorPrivateTestnetNode))); + //Set timeout to 10 min + request.setConnectTimeout(300000); + request.setReadTimeout(600000); + }else{ + request = requestFactory.buildGetRequest(new GenericUrl(context.getResources().getString(R.string.generatorPublicTestnetNode))); + //Set timeout to 10 min + request.setConnectTimeout(300000); + request.setReadTimeout(600000); + } + } catch (IOException e) { + e.printStackTrace(); + } + + //TODO: Set timeout + /* + ExponentialBackOff backoff = new ExponentialBackOff.Builder() + .setInitialIntervalMillis(300000) + .setMaxElapsedTimeMillis(900000) + .setMaxIntervalMillis(600000) + .setMultiplier(1.5) + .setRandomizationFactor(0.999) + .build(); + request.setUnsuccessfulResponseHandler( + new HttpBackOffUnsuccessfulResponseHandler(backoff)); + */ + + Type type = new TypeToken() {}.getType(); + TokenGenJSONResponse token = new TokenGenJSONResponse(); + try{ + token = (TokenGenJSONResponse) request + .execute() + .parseAs(type); + token.setSuccess("Success"); + } + catch (Exception e){ + if(e.toString().contains("Connection closed by peer")){ + //Could not reach token gen page + token.setSuccess("Connection Error"); + e.printStackTrace(); + } + else if(e.toString().contains("Unable to resolve host")){ + //Could not reach token gen page + token.setSuccess("Unable to resolve host. Please try again."); + e.printStackTrace(); + } + else{ + //Unknown error + token.setSuccess("Unknown Error. Please try again."); + e.printStackTrace(); + } + } + return token; + } + + @Override + protected Void doInBackground(Void... voids) { + TokenGenJSONResponse token = genToken(); + if(token.getSuccess().equals("Success")){ + WalletAddressChecker addressChecker = new WalletAddressChecker(context,prefFile); + List destAddressses = addressChecker.getAddress(seed); + transferToWallet(destAddressses, token); + } + else{ + Message completeMessage = settingsFragmentHandler.obtainMessage(TOKEN_TESTNET_RETRIEVE_TASK_COMPLETE,token.getSuccess()); + completeMessage.sendToTarget(); + } + return null; + } + + private void transferToWallet(List destAddressses, TokenGenJSONResponse token) { + + if(destAddressses != null && destAddressses.get(0).equals("Unable to resolve host")){ + //Host Error + Message completeMessage = settingsFragmentHandler.obtainMessage(TOKEN_TESTNET_RETRIEVE_TASK_COMPLETE, "hostError"); + completeMessage.sendToTarget(); + } + else if(destAddressses != null){ + //No Address Retrieval Error + Message completeMessage = settingsFragmentHandler.obtainMessage(TOKEN_TESTNET_STATUS_UPDATE, "Sending"); + completeMessage.sendToTarget(); + String destAddress = destAddressses.get(destAddressses.size()-1); + + WalletTransferRequest transferRequest = new WalletTransferRequest(destAddress,token.getSeed(),token.getAmount().toString(),"","",context,testnetTokenGenHandler); + transferRequest.execute(); + } + else{ + //Address Retrieval Error + Message completeMessage = settingsFragmentHandler.obtainMessage(TOKEN_TESTNET_RETRIEVE_TASK_COMPLETE,"addressError"); + completeMessage.sendToTarget(); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/flashwifi/wifip2p/iotaAPI/Requests/WalletTransferRequest.java b/app/src/main/java/com/flashwifi/wifip2p/iotaAPI/Requests/WalletTransferRequest.java index 3156f56..0720dd5 100644 --- a/app/src/main/java/com/flashwifi/wifip2p/iotaAPI/Requests/WalletTransferRequest.java +++ b/app/src/main/java/com/flashwifi/wifip2p/iotaAPI/Requests/WalletTransferRequest.java @@ -1,11 +1,14 @@ package com.flashwifi.wifip2p.iotaAPI.Requests; import android.content.Context; +import android.content.SharedPreferences; import android.os.AsyncTask; import android.os.Handler; import android.os.Message; +import android.preference.PreferenceManager; import com.flashwifi.wifip2p.AddressBalanceTransfer; +import com.flashwifi.wifip2p.R; import java.util.ArrayList; import java.util.List; @@ -21,6 +24,8 @@ public class WalletTransferRequest extends AsyncTask { private static IotaAPI api; private static Context context; + Boolean testnet; + Boolean testnetPrivate; private String appWalletSeed; private String sendAddress; private String sendAmount; @@ -39,28 +44,41 @@ public class WalletTransferRequest extends AsyncTask { context = inContext; mHandler = inMHandler; - //Mainnet node: - /* - api = new IotaAPI.Builder() - .protocol("http") - .host("node.iotawallet.info") - .port("14265") - .build(); - */ - - //Testnet node: - api = new IotaAPI.Builder() - .protocol("https") - .host("testnet140.tangle.works") - .port("443") - .build(); + SharedPreferences prefManager = PreferenceManager.getDefaultSharedPreferences(context); + testnet = prefManager.getBoolean("pref_key_switch_testnet",false); + testnetPrivate = prefManager.getBoolean("pref_key_switch_testnet_private",false); + if(testnet){ + //Testnet node: + if(testnetPrivate){ + //Private test node + api = new IotaAPI.Builder() + .protocol(context.getResources().getString(R.string.protocolPrivateTestnetNode)) + .host(context.getResources().getString(R.string.hostPrivateTestnetNode)) + .port(context.getResources().getString(R.string.portPrivateTestnetNode)) + .build(); + } + else{ + //Public test node + api = new IotaAPI.Builder() + .protocol(context.getResources().getString(R.string.protocolPublicTestnetNode)) + .host(context.getResources().getString(R.string.hostPublicTestnetNode)) + .port(context.getResources().getString(R.string.portPublicTestnetNode)) + .build(); + } + } + else{ + //Mainnet node: + api = new IotaAPI.Builder() + .protocol(context.getResources().getString(R.string.protocolDefaultMainnetNode)) + .host(context.getResources().getString(R.string.hostDefaultMainnetNode)) + .port(context.getResources().getString(R.string.portDefaultMainnetNode)) + .build(); + } } @Override protected Void doInBackground(Void... voids) { - sendRequest(); - String result = null; if(context != null){ @@ -82,26 +100,36 @@ public class WalletTransferRequest extends AsyncTask { SendTransferResponse sendTransferResponse = null; + SharedPreferences prefManager = PreferenceManager.getDefaultSharedPreferences(context); + String security = prefManager.getString("pref_key_security","2"); + int securityInt = Integer.parseInt(security); + try { - //Mainnet - //sendTransferResponse = api.sendTransfer(appWalletSeed, 2, 4, 18, transfers, null, null, false); - + if(testnet) { //Testnet - sendTransferResponse = api.sendTransfer(appWalletSeed, 2, 4, 9, transfers, null, null, false); - + if(testnetPrivate){ + sendTransferResponse = api.sendTransfer(appWalletSeed, securityInt, 4, 3, transfers, null, null, false); + } + else{ + sendTransferResponse = api.sendTransfer(appWalletSeed, securityInt, 4, 9, transfers, null, null, false); + } + } + else{ + //Mainnet + sendTransferResponse = api.sendTransfer(appWalletSeed, securityInt, 4, 14, transfers, null, null, false); + } } catch (ArgumentException | IllegalAccessError | IllegalStateException e) { if (e instanceof ArgumentException) { if (e.getMessage().contains("Sending to a used address.") || e.getMessage().contains("Private key reuse detect!") || e.getMessage().contains("Send to inputs!")) { transferResult = "Sending to a used address/Private key reuse detect. Error Occurred."; } - else if(e.getMessage().contains("Failed to connect to")){ - transferResult = "Failed to connect to"; + else if(e.getMessage().contains("Failed to connect to node")){ + transferResult = "Failed to connect to node"; } else{ transferResult = "Network Error: Attaching new address failed or Transaction failed"; } } - if (e instanceof IllegalAccessError) { transferResult = "Local POW needs to be enabled"; } @@ -116,14 +144,12 @@ public class WalletTransferRequest extends AsyncTask { break; } } - if(success){ transferResult = "Sent"; } else{ transferResult = "Transfer not successful, check transfer status via Tangle explorer"; } - } else{ transferResult = "Transfer error: Send Response not received"; diff --git a/app/src/main/java/com/flashwifi/wifip2p/negotiation/Negotiator.java b/app/src/main/java/com/flashwifi/wifip2p/negotiation/Negotiator.java index 79609bf..537234b 100644 --- a/app/src/main/java/com/flashwifi/wifip2p/negotiation/Negotiator.java +++ b/app/src/main/java/com/flashwifi/wifip2p/negotiation/Negotiator.java @@ -1,8 +1,13 @@ package com.flashwifi.wifip2p.negotiation; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; import android.util.Log; +import com.flashwifi.wifip2p.R; import com.flashwifi.wifip2p.datastore.PeerStore; import com.flashwifi.wifip2p.protocol.NegotiationFinalization; import com.flashwifi.wifip2p.protocol.NegotiationOffer; @@ -11,6 +16,7 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import java.io.IOException; +import java.lang.reflect.Method; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; @@ -34,7 +40,12 @@ public class Negotiator { // consumer as in consumer-hotspot private boolean isConsumer; + String mac = null; + String peer_mac_address = null; + private Gson gson; + private SharedPreferences prefs; + private Context context; public enum ConsumerState { INITIAL, @@ -59,18 +70,30 @@ public class Negotiator { ERROR } - public Negotiator(boolean isConsumer, String ownMacAddress) { + private void sendUpdateUIBroadcastWithMessage(String message, String snd_message){ + Intent local = new Intent(); + local.putExtra("message", message); + local.putExtra("snd_message", snd_message); + local.setAction("com.flashwifi.wifip2p.update_ui"); + context.sendBroadcast(local); + } + + public Negotiator(boolean isConsumer, String ownMacAddress, SharedPreferences prefs, Context context) { this.isConsumer = isConsumer; gson = new GsonBuilder().create(); this.ownMacAddress = ownMacAddress; + this.prefs = prefs; + this.context = context; Log.d(TAG, "Negotiator: " + ownMacAddress); } - public String workAsClient(String serverIPAddress) { + public NegotiationReturn workAsClient(String serverIPAddress) { this.isClient = true; - - String success = null; Socket socket = null; + NegotiationReturn negReturn; + int code = 0; + boolean restartAfterwards = false; + try { // create client socket that connects to server @@ -87,13 +110,23 @@ public class Negotiator { // Whether we want to provide a hotspot or use one if (isConsumer) { - success = runConsumerProtocol(serverIPAddress); + negReturn = runConsumerProtocol(serverIPAddress); } else { - success = runHotspotProtocol(serverIPAddress); + negReturn = runHotspotProtocol(serverIPAddress); } + + mac = negReturn.mac; + restartAfterwards = negReturn.restartAfterwards; + code = negReturn.code; } catch (SocketTimeoutException ste) { - Log.d(TAG, "workAsServer: ### Timed out after 1 seconds"); + mac = null; + restartAfterwards = true; + code = R.string.err_timeout; + Log.d(TAG, "workAsServer: ### Timed out"); } catch (IOException e) { + mac = null; + restartAfterwards = true; + code = R.string.err_io_exception; Log.d("", e.getMessage()); } finally { if (socket != null) { @@ -104,16 +137,19 @@ public class Negotiator { } } } - return success; + return new NegotiationReturn(code, mac, restartAfterwards); } - public String workAsServer() { + public NegotiationReturn workAsServer() { // this device is the socket server this.isClient = false; - String peer_mac_address = null; + peer_mac_address = null; ServerSocket serverSocket = null; Socket socket = null; + int code = 0; + NegotiationReturn negReturn; + boolean restartAfterwards = false; try { // use the port to start @@ -131,28 +167,36 @@ public class Negotiator { // WAIT FOR CLIENT String hello = socketWrapper.getLine(); - if (hello == null) { - error(0, "no hello received"); - return null; + if (hello == null || hello.contains("java.net.SocketException")) { + return error(R.string.no_hello_received, true); } // Check: Is the peer in the same role as we are // server and server or client and client MAKES NO SENSE if (hello.contains("SERVER") && !isClient || hello.contains("CLIENT") && isClient){ - error(1, "Pairing roles are broken"); - return null; + return error(R.string.err_pairing_roles_broken, true); } // Whether we want to provide a hotspot or use one if (isConsumer) { - peer_mac_address = runConsumerProtocol(socketWrapper.getClientAddress().getHostAddress()); + negReturn = runConsumerProtocol(socketWrapper.getClientAddress().getHostAddress()); } else { - peer_mac_address = runHotspotProtocol(socketWrapper.getClientAddress().getHostAddress()); + negReturn = runHotspotProtocol(socketWrapper.getClientAddress().getHostAddress()); } + peer_mac_address = negReturn.mac; + code = negReturn.code; + restartAfterwards = negReturn.restartAfterwards; + } catch (SocketTimeoutException ste) { - Log.d(TAG, "workAsServer: ### Timed out after 1 seconds"); + Log.d(TAG, "workAsServer: ### Timed out"); + code = R.string.err_timeout; + peer_mac_address = null; + restartAfterwards = true; } catch (IOException e) { - Log.d("", e.getMessage()); + Log.d(TAG, e.getMessage()); + code = R.string.err_io_exception; + peer_mac_address = null; + restartAfterwards = true; } finally { if (socket != null) { try { @@ -168,19 +212,17 @@ public class Negotiator { e.printStackTrace(); } } - } - return peer_mac_address; + return new NegotiationReturn(code, peer_mac_address, restartAfterwards); } - private String runConsumerProtocol(String ipAddress) throws IOException { + private NegotiationReturn runConsumerProtocol(String ipAddress) throws IOException { consumer_state = ConsumerState.WAIT_FOR_OFFER; // RECEIVE OFFER String offerString = socketWrapper.getLine(); - if (offerString == null) { - error(2, "No offer received"); - return null; + if (offerString == null || offerString.contains("java.net.SocketException")) { + return error(R.string.err_no_offer_received, true); } // CHECK OFFER @@ -190,26 +232,49 @@ public class Negotiator { // Write offer to the PeerStore PeerStore.getInstance().setLatestOffer(otherMac, offer); - // ToDo: implement accept or deny logic - if (!true) { - error(3, "Offer not acceptable"); - return null; + // accept or deny logic + boolean agree = true; + int disagree_reason = 0; + int iotaPerMegabyte = Integer.valueOf(prefs.getString("edit_text_buy_price", "-1")); + if (iotaPerMegabyte < 0) { + return error(R.string.err_buy_price_bad_setting, false); + } + if (offer.getIotaPerMegabyte() > iotaPerMegabyte) { + agree = false; + disagree_reason = R.string.err_price_too_high; + } + + int hotspot_max_minutes = offer.getMaxMinutes(); + int hotspot_min_minutes = offer.getMinMinutes(); + int client_roaming_minutes = Integer.valueOf(prefs.getString("edit_text_client_minutes", "-1")); + + if (client_roaming_minutes < 0) { + return error(R.string.err_client_minutes_bad_setting, false); + } + + if (client_roaming_minutes < hotspot_min_minutes || client_roaming_minutes > hotspot_max_minutes) { + agree = false; + disagree_reason = R.string.err_client_minutes_not_acceptable_for_hotspot; } // SEND NegotiationAnswer - // ToDo: where shall the input come from? - NegotiationOfferAnswer answer = new NegotiationOfferAnswer(true, 10, ownMacAddress); + NegotiationOfferAnswer answer = new NegotiationOfferAnswer(agree, client_roaming_minutes, ownMacAddress); PeerStore.getInstance().setLatestOfferAnswer(otherMac, answer); String answerString = gson.toJson(answer); socketWrapper.sendLine(answerString); + if (!agree) { + return error(disagree_reason, false); + } + // WAIT FOR PASSWORD and hostname consumer_state = ConsumerState.WAIT_FOR_PASSWORD; String finalizationString = socketWrapper.getLine(); - if (finalizationString == null) { - error(4, "No finalization received"); - return null; + if (finalizationString == null || finalizationString.contains("java.net.SocketException")) { + return error(R.string.err_no_finalization_received, true); + } else if (answerString.equals("BYE")) { + return error(R.string.err_peer_quit, false); } NegotiationFinalization finalization = gson.fromJson(finalizationString, NegotiationFinalization.class); @@ -224,16 +289,27 @@ public class Negotiator { // End socketWrapper.close(); - return otherMac; + return new NegotiationReturn(0, otherMac, false); } - private String runHotspotProtocol(String ipAddress) throws IOException { + private NegotiationReturn runHotspotProtocol(String ipAddress) throws IOException { // CHECK_CLIENT_REQUEST hotspot_state = HotspotState.CHECK_CLIENT_REQUEST; // send offer - int iotaPerMegabyte = (int) (Math.random() * (1000 - 10)) + 10; - NegotiationOffer offer = new NegotiationOffer(1, 100, iotaPerMegabyte, ownMacAddress); + int iotaPerMegabyte = Integer.valueOf(prefs.getString("edit_text_sell_price", "-1")); + if (iotaPerMegabyte < 0) { + return error(R.string.err_sell_price_bad_setting, false); + } + int minMinutes = Integer.valueOf(prefs.getString("edit_text_sell_min_minutes", "-1")); + if (minMinutes < 0) { + return error(R.string.err_min_minutes_bad_setting, false); + } + int maxMinutes = Integer.valueOf(prefs.getString("edit_text_sell_max_minutes", "-1")); + if (maxMinutes < 0) { + return error(R.string.err_max_minutes_bad_setting, false); + } + NegotiationOffer offer = new NegotiationOffer(minMinutes, maxMinutes, iotaPerMegabyte, ownMacAddress); String offerString = gson.toJson(offer); socketWrapper.sendLine(offerString); @@ -242,14 +318,16 @@ public class Negotiator { hotspot_state = HotspotState.WAIT_FOR_ANSWER; String answerString = socketWrapper.getLine(); - if (answerString == null) { - error(8, "No answer received"); - return null; + if (answerString == null || answerString.contains("java.net.SocketException")) { + return error(R.string.err_no_answer_received, true); + } else if (answerString.equals("BYE")) { + return error(R.string.err_peer_quit, false); } // Parse the answer NegotiationOfferAnswer answer = gson.fromJson(answerString, NegotiationOfferAnswer.class); String otherMac = answer.getConsumerMac(); + peer_mac_address = otherMac; PeerStore.getInstance().setLatestOffer(otherMac, offer); PeerStore.getInstance().setLatestOfferAnswer(otherMac, answer); @@ -257,8 +335,11 @@ public class Negotiator { hotspot_state = HotspotState.CHECK_ANSWER; if (!answer.isAgreeToConditions()) { - error(5, "Client does not agree to conditions"); - return null; + return error(R.string.err_client_does_not_agree, false); + } + + if (answer.getDuranceInMinutes() > maxMinutes || answer.getDuranceInMinutes() < minMinutes) { + return error(R.string.err_client_minutes_out_of_bounds, false); } // CHECK_ITP @@ -266,7 +347,6 @@ public class Negotiator { // if (!true) { // writeError(0, ""); // server_state = NegotiationServerTask.State.ERROR; -// // ToDo: Error handling // } // GENERATE_PASSWORD and hotspot name @@ -274,11 +354,20 @@ public class Negotiator { int min = 100000000; int max = 999999999; String password = Integer.toString(ThreadLocalRandom.current().nextInt(min, max + 1)); - String hotspotName = "Iotify-"+Integer.toString(ThreadLocalRandom.current().nextInt(100, 10000));; + String hotspotName = "Iotify-"+Integer.toString(ThreadLocalRandom.current().nextInt(100, 10000)); - // send password and hotspot name + + // ToDo: get depositAddressFlashChannel from SharedPreferences + // ToDo: get the bandwidth of the hotspot + // ToDo: create initial flash object + double bandwidth = 0.1; // megabyte per second; + int max_data_volume_megabytes = (int) (answer.getDuranceInMinutes() * bandwidth); + int max_iota_transferred = max_data_volume_megabytes * offer.getIotaPerMegabyte(); + max_iota_transferred = 10; + String rootAddress = "JZWUMRUEYFJOCDDRZCNIIMDZSX9LWMITNMDIAIUJKUV9LVDLSICDABFYTTBZFGEBJOADDN9WZ9IJJJD9DXRJRR9TOW"; + // send the most important message to the user NegotiationFinalization finalization = new NegotiationFinalization(hotspotName, password, - "Address", 0, 0, ""); + rootAddress, max_iota_transferred, max_iota_transferred, ""); PeerStore.getInstance().setLatestFinalization(otherMac, finalization); String finalizationString = gson.toJson(finalization); @@ -296,20 +385,46 @@ public class Negotiator { socketWrapper.close(); - return otherMac; + return new NegotiationReturn(0, otherMac, false); } // ------------------------ // HELPER METHODS BELOW // ------------------------ - private void error(int code, String msg) throws IOException { - Log.d(TAG, "error: " + msg); + private NegotiationReturn error(int err_no, boolean restartAfterwards) throws IOException { + String str = context.getString(err_no); + Log.d(TAG, "error: " + str); if (isConsumer) { consumer_state = ConsumerState.ERROR; } else { hotspot_state = HotspotState.ERROR; } + String macAdd = (restartAfterwards) ? null : this.peer_mac_address; + sendUpdateUIBroadcastWithMessage("error", str); + if (this.peer_mac_address != null) { + PeerStore.getInstance().setErrorMessage(this.peer_mac_address, str); + } + if (!restartAfterwards) { + try { + socketWrapper.sendLine("BYE"); + } catch (Exception e) { + e.printStackTrace(); + } + } socketWrapper.close(); + return new NegotiationReturn(err_no, macAdd, restartAfterwards); + } + + public class NegotiationReturn { + public int code; + public String mac; + public boolean restartAfterwards; + + public NegotiationReturn(int code, String mac, boolean restartAfterwards) { + this.mac = mac; + this.code = code; + this.restartAfterwards = restartAfterwards; + } } } diff --git a/app/src/main/java/com/flashwifi/wifip2p/protocol/BillingOpenChannel.java b/app/src/main/java/com/flashwifi/wifip2p/protocol/BillingOpenChannel.java index 9ce6cd1..067b795 100644 --- a/app/src/main/java/com/flashwifi/wifip2p/protocol/BillingOpenChannel.java +++ b/app/src/main/java/com/flashwifi/wifip2p/protocol/BillingOpenChannel.java @@ -3,6 +3,7 @@ package com.flashwifi.wifip2p.protocol; public class BillingOpenChannel { private int totalMegabytes; + private int totalMinutes; private int iotaPerMegabyte; private int timeoutMinutesClient; private String clientRefundAddress; @@ -10,8 +11,10 @@ public class BillingOpenChannel { private String[] clientDigests; public BillingOpenChannel(int totalMegabytes, int iotaPerMegabyte, String clientRefundAddress, - int treeDepth, String[] clientDigests, int timeoutMinutesClient) { + int treeDepth, String[] clientDigests, int timeoutMinutesClient, + int totalMinutes) { this.totalMegabytes = totalMegabytes; + this.totalMinutes = totalMinutes; this.iotaPerMegabyte = iotaPerMegabyte; this.clientRefundAddress = clientRefundAddress; this.treeDepth = treeDepth; @@ -42,4 +45,8 @@ public class BillingOpenChannel { public String[] getClientDigests() { return clientDigests; } + + public int getTotalMinutes() { + return totalMinutes; + } } diff --git a/app/src/main/java/com/flashwifi/wifip2p/protocol/NegotiationFinalization.java b/app/src/main/java/com/flashwifi/wifip2p/protocol/NegotiationFinalization.java index 8ac498e..ee8c973 100644 --- a/app/src/main/java/com/flashwifi/wifip2p/protocol/NegotiationFinalization.java +++ b/app/src/main/java/com/flashwifi/wifip2p/protocol/NegotiationFinalization.java @@ -6,6 +6,8 @@ public class NegotiationFinalization { private String hotspotName; private String hotspotPassword; private String depositAddressFlashChannel; + private String clientRefundAddress; + private String hotspotRefundAddress; private int depositServerFlashChannelInIota; private int depositClientFlashChannelInIota; private String flashObject; @@ -27,4 +29,28 @@ public class NegotiationFinalization { public String getHotspotPassword() { return hotspotPassword; } + + public String getDepositAddressFlashChannel() { + return depositAddressFlashChannel; + } + + public int getDepositServerFlashChannelInIota() { + return depositServerFlashChannelInIota; + } + + public int getDepositClientFlashChannelInIota() { + return depositClientFlashChannelInIota; + } + + public String getFlashObject() { + return flashObject; + } + + public String getClientRefundAddress() { + return clientRefundAddress; + } + + public String getHotspotRefundAddress() { + return hotspotRefundAddress; + } } diff --git a/app/src/main/res/layout/activity_roaming.xml b/app/src/main/res/layout/activity_roaming.xml index e7c87f8..39b9c52 100644 --- a/app/src/main/res/layout/activity_roaming.xml +++ b/app/src/main/res/layout/activity_roaming.xml @@ -60,14 +60,55 @@ android:clickable="false" android:text="Flash channel" /> -