I've just begun programming Java and I'm working on the following code that deals with socket-based communications. Basically it connects to a client on port 8888:
package com.mobile.stoq.stoqpay;
import android.net.Uri;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Set;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.widget.Toast;
import com.google.android.gms.appindexing.Action;
import com.google.android.gms.appindexing.AppIndex;
import com.google.android.gms.common.api.GoogleApiClient;
import stone.application.enums.ErrorsEnum;
import stone.application.enums.InstalmentTransactionEnum;
import stone.application.enums.TypeOfTransactionEnum;
import stone.application.interfaces.StoneCallbackInterface;
import stone.providers.BluetoothConnectionProvider;
import stone.providers.LoadTablesProvider;
import stone.providers.TransactionProvider;
import stone.utils.GlobalInformations;
import stone.utils.PinpadObject;
import stone.providers.DisplayMessageProvider;
import stone.utils.StoneTransaction;
public class MainActivity extends AppCompatActivity {
private BufferedReader in;
private PrintWriter out;
private ServerSocket server;
private boolean isConnected = false;
private PinpadObject pinpadSelected;
private final BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
/**
* ATTENTION: This was auto-generated to implement the App Indexing API.
* See https://g.co/AppIndexing/AndroidStudio for more information.
*/
private GoogleApiClient client2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
turnBluetoothOn();
// Networking can't happen on the main thread so we make a new one
new Thread(new Runnable() {
public void run() {
String inMsg;
JSONObject fromClient;
tryConnect(); // Attempts connection.
while (true) {
try {
inMsg = in.readLine();
// The message being null implies a loss of connection, so we recPinpadObject pinpad, BluetoothConnectionProvider
if (inMsg == null) {
server.close();
pinUnPar();
turnBluetoothOn();
tryConnect();
continue;
}
// Converts the message to JSON
fromClient = new JSONObject(inMsg);
System.out.println("In: " + fromClient);
final String cmd = fromClient.getString("command"); // Parses the command
final JSONArray args = fromClient.getJSONArray("args"); // Parses the arguments
// Series of if statements to interpret commands
switch (cmd) {
case "list": // Lists all available bluetooth devices
if (args.length() < 1) {
out.println("Incorrect number of arguments for \"list\"");
}
if (args.getString(0).equals("devices")) {
String devices = "devices{";
BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
Set<BluetoothDevice> pairedDevices = btAdapter.getBondedDevices();
if (pairedDevices.size() > 0) {
for (BluetoothDevice device : pairedDevices) {
devices += String.format("{name:%s, address:%s}", device.getName(), device.getAddress());
}
out.println(devices + "}");
} else {
out.println("No paired devices");
}
} else {
out.println("Unknown argument for \"list\"");
}
break;
case "connect": // Connects to a pinpad device
if (args.length() != 2) {
out.println("Incorrect number of arguments for \"connect\"");
continue;
}
pinPar(args.getString(0), args.getString(1));
break;
case "disconnect": // Disconnects
if(!isConnected){
out.println("You are not connected to a valid device.");
continue;
}
pinUnPar();
break;
case "print": // Prints to the pinpad's display
if (args.length() < 1) {
out.println("Incorrect number of arguments for \"print\"");
continue;
}
if(!isConnected){
out.println("You are not connected to a valid device.");
continue;
}
String say = "";
for (int i = 0; i < args.length(); i++) {
say += args.getString(i) + " ";
}
System.out.println(say);
pinPrint(say, true);
break;
case "trans": // Handles transactions.
if (args.length() < 3) {
out.println("Incorrect number of arguments for \"trans\"");
continue;
}
if(!isConnected){
out.println("You are not connected to a valid device.");
continue;
}
pinTrans(args.getString(0), args.getString(1), args.getString(2), "0");
break;
default: // Handles unknown commands.
out.println("Unknown command \"" + cmd + "\"");
break;
}
} catch (Exception e) {
try {
server.close();
tryConnect();
} catch (IOException e1) {
e1.printStackTrace();
break;
}
}
}
}
}).start();
// ATTENTION: This was auto-generated to implement the App Indexing API.
// See https://g.co/AppIndexing/AndroidStudio for more information.
client2 = new GoogleApiClient.Builder(this).addApi(AppIndex.API).build();
}
private void tryConnect() { //Attempts a connection to the client
try {
int port = 8888;
server = new ServerSocket(port);
System.out.println("Waiting for client on port " + port);
Socket client = server.accept();
System.out.println("Just connected to " + client.getRemoteSocketAddress());
// Starts a new reader on the client input stream
in = new BufferedReader(new InputStreamReader(client.getInputStream()));
// Starts a new writer to the client output streamPinpadObject pinpad, BluetoothConnectionProvider conn
out = new PrintWriter(client.getOutputStream(), true);
} catch (IOException IOError) {
IOError.printStackTrace();
}
}
private void turnBluetoothOn() { //Toggles bluetooth
if (mBluetoothAdapter == null) {
Toast.makeText(getApplicationContext(), "This device does not support Bluetooth", Toast.LENGTH_LONG).show();
} else {
if (!mBluetoothAdapter.isEnabled()) {
try {
mBluetoothAdapter.enable();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
private void pinPar(final String name, final String address) { //Connects to a pinpad (credit card machine)
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
pinpadSelected = new PinpadObject(name, address, false);
BluetoothConnectionProvider bluetoothConnectionProvider = new BluetoothConnectionProvider(MainActivity.this, pinpadSelected);
bluetoothConnectionProvider.setDialogMessage("Connecting to pinpad");
bluetoothConnectionProvider.setWorkInBackground(false);
bluetoothConnectionProvider.setConnectionCallback(new StoneCallbackInterface() {
@Override
public void onSuccess() {
isConnected = true;
Toast.makeText(getApplicationContext(), "Pinpad connected", Toast.LENGTH_SHORT).show();
out.println("Connected to " + name + " at " + address);
pinPrint("Stoq ", false);
}
@Override
public void onError() {
isConnected = false;
Toast.makeText(getApplicationContext(), "Connection failed", Toast.LENGTH_SHORT).show();
out.println("Failed connecting to " + name + " at " + address);
}
}
);
bluetoothConnectionProvider.execute();
}
});
}
private void pinUnPar() { // Disconnects the pinpad
//TODO: Hackish way of disconnecting the pinpad, must find a proper way.
try {
mBluetoothAdapter.disable();
android.os.SystemClock.sleep(10);
mBluetoothAdapter.enable();
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "Pinpad disconnected", Toast.LENGTH_SHORT).show();
}
});
out.println(pinpadSelected.getName() + " disconnected.");
isConnected = false;
}catch (Exception e){
e.printStackTrace();
}
}
private void pinPrint(final String text, final boolean doOutput) { // Prints to the pinpad's screen
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
DisplayMessageProvider display = new DisplayMessageProvider(getApplicationContext(), text, pinpadSelected);
if (doOutput) {
display.setConnectionCallback(new StoneCallbackInterface() {
@Override
public void onSuccess() {
out.println("Printed");
}
@Override
public void onError() {
out.println("Printing failed");
}
});
}
display.execute();
}
});
}
private void pinTrans(final String type, final String val, final String installments, final String interest) { // Starts a transaction with the pinpad
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
if (GlobalInformations.isConnectedToPinpad()) {
StoneTransaction stoneTransaction = new StoneTransaction(pinpadSelected);
stoneTransaction.setAmount(val);
stoneTransaction.setEmailClient(null);
stoneTransaction.setRequestId(null);
stoneTransaction.setUserModel(GlobalInformations.getUserModel(0));
//TODO: This is sorta hackish, and I only did it this way because I couldn't make
//sense out of the solution I got off code review (http://goo.gl/35kXdn). Should
//try to implement that one some day.
boolean hasInterest = false;
if (interest.toLowerCase().equals("true") || interest.equals("1")){
hasInterest = true;
}
int index = (Integer.parseInt(installments) - 1) + (hasInterest ? 0 : 11);
InstalmentTransactionEnum installs = InstalmentTransactionEnum.values()[index];
stoneTransaction.setInstalmentTransactionEnum(installs);
if (type.equals("CREDIT")) {
stoneTransaction.setTypeOfTransaction(TypeOfTransactionEnum.CREDIT);
} else {
stoneTransaction.setTypeOfTransaction(TypeOfTransactionEnum.DEBIT);
}
final TransactionProvider provider = new TransactionProvider(MainActivity.this, stoneTransaction, pinpadSelected);
provider.setDialogMessage("Sending");
provider.setDialogTitle("Please wait");
provider.setWorkInBackground(false);
provider.setConnectionCallback(new StoneCallbackInterface() {
@Override
public void onSuccess() {
System.out.println(provider.getTransactionStatus().toString());
System.out.println(provider.getMessageFromAuthorize());
Toast.makeText(getApplicationContext(), "Transaction sent successfully.", Toast.LENGTH_SHORT).show();
out.println(provider.getTransactionStatus().toString());
}
@Override
public void onError() {
if (provider.theListHasError(ErrorsEnum.NEED_LOAD_TABLES)) {
LoadTablesProvider loadTablesProvider = new LoadTablesProvider(MainActivity.this, provider.getGcrRequestCommand(), pinpadSelected);
loadTablesProvider.setDialogMessage("Uploading tables");
loadTablesProvider.setWorkInBackground(false);
loadTablesProvider.setConnectionCallback(new StoneCallbackInterface() {
@Override
public void onSuccess() {
pinTrans(type, val, installments, interest);
}
@Override
public void onError() {
Toast.makeText(getApplicationContext(), "Updating tables failed", Toast.LENGTH_SHORT).show();
out.println("Transaction failed.");
}
});
loadTablesProvider.execute();
} else if (provider.theListHasError(ErrorsEnum.OPERATION_CANCELLED_BY_USER)) {
Toast.makeText(getApplicationContext(), "Operation cancelled by user", Toast.LENGTH_SHORT).show();
out.println("Operation cancelled by user");
} else {
Toast.makeText(getApplicationContext(), "Transaction failed.", Toast.LENGTH_SHORT).show();
out.println("Transaction failed");
}
}
});
provider.execute();
}
}
});
}
@Override
public void onStart() {
super.onStart();
// ATTENTION: This was auto-generated to implement the App Indexing API.
// See https://g.co/AppIndexing/AndroidStudio for more information.
client2.connect();
Action viewAction = Action.newAction(
Action.TYPE_VIEW, // TODO: choose an action type.
"Main Page", // TODO: Define a title for the content shown.
// TODO: If you have web page content that matches this app activity's content,
// make sure this auto-generated web page URL is correct.
// Otherwise, set the URL to null.
Uri.parse("http://host/path"),
// TODO: Make sure this auto-generated app deep link URI is correct.
Uri.parse("android-app://com.mobile.stoq.stoqpay/http/host/path")
);
AppIndex.AppIndexApi.start(client2, viewAction);
}
@Override
public void onStop() {
super.onStop();
// ATTENTION: This was auto-generated to implement the App Indexing API.
// See https://g.co/AppIndexing/AndroidStudio for more information.
Action viewAction = Action.newAction(
Action.TYPE_VIEW, // TODO: choose an action type.
"Main Page", // TODO: Define a title for the content shown.
// TODO: If you have web page content that matches this app activity's content,
// make sure this auto-generated web page URL is correct.
// Otherwise, set the URL to null.
Uri.parse("http://host/path"),
// TODO: Make sure this auto-generated app deep link URI is correct.
Uri.parse("android-app://com.mobile.stoq.stoqpay/http/host/path")
);
AppIndex.AppIndexApi.end(client2, viewAction);
client2.disconnect();
}
@Override
protected void onDestroy() {
super.onDestroy();
try {
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Now, I'm positive that this code is filled with bad practices, but I can really fix those without knowing which are they, so I ask:
- What am I doing here that I shouldn't be doing?
- Where am I missing the opportunity to be elegant in my code?
- Any tips for someone coming from a pure C and Python background?