diff --git a/app/build.gradle b/app/build.gradle index 5bfba5ce9..188bf4060 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -17,6 +17,8 @@ android { outputFileName = new File("remote-" + android.defaultConfig.versionCode + ".apk") } } + multiDexEnabled true + } buildTypes { release { @@ -40,31 +42,34 @@ dependencies { androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0', { exclude group: 'com.android.support', module: 'support-annotations' }) - implementation 'androidx.appcompat:appcompat:1.0.2' - implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha3' + implementation 'androidx.appcompat:appcompat:1.1.0' + implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta4' implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'androidx.multidex:multidex:2.0.1' implementation 'com.afollestad.material-dialogs:commons:0.9.6.0' implementation 'com.mikepenz:materialdrawer:6.1.1' - implementation 'androidx.recyclerview:recyclerview:1.0.0' - implementation 'androidx.annotation:annotation:1.0.1' - implementation 'com.google.android.material:material:1.0.0' + implementation 'androidx.recyclerview:recyclerview:1.1.0' + implementation 'androidx.annotation:annotation:1.1.0' + implementation 'com.google.android.material:material:1.1.0' implementation 'com.mikepenz:google-material-typeface:3.0.1.3.original@aar' implementation 'com.mikepenz:fontawesome-typeface:5.3.1.1@aar' implementation 'com.mikepenz:octicons-typeface:3.2.0.5@aar' implementation 'de.hdodenhof:circleimageview:2.2.0' - implementation 'com.mikepenz:itemanimators:1.1.0' implementation 'com.mikepenz:crossfader:1.5.1@aar' implementation 'com.mikepenz:crossfadedrawerlayout:1.0.1@aar' implementation 'com.google.android:flexbox:1.0.0' implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.legacy:legacy-support-v4:1.0.0' - implementation "androidx.preference:preference:1.1.0" + implementation "androidx.preference:preference:1.1.1" testImplementation 'junit:junit:4.12' implementation 'com.github.parse-community:ParseLiveQuery-Android:1.1.0' implementation 'com.google.code.gson:gson:2.8.6' - androidTestImplementation 'androidx.test:runner:1.1.1' - implementation 'me.aflak.libraries:bluetooth:1.3.4' + androidTestImplementation 'androidx.test:runner:1.2.0' + implementation 'com.github.ivbaranov:rxbluetooth2:2.1.1' + implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' implementation 'com.caverock:androidsvg-aar:1.4' + implementation 'com.android.support:multidex:1.0.3' + + } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d7dfcd13c..68a9b7a19 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -28,7 +28,8 @@ - @@ -37,15 +38,18 @@ - + android:theme="@style/AppTheme" + android:usesCleartextTraffic="true"> + @@ -56,9 +60,9 @@ android:name=".InitialActivity" android:label="@string/app_name" android:screenOrientation="portrait" - android:theme="@style/MyMaterialTheme.Base" - android:windowSoftInputMode="stateHidden|adjustResize"> - + android:theme="@style/AppTheme.NoActionBar" + android:windowSoftInputMode="stateHidden|adjustResize" + tools:ignore="LockedOrientationActivity" /> diff --git a/app/src/main/java/io/treehouses/remote/Constants.java b/app/src/main/java/io/treehouses/remote/Constants.java index 8da51f6a3..443824d02 100644 --- a/app/src/main/java/io/treehouses/remote/Constants.java +++ b/app/src/main/java/io/treehouses/remote/Constants.java @@ -53,6 +53,7 @@ public class Constants { public static final int STATE_LISTEN = 1; // now listening for incoming connections public static final int STATE_CONNECTING = 2; // now initiating an outgoing connection public static final int STATE_CONNECTED = 3; // now connected to a remote device + public static final int STATE_FAILED = -1; // Message types sent from the BluetoothChatService Handler public static final int MESSAGE_STATE_CHANGE = 1; diff --git a/app/src/main/java/io/treehouses/remote/Fragments/DialogFragments/RPIDialogFragment.java b/app/src/main/java/io/treehouses/remote/Fragments/DialogFragments/RPIDialogFragment.java index 97c9202e7..9731785ff 100644 --- a/app/src/main/java/io/treehouses/remote/Fragments/DialogFragments/RPIDialogFragment.java +++ b/app/src/main/java/io/treehouses/remote/Fragments/DialogFragments/RPIDialogFragment.java @@ -40,14 +40,13 @@ import io.treehouses.remote.R; import io.treehouses.remote.adapter.RPIListAdapter; import io.treehouses.remote.bases.BaseDialogFragment; +import io.treehouses.remote.callback.BluetoothDeviceCallback; import io.treehouses.remote.callback.SetDisconnect; import io.treehouses.remote.pojo.DeviceInfo; import static android.widget.Toast.LENGTH_LONG; -public class RPIDialogFragment extends BaseDialogFragment { - - private static BluetoothChatService mChatService = null; +public class RPIDialogFragment extends BaseDialogFragment implements BluetoothDeviceCallback { private static RPIDialogFragment instance = null; private List raspberry_devices = new ArrayList(), all_devices = new ArrayList(); @@ -55,7 +54,6 @@ public class RPIDialogFragment extends BaseDialogFragment { private static BluetoothDevice mainDevice = null; private ListView listView; private ArrayAdapter mArrayAdapter; - private BluetoothAdapter mBluetoothAdapter; private SetDisconnect checkConnectionState; private Context context; private Switch mDiscoverRaspberry; @@ -77,25 +75,19 @@ public static androidx.fragment.app.DialogFragment newInstance(int num) { public Dialog onCreateDialog(Bundle savedInstanceState) { instance = this; context = getContext(); - mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); bluetoothCheck(); - if (mBluetoothAdapter.isDiscovering()) { mBluetoothAdapter.cancelDiscovery(); } - mBluetoothAdapter.startDiscovery(); - LayoutInflater inflater = Objects.requireNonNull(getActivity()).getLayoutInflater(); + LayoutInflater inflater = getActivity().getLayoutInflater(); final View mView = inflater.inflate(R.layout.activity_rpi_dialog_fragment, null); initDialog(mView); - - if (mChatService == null) { mChatService = new BluetoothChatService(mHandler, getActivity().getApplicationContext()); } - - pairedDevices = mBluetoothAdapter.getBondedDevices(); + mChatService.updateHandler(mHandler); + mChatService.startDiscovery(this); + pairedDevices = mChatService.getPairedDevices(); setAdapterNotNull(raspberryDevicesText); for (BluetoothDevice d : pairedDevices) { if (checkPiAddress(d.getAddress())) { addToDialog(d, raspberryDevicesText, raspberry_devices, false); progressBar.setVisibility(View.INVISIBLE); } } - intentFilter(); - return mDialog; } @@ -106,9 +98,13 @@ private void initDialog(View mView) { mDialog.setTitle(R.string.select_device); listViewOnClickListener(mView); Button mCloseButton = mView.findViewById(R.id.rpi_close_button); - mCloseButton.setOnClickListener(v -> { - bluetoothCheck("unregister"); - dismiss(); + mCloseButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + checkConnectionState.checkConnectionState(); + mChatService.disconnect(); + dismiss(); + } }); mDiscoverRaspberry = mView.findViewById(R.id.rpi_switch); mDiscoverRaspberry.setChecked(true); @@ -116,23 +112,15 @@ private void initDialog(View mView) { progressBar = mView.findViewById(R.id.progressBar); } - private void intentFilter() { - IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); - filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED); - filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); - filter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED); - getActivity().registerReceiver(mReceiver, filter); - } - private void listViewOnClickListener(View mView) { listView.setOnItemClickListener((parent, view, position, id) -> { - mChatService = new BluetoothChatService(mHandler, getActivity().getApplicationContext()); + mChatService.updateHandler(mHandler); List deviceList; if (mDiscoverRaspberry.isChecked()) deviceList = raspberry_devices; else deviceList = all_devices; if (checkPiAddress(deviceList.get(position).getAddress())) { mainDevice = deviceList.get(position); - mChatService.connect(deviceList.get(position),true); + mChatService.connectToDevice(deviceList.get(position)); int status = mChatService.getState(); mDialog.cancel(); finish(status, mView); @@ -179,27 +167,22 @@ private void finish(int status, View mView) { @Override public void onDestroy() { super.onDestroy(); - try { if (mBluetoothAdapter == null) context.unregisterReceiver(mReceiver); } catch (Exception e) { e.printStackTrace(); } } private AlertDialog getAlertDialog(View mView, Context context, Boolean wifi) { return new AlertDialog.Builder(context).setView(mView).setIcon(R.drawable.dialog_icon).create(); } - public void bluetoothCheck(String... args) { - if (mBluetoothAdapter == null) { + public void bluetoothCheck() { + if (!mChatService.isBluetoothSupported()) { Toast.makeText(getActivity(), "Your Bluetooth Is Not Enabled or Not Supported", LENGTH_LONG).show(); - getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_CANCELED, getActivity().getIntent()); - context.unregisterReceiver(mReceiver); +// getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_CANCELED, getActivity().getIntent()); + dismiss(); } - if (args.length >= 1) { - if (args[0].equals("unregister")) { - context.unregisterReceiver(mReceiver); - Intent intent = new Intent(); - intent.putExtra("mChatService", mChatService); - getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_OK, intent); - } + else if (!mChatService.isBluetoothEnabled()) { + Toast.makeText(context, "Please enable bluetooth", Toast.LENGTH_SHORT).show(); } + } private void setAdapterNotNull(List listVal) { @@ -207,20 +190,6 @@ private void setAdapterNotNull(List listVal) { listView.setAdapter(mArrayAdapter); } - private final BroadcastReceiver mReceiver = new BroadcastReceiver() { - public void onReceive(Context context, Intent intent) { - if (BluetoothDevice.ACTION_FOUND.equals(intent.getAction())) { - BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); - if (checkPiAddress(device.getAddress())) { - addToDialog(device, raspberryDevicesText, raspberry_devices, true); - progressBar.setVisibility(View.INVISIBLE); - } - addToDialog(device, allDevicesText, all_devices, true); - Log.e("Broadcast BT", device.getName() + "\n" + device.getAddress()); - } - } - }; - private void addToDialog(BluetoothDevice device, List textList, List mDevices, Boolean inRange) { if (!mDevices.contains(device)){ mDevices.add(device); @@ -248,17 +217,17 @@ public void handleMessage(Message msg) { switch (msg.what) { case Constants.MESSAGE_STATE_CHANGE: + checkConnectionState.checkConnectionState(); switch (msg.arg1) { case Constants.STATE_CONNECTED: - Log.e("RPIDialogFragment", "Bluetooth Connection Status Change: State Listen"); + Log.e("RPIDialogFragment", "Bluetooth Connection Status Change: State Connected"); pDialog.dismiss(); - listener.setChatService(mChatService); - checkConnectionState.checkConnectionState(); - mBluetoothAdapter.cancelDiscovery(); + dismiss(); Toast.makeText(context, "Bluetooth Connected", LENGTH_LONG).show(); break; - case Constants.STATE_NONE: + case Constants.STATE_FAILED: pDialog.dismiss(); + dismiss(); Toast.makeText(context, "Connection Failed: Please Try Again", LENGTH_LONG).show(); Log.e("RPIDialogFragment", "Bluetooth Connection Status Change: State None"); break; @@ -272,4 +241,14 @@ public void handleMessage(Message msg) { }; public Handler getmHandler() { return mHandler; } + + @Override + public void onDeviceFound(BluetoothDevice device) { + Log.e("RPIDIALOG", "onDeviceFound: "+ device.getName()); + if (checkPiAddress(device.getAddress())) { + addToDialog(device, raspberryDevicesText, raspberry_devices, true); + progressBar.setVisibility(View.INVISIBLE); + } + addToDialog(device, allDevicesText, all_devices, true); + } } \ No newline at end of file diff --git a/app/src/main/java/io/treehouses/remote/Fragments/HomeFragment.java b/app/src/main/java/io/treehouses/remote/Fragments/HomeFragment.java index 4ee232bee..f904dfadd 100644 --- a/app/src/main/java/io/treehouses/remote/Fragments/HomeFragment.java +++ b/app/src/main/java/io/treehouses/remote/Fragments/HomeFragment.java @@ -1,5 +1,6 @@ package io.treehouses.remote.Fragments; +import android.annotation.SuppressLint; import android.app.AlertDialog; import android.app.ProgressDialog; import android.bluetooth.BluetoothAdapter; @@ -42,6 +43,7 @@ import io.treehouses.remote.pojo.NetworkProfile; import io.treehouses.remote.utils.SaveUtils; +import static android.widget.Toast.LENGTH_LONG; import static io.treehouses.remote.Constants.REQUEST_ENABLE_BT; @@ -55,7 +57,6 @@ public class HomeFragment extends BaseHomeFragment implements SetDisconnect { private ExpandableListView network_profiles; private BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); - private BluetoothChatService mChatService = null; private Button connectRpi, getStarted, testConnection; private Boolean connectionState = false; private Boolean result = false; @@ -73,8 +74,8 @@ public class HomeFragment extends BaseHomeFragment implements SetDisconnect { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.activity_home_fragment, container, false); - mChatService = listener.getChatService(); connectRpi = view.findViewById(R.id.btn_connect); getStarted = view.findViewById(R.id.btn_getStarted); preferences = PreferenceManager.getDefaultSharedPreferences(getActivity()); @@ -155,11 +156,10 @@ public void onActivityCreated(@Nullable Bundle savedInstanceState) { } - private void connectRpiListener() { + public void connectRpiListener() { connectRpi.setOnClickListener(v -> { if (connectionState) { - RPIDialogFragment.getInstance().bluetoothCheck("unregister"); - mChatService.stop(); + mChatService.disconnect(); connectionState = false; checkConnectionState(); return; @@ -181,7 +181,7 @@ public void testConnectionListener() { List options = Arrays.asList(getResources().getStringArray(R.array.led_options)); String[] options_code = getResources().getStringArray(R.array.led_options_commands); selected_LED = options.indexOf(preference); - writeToRPI(options_code[selected_LED]); + mChatService.write(options_code[selected_LED]); testConnectionDialog = showTestConnectionDialog(false, "Testing Connection...", R.string.test_connection_message, selected_LED); testConnectionDialog.show(); result = false; @@ -189,19 +189,18 @@ public void testConnectionListener() { } public void checkConnectionState() { - mChatService = listener.getChatService(); if (mChatService.getState() == Constants.STATE_CONNECTED) { + mChatService.updateHandler(mHandler); showLogDialog(preferences); transitionOnConnected(); connectionState = true; checkVersionSent = true; - writeToRPI("treehouses remote version " + BuildConfig.VERSION_CODE + "\n"); + mChatService.write("treehouses remote version " + BuildConfig.VERSION_CODE + "\n"); } else { transitionDisconnected(); connectionState = false; } - mChatService.updateHandler(mHandler); } private void transitionOnConnected() { @@ -236,10 +235,6 @@ private void dismissTestConnection() { } } - private void writeToRPI(String ping) { - mChatService.write(ping.getBytes()); - } - @Override public void onAttach(@NonNull Context context) { super.onAttach(context); @@ -256,7 +251,7 @@ private void checkVersion(String output) { showUpgradeCLI(); } else if(BuildConfig.VERSION_CODE == 2 || output.contains("true")) { - writeToRPI("treehouses remote check\n"); + mChatService.write("treehouses remote check\n"); } else if (output.contains("false")){ AlertDialog alertDialog = new AlertDialog.Builder(getContext()) @@ -291,7 +286,7 @@ private void readMessage(String output) { internetSent = false; if (output.trim().contains("true")) internetstatus.setImageDrawable(getResources().getDrawable(R.drawable.circle_green)); else internetstatus.setImageDrawable(getResources().getDrawable(R.drawable.circle)); - writeToRPI("treehouses upgrade --check\n"); + mChatService.write("treehouses upgrade --check\n"); } else { moreActions(output); @@ -319,10 +314,11 @@ private void moreActions(String output) { } } + /** * The Handler that gets information back from the BluetoothChatService */ - + @SuppressLint("HandlerLeak") private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { @@ -334,16 +330,19 @@ public void handleMessage(Message msg) { readMessage(output); } break; + case Constants.MESSAGE_STATE_CHANGE: + checkConnectionState(); + switch (msg.arg1) { + case Constants.STATE_CONNECTED: + Log.e("Home Fragment", "Bluetooth Connection Status Change: State Connected"); + Toast.makeText(getContext(), "Bluetooth Connected", LENGTH_LONG).show(); + break; + case Constants.STATE_NONE: + Log.e("RPIDialogFragment", "Bluetooth Connection Status Change: State None"); + break; + } + break; } } }; - - @Override - public void onResume() { - super.onResume(); - if (mChatService.getState() == Constants.STATE_CONNECTED) { - checkVersionSent = true; - writeToRPI("treehouses remote version " + BuildConfig.VERSION_CODE + "\n"); - } - } } diff --git a/app/src/main/java/io/treehouses/remote/Fragments/NewNetworkFragment.java b/app/src/main/java/io/treehouses/remote/Fragments/NewNetworkFragment.java index 031166536..fc2af7463 100644 --- a/app/src/main/java/io/treehouses/remote/Fragments/NewNetworkFragment.java +++ b/app/src/main/java/io/treehouses/remote/Fragments/NewNetworkFragment.java @@ -116,7 +116,7 @@ private void updateNetworkText(String mode) { private void updateNetworkMode() { String s = "treehouses networkmode"; - mChatService.write(s.getBytes()); + mChatService.write(s); Toast.makeText(getContext(), "Network Mode updated", Toast.LENGTH_LONG).show(); } diff --git a/app/src/main/java/io/treehouses/remote/Fragments/ServicesDetailsFragment.java b/app/src/main/java/io/treehouses/remote/Fragments/ServicesDetailsFragment.java index 79ba8dc07..afb748b47 100644 --- a/app/src/main/java/io/treehouses/remote/Fragments/ServicesDetailsFragment.java +++ b/app/src/main/java/io/treehouses/remote/Fragments/ServicesDetailsFragment.java @@ -1,5 +1,6 @@ package io.treehouses.remote.Fragments; +import android.annotation.SuppressLint; import android.app.AlertDialog; import android.os.Bundle; import android.os.Handler; @@ -72,7 +73,8 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa return view; } - final Handler handlerDetails = new Handler() { + @SuppressLint("HandlerLeak") + public final Handler handlerDetails = new Handler() { @Override public void handleMessage(Message msg) { @@ -83,7 +85,8 @@ public void handleMessage(Message msg) { break; case Constants.MESSAGE_WRITE: - String write_msg = new String((byte[]) msg.obj); + String write_msg = (String) msg.obj; + Log.d("WRITE", write_msg); break; } } diff --git a/app/src/main/java/io/treehouses/remote/Fragments/ServicesFragment.java b/app/src/main/java/io/treehouses/remote/Fragments/ServicesFragment.java index 2adb93dad..d4e77689d 100644 --- a/app/src/main/java/io/treehouses/remote/Fragments/ServicesFragment.java +++ b/app/src/main/java/io/treehouses/remote/Fragments/ServicesFragment.java @@ -62,7 +62,6 @@ public void onTabReselected(TabLayout.Tab tab) { } mChatService = listener.getChatService(); mChatService.updateHandler(handler); writeToRPI("treehouses remote allservices\n"); - return view; } @@ -91,7 +90,7 @@ else if(a==0) { } break; case Constants.MESSAGE_WRITE: - String write_msg = new String((byte[]) msg.obj); + String write_msg = (String) msg.obj; Log.d("WRITE", write_msg); break; } diff --git a/app/src/main/java/io/treehouses/remote/Fragments/ServicesTabFragment.java b/app/src/main/java/io/treehouses/remote/Fragments/ServicesTabFragment.java index 4eb073b2a..87744184f 100644 --- a/app/src/main/java/io/treehouses/remote/Fragments/ServicesTabFragment.java +++ b/app/src/main/java/io/treehouses/remote/Fragments/ServicesTabFragment.java @@ -64,7 +64,7 @@ public void handleMessage(Message msg) { moreAction(output); break; case Constants.MESSAGE_WRITE: - String write_msg = new String((byte[]) msg.obj); + String write_msg = (String) msg.obj; Log.d("WRITE", write_msg); break; diff --git a/app/src/main/java/io/treehouses/remote/Fragments/StatusFragment.java b/app/src/main/java/io/treehouses/remote/Fragments/StatusFragment.java index 629f69eba..858ea70be 100644 --- a/app/src/main/java/io/treehouses/remote/Fragments/StatusFragment.java +++ b/app/src/main/java/io/treehouses/remote/Fragments/StatusFragment.java @@ -1,5 +1,6 @@ package io.treehouses.remote.Fragments; +import android.annotation.SuppressLint; import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.Context; @@ -56,11 +57,9 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa if (mChatService.getState() == Constants.STATE_CONNECTED) { btStatus.setImageDrawable(getResources().getDrawable(R.drawable.tick)); } - checkStatusNow(); String ping = "hostname"; - byte[] pSend1 = ping.getBytes(); - mChatService.write(pSend1); + mChatService.write(ping); return view; } @@ -114,7 +113,7 @@ private void updateStatus(String readMessage) { rpiVersion = res[3]; writeToRPI("treehouses memory free"); } else if (lastCommand.equals("treehouses memory free")) { - setCard(tvMemory, memoryStatus, "Memory: " + readMessage + "bytes available"); + setCard(tvMemory, memoryStatus, "Memory: " + readMessage + " bytes available"); writeToRPI("treehouses internet"); } else if (lastCommand.equals("treehouses internet")) { checkWifiStatus(readMessage); @@ -137,8 +136,7 @@ private void checkWifiStatus(String readMessage) { private void writeToRPI(String ping) { lastCommand = ping; - byte[] pSend = ping.getBytes(); - mChatService.write(pSend); + mChatService.write(ping); } private void setCard(TextView textView, ImageView tick, String text) { @@ -157,11 +155,11 @@ private void checkUpgradeNow() { } private void checkUpgradeStatus(String readMessage) { checkUpgradeNow(); - if (readMessage.startsWith("false ") && readMessage.length() < 14) { + if (readMessage.contains("false")) { ivUpgrade.setImageDrawable(getResources().getDrawable(R.drawable.tick)); tvUpgrade.setText(String.format("Upgrade Status: Latest Version: %s", rpiVersion)); upgrade.setVisibility(View.GONE); - } else if (readMessage.startsWith("true ") && readMessage.length() < 14){ + } else if (readMessage.contains("true")){ ivUpgrade.setImageDrawable(getResources().getDrawable(R.drawable.tick_png)); tvUpgrade.setText(String.format("Upgrade available from %s to %s", rpiVersion, readMessage.substring(4))); upgrade.setVisibility(View.VISIBLE); @@ -205,16 +203,16 @@ public void onAttach(@NonNull Context context) { /** * The Handler that gets information back from the BluetoothChatService */ + @SuppressLint("HandlerLeak") public final Handler mHandler = new Handler() { + @SuppressLint("HandlerLeak") @Override public void handleMessage(Message msg) { switch (msg.what) { case Constants.MESSAGE_STATE_CHANGE: - checkStatusNow(); break; case Constants.MESSAGE_WRITE: - byte[] writeBuf = (byte[]) msg.obj; - String writeMessage = new String(writeBuf); + String writeMessage = (String) msg.obj; Log.d(TAG, "writeMessage = " + writeMessage); break; case Constants.MESSAGE_READ: diff --git a/app/src/main/java/io/treehouses/remote/Fragments/TerminalFragment.java b/app/src/main/java/io/treehouses/remote/Fragments/TerminalFragment.java index e4215acbd..c255fdfe1 100644 --- a/app/src/main/java/io/treehouses/remote/Fragments/TerminalFragment.java +++ b/app/src/main/java/io/treehouses/remote/Fragments/TerminalFragment.java @@ -136,7 +136,7 @@ public void onDestroy() { // not enabled during onStart(), so we were paused to enable it... // onResume() will be called when ACTION_REQUEST_ENABLE activity returns. if (mChatService != null && mChatService.getState() == Constants.STATE_NONE) { - mChatService.start(); +// mChatService.start(); idle(mPingStatus, pingStatusButton); } } @@ -248,26 +248,18 @@ private void onResultCaseDialogChpass(int resultCode, Intent data) { } } - private void buildJSON() { + private void buildJSON(String s) { try { - JSONObject jsonObject = new JSONObject(jsonString); + JSONObject jsonObject = new JSONObject(s); commands = new Gson().fromJson(jsonObject.toString(), CommandsList.class); if (commands != null) updateArrayAdapters(commands); } catch (JSONException e) { e.printStackTrace(); } } private void handleJson(String readMessage) { - if (jsonReceiving) { - jsonString += readMessage.trim(); - if (jsonString.endsWith("]}")) { - jsonString += readMessage.trim(); - buildJSON(); - jsonReceiving = false; - jsonSent = false; - } - } else if (readMessage.startsWith("{")) { - jsonReceiving = true; - jsonString = readMessage.trim(); + if (readMessage.startsWith("{")) { + buildJSON(readMessage); + jsonSent = false; } } @@ -294,9 +286,6 @@ public void handleMessage(Message msg) { else { handlerCaseRead(readMessage, mPingStatus, pingStatusButton); filterMessages(readMessage, mConversationArrayAdapter, MainApplication.getTerminalList()); } break; - case Constants.MESSAGE_DEVICE_NAME: - handlerCaseName(msg, getActivity()); - break; case Constants.MESSAGE_TOAST: handlerCaseToast(msg); break; diff --git a/app/src/main/java/io/treehouses/remote/Fragments/TunnelFragment.java b/app/src/main/java/io/treehouses/remote/Fragments/TunnelFragment.java index 70f8bac4d..3f1f8139c 100644 --- a/app/src/main/java/io/treehouses/remote/Fragments/TunnelFragment.java +++ b/app/src/main/java/io/treehouses/remote/Fragments/TunnelFragment.java @@ -94,10 +94,6 @@ public void setupChat() { mConversationView.setAdapter(mConversationArrayAdapter); - // Initialize the BluetoothChatService to perform bluetooth connections - if (mChatService.getState() == Constants.STATE_NONE) { - mChatService = new BluetoothChatService(mHandler, getActivity().getApplicationContext()); - } // Initialize the buffer for outgoing messages new StringBuilder(); } @@ -125,17 +121,10 @@ private void onSwitchChecked() { private void configConditions(String readMessage) { if (readMessage.trim().contains("Error")) { - try { - listener.sendMessage("treehouses tor start"); - Thread.sleep(300); - listener.sendMessage("treehouses tor add 80"); - Thread.sleep(300); - listener.sendMessage("treehouses tor add 22"); - Thread.sleep(300); - listener.sendMessage("treehouses tor add 2200"); - } catch (InterruptedException e) { - e.printStackTrace(); - } + listener.sendMessage("treehouses tor start\n"); + listener.sendMessage("treehouses tor add 80\n"); + listener.sendMessage("treehouses tor add 22\n"); + listener.sendMessage("treehouses tor add 2200\n"); } } @@ -158,10 +147,6 @@ public void handleMessage(Message msg) { handlerCaseRead(readMessage, mPingStatus, pingStatusButton); filterMessages(readMessage, mConversationArrayAdapter, MainApplication.getTunnelList()); break; - case Constants.MESSAGE_DEVICE_NAME: - Activity activity = getActivity(); - handlerCaseName(msg, activity); - break; case Constants.MESSAGE_TOAST: handlerCaseToast(msg); break; diff --git a/app/src/main/java/io/treehouses/remote/InitialActivity.java b/app/src/main/java/io/treehouses/remote/InitialActivity.java index 196644094..3f46ead04 100644 --- a/app/src/main/java/io/treehouses/remote/InitialActivity.java +++ b/app/src/main/java/io/treehouses/remote/InitialActivity.java @@ -1,6 +1,7 @@ package io.treehouses.remote; import android.Manifest; +import android.annotation.SuppressLint; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; @@ -48,9 +49,9 @@ public class InitialActivity extends PermissionActivity private static InitialActivity instance = null; private Boolean validBluetoothConnection = false; int REQUEST_COARSE_LOCATION = 99; - private static BluetoothChatService mChatService = null; private String mConnectedDeviceName = null; private NavigationView navigationView; + private BluetoothChatService mChatService; DrawerLayout drawer; private String TAG = "InitialActivity"; @@ -65,16 +66,10 @@ protected void onCreate(Bundle savedInstanceState) { setSupportActionBar(toolbar); drawer = findViewById(R.id.drawer_layout); - + mChatService = getChatService(); checkLocationPermission(); - - if (mChatService == (null)) { - Log.e(TAG, "mChatService Status: NULL"); - mChatService = new BluetoothChatService(mHandler, getApplicationContext()); - } else { - Log.e(TAG, "mChatService Status: " + mChatService.getState()); - mChatService.updateHandler(mHandler); - } + Log.e(TAG, "mChatService Status: " + mChatService.getState()); + mChatService.updateHandler(mHandler); checkStatusNow(); @@ -90,6 +85,7 @@ protected void onCreate(Bundle savedInstanceState) { new GPSService(this); } + public static InitialActivity getInstance() { return instance; } @@ -204,17 +200,9 @@ protected void onActivityResult(int requestCode, int resultCode, @Nullable Inten mChatService.updateHandler(mHandler); } - - @Override - public void setChatService(BluetoothChatService chatService) { - mChatService = chatService; - mChatService.updateHandler(mHandler); - checkStatusNow(); - } - @Override public BluetoothChatService getChatService() { - return mChatService; + return ((MainApplication)this.getApplicationContext()).mChatService; } private void checkStatusNow() { @@ -271,17 +259,14 @@ public void sendMessage(String message) { // Check that there's actually something to send if (message.length() > 0) { // Get the message bytes and tell the BluetoothChatService to write - byte[] send = message.getBytes(); - mChatService.write(send); - - // Reset out string buffer to zero and clear the edit text field -// mOutStringBuffer.setLength(0); + mChatService.write(message); } } /** * The Handler that gets information back from the BluetoothChatService */ + @SuppressLint("HandlerLeak") public final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { @@ -291,14 +276,18 @@ public void handleMessage(Message msg) { case Constants.MESSAGE_DEVICE_NAME: // save the connected device's name mConnectedDeviceName = msg.getData().getString(Constants.DEVICE_NAME); - if (!mConnectedDeviceName.equals("") || mConnectedDeviceName != null) { - Log.e("DEVICE", "" + mConnectedDeviceName); - checkStatusNow(); - } + Log.e("DEVICE", "" + mConnectedDeviceName); + checkStatusNow(); + break; } } }; - + @Override + protected void onDestroy() { + super.onDestroy(); + Log.e(TAG, "onDestroy: "+"DESTROYED"); + mChatService.disconnect(); + } } diff --git a/app/src/main/java/io/treehouses/remote/MainApplication.java b/app/src/main/java/io/treehouses/remote/MainApplication.java index c57537e0c..b69ffdf1f 100644 --- a/app/src/main/java/io/treehouses/remote/MainApplication.java +++ b/app/src/main/java/io/treehouses/remote/MainApplication.java @@ -6,6 +6,7 @@ import java.util.ArrayList; +import io.treehouses.remote.Network.BluetoothChatService; import io.treehouses.remote.utils.SaveUtils; public class MainApplication extends Application { @@ -13,9 +14,12 @@ public class MainApplication extends Application { private static ArrayList terminalList, tunnelList, commandList; public static boolean showLogDialog = true; public static boolean ratingDialog = true; + + public BluetoothChatService mChatService; @Override public void onCreate() { super.onCreate(); + mChatService = new BluetoothChatService(null, getApplicationContext()); terminalList = new ArrayList(); tunnelList = new ArrayList(); commandList = new ArrayList(); diff --git a/app/src/main/java/io/treehouses/remote/Network/BluetoothChatService.java b/app/src/main/java/io/treehouses/remote/Network/BluetoothChatService.java index 04fd5e269..545ad26fa 100644 --- a/app/src/main/java/io/treehouses/remote/Network/BluetoothChatService.java +++ b/app/src/main/java/io/treehouses/remote/Network/BluetoothChatService.java @@ -26,21 +26,31 @@ import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothSocket; import android.content.Context; -import android.content.SharedPreferences; -import android.os.Bundle; import android.os.Handler; -import android.os.Message; import android.util.Log; -import androidx.preference.PreferenceManager; +import com.github.ivbaranov.rxbluetooth.BluetoothConnection; +import com.github.ivbaranov.rxbluetooth.RxBluetooth; + +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; +import java.util.Set; import java.util.UUID; +import io.reactivex.FlowableOperator; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.schedulers.Schedulers; + import io.treehouses.remote.Constants; +import io.treehouses.remote.callback.BluetoothDeviceCallback; /** * This class does all the work for setting up and managing Bluetooth @@ -50,35 +60,31 @@ */ public class BluetoothChatService implements Serializable{ - // Debugging private static final String TAG = "BluetoothChatService"; - // Name for the SDP record when creating server socket private static final String NAME_SECURE = "BluetoothChatSecure"; - //private static final String NAME_INSECURE = "BluetoothChatInsecure"; // well-known SPP UUID 00001101-0000-1000-8000-00805F9B34FB - private static final UUID MY_UUID_SECURE = - UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); + private static final UUID MY_UUID_SECURE = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); + private CompositeDisposable compositeDisposable; + private BluetoothConnection bluetoothConnection; + - //private static final UUID MY_UUID_INSECURE = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); private static String connectedDeviceName = "NULL"; // Member fields - private final BluetoothAdapter mAdapter; private BluetoothDevice mDevice; private static Handler mHandler; -// private AcceptThread mSecureAcceptThread; - //private AcceptThread mInsecureAcceptThread; - private ConnectThread mConnectThread; - private ConnectedThread mConnectedThread; + private int mCurrentState; - private int mNewState; - private boolean bNoReconnect; + private RxBluetooth bluetooth; private Context context; -// private BluetoothSocket socket = null; + private Queue sentCommands; + private boolean switchedHandler = false; + private Handler tempHandler; + private Queue tempToSendCommands; /** * Constructor. Prepares a new BluetoothChat session. * @@ -86,499 +92,175 @@ public class BluetoothChatService implements Serializable{ * @param handler A Handler to send messages back to the UI Activity */ public BluetoothChatService(Handler handler, Context applicationContext) { - mAdapter = BluetoothAdapter.getDefaultAdapter(); - mCurrentState = Constants.STATE_NONE; - mNewState = mCurrentState; - mHandler = handler; this.context = applicationContext; + init(); + } + private void init() { + compositeDisposable = new CompositeDisposable(); + mCurrentState = Constants.STATE_NONE; + mHandler = null; + sentCommands = new LinkedList<>(); + tempToSendCommands = new LinkedList<>(); + bluetooth = new RxBluetooth(context); } - public void updateHandler(Handler handler){ - mHandler = handler; + public void updateHandler(Handler handler, boolean... args){ + Log.e(TAG, "updateHandler:"); + if (!sentCommands.isEmpty() && mCurrentState == Constants.STATE_CONNECTED) { + Log.e(TAG, "updateHandler: with overflow"); + switchedHandler = true; + tempHandler = handler; + tempToSendCommands.clear(); + } else { + Log.e(TAG, "updateHandler: without overflow"); + mHandler = handler; + } } - /** - * Update UI title according to the current state of the chat connection - */ - private synchronized void updateUserInterfaceTitle() { - mCurrentState = getState(); - Log.d(TAG, "updateUserInterfaceTitle() " + mNewState + " -> " + mCurrentState); - mNewState = mCurrentState; + private synchronized void updateUserInterfaceTitle() { + Log.d(TAG, "updateUserInterfaceTitle() " + " -> " + mCurrentState); // Give the new state to the Handler so the UI Activity can update - mHandler.sendMessage(mHandler.obtainMessage(Constants.MESSAGE_STATE_CHANGE, mNewState, -1)); + mHandler.sendMessage(mHandler.obtainMessage(Constants.MESSAGE_STATE_CHANGE, mCurrentState, -1)); } - /** - * Return the current connection state. - */ public synchronized int getState() { return mCurrentState; } - public String getConnectedDeviceName(){return connectedDeviceName;} - /** - * Start the chat service. Specifically start AcceptThread to begin a - * session in listening (server) mode. Called by the Activity onResume() - */ - public synchronized void start() { - Log.d(TAG, "start"); - bNoReconnect = false; - // Cancel any thread attempting to make a connection - if (mConnectThread != null) { - mConnectThread.cancel(); - mConnectThread = null; - } - - // Cancel any thread currently running a connection - if (mConnectedThread != null) { - mConnectedThread.cancel(); - mConnectedThread = null; - } - - // Start the thread to listen on a BluetoothServerSocket -// if (mSecureAcceptThread == null) { -// mSecureAcceptThread = new AcceptThread(true); -// mSecureAcceptThread.start(); -// } -// if (mInsecureAcceptThread == null) { -// mInsecureAcceptThread = new AcceptThread(false); -// mInsecureAcceptThread.start(); -// } - // Update UI title - updateUserInterfaceTitle(); + public String getConnectedDeviceName(){ + if (mDevice != null) return mDevice.getName(); + return "NO DEVICE CONNECTED"; } - /** - * Start the ConnectThread to initiate a connection to a remote device. - * - * @param device The BluetoothDevice to connect - * @param secure Socket Security type - Secure (true) , Insecure (false) - */ - public synchronized void connect(BluetoothDevice device, boolean secure) { - Log.d(TAG, "connect to: " + device); - - // Cancel any thread attempting to make a connection - if (mCurrentState == Constants.STATE_CONNECTING) { - if (mConnectThread != null) { - mConnectThread.cancel(); - mConnectThread = null; - } - } - - // Cancel any thread currently running a connection - if (mConnectedThread != null) { - mConnectedThread.cancel(); - mConnectedThread = null; - } - - // Start the thread to connect with the given device - mConnectThread = new ConnectThread(device, secure); - mConnectThread.start(); - // Update UI title - updateUserInterfaceTitle(); + public void connectToDevice(BluetoothDevice device) { + stopDiscovery(); + mCurrentState = Constants.STATE_CONNECTING; + compositeDisposable.add(bluetooth.connectAsClient(device, MY_UUID_SECURE).subscribeOn(Schedulers.computation()).subscribe( + bluetoothSocket -> { + Log.e(TAG, "connectToDevice: CONNECTED to " + device.getName()); + mDevice = device; + mCurrentState = Constants.STATE_CONNECTED; + bluetooth.cancelDiscovery(); + updateUserInterfaceTitle(); + compositeDisposable.clear(); + startChat(bluetoothSocket); + }, throwable -> { + Log.e(TAG, "connectToDevice: FAILED TO CONNECT"); + mCurrentState = Constants.STATE_FAILED; + updateUserInterfaceTitle(); + init(); + })); } - /** - * Start the ConnectedThread to begin managing a Bluetooth connection - * - * @param socket The BluetoothSocket on which the connection was made - * @param device The BluetoothDevice that has been connected - */ - public synchronized void connected(BluetoothSocket socket, BluetoothDevice - device, final String socketType) { - Log.d(TAG, "connected, Socket Type:" + socketType); - connectedDeviceName = device.getName(); - mDevice = device; - // Cancel the thread that completed the connection - if (mConnectThread != null) { - mConnectThread.cancel(); - mConnectThread = null; - } - - // Cancel any thread currently running a connection - if (mConnectedThread != null) { - mConnectedThread.cancel(); - mConnectedThread = null; - } - - // Cancel the accept thread because we only want to connect to one device -// if (mSecureAcceptThread != null) { -// mSecureAcceptThread.cancel(); -// mSecureAcceptThread = null; -// } -// if (mInsecureAcceptThread != null) { -// mInsecureAcceptThread.cancel(); -// mInsecureAcceptThread = null; -// } - - // Start the thread to manage the connection and perform transmissions - mConnectedThread = new ConnectedThread(socket, socketType); - mConnectedThread.start(); - - // Send the name of the connected device back to the UI Activity - - updateUserInterfaceTitle(); - - Message msg = mHandler.obtainMessage(Constants.MESSAGE_DEVICE_NAME); - Bundle bundle = new Bundle(); - bundle.putString(Constants.DEVICE_NAME, device.getName()); - msg.setData(bundle); - mHandler.sendMessage(msg); - // Update UI title - Log.e(TAG,"Connected"); + private void startChat(BluetoothSocket socket) throws Exception { + Log.e(TAG, "startChat: "+"START READING" ); + bluetoothConnection = new BluetoothConnection(socket); + + compositeDisposable.add(bluetoothConnection.observeByteStream().lift((FlowableOperator) this::getWriter).onBackpressureBuffer().observeOn(AndroidSchedulers.mainThread()).subscribeOn(Schedulers.io()) + .subscribe(s -> { + // READ COMMAND RESPONSE + Log.d(TAG, "READ: " + s); + //Remove the last command because response has been received + if (!sentCommands.isEmpty()) sentCommands.remove(); + //Send the response to the target + if (!switchedHandler) mHandler.obtainMessage(Constants.MESSAGE_READ, s).sendToTarget(); + + //Sent all the waiting commands + if (switchedHandler && sentCommands.isEmpty()) { + Log.e(TAG, "SENT ALL PREVIOUS"); + switchedHandler = false; + mHandler = tempHandler; + writeOverflow(); + } + + }, throwable -> { + Log.e(TAG, "startChat: "+ "ERROR OCCURRED WHILE READING"); + mCurrentState = Constants.STATE_FAILED; + updateUserInterfaceTitle(); + disconnect(); + })); } - /** - * Stop all threads - */ - public synchronized void stop() { - Log.d(TAG, "stop"); - bNoReconnect = true; - if (mConnectThread != null) { - mConnectThread.cancel(); - mConnectThread = null; + private void writeOverflow() { + while (!tempToSendCommands.isEmpty()) { + write(tempToSendCommands.remove()); } + } - if (mConnectedThread != null) { - mConnectedThread.cancel(); - mConnectedThread = null; + public void disconnect() { + Log.e(TAG, "DISCONNECTING"); + if (bluetoothConnection != null) { + bluetoothConnection.closeConnection(); + bluetoothConnection = null; + mDevice = null; } - -// if (mSecureAcceptThread != null) { -// mSecureAcceptThread.cancel(); -// mSecureAcceptThread = null; -// } - -// if (mInsecureAcceptThread != null) { -// mInsecureAcceptThread.cancel(); -// mInsecureAcceptThread = null; -// } mCurrentState = Constants.STATE_NONE; - // Update UI title updateUserInterfaceTitle(); + destroy(); + init(); } - /** - * Write to the ConnectedThread in an unsynchronized manner - * - * @param out The bytes to write - * @see ConnectedThread#write(byte[]) - */ - public void write(byte[] out) { - // Create temporary object - Log.d(TAG, "write: " + new String(out)); - ConnectedThread r; - // Synchronize a copy of the ConnectedThread - synchronized (this) { - if (mCurrentState != Constants.STATE_CONNECTED) return; - r = mConnectedThread; - } - // Perform the write unsynchronized - r.write(out); - } - - /** - * Indicate that the connection attempt failed and notify the UI Activity. - */ - private void connectionFailed() { - // Send a failure message back to the Activity - callHandler("Unable to connect to device"); - - mCurrentState = Constants.STATE_NONE; - // Update UI title + public void startDiscovery(BluetoothDeviceCallback callback) { + Log.e(TAG, "STARTING DISCOVERY"); + if (bluetooth.isDiscovering()) bluetooth.cancelDiscovery(); + bluetooth.startDiscovery(); + mCurrentState = Constants.STATE_LISTEN; updateUserInterfaceTitle(); - - // Start the service over to restart listening mode - BluetoothChatService.this.start(); + compositeDisposable.add(bluetooth.observeDevices() + .observeOn(AndroidSchedulers.mainThread()) + .subscribeOn(Schedulers.io()) + .subscribe(bluetoothDevice -> { + callback.onDeviceFound(bluetoothDevice); + Log.e(TAG, "DEVICE FOUND: "+ bluetoothDevice.getName()+ " ADDRESS: " + bluetoothDevice.getAddress()); + })); } - /** - * Indicate that the connection was lost and notify the UI Activity. - */ - private void connectionLost() { - // Send a failure message back to the Activity - callHandler("Device connection was lost"); - SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); - Log.d(TAG, "connectionLost: "); - if (mDevice != null && !bNoReconnect && preferences.getBoolean("reconnectBluetooth", true)) { - BluetoothChatService.this.connect(mDevice, true); - } else { - mCurrentState = Constants.STATE_NONE; - // Update UI title - updateUserInterfaceTitle(); - // Start the service over to restart listening mode - BluetoothChatService.this.start(); - } + public void stopDiscovery() { + bluetooth.cancelDiscovery(); } - public void callHandler(String message) { - Message msg = mHandler.obtainMessage(Constants.MESSAGE_TOAST); - Bundle bundle = new Bundle(); - bundle.putString(Constants.TOAST, message); - msg.setData(bundle); - mHandler.sendMessage(msg); + public Set getPairedDevices() { + return bluetooth.getBondedDevices(); } - /** - * This thread runs while listening for incoming connections. It behaves - * like a server-side client. It runs until a connection is accepted - * (or until cancelled). - */ - // Uncomment this if phone needs to be able to accept connections from other devices -// private class AcceptThread extends Thread { -// // The local server socket -// private final BluetoothServerSocket mmServerSocket; -// private String mSocketType; -// -// public AcceptThread(boolean secure) { -// BluetoothServerSocket tmp = null; -// mSocketType = secure ? "Secure" : "Insecure"; -// -// // Create a new listening server socket -// try { -//// if (secure) { -// tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE, MY_UUID_SECURE); -//// } else { -//// tmp = mAdapter.listenUsingInsecureRfcommWithServiceRecord(NAME_INSECURE, MY_UUID_INSECURE); -//// } -// } catch (Exception e) { -// Log.e(TAG, "Socket Type: " + mSocketType + "listen() failed", e); -// } -// mmServerSocket = tmp; -// mCurrentState = Constants.STATE_LISTEN; -// } -// -// @Override -// public void run() { -// Log.d(TAG, "Socket Type: " + mSocketType + "BEGIN mAcceptThread" + this); -// setName("AcceptThread" + mSocketType); -// -// // Listen to the server socket if we're not connected -// while (mCurrentState != Constants.STATE_CONNECTED) { -// try { -// // This is a blocking call and will only return on a successful connection or an exception -// Log.e("TAG", "currentState: " + mCurrentState); -// -// socket = mmServerSocket.accept(); -// } catch (Exception e) { -// Log.e(TAG, "Socket Type: " + mSocketType + " accept() failed" + e); -// mCurrentState = Constants.STATE_NONE; -// updateUserInterfaceTitle(); -// checkConnection("connectionCheck"); -// break; -// } -// -// // If a connection was accepted -// if (socket != null) { -// synchronized (BluetoothChatService.this) { -// switch (mCurrentState) { -// case Constants.STATE_LISTEN: -// case Constants.STATE_CONNECTING: -// // Situation normal. Start the connected thread. -// connected(socket, socket.getRemoteDevice(), mSocketType); -// break; -// case Constants.STATE_NONE: -// case Constants.STATE_CONNECTED: -// // Either not ready or already connected. Terminate new socket. -// try { -// mmServerSocket.close(); -// HomeFragment homeFragment = new HomeFragment(); -// homeFragment.checkConnectionState(); -// } catch (IOException e) { -// Log.e(TAG, "Could not close unwanted socket", e); -// } -// break; -// } -// } -// } -// } -// Log.i(TAG, "END mAcceptThread, socket Type: " + mSocketType); -// -// } -// -// public void cancel() { -// Log.d(TAG, "Socket Type" + mSocketType + "cancel " + this); -// try { -// mmServerSocket.close(); -// } catch (IOException e) { -// Log.e(TAG, "Socket Type" + mSocketType + "close() of server failed", e); -// } -// } -// } -// -// private void checkConnection(String message) { -// mCurrentState = getState(); -// if (mCurrentState == Constants.STATE_CONNECTED) { -// Message msg = mHandler.obtainMessage(Constants.MESSAGE_READ); -// Bundle bundle = new Bundle(); -// bundle.putString(Constants.TOAST, message); -// msg.setData(bundle); -// RPIDialogFragment rpiDialogFragment = new RPIDialogFragment(); -// rpiDialogFragment.getmHandler().handleMessage(msg); -// } -// } - - /** - * This thread runs while attempting to make an outgoing connection - * with a device. It runs straight through; the connection either - * succeeds or fails. - */ - private class ConnectThread extends Thread { - private final BluetoothSocket mmSocket; - private final BluetoothDevice mmDevice; - private String mSocketType; - - public ConnectThread(BluetoothDevice device, boolean secure) { - mmDevice = device; - BluetoothSocket tmp = null; - mSocketType = secure ? "Secure" : "Insecure"; - - // Get a BluetoothSocket for a connection with the - // given BluetoothDevice - try { -// if (secure) { - tmp = device.createRfcommSocketToServiceRecord( - MY_UUID_SECURE); -// } else { -// tmp = device.createInsecureRfcommSocketToServiceRecord( -// MY_UUID_INSECURE); -// } - mCurrentState = Constants.STATE_CONNECTING; - } catch (IOException e) { - Log.e(TAG, "Socket Type: " + mSocketType + "create() failed", e); - mCurrentState = Constants.STATE_NONE; - } - mmSocket = tmp; - - } - - public void run() { - Log.i(TAG, "BEGIN mConnectThread SocketType:" + mSocketType); - setName("ConnectThread" + mSocketType); - - // Always cancel discovery because it will slow down a connection - mAdapter.cancelDiscovery(); - - // Make a connection to the BluetoothSocket - try { - // This is a blocking call and will only return on a - // successful connection or an exception - mmSocket.connect(); - } catch (IOException e) { - // Close the socket - try { - mmSocket.close(); - } catch (IOException e2) { - Log.e(TAG, "unable to close() " + mSocketType + - " socket during connection failure", e2); - } - connectionFailed(); - return; - } - - // Reset the ConnectThread because we're done - synchronized (BluetoothChatService.this) { - mConnectThread = null; - } - - // Start the connected thread - connected(mmSocket, mmDevice, mSocketType); - } - - public void cancel() { - try { - mmSocket.close(); - } catch (IOException e) { - Log.e(TAG, "close() of connect " + mSocketType + " socket failed", e); - } - } + public boolean isBluetoothSupported() { + return bluetooth.isBluetoothAvailable(); } - /** - * This thread runs during a connection with a remote device. - * It handles all incoming and outgoing transmissions. - */ - private class ConnectedThread extends Thread { - private final BluetoothSocket mmSocket; - private final InputStream mmInStream; - private final OutputStream mmOutStream; - - public ConnectedThread(BluetoothSocket socket, String socketType) { - Log.d(TAG, "create ConnectedThread: " + socketType); - mmSocket = socket; - InputStream tmpIn = null; - OutputStream tmpOut = null; - - // Get the BluetoothSocket input and output streams - try { - tmpIn = socket.getInputStream(); - Log.d(TAG, " tmpIn = " + tmpIn); - tmpOut = socket.getOutputStream(); - Log.d(TAG, " tmpOut = " + tmpOut); - } catch (IOException e) { - Log.e(TAG, "temp sockets not created", e); - } - - mmInStream = tmpIn; - mmOutStream = tmpOut; - mCurrentState = Constants.STATE_CONNECTED; - } - - public void run() { - Log.i(TAG, "BEGIN mConnectedThread"); - byte[] buffer = new byte[10000]; - int bytes; - String out; - - // Keep listening to the InputStream while connected - while (true) { - try { - // Read from the InputStream - bytes = mmInStream.read(buffer); - out = new String(buffer, 0, bytes); - Log.d(TAG, "out = " + out + "size of out = " + out.length() + ", bytes = " + bytes); - mHandler.obtainMessage(Constants.MESSAGE_READ, bytes, -1, out) - .sendToTarget(); -// mEmulatorView.write(buffer, bytes); - // Send the obtained bytes to the UI Activity - //mHandler.obtainMessage(BlueTerm.MESSAGE_READ, bytes, -1, buffer).sendToTarget(); - } catch (IOException e) { - Log.e(TAG, "disconnected", e); - connectionLost(); - break; - } - } + public boolean isBluetoothEnabled() { + return bluetooth.isBluetoothEnabled(); + } + private void destroy() { + Log.e(TAG, "DESTROYING"); + if (bluetooth != null) { + bluetooth.cancelDiscovery(); } + compositeDisposable.clear(); + mCurrentState = Constants.STATE_NONE; + } - /** - * Write to the connected OutStream. - * - * @param buffer The bytes to write - */ - public void write(byte[] buffer) { - try { - Log.d(TAG, "write: I am in inside write method"); - mmOutStream.write(buffer); - - // Share the sent message back to the UI Activity - mHandler.obtainMessage(Constants.MESSAGE_WRITE, -1, -1, buffer) - .sendToTarget(); - } catch (IOException e) { - Log.e(TAG, "Exception during write", e); - Log.d(TAG, "write: i am in inside write method exception"); + public void write(String message) { + if (bluetoothConnection != null) { + + Log.d(TAG, "write: " + message); + if (switchedHandler) { + Log.e(TAG, "WRITING TO TEMP: "+message); + tempToSendCommands.add(message); + } else { + Log.e(TAG, "SENDING TO BLUETOOTH: "+message); + sentCommands.add(message); + bluetoothConnection.send(message); } + mHandler.obtainMessage(Constants.MESSAGE_WRITE, message).sendToTarget(); + } else { + Log.e(TAG, "Error while writing to bluetooth"); } + } - public void cancel() { - try { - mmSocket.close(); - } catch (IOException e) { - Log.e(TAG, "close() of connect socket failed", e); - } - } + private Subscriber getWriter(Subscriber subscriber) { + return new MyWriter(subscriber); } - } diff --git a/app/src/main/java/io/treehouses/remote/Network/MyWriter.java b/app/src/main/java/io/treehouses/remote/Network/MyWriter.java new file mode 100644 index 000000000..994e8258c --- /dev/null +++ b/app/src/main/java/io/treehouses/remote/Network/MyWriter.java @@ -0,0 +1,52 @@ +package io.treehouses.remote.Network; + +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; + +import java.util.ArrayList; + +public class MyWriter implements Subscriber { + ArrayList buffer = new ArrayList<>(); + Subscriber subscriber; + private static int DELIMITER = '~'; + + public MyWriter(Subscriber subscriber) { + this.subscriber = subscriber; + } + + @Override + public void onSubscribe(Subscription s) { + subscriber.onSubscribe(s); + } + + @Override + public void onNext(Byte b) { + if (b == DELIMITER) emit(); + else buffer.add(b); + } + + @Override + public void onError(Throwable t) { + if (!buffer.isEmpty()) emit(); + subscriber.onError(t); + } + + @Override + public void onComplete() { + if (!buffer.isEmpty()) emit(); + subscriber.onComplete(); + } + + private void emit() { + if (buffer.isEmpty()) { + subscriber.onNext(""); + return; + } + + byte[] bArray = new byte[buffer.size()]; + + for (int i = 0; i < buffer.size(); i++) bArray[i] = buffer.get(i); + subscriber.onNext(new String(bArray)); + buffer.clear(); + } +} diff --git a/app/src/main/java/io/treehouses/remote/bases/BaseDialogFragment.java b/app/src/main/java/io/treehouses/remote/bases/BaseDialogFragment.java index fd7bd1e5c..e49a6cc52 100644 --- a/app/src/main/java/io/treehouses/remote/bases/BaseDialogFragment.java +++ b/app/src/main/java/io/treehouses/remote/bases/BaseDialogFragment.java @@ -5,15 +5,21 @@ import androidx.annotation.NonNull; import androidx.fragment.app.DialogFragment; +import io.treehouses.remote.Network.BluetoothChatService; import io.treehouses.remote.callback.HomeInteractListener; public class BaseDialogFragment extends DialogFragment { public HomeInteractListener listener; + public BluetoothChatService mChatService; @Override public void onAttach(@NonNull Context context) { super.onAttach(context); if (context instanceof HomeInteractListener) listener = (HomeInteractListener) context; + else + throw new RuntimeException("Implement interface first"); + + mChatService = listener.getChatService(); } } diff --git a/app/src/main/java/io/treehouses/remote/bases/BaseFragment.java b/app/src/main/java/io/treehouses/remote/bases/BaseFragment.java index 2e5d2c004..5da0a0465 100644 --- a/app/src/main/java/io/treehouses/remote/bases/BaseFragment.java +++ b/app/src/main/java/io/treehouses/remote/bases/BaseFragment.java @@ -14,8 +14,7 @@ import io.treehouses.remote.callback.HomeInteractListener; public class BaseFragment extends Fragment { - public BluetoothChatService mChatService = null; - public BluetoothAdapter mBluetoothAdapter = null; + public BluetoothChatService mChatService; public HomeInteractListener listener; @@ -26,31 +25,28 @@ public void onAttach(@NonNull Context context) { listener = (HomeInteractListener) context; else throw new RuntimeException("Implement interface first"); + + mChatService = listener.getChatService(); + + } + + public void setupChat() { + } protected void onLoad(Handler mHandler) { mChatService = listener.getChatService(); mChatService.updateHandler(mHandler); - mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); // If the adapter is null, then Bluetooth is not supported - if (mBluetoothAdapter == null) { + if (!mChatService.isBluetoothSupported()) { Toast.makeText(getActivity(), "Bluetooth is not available", Toast.LENGTH_LONG).show(); getActivity().finish(); } - checkStatusNow(); - if (!mBluetoothAdapter.isEnabled()) { + if (!mChatService.isBluetoothEnabled()) { Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableIntent, Constants.REQUEST_ENABLE_BT); - } else { - setupChat(); } } - public void checkStatusNow() { - } - - public void setupChat() { - } - } diff --git a/app/src/main/java/io/treehouses/remote/bases/BaseHomeFragment.java b/app/src/main/java/io/treehouses/remote/bases/BaseHomeFragment.java index 2d0cbce2e..f95817248 100644 --- a/app/src/main/java/io/treehouses/remote/bases/BaseHomeFragment.java +++ b/app/src/main/java/io/treehouses/remote/bases/BaseHomeFragment.java @@ -164,7 +164,7 @@ protected void showRPIDialog(SetDisconnect s) { androidx.fragment.app.DialogFragment dialogFrag = RPIDialogFragment.newInstance(123); ((RPIDialogFragment) dialogFrag).setCheckConnectionState(s); dialogFrag.setTargetFragment(this, Constants.REQUEST_DIALOG_FRAGMENT_HOTSPOT); - dialogFrag.show(getFragmentManager().beginTransaction(), "rpiDialog"); + dialogFrag.show(getParentFragmentManager().beginTransaction(), "rpiDialog"); } protected boolean matchResult(String output, String option1, String option2) { return output.contains(option1) || output.contains(option2); } diff --git a/app/src/main/java/io/treehouses/remote/bases/BaseServicesFragment.java b/app/src/main/java/io/treehouses/remote/bases/BaseServicesFragment.java index 3dd90aeea..d7838fe0f 100644 --- a/app/src/main/java/io/treehouses/remote/bases/BaseServicesFragment.java +++ b/app/src/main/java/io/treehouses/remote/bases/BaseServicesFragment.java @@ -23,8 +23,6 @@ public class BaseServicesFragment extends BaseFragment { private static final int[] MINIMUM_VERSION = {1, 14, 1}; - private String startJson = ""; - private boolean gettingJSON = false; protected ServicesData servicesData; protected void openLocalURL(String url) { @@ -61,7 +59,7 @@ protected boolean checkVersion(int[] versionIntNumber) { return (versionIntNumber[0] == MINIMUM_VERSION[0]) && (versionIntNumber[1] == MINIMUM_VERSION[1]) && (versionIntNumber[2] >= MINIMUM_VERSION[2]); } protected void writeToRPI(String ping) { - mChatService.write(ping.getBytes()); + mChatService.write(ping); } protected void performService(String action, String command, String name) { @@ -130,26 +128,14 @@ protected int performAction(String output, ArrayList services) { if (isError(output)) { i = 0; } - else if (gettingJSON) { - startJson += output.trim(); - if (startJson.endsWith("}}")) { - startJson += output.trim(); - try { - JSONObject jsonObject = new JSONObject(startJson); - servicesData = new Gson().fromJson(jsonObject.toString(), ServicesData.class); - constructServiceList(servicesData, services); - - } catch (JSONException e) { e.printStackTrace(); } - gettingJSON = false; - } - i = 1; - } - else if (output.trim().startsWith("{")) { - Log.d("STARTED", "performAction: "); - startJson = output.trim(); - gettingJSON = true; + else if (output.startsWith("{")) { + try{ + JSONObject jsonObject = new JSONObject(output); + servicesData = new Gson().fromJson(jsonObject.toString(), ServicesData.class); + constructServiceList(servicesData, services); + i = 1; + } catch (JSONException e) { e.printStackTrace();i= 0;} } - return i; } diff --git a/app/src/main/java/io/treehouses/remote/bases/BaseTerminalFragment.java b/app/src/main/java/io/treehouses/remote/bases/BaseTerminalFragment.java index e9ec55edc..20064d83d 100644 --- a/app/src/main/java/io/treehouses/remote/bases/BaseTerminalFragment.java +++ b/app/src/main/java/io/treehouses/remote/bases/BaseTerminalFragment.java @@ -40,7 +40,7 @@ public class BaseTerminalFragment extends BaseFragment{ public String handlerCaseWrite(String TAG, ArrayAdapter mConversationArrayAdapter, Message msg) { - byte[] writeBuf = (byte[]) msg.obj; + String writeBuf = (String) msg.obj; // construct a string from the buffer String writeMessage = new String(writeBuf); if (!writeMessage.contains("google.com") && !writeMessage.contains("remote")) { @@ -50,14 +50,6 @@ public String handlerCaseWrite(String TAG, ArrayAdapter mConversationArr return writeMessage; } - public void handlerCaseName(Message msg, Activity activity ) { - // save the connected device's name - String mConnectedDeviceName = msg.getData().getString(Constants.DEVICE_NAME); - if (null != activity) { - Toast.makeText(activity, "Connected to " + mConnectedDeviceName, Toast.LENGTH_SHORT).show(); - } - } - public void handlerCaseToast(Message msg) { if (null != getActivity()) { Toast.makeText(getActivity(), msg.getData().getString(Constants.TOAST), Toast.LENGTH_SHORT).show(); @@ -121,7 +113,7 @@ private boolean filterMessage(String readMessage) { protected void filterMessages(String readMessage, ArrayAdapter mConversationArrayAdapter, ArrayList list) { //make it so text doesn't show on chat (need a better way to check multiple strings since mConversationArrayAdapter only takes messages line by line) - if (filterMessage(readMessage)) { + if (filterMessage(readMessage) && mConversationArrayAdapter != null) { list.add(readMessage); mConversationArrayAdapter.notifyDataSetChanged(); } diff --git a/app/src/main/java/io/treehouses/remote/bases/PermissionActivity.java b/app/src/main/java/io/treehouses/remote/bases/PermissionActivity.java index 3bf10e6db..258813acd 100644 --- a/app/src/main/java/io/treehouses/remote/bases/PermissionActivity.java +++ b/app/src/main/java/io/treehouses/remote/bases/PermissionActivity.java @@ -12,6 +12,9 @@ import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; +import io.treehouses.remote.MainApplication; +import io.treehouses.remote.Network.BluetoothChatService; + public abstract class PermissionActivity extends AppCompatActivity { private static final int PERMISSION_REQUEST_WIFI = 111; private static final int PERMISSION_REQUEST_CODE_CAMERA = 112; diff --git a/app/src/main/java/io/treehouses/remote/callback/BluetoothDeviceCallback.java b/app/src/main/java/io/treehouses/remote/callback/BluetoothDeviceCallback.java new file mode 100644 index 000000000..4c3bd3b1d --- /dev/null +++ b/app/src/main/java/io/treehouses/remote/callback/BluetoothDeviceCallback.java @@ -0,0 +1,7 @@ +package io.treehouses.remote.callback; + +import android.bluetooth.BluetoothDevice; + +public interface BluetoothDeviceCallback { + void onDeviceFound(BluetoothDevice bluetoothDevice); +} diff --git a/app/src/main/java/io/treehouses/remote/callback/HomeInteractListener.java b/app/src/main/java/io/treehouses/remote/callback/HomeInteractListener.java index f28bac2f3..624e62584 100644 --- a/app/src/main/java/io/treehouses/remote/callback/HomeInteractListener.java +++ b/app/src/main/java/io/treehouses/remote/callback/HomeInteractListener.java @@ -9,5 +9,4 @@ public interface HomeInteractListener { void openCallFragment(Fragment f); // void updateHandler(Handler handler); BluetoothChatService getChatService(); - void setChatService(BluetoothChatService service); } diff --git a/app/src/main/res/layout/activity_splash_screen.xml b/app/src/main/res/layout/activity_splash_screen.xml index d18a98cbe..9cbae0f2c 100644 --- a/app/src/main/res/layout/activity_splash_screen.xml +++ b/app/src/main/res/layout/activity_splash_screen.xml @@ -1,7 +1,7 @@ - diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 70bd3176d..15a80e342 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -41,6 +41,10 @@ @color/colorAccent +