diff --git a/.gitignore b/.gitignore index f6b286c..adc250c 100644 --- a/.gitignore +++ b/.gitignore @@ -14,11 +14,14 @@ gen/ out/ # Gradle files +.gradle .gradle/ build/ +/build # Local configuration file (sdk path, etc) local.properties +/local.properties # Proguard folder generated by Eclipse proguard/ @@ -31,10 +34,15 @@ proguard/ # Android Studio captures folder captures/ +/captures # Intellij *.iml +.idea/ .idea/workspace.xml +/.idea/libraries # Keystore files *.jks + +.DS_Store \ No newline at end of file diff --git a/README.md b/README.md index f5c9bd8..3b18619 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,4 @@ # rpi3-wifi-conf-android Simple Android application to configure wifi over bluetooth for a Raspberry Pi 3 + +Used with [this Python script](https://github.com/brendan-myers/rpi3-wifi-conf) running on the target Rasperry Pi. \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..632ed64 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,26 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 23 + buildToolsVersion "23.0.3" + + defaultConfig { + applicationId "io.skygrid.bluetoothtest" + minSdkVersion 19 + targetSdkVersion 23 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + testCompile 'junit:junit:4.12' + compile 'com.android.support:appcompat-v7:23.3.0' +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..1a1913c --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in C:\Users\Brendan\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..ce41638 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/io/brendanmyers/rpiconf/DeviceAdapter.java b/app/src/main/java/io/brendanmyers/rpiconf/DeviceAdapter.java new file mode 100644 index 0000000..39955b9 --- /dev/null +++ b/app/src/main/java/io/brendanmyers/rpiconf/DeviceAdapter.java @@ -0,0 +1,59 @@ +package io.brendanmyers.rpiconf; + +import android.bluetooth.BluetoothDevice; +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.TextView; + +import java.util.ArrayList; + +public class DeviceAdapter extends ArrayAdapter { + + private LayoutInflater inflater; + private ArrayList devices; + + public DeviceAdapter(Context context, int resource, ArrayList devices) { + super(context, resource, devices); + + this.devices = devices; + inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + } + + @Override + public BluetoothDevice getItem(int position) { + return super.getItem(position); + } + + @Override + public View getDropDownView(int position, View convertView,ViewGroup parent) { + View row = inflater.inflate(R.layout.spinner_devices_padded, parent, false); + + BluetoothDevice device = devices.get(position); + + TextView name = (TextView)row.findViewById(R.id.name_label); + TextView address = (TextView)row.findViewById(R.id.address_label); + + name.setText(device.getName()); + address.setText(device.getAddress()); + + return row; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View row = inflater.inflate(R.layout.spinner_devices, parent, false); + + BluetoothDevice device = devices.get(position); + + TextView name = (TextView)row.findViewById(R.id.name_label); + TextView address = (TextView)row.findViewById(R.id.address_label); + + name.setText(device.getName()); + address.setText(device.getAddress()); + + return row; + } +} diff --git a/app/src/main/java/io/brendanmyers/rpiconf/MainActivity.java b/app/src/main/java/io/brendanmyers/rpiconf/MainActivity.java new file mode 100644 index 0000000..49f6da0 --- /dev/null +++ b/app/src/main/java/io/brendanmyers/rpiconf/MainActivity.java @@ -0,0 +1,200 @@ +package io.brendanmyers.rpiconf; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Set; +import java.util.UUID; + +import android.app.Activity; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothSocket; +import android.content.Intent; +import android.os.Bundle; +import android.view.View; +import android.widget.Button; +import android.widget.Spinner; +import android.widget.TextView; + +public class MainActivity extends Activity { + + BluetoothSocket mmSocket; + + Spinner devicesSpinner; + Button refreshDevicesButton; + TextView ssidTextView; + TextView pskTextView; + Button startButton; + TextView messageTextView; + + private DeviceAdapter adapter_devices; + + final UUID uuid = UUID.fromString("815425a5-bfac-47bf-9321-c5ff980b5e11"); + final byte delimiter = 33; + int readBufferPosition = 0; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + ssidTextView = (TextView) findViewById(R.id.ssid_text); + pskTextView = (TextView) findViewById(R.id.psk_text); + messageTextView = (TextView) findViewById(R.id.messages_text); + + devicesSpinner = (Spinner) findViewById(R.id.devices_spinner); + + refreshDevicesButton = (Button) findViewById(R.id.refresh_devices_button); + startButton = (Button) findViewById(R.id.start_button); + + refreshDevicesButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + refreshDevices(); + } + }); + + startButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + String ssid = ssidTextView.getText().toString(); + String psk = pskTextView.getText().toString(); + + BluetoothDevice device = (BluetoothDevice) devicesSpinner.getSelectedItem(); + (new Thread(new workerThread(ssid, psk, device))).start(); + } + }); + + refreshDevices(); + } + + private void refreshDevices() { + adapter_devices = new DeviceAdapter(this, R.layout.spinner_devices, new ArrayList()); + devicesSpinner.setAdapter(adapter_devices); + + BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + + if (!mBluetoothAdapter.isEnabled()) { + Intent enableBluetooth = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); + startActivityForResult(enableBluetooth, 0); + } + + Set pairedDevices = mBluetoothAdapter.getBondedDevices(); + if (pairedDevices.size() > 0) { + for (BluetoothDevice device : pairedDevices) { + adapter_devices.add(device); + } + } + } + + final class workerThread implements Runnable { + private String ssid; + private String psk; + private BluetoothDevice device; + + public workerThread(String ssid, String psk, BluetoothDevice device) { + this.ssid = ssid; + this.psk = psk; + this.device = device; + } + + public void run() { + clearOutput(); + writeOutput("Starting config update."); + + writeOutput("Device: " + device.getName() + " - " + device.getAddress()); + + try { + mmSocket = device.createRfcommSocketToServiceRecord(uuid); + if (!mmSocket.isConnected()) { + mmSocket.connect(); + Thread.sleep(1000); + } + + writeOutput("Connected."); + + OutputStream mmOutputStream = mmSocket.getOutputStream(); + final InputStream mmInputStream = mmSocket.getInputStream(); + + waitForResponse(mmInputStream, -1); + + writeOutput("Sending SSID."); + + mmOutputStream.write(ssid.getBytes()); + mmOutputStream.flush(); + waitForResponse(mmInputStream, -1); + + writeOutput("Sending PSK."); + + mmOutputStream.write(psk.getBytes()); + mmOutputStream.flush(); + waitForResponse(mmInputStream, -1); + + mmSocket.close(); + + writeOutput("Success."); + + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + + writeOutput("Failed."); + } + + writeOutput("Done."); + } + } + + private void writeOutput(final String text) { + runOnUiThread(new Runnable() { + @Override + public void run() { + String currentText = messageTextView.getText().toString(); + messageTextView.setText(currentText + "\n" + text); + } + }); + } + + private void clearOutput() { + runOnUiThread(new Runnable() { + @Override + public void run() { + messageTextView.setText(""); + } + }); + } + + /* + * TODO actually use the timeout + */ + private void waitForResponse(InputStream mmInputStream, long timeout) throws IOException { + int bytesAvailable; + + while (true) { + bytesAvailable = mmInputStream.available(); + if (bytesAvailable > 0) { + byte[] packetBytes = new byte[bytesAvailable]; + byte[] readBuffer = new byte[1024]; + mmInputStream.read(packetBytes); + + for (int i = 0; i < bytesAvailable; i++) { + byte b = packetBytes[i]; + + if (b == delimiter) { + byte[] encodedBytes = new byte[readBufferPosition]; + System.arraycopy(readBuffer, 0, encodedBytes, 0, encodedBytes.length); + final String data = new String(encodedBytes, "US-ASCII"); + + writeOutput("Received:" + data); + + return; + } else { + readBuffer[readBufferPosition++] = b; + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..9e847a1 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,93 @@ + + + + + + + +