diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 74878b4..ed323d1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,9 +4,6 @@ package="com.samourai.txtenna"> - - - @@ -26,7 +23,7 @@ android:name=".MainActivity" android:label="@string/app_name" android:theme="@style/AppTheme.NoActionBar" - android:launchMode="singleTop"> + android:launchMode="singleTask"> @@ -55,13 +52,6 @@ android:configChanges="keyboardHidden|orientation|screenSize" /> - - - - - - - \ No newline at end of file diff --git a/app/src/main/java/com/samourai/sms/SMSReceiver.java b/app/src/main/java/com/samourai/sms/SMSReceiver.java deleted file mode 100755 index 4913139..0000000 --- a/app/src/main/java/com/samourai/sms/SMSReceiver.java +++ /dev/null @@ -1,221 +0,0 @@ -package com.samourai.sms; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.os.Handler; -import android.telephony.SmsMessage; -import android.util.Log; -import android.widget.Toast; - -import com.samourai.txtenna.payload.PayloadFactory; - -import com.google.gson.Gson; -import com.samourai.txtenna.utils.TransactionHandler; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; - -public class SMSReceiver extends BroadcastReceiver { - - /** TAG used for Debug-Logging */ - protected static final String LOG_TAG = "SMSReceiver"; - - /** The Action fired by the Android-System when a SMS was received. - * We are using the Default Package-Visibility */ - private static final String ACTION = "android.provider.Telephony.SMS_RECEIVED"; - - private static HashMap>> incoming = new HashMap>>(); - - private static List seen = new ArrayList(); - - private static TransactionHandler transactionHandler = null; - - public SMSReceiver() { - ; - } - - public SMSReceiver(TransactionHandler transactionHandler) { - this.transactionHandler = transactionHandler; - } - - // @Override - public void onReceive(final Context context, Intent intent) { - - if ((intent.getAction().equals(ACTION) || intent.getAction().contains("SMS_RECEIVED"))) { - - StringBuilder sb = new StringBuilder(); - Bundle bundle = intent.getExtras(); - - Handler handler = new Handler(); - - if (bundle != null) { - - Object[] pdusObj = (Object[]) bundle.get("pdus"); - SmsMessage[] messages = new SmsMessage[pdusObj.length]; - for(int i = 0; i < pdusObj.length; i++) { - messages[i] = SmsMessage.createFromPdu((byte[])pdusObj[i]); - } - - String incomingTelNo = null; - String id = null; - for(SmsMessage currentMessage : messages) { - final String msg = currentMessage.getDisplayMessageBody().trim(); - incomingTelNo = currentMessage.getDisplayOriginatingAddress(); - - if(seen.contains(incomingTelNo + ":" + msg)) { - continue; - } - else { - seen.add(incomingTelNo + ":" + msg); - } - - Log.d("SMSReceiver", incomingTelNo + ":" + msg); - - // - // test for segment count, if present assume Segment0 - // - String i = null; - int c = -1; - PayloadFactory.Seg0 seg0 = null; - PayloadFactory.SegN segn = null; - Gson gson = new Gson(); - if(msg.contains("\"s\":")) { - seg0 = gson.fromJson(msg, PayloadFactory.Seg0.class); - c = 0; - i = seg0.i; - } - else { - segn = gson.fromJson(msg, PayloadFactory.SegN.class); - c = segn.c; - i = segn.i; - } - - if(i != null && i.length() != 0 && c != -1) { - HashMap> ids = incoming.get(incomingTelNo); - HashMap segments = null; - if(ids == null) { - ids = new HashMap>(); - } - else { - segments = ids.get(i); - } - id = i; - if(segments == null) { - segments = new HashMap(); - } - segments.put(Integer.toString(c), msg); - if(ids == null) { - ids = new HashMap>(); - } - ids.put(i, segments); - incoming.put(incomingTelNo, ids); - - handler.post(new Runnable() { - @Override - public void run() { - Toast.makeText(context, "receiving:" + msg, Toast.LENGTH_SHORT).show(); - } - }); - - } - - } - - /* - final String _incomingTelNo = incomingTelNo; - handler.post(new Runnable() { - @Override - public void run() { - Intent intent = new Intent("com.samourai.ponydirect.LOG"); - intent.putExtra("msg", "incoming from:" + _incomingTelNo); - LocalBroadcastManager.getInstance(context).sendBroadcast(intent); - } - }); - */ - - if(incomingTelNo != null && id != null && id.length() != 0) { - verifyIncoming(context, incomingTelNo, id); - } - - } - } - } - - private void verifyIncoming(final Context context, String incomingTelNo, String id) { - - Handler handler = new Handler(); - - HashMap> ids = incoming.get(incomingTelNo); - HashMap segments = ids.get(id); - - int segs = -1; - String hash = null; - String net = null; - - Gson gson = new Gson(); - PayloadFactory.Seg0 seg0 = null; - PayloadFactory.SegN segn = null; - - for(String key : segments.keySet()) { - - String msg = segments.get(key); - if(msg.contains("\"s\":")) { - seg0 = gson.fromJson(msg, PayloadFactory.Seg0.class); - segs = seg0.s; - hash = seg0.h; - net = (seg0.n != null && seg0.n.length() > 0) ? seg0.n : "m"; - } - else { - segn = gson.fromJson(msg, PayloadFactory.SegN.class); - } - - } - - if(segs != -1 && segs == segments.size()) { - - String[] s = new String[segs]; - - int c = -1; - - for(String key : segments.keySet()) { - - final String msg = segments.get(key); - - if(msg.contains("\"s\":")) { - seg0 = gson.fromJson(msg, PayloadFactory.Seg0.class); - c = 0; - } - else { - segn = gson.fromJson(msg, PayloadFactory.SegN.class); - c = segn.c; - } - - if(c != -1) { - s[c] = msg; - } - - } - - final List segmentList = Arrays.asList(s); - - handler.post(new Runnable() { - @Override - public void run() { - Toast.makeText(context, "received:" + segmentList.size(), Toast.LENGTH_SHORT).show(); - } - }); - - PayloadFactory.getInstance(context, transactionHandler).broadcastPayload(segmentList, (net != null && net.equals("t")) ? false : true, false); - - } - else { - Log.d("SMSReceiver", "verifyIncoming(): segment size not recognized"); - } - - } - -} diff --git a/app/src/main/java/com/samourai/sms/SMSSender.java b/app/src/main/java/com/samourai/sms/SMSSender.java deleted file mode 100644 index 81cf6d7..0000000 --- a/app/src/main/java/com/samourai/sms/SMSSender.java +++ /dev/null @@ -1,88 +0,0 @@ -package com.samourai.sms; - -import android.app.Activity; -import android.app.PendingIntent; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.telephony.SmsManager; -import android.widget.Toast; -//import android.widget.Toast; -//import android.util.Log; - -public class SMSSender { - - private static Context context = null; - private static PendingIntent sentPI = null; - private static PendingIntent deliveredPI = null; - - private static SMSSender instance = null; - - private SMSSender() { ; } - - public static SMSSender getInstance(Context ctx) { - context = ctx; - - if(instance == null) { - instance = new SMSSender(); - setReceivers(); - } - - return instance; - } - - public void send(String text, String dest) { - SmsManager sm = SmsManager.getDefault(); - sm.sendTextMessage(dest, null, text, null, null); - } - - public static void setReceivers() { - String SENT = "SMS_SENT"; - String DELIVERED = "SMS_DELIVERED"; - - sentPI = PendingIntent.getBroadcast(context, 0, new Intent(SENT), 0); - deliveredPI = PendingIntent.getBroadcast(context, 0, new Intent(DELIVERED), 0); - - //---when the SMS has been sent--- - context.registerReceiver(new BroadcastReceiver(){ - @Override - public void onReceive(Context arg0, Intent arg1) { - switch (getResultCode()) - { - case Activity.RESULT_OK: - Toast.makeText(((Activity)context).getBaseContext(), "SMS sent", Toast.LENGTH_SHORT).show(); - break; - case SmsManager.RESULT_ERROR_GENERIC_FAILURE: -// Toast.makeText(((Activity)context).getBaseContext(), "SMS not sent: Generic failure", Toast.LENGTH_SHORT).show(); - break; - case SmsManager.RESULT_ERROR_NO_SERVICE: -// Toast.makeText(((Activity)context).getBaseContext(), "SMS not sent: No service", Toast.LENGTH_SHORT).show(); - break; - case SmsManager.RESULT_ERROR_NULL_PDU: -// Toast.makeText(((Activity)context).getBaseContext(), "SMS not sent: Null PDU", Toast.LENGTH_SHORT).show(); - break; - case SmsManager.RESULT_ERROR_RADIO_OFF: -// Toast.makeText(((Activity)context).getBaseContext(), "SMS not sent: Radio off", Toast.LENGTH_SHORT).show(); - break; - } - } - }, new IntentFilter(SENT)); - - //---when the SMS has been delivered--- - context.registerReceiver(new BroadcastReceiver(){ - @Override - public void onReceive(Context arg0, Intent arg1) { - switch (getResultCode()) - { - case Activity.RESULT_OK: - Toast.makeText(((Activity)context).getBaseContext(), "SMS delivered", Toast.LENGTH_SHORT).show(); - break; - case Activity.RESULT_CANCELED: -// Toast.makeText(((Activity)context).getBaseContext(), "SMS not delivered", Toast.LENGTH_SHORT).show(); - break; - } - } - }, new IntentFilter(DELIVERED)); - } -} diff --git a/app/src/main/java/com/samourai/txtenna/MainActivity.java b/app/src/main/java/com/samourai/txtenna/MainActivity.java index 3ff746a..38eb572 100644 --- a/app/src/main/java/com/samourai/txtenna/MainActivity.java +++ b/app/src/main/java/com/samourai/txtenna/MainActivity.java @@ -37,7 +37,6 @@ import com.dm.zbar.android.scanner.ZBarScannerActivity; import com.gotenna.sdk.GoTenna; import com.gotenna.sdk.exceptions.GTInvalidAppTokenException; -import com.samourai.sms.SMSReceiver; import com.samourai.txtenna.adapters.BroadcastLogsAdapter; import com.samourai.txtenna.payload.PayloadFactory; import com.samourai.txtenna.prefs.PrefsUtil; @@ -68,7 +67,6 @@ public class MainActivity extends AppCompatActivity implements IncomingMessagesM private final static int SCAN_HEX_TX = 2011; - private static final int SMS_PERMISSION_CODE = 0; private static final int CAMERA_PERMISSION_CODE = 1; private Group emptyView; private LinearLayout BottomSheetMenu; @@ -129,17 +127,6 @@ public void onClick(View view) { } }); - smsSelection = BottomSheetMenu.findViewById(R.id.sms); - smsSelection.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - relayViaGoTenna = false; - bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); - doGetHex(); - } - }); - - if (!hasCameraPermission()) { showRequestCameraPermissionInfoAlertDialog(); } @@ -148,9 +135,10 @@ public void onClick(View view) { showRequestCameraPermissionInfoAlertDialog(); } - if (!hasReadSmsPermission() || !hasSendSmsPermission()) { - showRequestSMSPermissionInfoAlertDialog(); - } + transactionHandler = new TransactionHandler("TransactionHandler", adapter); + transactionHandler.start(); + + Log.d("MainActivity", "onCreate " + Integer.toHexString(this.hashCode()) + " transactionHandler:" + Integer.toHexString(transactionHandler.hashCode()) + " [lifecycle]"); try { PayloadFactory.getInstance(MainActivity.this, transactionHandler).readBroadcastLog(); @@ -161,14 +149,26 @@ public void onClick(View view) { try { goTennaUtil.getInstance(MainActivity.this).init(); } - catch (GTInvalidAppTokenException e) { + catch (GTInvalidAppTokenException | java.lang.IllegalArgumentException e) { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(R.string.invalid_token_title); + builder.setMessage(R.string.invalid_token); + builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } + }); + builder.show(); + // should set the value goTennaUtil::GOTENNA_APP_TOKEN e.printStackTrace(); } - Log.d("MainActivity", "checking connected address:" + goTennaUtil.getInstance(MainActivity.this).getGtConnectionManager().getConnectedGotennaAddress()); - if (GoTenna.tokenIsVerified()) { + if (goTennaUtil.tokenIsVerified()) { // if NOT already paired, and previous hardware address saved, try to connect to a goTenna + Log.d("MainActivity", "checking connected address:" + goTennaUtil.getInstance(MainActivity.this).getGtConnectionManager().getConnectedGotennaAddress()); + if (!goTennaUtil.getInstance(MainActivity.this).isPaired() && goTennaUtil.getInstance(MainActivity.this).GetHardwareAddress() != null) { // set geolocation after we connect to a device @@ -176,6 +176,9 @@ public void onClick(View view) { IncomingMessagesManager.getInstance(MainActivity.this.getApplicationContext()).addIncomingMessageListener(this); IncomingMessagesManager.getInstance(MainActivity.this.getApplicationContext()).startListening(); + + // use GID from last *unconfirmed* broadcast transaction, otherwise a new random GID + long gid = BroadcastLogUtil.getInstance().lastGID(); goTennaUtil.getInstance(MainActivity.this).connect(null, region); } } @@ -195,445 +198,390 @@ public void onClick(View view) { doSendHex(s[0], params); } } - } - - @Override - protected void onPostCreate(@Nullable Bundle savedInstanceState) { - super.onPostCreate(savedInstanceState); - try { - transactionHandler.start(); - - refreshData(); - transactionHandler.startTransactionChecker(); - IncomingMessagesManager.getInstance(MainActivity.this.getApplicationContext()).addIncomingMessageListener(this); - } - catch (Exception e) { - e.printStackTrace(); - } - } - + } - @Override - protected void onResume() { - super.onResume(); - - if(isReceiver == null) { - isFilter = new IntentFilter(); - isFilter.addAction("android.provider.Telephony.SMS_RECEIVED"); - isFilter.setPriority(2147483647); - isReceiver = new SMSReceiver(transactionHandler); - MainActivity.this.registerReceiver(isReceiver, isFilter); - } - refreshData(); - transactionHandler.startTransactionChecker(); - IncomingMessagesManager.getInstance(MainActivity.this.getApplicationContext()).addIncomingMessageListener(this); + @Override + protected void onRestart() { + Log.d("MainActivity", "onRestart " + Integer.toHexString(this.hashCode()) + " [lifecycle]"); + super.onRestart(); + if (transactionHandler == null) { + Log.d("MainActivity", "onRestart, transactionHandler is null!! " + Integer.toHexString(this.hashCode()) + " [lifecycle]"); } + } - @Override - protected void onPause() { - super.onPause(); - - try { - if(isReceiver != null) { - MainActivity.this.unregisterReceiver(isReceiver); - IncomingMessagesManager.getInstance(MainActivity.this.getApplicationContext()).removeIncomingMessageListener(this); - transactionHandler.stopTransactionChecker(); - } - } - catch(IllegalArgumentException iae) { - ; - } + @Override + protected void onStart() { + Log.d("MainActivity", "onStart " + Integer.toHexString(this.hashCode()) + " [lifecycle]"); + super.onStart(); + if (transactionHandler == null) { + Log.d("MainActivity", "onStart, transactionHandler is null!! " + Integer.toHexString(this.hashCode()) + " [lifecycle]"); } + } - @Override - protected void onDestroy() { - - try { - if(isReceiver != null) { - MainActivity.this.unregisterReceiver(isReceiver); - IncomingMessagesManager.getInstance(MainActivity.this.getApplicationContext()).removeIncomingMessageListener(this); - transactionHandler.stopTransactionChecker(); - } - } - catch(IllegalArgumentException iae) { - ; - } - - try { - PayloadFactory.getInstance(MainActivity.this, transactionHandler).writeBroadcastLog(); - } - catch(JSONException | IOException e) { - e.printStackTrace(); - } - - transactionHandler.quit(); - transactionHandler = null; + @Override + protected void onResume() { + Log.d("MainActivity", "onResume " + Integer.toHexString(this.hashCode()) + " [lifecycle]"); + super.onResume(); - super.onDestroy(); + if (transactionHandler == null) { + Log.d("MainActivity", "onResume, transactionHandler is null!! " + Integer.toHexString(this.hashCode()) + " [lifecycle]"); } - /** - * Listener fpr bottomsheet events - * this will set fab icon based on the bottomsheet's state - */ - private BottomSheetBehavior.BottomSheetCallback bottomSheetCallback = new BottomSheetBehavior.BottomSheetCallback() { - @Override - public void onStateChanged(@NonNull View bottomSheet, int newState) { - if (newState == BottomSheetBehavior.STATE_EXPANDED) { - fab.setImageResource(R.drawable.ic_keyboard_arrow_down); - } - if (newState == BottomSheetBehavior.STATE_HIDDEN) { - fab.setImageResource(R.drawable.ic_txtenna_fab_new); - } - } - - @Override - public void onSlide(@NonNull View bottomSheet, float slideOffset) { + refreshData(); + transactionHandler.startTransactionChecker(); + IncomingMessagesManager.getInstance(MainActivity.this.getApplicationContext()).addIncomingMessageListener(this); + } - } - }; + @Override + protected void onPause() { + Log.d("MainActivity", "onPause " + Integer.toHexString(this.hashCode()) + " [lifecycle]"); + super.onPause(); - @Override - public boolean onCreateOptionsMenu(Menu menu) { - // Inflate the menu; this adds items to the action bar if it is present. - getMenuInflater().inflate(R.menu.menu_main, menu); - return true; + if (transactionHandler == null) { + Log.d("MainActivity", "onPause, transactionHandler is null!! " + Integer.toHexString(this.hashCode()) + " [lifecycle]"); } - @Override - public void onBackPressed() { - if (bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED) { - bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); - } else { - super.onBackPressed(); - } + try { + PayloadFactory.getInstance(MainActivity.this, transactionHandler).writeBroadcastLog(); + } + catch(JSONException | IOException e) { + e.printStackTrace(); } - @Override - public boolean onOptionsItemSelected(MenuItem item) { - // Handle action bar item clicks here. The action bar will - // automatically handle clicks on the Home/Up button, so long - // as you specify a parent activity in AndroidManifest.xml. - int id = item.getItemId(); - - //noinspection SimplifiableIfStatement - if (id == R.id.action_settings) { - startActivity(new Intent(this, SettingsActivity.class)); - return true; - } - if (id == R.id.networkmenu) { - startActivity(new Intent(this, NetworkingActivity.class)); - return true; - } + IncomingMessagesManager.getInstance(MainActivity.this.getApplicationContext()).removeIncomingMessageListener(this); + transactionHandler.stopTransactionChecker(); + } - if (id == R.id.qr_scan) { - relayViaGoTenna = null; - doScanHexTx(); - return true; - } + @Override + protected void onStop() { + Log.d("MainActivity", "onStop " + Integer.toHexString(this.hashCode()) + " [lifecycle]"); + super.onStop(); - return super.onOptionsItemSelected(item); + if (transactionHandler == null) { + Log.d("MainActivity", "onStop, transactionHandler is null!! " + Integer.toHexString(this.hashCode()) + " [lifecycle]"); } + } + + @Override + protected void onDestroy() { + Log.d("MainActivity", "onDestroy " + Integer.toHexString(this.hashCode()) + " [lifecycle]"); + super.onDestroy(); - private void refreshData() { + transactionHandler.quit(); + transactionHandler = null; + } - if (BroadcastLogUtil.getInstance().getBroadcastLog().size() == 0) { - recyclerView.setVisibility(View.GONE); - emptyView.setVisibility(View.VISIBLE); - return; - }else { - recyclerView.setVisibility(View.VISIBLE); - emptyView.setVisibility(View.GONE); + /** + * Listener fpr bottomsheet events + * this will set fab icon based on the bottomsheet's state + */ + private BottomSheetBehavior.BottomSheetCallback bottomSheetCallback = new BottomSheetBehavior.BottomSheetCallback() { + @Override + public void onStateChanged(@NonNull View bottomSheet, int newState) { + if (newState == BottomSheetBehavior.STATE_EXPANDED) { + fab.setImageResource(R.drawable.ic_keyboard_arrow_down); + } + if (newState == BottomSheetBehavior.STATE_HIDDEN) { + fab.setImageResource(R.drawable.ic_txtenna_fab_new); } - transactionHandler.refresh(); } @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - - if (resultCode == Activity.RESULT_OK && requestCode == SCAN_HEX_TX) { - - if (data != null && data.getStringExtra(ZBarConstants.SCAN_RESULT) != null) { + public void onSlide(@NonNull View bottomSheet, float slideOffset) { - final String strResult = data.getStringExtra(ZBarConstants.SCAN_RESULT).trim(); - - doSendHex(strResult, null); + } + }; - } - } else { - ; - } + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_main, menu); + return true; + } + @Override + public void onBackPressed() { + Log.d("MainActivity", "onBackPressed " + Integer.toHexString(this.hashCode()) + " [lifecycle]"); + if (bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED) { + bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); + } else { + super.onBackPressed(); } + } - private void doGetHex() { + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + + //noinspection SimplifiableIfStatement + if (id == R.id.action_settings) { + startActivity(new Intent(this, SettingsActivity.class)); + return true; + } + if (id == R.id.networkmenu) { + startActivity(new Intent(this, NetworkingActivity.class)); + return true; + } - final EditText edHexTx = new EditText(MainActivity.this); - edHexTx.setSingleLine(false); - edHexTx.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE); - edHexTx.setLines(10); - edHexTx.setHint(R.string.tx_hex); - edHexTx.setGravity(Gravity.START); - TextWatcher textWatcher = new TextWatcher() { + if (id == R.id.qr_scan) { + relayViaGoTenna = null; + doScanHexTx(); + return true; + } - public void afterTextChanged(Editable s) { - edHexTx.setSelection(0); - } + return super.onOptionsItemSelected(item); + } - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - ; - } - public void onTextChanged(CharSequence s, int start, int before, int count) { - ; - } - }; - edHexTx.addTextChangedListener(textWatcher); + private void refreshData() { - AlertDialog.Builder dlg = new AlertDialog.Builder(MainActivity.this) - .setTitle(R.string.app_name) - .setView(edHexTx) - .setMessage(R.string.enter_tx_hex) - .setCancelable(true) - .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int whichButton) { + if (BroadcastLogUtil.getInstance().getBroadcastLog().size() == 0) { + recyclerView.setVisibility(View.GONE); + emptyView.setVisibility(View.VISIBLE); + return; + }else { + recyclerView.setVisibility(View.VISIBLE); + emptyView.setVisibility(View.GONE); + } + transactionHandler.refresh(); + } - dialog.dismiss(); - relayViaGoTenna = null; + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { - final String strHexTx = edHexTx.getText().toString().trim(); + if (resultCode == Activity.RESULT_OK && requestCode == SCAN_HEX_TX) { - Log.d("MainActivity", "hex:" + strHexTx); + if (data != null && data.getStringExtra(ZBarConstants.SCAN_RESULT) != null) { - doSendHex(strHexTx, null); + final String strResult = data.getStringExtra(ZBarConstants.SCAN_RESULT).trim(); - } - }).setNegativeButton(R.string.scan, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int whichButton) { + doSendHex(strResult, null); - dialog.dismiss(); - relayViaGoTenna = null; + } + } else { + ; + } - doScanHexTx(); - } - }); + } - dlg.show(); + private void doGetHex() { - } + final EditText edHexTx = new EditText(MainActivity.this); + edHexTx.setSingleLine(false); + edHexTx.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE); + edHexTx.setLines(10); + edHexTx.setHint(R.string.tx_hex); + edHexTx.setGravity(Gravity.START); + TextWatcher textWatcher = new TextWatcher() { - private void doScanHexTx() { - Intent intent = new Intent(MainActivity.this, ZBarScannerActivity.class); - intent.putExtra(ZBarConstants.SCAN_MODES, new int[]{ Symbol.QRCODE } ); - startActivityForResult(intent, SCAN_HEX_TX); - } + public void afterTextChanged(Editable s) { + edHexTx.setSelection(0); + } - private void showRequestCameraPermissionInfoAlertDialog() { + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + ; + } + public void onTextChanged(CharSequence s, int start, int before, int count) { + ; + } + }; + edHexTx.addTextChangedListener(textWatcher); + + AlertDialog.Builder dlg = new AlertDialog.Builder(MainActivity.this) + .setTitle(R.string.app_name) + .setView(edHexTx) + .setMessage(R.string.enter_tx_hex) + .setCancelable(true) + .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(R.string.permission_camera_alert_dialog_title); - builder.setMessage(R.string.permission_camera_dialog_message); - builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - requestCameraPermission(); - } - }); - builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - } - }); + dialog.dismiss(); + relayViaGoTenna = null; - builder.show(); + final String strHexTx = edHexTx.getText().toString().trim(); - } + Log.d("MainActivity", "hex:" + strHexTx); - private void showRequestSMSPermissionInfoAlertDialog() { + doSendHex(strHexTx, null); - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(R.string.permission_sms_alert_dialog_title); - builder.setMessage(R.string.permission_sms_dialog_message); - builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - requestSmsPermission(); - } - }); - builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - } - }); + } + }).setNegativeButton(R.string.scan, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { - builder.show(); + dialog.dismiss(); + relayViaGoTenna = null; - } + doScanHexTx(); + } + }); - private boolean hasCameraPermission() { - return ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED; - } + dlg.show(); - private boolean hasReadSmsPermission() { - return ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_SMS) == PackageManager.PERMISSION_GRANTED; - } + } - private boolean hasSendSmsPermission() { - return ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.SEND_SMS) == PackageManager.PERMISSION_GRANTED; - } + private void doScanHexTx() { + Intent intent = new Intent(MainActivity.this, ZBarScannerActivity.class); + intent.putExtra(ZBarConstants.SCAN_MODES, new int[]{ Symbol.QRCODE } ); + startActivityForResult(intent, SCAN_HEX_TX); + } - private void requestSmsPermission() { + private void showRequestCameraPermissionInfoAlertDialog() { - if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, Manifest.permission.SEND_SMS) && - ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, Manifest.permission.READ_SMS)) { - Log.d("MainActivity", "shouldShowRequestPermissionRationale(), no permission requested"); - return; + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(R.string.permission_camera_alert_dialog_title); + builder.setMessage(R.string.permission_camera_dialog_message); + builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + requestCameraPermission(); } + }); + builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } + }); - ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.SEND_SMS, Manifest.permission.READ_SMS}, SMS_PERMISSION_CODE); - - } + builder.show(); - private void requestCameraPermission() { + } - if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, Manifest.permission.CAMERA) - ) { - Log.d("MainActivity", "shouldShowRequestPermissionRationale(), no permission requested"); - return; - } + private boolean hasCameraPermission() { + return ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED; + } - ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CAMERA}, CAMERA_PERMISSION_CODE); + private void requestCameraPermission() { + if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, Manifest.permission.CAMERA) + ) { + Log.d("MainActivity", "shouldShowRequestPermissionRationale(), no permission requested"); + return; } - private void doSendHex(final String hexTx, final NetworkParameters params) { - - // show transaction log after sending a transaction - recyclerView.setVisibility(View.VISIBLE); - emptyView.setVisibility(View.GONE); - - if(!hexTx.matches("^[A-Fa-f0-9]+$")) { - return; - } + ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CAMERA}, CAMERA_PERMISSION_CODE); - Transaction tx = null; - String msg = null; - try { - tx = new Transaction(PrefsUtil.getInstance(MainActivity.this).getValue(PrefsUtil.USE_MAINNET, true) == true ? MainNetParams.get() : TestNet3Params.get(), Hex.decode(hexTx)); - msg = MainActivity.this.getString(R.string.broadcast) + ":" + tx.getHashAsString() + " " + getText(R.string.to) + " " + PrefsUtil.getInstance(MainActivity.this).getValue(PrefsUtil.SMS_RELAY, MainActivity.this.getText(R.string.default_relay).toString()) + " ?"; - Log.d("MainActivity", "hash:" + tx.getHashAsString()); - tx.verify(); - } - catch(VerificationException ve) { - Log.d("MainActivity", "Invalid transaction, hash:" + tx.getHashAsString()); - Toast.makeText(MainActivity.this, R.string.invalid_tx, Toast.LENGTH_SHORT).show(); - return; - } + } - final TextView tvHexTx = new TextView(MainActivity.this); - tvHexTx.setSingleLine(false); - tvHexTx.setLines(10); - tvHexTx.setGravity(Gravity.START); - tvHexTx.setText(hexTx); + private void doSendHex(final String hexTx, final NetworkParameters params) { - AlertDialog.Builder dlg = new AlertDialog.Builder(MainActivity.this) - .setTitle(R.string.app_name) - .setMessage(msg) - .setCancelable(true); + // show transaction log after sending a transaction + recyclerView.setVisibility(View.VISIBLE); + emptyView.setVisibility(View.GONE); - if(relayViaGoTenna != null) { - dlg.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int whichButton) { + if(!hexTx.matches("^[A-Fa-f0-9]+$")) { + return; + } - dialog.dismiss(); + Transaction tx = null; + String msg = null; + try { + tx = new Transaction(PrefsUtil.getInstance(MainActivity.this).getValue(PrefsUtil.USE_MAINNET, true) == true ? MainNetParams.get() : TestNet3Params.get(), Hex.decode(hexTx)); + Log.d("MainActivity", "hash:" + tx.getHashAsString()); + tx.verify(); + } + catch(VerificationException ve) { + Log.d("MainActivity", "Invalid transaction, hash:" + tx.getHashAsString()); + Toast.makeText(MainActivity.this, R.string.invalid_tx, Toast.LENGTH_SHORT).show(); + return; + } - List payload = PayloadFactory.toJSON(hexTx, relayViaGoTenna, params); - PayloadFactory.getInstance(MainActivity.this, transactionHandler).relayPayload(payload, relayViaGoTenna); - relayViaGoTenna = null; - } + final TextView tvHexTx = new TextView(MainActivity.this); + tvHexTx.setSingleLine(false); + tvHexTx.setLines(10); + tvHexTx.setGravity(Gravity.START); + tvHexTx.setText(hexTx); - }); - dlg.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int whichButton) { + AlertDialog.Builder dlg = new AlertDialog.Builder(MainActivity.this) + .setTitle(R.string.app_name) + .setMessage(msg) + .setCancelable(true); - dialog.dismiss(); - relayViaGoTenna = null; + if(relayViaGoTenna != null) { + dlg.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { - } - }); - } - else { - dlg.setPositiveButton(R.string.gotenna_mesh, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int whichButton) { + dialog.dismiss(); - dialog.dismiss(); + List payload = PayloadFactory.toJSON(hexTx, relayViaGoTenna, params); + PayloadFactory.getInstance(MainActivity.this, transactionHandler).relayPayload(payload, relayViaGoTenna); + relayViaGoTenna = null; + } - List payload = PayloadFactory.toJSON(hexTx, true, params); - PayloadFactory.getInstance(MainActivity.this, transactionHandler).relayPayload(payload, true); - relayViaGoTenna = null; + }); + dlg.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { - } + dialog.dismiss(); + relayViaGoTenna = null; - }); - dlg.setNeutralButton(R.string.cancel, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int whichButton) { + } + }); + } + else { + dlg.setPositiveButton(R.string.gotenna_mesh, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { - dialog.dismiss(); - relayViaGoTenna = null; + dialog.dismiss(); - } + List payload = PayloadFactory.toJSON(hexTx, true, params); + PayloadFactory.getInstance(MainActivity.this, transactionHandler).relayPayload(payload, true); + relayViaGoTenna = null; - }); - dlg.setNegativeButton(R.string.sms_broadcast, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int whichButton) { + } - dialog.dismiss(); + }); + dlg.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { - List payload = PayloadFactory.toJSON(hexTx, false, params); - PayloadFactory.getInstance(MainActivity.this, transactionHandler).relayPayload(payload, false); - relayViaGoTenna = null; - } - }); - } + dialog.dismiss(); + relayViaGoTenna = null; - dlg.show(); + } + }); } - public void onIncomingMessage(Message incomingMessage) { + dlg.show(); + } - // show transaction log after receiving an incoming message - recyclerView.setVisibility(View.VISIBLE); - emptyView.setVisibility(View.GONE); + public void onIncomingMessage(Message incomingMessage) { - try { - JSONObject obj = new JSONObject(incomingMessage.getText()); - if(obj.has("i")) { - String id = obj.getString("i"); - int idx = 0; - if (obj.has("c")) { - idx = obj.getInt("c"); - } + // show transaction log after receiving an incoming message + recyclerView.setVisibility(View.VISIBLE); + emptyView.setVisibility(View.GONE); - if (!SentTxUtil.getInstance().contains(id, idx)) { - // handle upload of segment to server - // if(ConnectivityStatus.hasConnectivity(this)) { - PayloadFactory.getInstance(this, transactionHandler).broadcastPayload(obj.toString(), incomingMessage.getSenderGID()); - // } - // else { - // rebroadcast - // } - } + try { + JSONObject obj = new JSONObject(incomingMessage.getText()); + if(obj.has("i")) { + String id = obj.getString("i"); + int idx = 0; + if (obj.has("c")) { + idx = obj.getInt("c"); } - else if (obj.has("b") && incomingMessage.getReceiverGID() == goTennaUtil.getGID()) { - // handle return receipt message - transactionHandler.confirmFromGateway(incomingMessage.getText()); + + if (!SentTxUtil.getInstance().contains(id, idx)) { + // handle upload of segment to server + // if(ConnectivityStatus.hasConnectivity(this)) { + PayloadFactory.getInstance(this, transactionHandler).broadcastPayload(obj.toString(), incomingMessage.getSenderGID()); + // } + // else { + // rebroadcast + // } } } - catch(JSONException je) { - ; + else if (obj.has("b") && incomingMessage.getReceiverGID() == goTennaUtil.getGID()) { + if (transactionHandler == null) { + Log.d("MainActivity", "onIncomingMessage, transactionHandler is null!! " + Integer.toHexString(this.hashCode()) + " [lifecycle]"); + } + // handle return receipt message + transactionHandler.confirmFromGateway(incomingMessage.getText()); } } + catch(JSONException je) { + Log.d("MainActivity", "onIncomingMessage, JSONException = " + je); + } } +} diff --git a/app/src/main/java/com/samourai/txtenna/NetworkingActivity.java b/app/src/main/java/com/samourai/txtenna/NetworkingActivity.java index b0047ba..b45a95e 100644 --- a/app/src/main/java/com/samourai/txtenna/NetworkingActivity.java +++ b/app/src/main/java/com/samourai/txtenna/NetworkingActivity.java @@ -43,6 +43,7 @@ public class NetworkingActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + setContentView(R.layout.activity_networking); ConstraintGroup = findViewById(R.id.mesh_card_group); mesh_card_detail = findViewById(R.id.mesh_card_detail); @@ -76,7 +77,7 @@ public void onClick(View view) { btPair.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - if(GoTenna.tokenIsVerified()) { + if(goTennaUtil.tokenIsVerified()) { if (!hasBluetoothPermisson()) { requestBluetoothPermission(); } @@ -123,12 +124,14 @@ public void onClick(View view) { } }); - Log.d("NetworkActivity", "gtConnectionState:" + goTennaUtil.getInstance(NetworkingActivity.this).getGtConnectionManager().getGtConnectionState()); - Log.d("NetworkActivity", "connected address:" + goTennaUtil.getInstance(NetworkingActivity.this).getGtConnectionManager().getConnectedGotennaAddress()); + if (goTennaUtil.tokenIsVerified()) { + Log.d("NetworkActivity", "gtConnectionState:" + goTennaUtil.getInstance(NetworkingActivity.this).getGtConnectionManager().getGtConnectionState()); + Log.d("NetworkActivity", "connected address:" + goTennaUtil.getInstance(NetworkingActivity.this).getGtConnectionManager().getConnectedGotennaAddress()); + } } public void setStatusText(boolean isPaired, boolean isScanning) { - String deviceName = goTennaUtil.getInstance(NetworkingActivity.this).getGtConnectionManager().getConnectedGotennaAddress(); + String deviceName = goTennaUtil.tokenIsVerified() ? goTennaUtil.getInstance(NetworkingActivity.this).getGtConnectionManager().getConnectedGotennaAddress() : null; int pairText = R.string.scan; if(isPaired && deviceName != null) { mesh_card_detail.setText(getText(R.string.mesh_device_detected) + ": " + deviceName); diff --git a/app/src/main/java/com/samourai/txtenna/SettingsActivity.java b/app/src/main/java/com/samourai/txtenna/SettingsActivity.java index fdded50..bd46390 100644 --- a/app/src/main/java/com/samourai/txtenna/SettingsActivity.java +++ b/app/src/main/java/com/samourai/txtenna/SettingsActivity.java @@ -35,6 +35,7 @@ public class SettingsActivity extends PreferenceActivity { private static final String regex_url = "^(https?)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]"; + private static final String regex_token = "[a-zA-Z0-9]{64}"; @Override protected void onCreate(Bundle savedInstanceState) { @@ -134,6 +135,22 @@ public boolean onPreferenceClick(Preference preference) { } }); + final EditTextPreference tokenPref = (EditTextPreference) findPreference("token"); + tokenPref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + public boolean onPreferenceChange(Preference preference, Object newValue) { + String token = newValue.toString(); + if (token != null && token.length() > 0) { + if (token.matches(regex_token)) { + PrefsUtil.getInstance(SettingsActivity.this).setValue(PrefsUtil.GOTENNA_TOKEN, token); + } + else { + Toast.makeText(SettingsActivity.this, R.string.invalid_token, Toast.LENGTH_SHORT).show(); + } + } + return true; + } + }); + Preference clearLogsPref = (Preference) findPreference("clearlogs"); clearLogsPref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { public boolean onPreferenceClick(Preference preference) { diff --git a/app/src/main/java/com/samourai/txtenna/payload/PayloadFactory.java b/app/src/main/java/com/samourai/txtenna/payload/PayloadFactory.java index ca98155..f47b130 100644 --- a/app/src/main/java/com/samourai/txtenna/payload/PayloadFactory.java +++ b/app/src/main/java/com/samourai/txtenna/payload/PayloadFactory.java @@ -10,7 +10,6 @@ import com.google.gson.GsonBuilder; import com.gotenna.sdk.gids.GIDManager; -import com.samourai.sms.SMSSender; import com.samourai.txtenna.utils.Message; import com.samourai.txtenna.R; import com.samourai.txtenna.utils.SendMessageInteractor; @@ -365,14 +364,6 @@ public void onMessageResponseReceived() Log.d("PayloadFactory", "goTenna relayed: " + s + " gid: " + gid); } - else { - SMSSender.getInstance(context).send(s, PrefsUtil.getInstance(context).getValue(PrefsUtil.SMS_RELAY, context.getString(R.string.default_relay))); - Log.d("PayloadFactory", "sms relayed:" + s); - - // add messages that were relayed over the SMS network to the broadcast log - BroadcastLogUtil.getInstance().add(payload.get(0), true, true, false, 0); - transactionHandler.refresh(); - } handler.post(new Runnable() { @Override diff --git a/app/src/main/java/com/samourai/txtenna/prefs/PrefsUtil.java b/app/src/main/java/com/samourai/txtenna/prefs/PrefsUtil.java index 40f2f2a..d52c227 100644 --- a/app/src/main/java/com/samourai/txtenna/prefs/PrefsUtil.java +++ b/app/src/main/java/com/samourai/txtenna/prefs/PrefsUtil.java @@ -13,7 +13,7 @@ public class PrefsUtil { public static final String USE_Z85 = "z85"; public static final String MESSAGE_IDX = "msgIdx"; public static final String REGION = "region"; - + public static final String GOTENNA_TOKEN = "token"; public static final String GOTENNA_UID = "goTennaID"; private static Context context = null; diff --git a/app/src/main/java/com/samourai/txtenna/utils/BroadcastLogUtil.java b/app/src/main/java/com/samourai/txtenna/utils/BroadcastLogUtil.java index 1e8c2c3..4827b15 100644 --- a/app/src/main/java/com/samourai/txtenna/utils/BroadcastLogUtil.java +++ b/app/src/main/java/com/samourai/txtenna/utils/BroadcastLogUtil.java @@ -10,9 +10,12 @@ import org.json.JSONException; import org.json.JSONObject; +import java.security.SecureRandom; import java.util.ArrayList; import java.util.List; +import static java.lang.StrictMath.abs; + public class BroadcastLogUtil { public class BroadcastLogEntry { @@ -152,4 +155,19 @@ public int findTransaction(String txid) { } return -1; } + + // return GID from last unconfirmed broadcast transaction, otherwise a new random GID + public long lastGID() { + + // set new random GID every time we connect to a goTenna device + long gid = abs(new SecureRandom().nextLong()) % 9999999999L; + + for (BroadcastLogEntry entry : broadcastLog) { + // use gid from last unconfirmed transaction + if (entry.confirmed == false) { + gid = entry.gid; + } + } + return gid; + } } diff --git a/app/src/main/java/com/samourai/txtenna/utils/IncomingMessagesManager.java b/app/src/main/java/com/samourai/txtenna/utils/IncomingMessagesManager.java index cda7379..8562f39 100644 --- a/app/src/main/java/com/samourai/txtenna/utils/IncomingMessagesManager.java +++ b/app/src/main/java/com/samourai/txtenna/utils/IncomingMessagesManager.java @@ -74,8 +74,14 @@ public void addIncomingMessageListener(IncomingMessageListener incomingMessageLi if (incomingMessageListener != null) { incomingMessageListeners.remove(incomingMessageListener); + if (incomingMessageListeners.size() > 0) { + Log.d("IncomingMessagesManager", "addIncomingMessageListener, NOT empty " + Integer.toHexString(this.hashCode()) + " [lifecycle]"); + } incomingMessageListeners.add(incomingMessageListener); } + else { + Log.d("IncomingMessagesManager", "addIncomingMessageListener, incomingMessageListener == null " + Integer.toHexString(this.hashCode()) + " [lifecycle]"); + } } } @@ -87,6 +93,12 @@ public void removeIncomingMessageListener(IncomingMessageListener incomingMessag { incomingMessageListeners.remove(incomingMessageListener); } + else { + Log.d("IncomingMessagesManager", "removeIncomingMessageListener, incomingMessageListener == null " + Integer.toHexString(this.hashCode()) + " [lifecycle]"); + } + if (incomingMessageListeners.size() > 0) { + Log.d("IncomingMessagesManager", "removeIncomingMessageListener, NOT empty " + Integer.toHexString(this.hashCode()) + " [lifecycle]"); + } } } diff --git a/app/src/main/java/com/samourai/txtenna/utils/TransactionHandler.java b/app/src/main/java/com/samourai/txtenna/utils/TransactionHandler.java index 189c605..2835dbc 100644 --- a/app/src/main/java/com/samourai/txtenna/utils/TransactionHandler.java +++ b/app/src/main/java/com/samourai/txtenna/utils/TransactionHandler.java @@ -11,6 +11,7 @@ import org.json.JSONObject; import java.io.IOException; +import java.net.UnknownHostException; import ch.boye.httpclientandroidlib.HttpResponse; import ch.boye.httpclientandroidlib.client.HttpClient; @@ -28,13 +29,13 @@ public class TransactionHandler extends HandlerThread { public TransactionHandler(String name, BroadcastLogsAdapter adapter) { super(name); this.adapter = adapter; + Log.d("TransactionHandler", "create object: " + Integer.toHexString(this.hashCode()) + " [lifecycle]"); } public synchronized void refresh() { try { android.os.Message message = new android.os.Message(); message.arg1 = 0; - waitUntilReady(); this.handler.sendMessage(message); } catch (Exception e) { @@ -47,7 +48,6 @@ public synchronized void confirmFromGateway(String segment) { android.os.Message message = new android.os.Message(); message.arg1 = 1; message.obj = segment; - waitUntilReady(); this.handler.sendMessage(message); } catch (Exception e) { @@ -82,7 +82,7 @@ public void handleMessage(android.os.Message msg) { if (pos > -1) { BroadcastLogUtil.BroadcastLogEntry entry = BroadcastLogUtil.getInstance().getBroadcastLog().get(pos); entry.broadcast = true; - if (blockHeight > 1) { + if (blockHeight >= 1) { entry.confirmed = true; } adapter.notifyDataSetChanged(); @@ -101,10 +101,10 @@ public void handleMessage(android.os.Message msg) { notify(); } - public synchronized void waitUntilReady() throws InterruptedException { - while (this.handler == null) { - wait(); - } + @Override + protected void finalize () throws Throwable { + super.finalize(); + Log.d("TransactionHandler", "finalize object: @" + Integer.toHexString(this.hashCode()) + " [lifecycle]"); } Runnable transactionChecker = new Runnable() { @@ -122,7 +122,6 @@ public void run() { public void startTransactionChecker() { try { - waitUntilReady(); transactionChecker.run(); } catch (Exception e) { @@ -132,7 +131,6 @@ public void startTransactionChecker() { public void stopTransactionChecker() { try { - waitUntilReady(); this.handler.removeCallbacks(transactionChecker); } catch (Exception e) { @@ -227,6 +225,9 @@ public void onMessageResponseReceived() { refresh(); } } + catch(UnknownHostException e) { + Log.d("TransactionHandler", "No Internet Connection. " + e); + } catch(IOException e) { Log.d("TransactionHandler", e.getMessage()); e.printStackTrace(); diff --git a/app/src/main/java/com/samourai/txtenna/utils/goTennaUtil.java b/app/src/main/java/com/samourai/txtenna/utils/goTennaUtil.java index 8c40324..85af1fc 100644 --- a/app/src/main/java/com/samourai/txtenna/utils/goTennaUtil.java +++ b/app/src/main/java/com/samourai/txtenna/utils/goTennaUtil.java @@ -23,6 +23,7 @@ import com.samourai.txtenna.NetworkingActivity; import com.gotenna.sdk.bluetooth.GTConnectionManager.GTDeviceType; import com.gotenna.sdk.bluetooth.GTConnectionManager.GTConnectionListener; +import com.samourai.txtenna.prefs.PrefsUtil; import java.security.SecureRandom; import java.util.UUID; @@ -57,26 +58,39 @@ public static goTennaUtil getInstance(Context ctx) { return instance; } + public static boolean tokenIsVerified() { + goTennaUtil gt = getInstance(null); + return (gt != null && gt.getGtConnectionManager() != null && GoTenna.tokenIsVerified()); + } + public String getAppToken() { - return GOTENNA_APP_TOKEN; + String token = PrefsUtil.getInstance(context.getApplicationContext()).getValue(PrefsUtil.GOTENNA_TOKEN, ""); + if (token == "") { + return GOTENNA_APP_TOKEN; + } + return token; } public boolean isPaired() { - if(getGtConnectionManager().getGtConnectionState() == GTConnectionState.CONNECTED) { + if(tokenIsVerified() && getGtConnectionManager().getGtConnectionState() == GTConnectionState.CONNECTED) { return true; } return false; } public String GetHardwareAddress() { - return getGtConnectionManager().getConnectedGotennaAddress(); + if (tokenIsVerified()) { + return getGtConnectionManager().getConnectedGotennaAddress(); + } + else { + return ""; + } } public void init() throws StringIndexOutOfBoundsException, GTInvalidAppTokenException { - GoTenna.setApplicationToken(context.getApplicationContext(), goTennaUtil.getInstance(context).getAppToken()); - if(GoTenna.tokenIsVerified()) { + if (GoTenna.tokenIsVerified()) { gtConnectionManager = GTConnectionManager.getInstance(); Log.d("goTennaUtil", "goTenna token is verified:" + GoTenna.tokenIsVerified()); Log.d("goTennaUtil", "connected address:" + gtConnectionManager.getConnectedGotennaAddress()); @@ -84,8 +98,8 @@ public void init() throws StringIndexOutOfBoundsException, GTInvalidAppTokenExce handler = new Handler(Looper.getMainLooper()) { @Override - public void handleMessage(Message msg){ - if(msg.what == 0 && callbackActivity != null) { + public void handleMessage(Message msg) { + if (msg.what == 0 && callbackActivity != null) { // no connection, scanning timed out callbackActivity.setStatusText(false, false); } @@ -158,16 +172,18 @@ public void onError(GTError error) { } public static void setGID(long gid) { - GTCommandCenter.getInstance().setGoTennaGID(gid, UUID.randomUUID().toString(), new GTErrorListener() { - @Override - public void onError(GTError error) { - User gtUser = UserDataStore.getInstance().getCurrentUser(); - Log.d("goTennaUtil", error.toString() + "," + error.getCode() + " gid: " + gtUser.getGID()); - } - }); + if (tokenIsVerified()) { + GTCommandCenter.getInstance().setGoTennaGID(gid, UUID.randomUUID().toString(), new GTErrorListener() { + @Override + public void onError(GTError error) { + User gtUser = UserDataStore.getInstance().getCurrentUser(); + Log.d("goTennaUtil", error.toString() + "," + error.getCode() + " gid: " + gtUser.getGID()); + } + }); - User gtUser = UserDataStore.getInstance().getCurrentUser(); - Log.d("goTennaUtil", "gtUser.getGID: " + gtUser.getGID()); + User gtUser = UserDataStore.getInstance().getCurrentUser(); + Log.d("goTennaUtil", "gtUser.getGID: " + gtUser.getGID()); + } } public static long getGID() { @@ -185,15 +201,17 @@ public void connect(NetworkingActivity activity, int region) { long gid = abs(new SecureRandom().nextLong()) % 9999999999L; setGID(gid); - callbackActivity = activity; - regionIndex = region; - gtConnectionManager.addGtConnectionListener(this); - gtConnectionManager.scanAndConnect(GTDeviceType.MESH); - handler.postDelayed(scanTimeoutRunnable, SCAN_TIMEOUT); + if (tokenIsVerified()) { + callbackActivity = activity; + regionIndex = region; + gtConnectionManager.addGtConnectionListener(this); + gtConnectionManager.scanAndConnect(GTDeviceType.MESH); + handler.postDelayed(scanTimeoutRunnable, SCAN_TIMEOUT); + } } public void sendEchoCommand() { - if(GTConnectionManager.getInstance().isConnected()) + if(tokenIsVerified() && GTConnectionManager.getInstance().isConnected()) { GTCommandCenter.getInstance().sendEchoCommand(null, null); } diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index ad325f0..61bbb48 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -31,6 +31,8 @@ Économiser de l\'espace en encodant les octets de transaction en Z85 Région géographique Sélectionner la région géographique où se situe votre GoTenna. + goTenna Jeton + Entrez votre jeton personnel goTenna ou laissez le champ vide pour le jeton par défaut. Supprimer contenus journal Supprimer contenus journal émis. Utiliser le format d\'appel international. Ex.:\'+447385555555\' @@ -39,6 +41,8 @@ https://api.samouraiwallet.com/test/v2/pushtx/ https://api.samouraiwallet.com/v2/txtenna/segments URL invalide + Votre jeton SDK n\'est pas valide. + Veuillez obtenir un jeton valide auprès de goTenna. MuleTools Scanner QR diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d5b51cf..9b5ac7f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -31,6 +31,8 @@ Save space by encoding transaction bytes in Z85 Geographical region Select geographical region where your goTenna is located. + goTenna Token + Enter your personal goTenna token, or leave blank for default token. Clear logs Clear broadcast logs. Use international dialing format. Ex.:\'+447385555555\' @@ -85,7 +87,8 @@ Scanning Are you sure you want to clear the broadcast logs? - Your SDK token is invalid. Please obtain a valid token from goTenna. + Your SDK token is invalid. + Please obtain a valid token from goTenna. Scanning for goTenna Mesh device. Please wait. diff --git a/app/src/main/res/xml/settings.xml b/app/src/main/res/xml/settings.xml index 7eca7d7..c1da218 100644 --- a/app/src/main/res/xml/settings.xml +++ b/app/src/main/res/xml/settings.xml @@ -42,16 +42,31 @@ android:key="region" /> - + + - + + + +