Seperated TNC monitor into regular monitoring of new packets and volume, and a recent packets activity connected via cursor loader. TNC Scanner seperated from Monitoring. Scanner starts TNC service and connects to TNC and returns to main application and TNC monitor can then be run from the main application.
Seperated TNC monitor into regular monitoring of new packets and volume, and a recent packets activity connected via cursor loader. TNC Scanner seperated from Monitoring. Scanner starts TNC service and connects to TNC and returns to main application and TNC monitor can then be run from the main application.

--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -29,16 +29,20 @@
             </intent-filter>

         </activity>
 
-        <activity android:name="com.dryerzinia.aprs_x.test.DeviceScanActivity" />
-        <activity android:name="com.dryerzinia.aprs_x.test.DeviceControlActivity"/>
+        <activity android:name="com.dryerzinia.aprs_x.test.BLETNCScanner" />
+        <activity android:name="com.dryerzinia.aprs_x.test.BLETNCMonitor"/>
+        <activity android:name="com.dryerzinia.aprs_x.test.RecentPacketsActivity" />
 
-		<provider
+   		<provider
        		android:authorities="com.dryerzinia.aprs_x.db"
        		android:name="com.dryerzinia.aprs_x.db.APRSContentProvider"
        		android:exported="true"
 		/>
 
-        <service android:name="com.dryerzinia.aprs_x.services.BLETNCService" android:enabled="true"/>

+        <service
+            android:name="com.dryerzinia.aprs_x.services.BLETNCService"
+            android:enabled="true"
+            android:process=":BLETNCService" />

 
     </application>

 


--- /dev/null
+++ b/res/layout/recent_packets.xml
@@ -1,1 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
 
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_margin="3dp">
+
+    <ListView android:id="@+id/recent_packet_list"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"/>
+</LinearLayout>

--- a/res/menu/main.xml
+++ b/res/menu/main.xml
@@ -12,6 +12,14 @@
           android:orderInCategory="1"

           android:showAsAction="never"

           android:title="@string/menu_ble_scan"/>

+    <item android:id="@+id/ble_monitor"

+          android:orderInCategory="2"

+          android:showAsAction="never"

+          android:title="@string/menu_ble_monitor"/>

+    <item android:id="@+id/recent_packets"

+          android:orderInCategory="3"

+          android:showAsAction="never"

+          android:title="@string/menu_recent_packets"/>

     

 </menu>

 

--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -21,7 +21,9 @@
     <string name="unknown_service">Unknown service</string>
 
     <string name="menu_ble_scan">BLE Scan</string>
-    
+    <string name="menu_ble_monitor">BLE Monitor</string>
+    <string name="menu_recent_packets">Recent Packets</string>
+
     <string name="menu_connect">Connect</string>
     <string name="menu_disconnect">Disconnect</string>
     <string name="menu_scan">Scan</string>

--- a/src/com/dryerzinia/aprs_x/data_model/packet/RawPacket.java
+++ b/src/com/dryerzinia/aprs_x/data_model/packet/RawPacket.java
@@ -60,6 +60,12 @@
 	public void setID(long id){
 
 		this.id = id;
+
+	}
+
+	public void setTimeStamp(long timeStamp){
+
+		this.timeStamp = timeStamp;
 
 	}
 

--- a/src/com/dryerzinia/aprs_x/services/BLETNCService.java
+++ b/src/com/dryerzinia/aprs_x/services/BLETNCService.java
@@ -26,15 +26,12 @@
 import android.bluetooth.BluetoothGattService;
 import android.bluetooth.BluetoothManager;
 import android.bluetooth.BluetoothProfile;
-import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
-import android.os.Binder;
 import android.os.IBinder;
 import android.util.Log;
 
-import java.io.UnsupportedEncodingException;
-import java.util.Arrays;
+import java.lang.System;
 import java.util.List;
 import java.util.UUID;
 
@@ -47,7 +44,8 @@
  * given Bluetooth LE device.
  */
 public class BLETNCService extends Service {
-    private final static String TAG = BLETNCService.class.getSimpleName();
+
+	private final static String TAG = BLETNCService.class.getSimpleName();
 
     private BluetoothManager mBluetoothManager;
     private BluetoothAdapter mBluetoothAdapter;
@@ -60,9 +58,11 @@
     private int packetCurrentLength = 1000;
     private byte[] packetData;
 
-    private static final int STATE_DISCONNECTED = 0;
-    private static final int STATE_CONNECTING = 1;
-    private static final int STATE_CONNECTED = 2;
+    public static final int STATE_DISCONNECTED = 0;
+    public static final int STATE_CONNECTING = 1;
+    public static final int STATE_CONNECTED = 2;
+
+    public static final String BLE_TNC_SERVICE = "00002220-0000-1000-8000-00805f9b34fb";
 
     public final static String ACTION_GATT_CONNECTED =
             "com.example.bluetooth.le.ACTION_GATT_CONNECTED";
@@ -108,7 +108,15 @@
         @Override
         public void onServicesDiscovered(BluetoothGatt gatt, int status) {
             if (status == BluetoothGatt.GATT_SUCCESS) {
-                broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
+
+            	List<BluetoothGattService> services = getSupportedGattServices();
+            	for(BluetoothGattService service : services){
+            		if(service.getUuid().equals(UUID.fromString(BLE_TNC_SERVICE))){
+            			BluetoothGattCharacteristic tncCharacteristic = service.getCharacteristic(UUID_BLE_TNC_PACKET_DATA);
+            			setCharacteristicNotification(tncCharacteristic, true);
+            		}
+            	}
+
             } else {
                 Log.e(TAG, "onServicesDiscovered received: " + status);
             }
@@ -204,147 +212,140 @@
 
         	if(packetData != null){
 	        	if(packetData.length == packetCurrentLength){
-	
+
 	        		Log.d(TAG, "Broadcasting packet");
-	
+
 	        		// Put packet in DB
 	        		RawPacket pkt = RawPacket.fromRawData(packetData);
+	        		pkt.setTimeStamp(System.currentTimeMillis());
 	        		getContentResolver().insert(
 	        			APRSContentProvider.RAW_PACKET_URI,
 	        			RawPacketTable.getContentValues(pkt)
 	        		);
-	
+
+                    final Intent intent = new Intent(action);
+                    intent.putExtra(BLE_TNC_PACKET_DATA, pkt);
+                    sendBroadcast(intent);
+
 	        		packetCurrentLength = MAX_PACKET_LENGTH;
 
 	        		Log.d(TAG, pkt.toString());
 
 	        	} else if(packetData.length < packetCurrentLength && packetCurrentLength != MAX_PACKET_LENGTH){
-	
+
+	        		// Error, don't broadcast
 	        		Log.e(TAG, "Error: Packet to long!");
-	
-	        		// Error, don't broadcast
+
 	        		packetCurrentLength = MAX_PACKET_LENGTH;
-	
+
 	        	}
         	}
 
         }
     }
 
-    public class LocalBinder extends Binder {
-        public BLETNCService getService() {
-            return BLETNCService.this;
-        }
-    }
-
     @Override
     public IBinder onBind(Intent intent) {
-        return mBinder;
+    	return mBinder;
     }
 
     @Override
     public boolean onUnbind(Intent intent) {
-        // After using a given device, you should make sure that BluetoothGatt.close() is called
-        // such that resources are cleaned up properly.  In this particular example, close() is
-        // invoked when the UI is disconnected from the Service.
-        close();
         return super.onUnbind(intent);
     }
 
-    private final IBinder mBinder = new LocalBinder();
-
-    /**
-     * Initializes a reference to the local Bluetooth adapter.
-     *
-     * @return Return true if the initialization is successful.
-     */
-    public boolean initialize() {
-        // For API level 18 and above, get a reference to BluetoothAdapter through
-        // BluetoothManager.
-        if (mBluetoothManager == null) {
-            mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
+    @Override
+    public void onDestroy(){
+    	close();
+    }
+
+    private final IBLETNCService.Stub mBinder = new IBLETNCService.Stub() {
+
+    	public boolean initialize() {
+            // For API level 18 and above, get a reference to BluetoothAdapter through
+            // BluetoothManager.
             if (mBluetoothManager == null) {
-                Log.e(TAG, "Unable to initialize BluetoothManager.");
+                mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
+                if (mBluetoothManager == null) {
+                    Log.e(TAG, "Unable to initialize BluetoothManager.");
+                    return false;
+                }
+            }
+
+            mBluetoothAdapter = mBluetoothManager.getAdapter();
+            if (mBluetoothAdapter == null) {
+                Log.e(TAG, "Unable to obtain a BluetoothAdapter.");
                 return false;
             }
-        }
-
-        mBluetoothAdapter = mBluetoothManager.getAdapter();
-        if (mBluetoothAdapter == null) {
-            Log.e(TAG, "Unable to obtain a BluetoothAdapter.");
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Connects to the GATT server hosted on the Bluetooth LE device.
-     *
-     * @param address The device address of the destination device.
-     *
-     * @return Return true if the connection is initiated successfully. The connection result
-     *         is reported asynchronously through the
-     *         {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
-     *         callback.
-     */
-    public boolean connect(final String address) {
-        if (mBluetoothAdapter == null || address == null) {
-            Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
-            return false;
-        }
-
-        // Previously connected device.  Try to reconnect.
-        if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress)
-                && mBluetoothGatt != null) {
-            Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection.");
-            if (mBluetoothGatt.connect()) {
-                mConnectionState = STATE_CONNECTING;
-                return true;
-            } else {
+
+            return true;
+        }
+
+        /**
+         * Connects to the GATT server hosted on the Bluetooth LE device.
+         *
+         * @param address The device address of the destination device.
+         *
+         * @return Return true if the connection is initiated successfully. The connection result
+         *         is reported asynchronously through the
+         *         {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
+         *         callback.
+         */
+        public boolean connect(final String address) {
+            if (mBluetoothAdapter == null || address == null) {
+                Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
                 return false;
             }
-        }
-
-        final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
-        if (device == null) {
-            Log.w(TAG, "Device not found.  Unable to connect.");
-            return false;
-        }
-        // We want to directly connect to the device, so we are setting the autoConnect
-        // parameter to false.
-        mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
-        Log.d(TAG, "Trying to create a new connection.");
-        mBluetoothDeviceAddress = address;
-        mConnectionState = STATE_CONNECTING;
-        return true;
-    }
-
-    /**
-     * Disconnects an existing connection or cancel a pending connection. The disconnection result
-     * is reported asynchronously through the
-     * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
-     * callback.
-     */
-    public void disconnect() {
-        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
-            Log.w(TAG, "BluetoothAdapter not initialized");
-            return;
-        }
-        mBluetoothGatt.disconnect();
-    }
-
-    /**
-     * After using a given BLE device, the app must call this method to ensure resources are
-     * released properly.
-     */
-    public void close() {
-        if (mBluetoothGatt == null) {
-            return;
-        }
-        mBluetoothGatt.close();
-        mBluetoothGatt = null;
-    }
+
+            // Previously connected device.  Try to reconnect.
+            if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress)
+                    && mBluetoothGatt != null) {
+                Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection.");
+                if (mBluetoothGatt.connect()) {
+                    mConnectionState = STATE_CONNECTING;
+                    return true;
+                } else {
+                    return false;
+                }
+            }
+
+            final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
+            if (device == null) {
+                Log.w(TAG, "Device not found.  Unable to connect.");
+                return false;
+            }
+            // We want to directly connect to the device, so we are setting the autoConnect
+            // parameter to false.
+            mBluetoothGatt = device.connectGatt(BLETNCService.this, false, mGattCallback);
+            Log.d(TAG, "Trying to create a new connection.");
+            mBluetoothDeviceAddress = address;
+            mConnectionState = STATE_CONNECTING;
+            return true;
+        }
+
+        /**
+         * Disconnects an existing connection or cancel a pending connection. The disconnection result
+         * is reported asynchronously through the
+         * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
+         * callback.
+         */
+        public void disconnect() {
+            if (mBluetoothAdapter == null || mBluetoothGatt == null) {
+                Log.w(TAG, "BluetoothAdapter not initialized");
+                return;
+            }
+            mBluetoothGatt.disconnect();
+        }
+
+    	public String getDeviceAddress() {
+    		return mBluetoothDeviceAddress;
+    	}
+
+    	public int getConnectionState() {
+    		return mConnectionState;
+    	}
+
+    };
 
     /**
      * Request a read on a given {@code BluetoothGattCharacteristic}. The read result is reported
@@ -368,7 +369,6 @@
         }
         mBluetoothGatt.writeDescriptor(descriptor);
     }
-
     
     /**
      * Enables or disables notification on a give characteristic.
@@ -407,5 +407,18 @@
 
         return mBluetoothGatt.getServices();
     }
+
+    /**
+     * After using a given BLE device, the app must call this method to ensure resources are
+     * released properly.
+     */
+    public void close() {
+        if (mBluetoothGatt == null) {
+            return;
+        }
+        mBluetoothGatt.close();
+        mBluetoothGatt = null;
+    }
+
 }
 

--- /dev/null
+++ b/src/com/dryerzinia/aprs_x/services/IBLETNCService.aidl
@@ -1,1 +1,15 @@
+package com.dryerzinia.aprs_x.services;
 
+import java.util.List;
+
+interface IBLETNCService {
+
+    boolean initialize();
+
+    boolean connect(String address);
+    void disconnect();
+
+	String getDeviceAddress();
+	int getConnectionState();
+
+}

--- /dev/null
+++ b/src/com/dryerzinia/aprs_x/test/APRSXService.java
@@ -1,1 +1,16 @@
+package com.dryerzinia.aprs_x.test;
 
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+public class APRSXService extends Service {
+
+	@Override
+	public IBinder onBind(Intent intent) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+}
+

--- /dev/null
+++ b/src/com/dryerzinia/aprs_x/test/BLETNCMonitor.java
@@ -1,1 +1,283 @@
-
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.dryerzinia.aprs_x.test;
+
+import android.app.Activity;
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.bluetooth.BluetoothGattService;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.graphics.Color;
+import android.graphics.PorterDuff.Mode;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import com.dryerzinia.aprs_x.R;
+import com.dryerzinia.aprs_x.data_model.packet.RawPacket;
+import com.dryerzinia.aprs_x.services.BLETNCService;
+import com.dryerzinia.aprs_x.services.IBLETNCService;
+
+/**
+ * For a given BLE device, this Activity provides the user interface to connect, display data,
+ * and display GATT services and characteristics supported by the device.  The Activity
+ * communicates with {@code BluetoothLeService}, which in turn interacts with the
+ * Bluetooth LE API.
+ */
+public class BLETNCMonitor extends Activity {
+
+	private final static String TAG = BLETNCMonitor.class.getSimpleName();
+
+    public static final String EXTRAS_DEVICE_NAME = "DEVICE_NAME";
+    public static final String EXTRAS_DEVICE_ADDRESS = "DEVICE_ADDRESS";
+    
+    public static final String BLE_TNC_SERVICE = "00002220-0000-1000-8000-00805f9b34fb";
+    public static final String BLE_TNC_PACKET_DATA = "00002221-0000-1000-8000-00805f9b34fb";
+
+    private String mDeviceName;
+    private String mDeviceAddress;
+    private IBLETNCService mIBLETNCService;
+    private boolean mConnected = false;
+
+    private ArrayList<String> mPacketList;
+    private ArrayAdapter<String> mPacketListArrayAdapter;
+    private ListView mPacketListView;
+
+    private TextView mConnectionState;
+    private ProgressBar volumeBar;
+
+    // Code to manage Service lifecycle.
+    private final ServiceConnection mServiceConnection = new ServiceConnection() {
+
+        @Override
+        public void onServiceConnected(ComponentName componentName, IBinder service) {
+            mIBLETNCService = IBLETNCService.Stub.asInterface(service);
+        	try {
+
+        		mDeviceAddress = mIBLETNCService.getDeviceAddress();
+        		int connectionState = mIBLETNCService.getConnectionState();
+        		if(connectionState == BLETNCService.STATE_DISCONNECTED
+        		 || connectionState == BLETNCService.STATE_CONNECTING){
+        			mConnected = false;
+                    updateConnectionState(R.string.disconnected);
+                    invalidateOptionsMenu();
+        		} else {
+        			mConnected = true;
+                    updateConnectionState(R.string.connected);
+                    invalidateOptionsMenu();
+        		}
+				((TextView) findViewById(R.id.device_address)).setText(mDeviceAddress);
+
+        	} catch (RemoteException e) {
+				Log.e(TAG, "Failed to get info from service!");
+			}
+       	}
+
+        @Override
+        public void onServiceDisconnected(ComponentName componentName) {
+        	mIBLETNCService = null;
+        }
+    };
+
+    // Handles various events fired by the Service.
+    // ACTION_GATT_CONNECTED: connected to a GATT server.
+    // ACTION_GATT_DISCONNECTED: disconnected from a GATT server.
+    // ACTION_GATT_SERVICES_DISCOVERED: discovered GATT services.
+    // ACTION_DATA_AVAILABLE: received data from the device.  This can be a result of read
+    //                        or notification operations.
+    private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            if (BLETNCService.ACTION_GATT_CONNECTED.equals(action)) {
+
+            	mConnected = true;
+                updateConnectionState(R.string.connected);
+                invalidateOptionsMenu();
+
+            } else if (BLETNCService.ACTION_GATT_DISCONNECTED.equals(action)) {
+
+            	mConnected = false;
+                updateConnectionState(R.string.disconnected);
+                invalidateOptionsMenu();
+
+            } else if (BLETNCService.ACTION_DATA_AVAILABLE.equals(action)) {
+
+                if(intent.hasExtra(BLETNCService.BLE_TNC_VOLUME)){
+
+                	int volume = intent.getIntExtra(BLETNCService.BLE_TNC_VOLUME, -1);
+                	if(volume != -1){
+
+                		volumeBar.setProgress(volume);
+                		if(volume > 110){
+                			volumeBar.getProgressDrawable().setColorFilter(Color.RED, Mode.MULTIPLY);
+                		} else if(volume > 70){
+                			volumeBar.getProgressDrawable().setColorFilter(Color.GREEN, Mode.MULTIPLY);
+                		} else {
+                			volumeBar.getProgressDrawable().setColorFilter(Color.BLUE, Mode.MULTIPLY);
+                		}
+
+                	}
+
+                } else if(intent.hasExtra(BLETNCService.BLE_TNC_PACKET_DATA)){
+ 
+                    Log.d(TAG, "Displaying Packet");
+ 
+                        RawPacket pkt = (RawPacket) intent.getSerializableExtra(BLETNCService.BLE_TNC_PACKET_DATA);        
+                        mPacketList.add(pkt.toString());
+                        mPacketListArrayAdapter.notifyDataSetChanged();
+
+                }
+
+            }
+        }
+    };
+
+    @SuppressWarnings("unchecked")
+	@Override
+    public void onCreate(Bundle savedInstanceState) {
+
+    	super.onCreate(savedInstanceState);
+        setContentView(R.layout.gatt_services_characteristics);
+
+        // Get previous Packet List if it exists
+        if(savedInstanceState != null) {
+            mPacketList = (ArrayList<String>) savedInstanceState.getSerializable("mPacketList");
+        } else {
+        	mPacketList = new ArrayList<String>();
+        }
+        
+        // Sets up UI references
+        ((TextView) findViewById(R.id.device_address)).setText("");
+        mPacketListView = (ListView) findViewById(R.id.packet_list);
+        mPacketListArrayAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mPacketList);
+        mPacketListView.setAdapter(mPacketListArrayAdapter);
+
+        mConnectionState = (TextView) findViewById(R.id.connection_state);
+
+        volumeBar = (ProgressBar) findViewById(R.id.volume_bar);
+        volumeBar.setProgress(0);
+
+        getActionBar().setTitle(mDeviceName);
+        getActionBar().setDisplayHomeAsUpEnabled(true);
+
+        Intent gattServiceIntent = new Intent(this, BLETNCService.class);
+        bindService(gattServiceIntent, mServiceConnection, 0);
+        registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter());
+
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        if (mIBLETNCService != null) {
+            //final boolean result = mBluetoothLeService.connect(mDeviceAddress);
+            //Log.d(TAG, "Connect request result=" + result);
+        }
+    }
+
+    @Override
+    protected void onSaveInstanceState(final Bundle outState) {
+        outState.putSerializable("mPacketList", mPacketList);
+    }
+    
+    @Override
+    protected void onPause() {
+        super.onPause();
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        unregisterReceiver(mGattUpdateReceiver);
+        unbindService(mServiceConnection);
+        mIBLETNCService = null;
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        getMenuInflater().inflate(R.menu.gatt_services, menu);
+        if (mConnected) {
+            menu.findItem(R.id.menu_connect).setVisible(false);
+            menu.findItem(R.id.menu_disconnect).setVisible(true);
+        } else {
+            menu.findItem(R.id.menu_connect).setVisible(true);
+            menu.findItem(R.id.menu_disconnect).setVisible(false);
+        }
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch(item.getItemId()) {
+            case R.id.menu_connect:
+            	try {
+            		mIBLETNCService.connect(mDeviceAddress);
+            	} catch (RemoteException e) {
+            		e.printStackTrace();
+            		return false;
+            	}
+                return true;
+            case R.id.menu_disconnect:
+            	try {
+            		mIBLETNCService.disconnect();
+            	} catch (RemoteException e) {
+            		e.printStackTrace();
+            		return false;
+            	}
+                return true;
+            case android.R.id.home:
+                onBackPressed();
+                return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    private void updateConnectionState(final int resourceId) {
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mConnectionState.setText(resourceId);
+            }
+        });
+    }
+
+    private static IntentFilter makeGattUpdateIntentFilter() {
+        final IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(BLETNCService.ACTION_GATT_CONNECTED);
+        intentFilter.addAction(BLETNCService.ACTION_GATT_DISCONNECTED);
+        intentFilter.addAction(BLETNCService.ACTION_DATA_AVAILABLE);
+        return intentFilter;
+    }
+
+}
+

--- /dev/null
+++ b/src/com/dryerzinia/aprs_x/test/BLETNCScanner.java
@@ -1,1 +1,355 @@
-
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.dryerzinia.aprs_x.test;
+
+import android.app.Activity;
+import android.app.ListActivity;
+import android.app.ProgressDialog;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.bluetooth.BluetoothGattService;
+import android.bluetooth.BluetoothManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import com.dryerzinia.aprs_x.R;
+import com.dryerzinia.aprs_x.services.BLETNCService;
+import com.dryerzinia.aprs_x.services.IBLETNCService;
+
+/**
+ * Activity for scanning and displaying available Bluetooth LE devices.
+ */
+public class BLETNCScanner extends ListActivity {
+
+	private final static String TAG = BLETNCScanner.class.getSimpleName();
+
+	private LeDeviceListAdapter mLeDeviceListAdapter;
+    private BluetoothAdapter mBluetoothAdapter;
+    private boolean mScanning;
+    private Handler mHandler;
+
+    private static final int REQUEST_ENABLE_BT = 1;
+    // Stops scanning after 10 seconds.
+    private static final long SCAN_PERIOD = 10000;
+
+    private IBLETNCService mIBLETNCService;
+    private ProgressDialog mProgressDialog;
+    private String mDeviceAddress;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        getActionBar().setTitle(R.string.title_devices);
+        mHandler = new Handler();
+
+        // Use this check to determine whether BLE is supported on the device.  Then you can
+        // selectively disable BLE-related features.
+        if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
+            Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
+            finish();
+        }
+
+        // Initializes a Bluetooth adapter.  For API level 18 and above, get a reference to
+        // BluetoothAdapter through BluetoothManager.
+        final BluetoothManager bluetoothManager =
+                (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
+        mBluetoothAdapter = bluetoothManager.getAdapter();
+
+        // Checks if Bluetooth is supported on the device.
+        if (mBluetoothAdapter == null) {
+            Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show();
+            finish();
+            return;
+        }
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        getMenuInflater().inflate(R.menu.ble_scan, menu);
+        if (!mScanning) {
+            menu.findItem(R.id.menu_stop).setVisible(false);
+            menu.findItem(R.id.menu_scan).setVisible(true);
+            menu.findItem(R.id.menu_refresh).setActionView(null);
+        } else {
+            menu.findItem(R.id.menu_stop).setVisible(true);
+            menu.findItem(R.id.menu_scan).setVisible(false);
+            menu.findItem(R.id.menu_refresh).setActionView(
+                    R.layout.actionbar_indeterminate_progress);
+        }
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.menu_scan:
+                mLeDeviceListAdapter.clear();
+                scanLeDevice(true);
+                break;
+            case R.id.menu_stop:
+                scanLeDevice(false);
+                break;
+        }
+        return true;
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        // Ensures Bluetooth is enabled on the device.  If Bluetooth is not currently enabled,
+        // fire an intent to display a dialog asking the user to grant permission to enable it.
+        if (!mBluetoothAdapter.isEnabled()) {
+            if (!mBluetoothAdapter.isEnabled()) {
+                Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
+                startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
+            }
+        }
+
+        // Initializes list view adapter.
+        mLeDeviceListAdapter = new LeDeviceListAdapter();
+        setListAdapter(mLeDeviceListAdapter);
+        scanLeDevice(true);
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        // User chose not to enable Bluetooth.
+        if (requestCode == REQUEST_ENABLE_BT && resultCode == Activity.RESULT_CANCELED) {
+            finish();
+            return;
+        }
+        super.onActivityResult(requestCode, resultCode, data);
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        scanLeDevice(false);
+        mLeDeviceListAdapter.clear();
+    }
+
+    private final ServiceConnection mServiceConnection = new ServiceConnection() {
+
+        @Override
+        public void onServiceConnected(ComponentName componentName, IBinder service) {
+        	mIBLETNCService = IBLETNCService.Stub.asInterface(service);
+            try {
+            	if (!mIBLETNCService.initialize()) {
+            		Log.e(TAG, "Unable to initialize Bluetooth");
+            		finish();
+            	}
+            	// Automatically connects to the device upon successful start-up initialization.
+
+				mIBLETNCService.connect(mDeviceAddress);
+
+            } catch (RemoteException e) {
+				e.printStackTrace();
+			}
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName componentName) {
+        	mIBLETNCService = null;
+        }
+    };
+
+    private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            if (BLETNCService.ACTION_GATT_CONNECTED.equals(action)) {
+
+                // Once tnc is set up close dialog and go back to main activity
+            	mProgressDialog.dismiss();
+                unregisterReceiver(mGattUpdateReceiver);
+                unbindService(mServiceConnection);
+            	finish();
+
+            }
+        }
+    };
+
+    @Override
+    protected void onListItemClick(ListView l, View v, int position, long id) {
+
+    	final BluetoothDevice device = mLeDeviceListAdapter.getDevice(position);
+        if (device == null) return;
+
+        if (mScanning) {
+            mBluetoothAdapter.stopLeScan(mLeScanCallback);
+            mScanning = false;
+        }
+
+        // Create Progress Dialog
+        mProgressDialog = ProgressDialog.show(this, "Connecting...", "Conneting to bluetooth TNC...", true);
+        mProgressDialog.setCancelable(false);
+        // Connect to service
+        mDeviceAddress = device.getAddress();
+        Intent gattServiceIntent = new Intent(this, BLETNCService.class);
+        startService(gattServiceIntent);
+        bindService(gattServiceIntent, mServiceConnection, 0);
+        registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter());
+        // On timeout close progress bar show fail dialog stay in scan activity
+
+        /*
+        final Intent intent = new Intent(this, DeviceControlActivity.class);
+        intent.putExtra(DeviceControlActivity.EXTRAS_DEVICE_NAME, device.getName());
+        intent.putExtra(DeviceControlActivity.EXTRAS_DEVICE_ADDRESS, device.getAddress());        
+        startActivity(intent);
+        */
+
+    }
+
+    private static IntentFilter makeGattUpdateIntentFilter() {
+        final IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(BLETNCService.ACTION_GATT_CONNECTED);
+        return intentFilter;
+    }
+
+    private void scanLeDevice(final boolean enable) {
+        if (enable) {
+            // Stops scanning after a pre-defined scan period.
+            mHandler.postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    mScanning = false;
+                    mBluetoothAdapter.stopLeScan(mLeScanCallback);
+                    invalidateOptionsMenu();
+                }
+            }, SCAN_PERIOD);
+
+            mScanning = true;
+            mBluetoothAdapter.startLeScan(mLeScanCallback);
+        } else {
+            mScanning = false;
+            mBluetoothAdapter.stopLeScan(mLeScanCallback);
+        }
+        invalidateOptionsMenu();
+    }
+
+    // Adapter for holding devices found through scanning.
+    private class LeDeviceListAdapter extends BaseAdapter {
+        private ArrayList<BluetoothDevice> mLeDevices;
+        private LayoutInflater mInflator;
+
+        public LeDeviceListAdapter() {
+            super();
+            mLeDevices = new ArrayList<BluetoothDevice>();
+            mInflator = BLETNCScanner.this.getLayoutInflater();
+        }
+
+        public void addDevice(BluetoothDevice device) {
+            if(!mLeDevices.contains(device)) {
+                mLeDevices.add(device);
+            }
+        }
+
+        public BluetoothDevice getDevice(int position) {
+            return mLeDevices.get(position);
+        }
+
+        public void clear() {
+            mLeDevices.clear();
+        }
+
+        @Override
+        public int getCount() {
+            return mLeDevices.size();
+        }
+
+        @Override
+        public Object getItem(int i) {
+            return mLeDevices.get(i);
+        }
+
+        @Override
+        public long getItemId(int i) {
+            return i;
+        }
+
+        @Override
+        public View getView(int i, View view, ViewGroup viewGroup) {
+            ViewHolder viewHolder;
+            // General ListView optimization code.
+            if (view == null) {
+                view = mInflator.inflate(R.layout.listitem_device, null);
+                viewHolder = new ViewHolder();
+                viewHolder.deviceAddress = (TextView) view.findViewById(R.id.device_address);
+                viewHolder.deviceName = (TextView) view.findViewById(R.id.device_name);
+                view.setTag(viewHolder);
+            } else {
+                viewHolder = (ViewHolder) view.getTag();
+            }
+
+            BluetoothDevice device = mLeDevices.get(i);
+            final String deviceName = device.getName();
+            if (deviceName != null && deviceName.length() > 0)
+                viewHolder.deviceName.setText(deviceName);
+            else
+                viewHolder.deviceName.setText(R.string.unknown_device);
+            viewHolder.deviceAddress.setText(device.getAddress());
+
+            return view;
+        }
+    }
+
+    // Device scan callback.
+    private BluetoothAdapter.LeScanCallback mLeScanCallback =
+            new BluetoothAdapter.LeScanCallback() {
+
+        @Override
+        public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
+            runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    mLeDeviceListAdapter.addDevice(device);
+                    mLeDeviceListAdapter.notifyDataSetChanged();
+                }
+            });
+        }
+    };
+
+    static class ViewHolder {
+        TextView deviceName;
+        TextView deviceAddress;
+    }
+}

--- a/src/com/dryerzinia/aprs_x/test/DeviceControlActivity.java
+++ /dev/null
@@ -1,298 +1,1 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
 
-package com.dryerzinia.aprs_x.test;
-
-import android.app.Activity;
-import android.app.LoaderManager;
-import android.bluetooth.BluetoothGattCharacteristic;
-import android.bluetooth.BluetoothGattService;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.CursorLoader;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.Loader;
-import android.content.ServiceConnection;
-import android.database.Cursor;
-import android.graphics.Color;
-import android.graphics.PorterDuff.Mode;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.CursorAdapter;
-import android.widget.ListView;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-
-import java.util.List;
-import java.util.UUID;
-
-import com.dryerzinia.aprs_x.R;
-import com.dryerzinia.aprs_x.data_model.packet.RawPacket;
-import com.dryerzinia.aprs_x.db.APRSContentProvider;
-import com.dryerzinia.aprs_x.db.table.RawPacketTable;
-import com.dryerzinia.aprs_x.services.BLETNCService;
-
-/**
- * For a given BLE device, this Activity provides the user interface to connect, display data,
- * and display GATT services and characteristics supported by the device.  The Activity
- * communicates with {@code BluetoothLeService}, which in turn interacts with the
- * Bluetooth LE API.
- */
-public class DeviceControlActivity extends Activity implements LoaderManager.LoaderCallbacks<Cursor> {
-
-	private final static String TAG = DeviceControlActivity.class.getSimpleName();
-
-    public static final String EXTRAS_DEVICE_NAME = "DEVICE_NAME";
-    public static final String EXTRAS_DEVICE_ADDRESS = "DEVICE_ADDRESS";
-    
-    public static final String BLE_TNC_SERVICE = "00002220-0000-1000-8000-00805f9b34fb";
-    public static final String BLE_TNC_PACKET_DATA = "00002221-0000-1000-8000-00805f9b34fb";
-
-    private TextView mConnectionState;
-    private String mDeviceName;
-    private String mDeviceAddress;
-    private RawPacketCursorAdapter mPacketListCursorAdapter;
-    private ListView mPacketListView;
-    private ProgressBar volumeBar;
-    private BLETNCService mBluetoothLeService;
-    private boolean mConnected = false;
-
-    // Code to manage Service lifecycle.
-    private final ServiceConnection mServiceConnection = new ServiceConnection() {
-
-        @Override
-        public void onServiceConnected(ComponentName componentName, IBinder service) {
-            mBluetoothLeService = ((BLETNCService.LocalBinder) service).getService();
-            if (!mBluetoothLeService.initialize()) {
-                Log.e(TAG, "Unable to initialize Bluetooth");
-                finish();
-            }
-            // Automatically connects to the device upon successful start-up initialization.
-            mBluetoothLeService.connect(mDeviceAddress);
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName componentName) {
-            mBluetoothLeService = null;
-        }
-    };
-
-    public class RawPacketCursorAdapter extends CursorAdapter {
-
-    	private LayoutInflater mInflater;
-
-		public RawPacketCursorAdapter(Context context, Cursor c, int flags) {
-			super(context, c, flags);
-			mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-		}
-
-		@Override
-		public View newView(Context context, Cursor cursor, ViewGroup parent) {
-			return mInflater.inflate(R.layout.packet_list_item, parent, false);
-		}
-
-		@Override
-		public void bindView(View view, Context context, Cursor cursor) {
-			TextView content = (TextView) view.findViewById(R.id.packet_list_item);
-			byte data[] = cursor.getBlob(cursor.getColumnIndex(RawPacketTable.COLUMN_RAW_DATA));
-			content.setText(RawPacket.fromRawData(data).toString());			
-		}
-    }
-
-    // Handles various events fired by the Service.
-    // ACTION_GATT_CONNECTED: connected to a GATT server.
-    // ACTION_GATT_DISCONNECTED: disconnected from a GATT server.
-    // ACTION_GATT_SERVICES_DISCOVERED: discovered GATT services.
-    // ACTION_DATA_AVAILABLE: received data from the device.  This can be a result of read
-    //                        or notification operations.
-    private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            final String action = intent.getAction();
-            if (BLETNCService.ACTION_GATT_CONNECTED.equals(action)) {
-
-            	mConnected = true;
-                updateConnectionState(R.string.connected);
-                invalidateOptionsMenu();
-
-            } else if (BLETNCService.ACTION_GATT_DISCONNECTED.equals(action)) {
-
-            	mConnected = false;
-                updateConnectionState(R.string.disconnected);
-                invalidateOptionsMenu();
-
-            } else if (BLETNCService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
-
-            	List<BluetoothGattService> services = mBluetoothLeService.getSupportedGattServices();
-            	for(BluetoothGattService service : services){
-            		if(service.getUuid().equals(UUID.fromString(BLE_TNC_SERVICE))){
-            			BluetoothGattCharacteristic tncCharacteristic = service.getCharacteristic(UUID.fromString(BLE_TNC_PACKET_DATA));
-           				mBluetoothLeService.setCharacteristicNotification(tncCharacteristic, true);
-            		}
-            	}
-
-            } else if (BLETNCService.ACTION_DATA_AVAILABLE.equals(action)) {
-
-                if(intent.hasExtra(BLETNCService.BLE_TNC_VOLUME)){
-
-                	int volume = intent.getIntExtra(BLETNCService.BLE_TNC_VOLUME, -1);
-                	if(volume != -1){
-
-                		volumeBar.setProgress(volume);
-                		if(volume > 110){
-                			volumeBar.getProgressDrawable().setColorFilter(Color.RED, Mode.MULTIPLY);
-                		} else if(volume > 70){
-                			volumeBar.getProgressDrawable().setColorFilter(Color.GREEN, Mode.MULTIPLY);
-                		} else {
-                			volumeBar.getProgressDrawable().setColorFilter(Color.BLUE, Mode.MULTIPLY);
-                		}
-
-                	}
-
-                }
-
-            }
-        }
-    };
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.gatt_services_characteristics);
-
-        final Intent intent = getIntent();
-        mDeviceName = intent.getStringExtra(EXTRAS_DEVICE_NAME);
-        mDeviceAddress = intent.getStringExtra(EXTRAS_DEVICE_ADDRESS);
-
-        // Sets up UI references.
-        ((TextView) findViewById(R.id.device_address)).setText(mDeviceAddress);
-        mPacketListView = (ListView) findViewById(R.id.packet_list);
-        //mPacketListArrayAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mPacketList);
-        //mPacketListView.setAdapter(mPacketListArrayAdapter);
-        mConnectionState = (TextView) findViewById(R.id.connection_state);
-        volumeBar = (ProgressBar) findViewById(R.id.volume_bar);
-        volumeBar.setProgress(0);
-
-        // Set-up loader
-        getLoaderManager().initLoader(0, null, this);
-        mPacketListCursorAdapter = new RawPacketCursorAdapter(this, null, 0);
-        mPacketListView.setAdapter(mPacketListCursorAdapter);
-
-        getActionBar().setTitle(mDeviceName);
-        getActionBar().setDisplayHomeAsUpEnabled(true);
-        Intent gattServiceIntent = new Intent(this, BLETNCService.class);
-        bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);
-        registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter());
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        //registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter());
-        if (mBluetoothLeService != null) {
-            final boolean result = mBluetoothLeService.connect(mDeviceAddress);
-            Log.d(TAG, "Connect request result=" + result);
-        }
-    }
-
-    @Override
-    protected void onPause() {
-        super.onPause();
-        //unregisterReceiver(mGattUpdateReceiver);
-    }
-
-    @Override
-    protected void onDestroy() {
-        super.onDestroy();
-        unregisterReceiver(mGattUpdateReceiver);
-        unbindService(mServiceConnection);
-        mBluetoothLeService = null;
-    }
-
-    @Override
-    public boolean onCreateOptionsMenu(Menu menu) {
-        getMenuInflater().inflate(R.menu.gatt_services, menu);
-        if (mConnected) {
-            menu.findItem(R.id.menu_connect).setVisible(false);
-            menu.findItem(R.id.menu_disconnect).setVisible(true);
-        } else {
-            menu.findItem(R.id.menu_connect).setVisible(true);
-            menu.findItem(R.id.menu_disconnect).setVisible(false);
-        }
-        return true;
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        switch(item.getItemId()) {
-            case R.id.menu_connect:
-                mBluetoothLeService.connect(mDeviceAddress);
-                return true;
-            case R.id.menu_disconnect:
-                mBluetoothLeService.disconnect();
-                return true;
-            case android.R.id.home:
-                onBackPressed();
-                return true;
-        }
-        return super.onOptionsItemSelected(item);
-    }
-
-    private void updateConnectionState(final int resourceId) {
-        runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mConnectionState.setText(resourceId);
-            }
-        });
-    }
-
-    private static IntentFilter makeGattUpdateIntentFilter() {
-        final IntentFilter intentFilter = new IntentFilter();
-        intentFilter.addAction(BLETNCService.ACTION_GATT_CONNECTED);
-        intentFilter.addAction(BLETNCService.ACTION_GATT_DISCONNECTED);
-        intentFilter.addAction(BLETNCService.ACTION_GATT_SERVICES_DISCOVERED);
-        intentFilter.addAction(BLETNCService.ACTION_DATA_AVAILABLE);
-        return intentFilter;
-    }
-
-	@Override
-	public Loader<Cursor> onCreateLoader(int id, Bundle args) {
-		String[] projection = { RawPacketTable.COLUMN_ID, RawPacketTable.COLUMN_RAW_DATA };
-		CursorLoader cursorLoader = new CursorLoader(this,
-			APRSContentProvider.RAW_PACKET_URI, projection, null, null, null);
-	    return cursorLoader;
-	}
-
-	@Override
-	public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
-		mPacketListCursorAdapter.swapCursor(data);
-	}
-
-	@Override
-	public void onLoaderReset(Loader<Cursor> loader) {
-		mPacketListCursorAdapter.swapCursor(null);		
-	}
-}
-

--- a/src/com/dryerzinia/aprs_x/test/DeviceScanActivity.java
+++ /dev/null
@@ -1,270 +1,1 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
 
-package com.dryerzinia.aprs_x.test;
-
-import android.app.Activity;
-import android.app.ListActivity;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothManager;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.os.Bundle;
-import android.os.Handler;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.ListView;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import java.util.ArrayList;
-
-import com.dryerzinia.aprs_x.R;
-
-/**
- * Activity for scanning and displaying available Bluetooth LE devices.
- */
-public class DeviceScanActivity extends ListActivity {
-    private LeDeviceListAdapter mLeDeviceListAdapter;
-    private BluetoothAdapter mBluetoothAdapter;
-    private boolean mScanning;
-    private Handler mHandler;
-
-    private static final int REQUEST_ENABLE_BT = 1;
-    // Stops scanning after 10 seconds.
-    private static final long SCAN_PERIOD = 10000;
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        getActionBar().setTitle(R.string.title_devices);
-        mHandler = new Handler();
-
-        // Use this check to determine whether BLE is supported on the device.  Then you can
-        // selectively disable BLE-related features.
-        if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
-            Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
-            finish();
-        }
-
-        // Initializes a Bluetooth adapter.  For API level 18 and above, get a reference to
-        // BluetoothAdapter through BluetoothManager.
-        final BluetoothManager bluetoothManager =
-                (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
-        mBluetoothAdapter = bluetoothManager.getAdapter();
-
-        // Checks if Bluetooth is supported on the device.
-        if (mBluetoothAdapter == null) {
-            Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show();
-            finish();
-            return;
-        }
-    }
-
-    @Override
-    public boolean onCreateOptionsMenu(Menu menu) {
-        getMenuInflater().inflate(R.menu.ble_scan, menu);
-        if (!mScanning) {
-            menu.findItem(R.id.menu_stop).setVisible(false);
-            menu.findItem(R.id.menu_scan).setVisible(true);
-            menu.findItem(R.id.menu_refresh).setActionView(null);
-        } else {
-            menu.findItem(R.id.menu_stop).setVisible(true);
-            menu.findItem(R.id.menu_scan).setVisible(false);
-            menu.findItem(R.id.menu_refresh).setActionView(
-                    R.layout.actionbar_indeterminate_progress);
-        }
-        return true;
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        switch (item.getItemId()) {
-            case R.id.menu_scan:
-                mLeDeviceListAdapter.clear();
-                scanLeDevice(true);
-                break;
-            case R.id.menu_stop:
-                scanLeDevice(false);
-                break;
-        }
-        return true;
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-
-        // Ensures Bluetooth is enabled on the device.  If Bluetooth is not currently enabled,
-        // fire an intent to display a dialog asking the user to grant permission to enable it.
-        if (!mBluetoothAdapter.isEnabled()) {
-            if (!mBluetoothAdapter.isEnabled()) {
-                Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
-                startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
-            }
-        }
-
-        // Initializes list view adapter.
-        mLeDeviceListAdapter = new LeDeviceListAdapter();
-        setListAdapter(mLeDeviceListAdapter);
-        scanLeDevice(true);
-    }
-
-    @Override
-    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
-        // User chose not to enable Bluetooth.
-        if (requestCode == REQUEST_ENABLE_BT && resultCode == Activity.RESULT_CANCELED) {
-            finish();
-            return;
-        }
-        super.onActivityResult(requestCode, resultCode, data);
-    }
-
-    @Override
-    protected void onPause() {
-        super.onPause();
-        scanLeDevice(false);
-        mLeDeviceListAdapter.clear();
-    }
-
-    @Override
-    protected void onListItemClick(ListView l, View v, int position, long id) {
-        final BluetoothDevice device = mLeDeviceListAdapter.getDevice(position);
-        if (device == null) return;
-        final Intent intent = new Intent(this, DeviceControlActivity.class);
-        intent.putExtra(DeviceControlActivity.EXTRAS_DEVICE_NAME, device.getName());
-        intent.putExtra(DeviceControlActivity.EXTRAS_DEVICE_ADDRESS, device.getAddress());
-        if (mScanning) {
-            mBluetoothAdapter.stopLeScan(mLeScanCallback);
-            mScanning = false;
-        }
-        startActivity(intent);
-    }
-
-    private void scanLeDevice(final boolean enable) {
-        if (enable) {
-            // Stops scanning after a pre-defined scan period.
-            mHandler.postDelayed(new Runnable() {
-                @Override
-                public void run() {
-                    mScanning = false;
-                    mBluetoothAdapter.stopLeScan(mLeScanCallback);
-                    invalidateOptionsMenu();
-                }
-            }, SCAN_PERIOD);
-
-            mScanning = true;
-            mBluetoothAdapter.startLeScan(mLeScanCallback);
-        } else {
-            mScanning = false;
-            mBluetoothAdapter.stopLeScan(mLeScanCallback);
-        }
-        invalidateOptionsMenu();
-    }
-
-    // Adapter for holding devices found through scanning.
-    private class LeDeviceListAdapter extends BaseAdapter {
-        private ArrayList<BluetoothDevice> mLeDevices;
-        private LayoutInflater mInflator;
-
-        public LeDeviceListAdapter() {
-            super();
-            mLeDevices = new ArrayList<BluetoothDevice>();
-            mInflator = DeviceScanActivity.this.getLayoutInflater();
-        }
-
-        public void addDevice(BluetoothDevice device) {
-            if(!mLeDevices.contains(device)) {
-                mLeDevices.add(device);
-            }
-        }
-
-        public BluetoothDevice getDevice(int position) {
-            return mLeDevices.get(position);
-        }
-
-        public void clear() {
-            mLeDevices.clear();
-        }
-
-        @Override
-        public int getCount() {
-            return mLeDevices.size();
-        }
-
-        @Override
-        public Object getItem(int i) {
-            return mLeDevices.get(i);
-        }
-
-        @Override
-        public long getItemId(int i) {
-            return i;
-        }
-
-        @Override
-        public View getView(int i, View view, ViewGroup viewGroup) {
-            ViewHolder viewHolder;
-            // General ListView optimization code.
-            if (view == null) {
-                view = mInflator.inflate(R.layout.listitem_device, null);
-                viewHolder = new ViewHolder();
-                viewHolder.deviceAddress = (TextView) view.findViewById(R.id.device_address);
-                viewHolder.deviceName = (TextView) view.findViewById(R.id.device_name);
-                view.setTag(viewHolder);
-            } else {
-                viewHolder = (ViewHolder) view.getTag();
-            }
-
-            BluetoothDevice device = mLeDevices.get(i);
-            final String deviceName = device.getName();
-            if (deviceName != null && deviceName.length() > 0)
-                viewHolder.deviceName.setText(deviceName);
-            else
-                viewHolder.deviceName.setText(R.string.unknown_device);
-            viewHolder.deviceAddress.setText(device.getAddress());
-
-            return view;
-        }
-    }
-
-    // Device scan callback.
-    private BluetoothAdapter.LeScanCallback mLeScanCallback =
-            new BluetoothAdapter.LeScanCallback() {
-
-        @Override
-        public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
-            runOnUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    mLeDeviceListAdapter.addDevice(device);
-                    mLeDeviceListAdapter.notifyDataSetChanged();
-                }
-            });
-        }
-    };
-
-    static class ViewHolder {
-        TextView deviceName;
-        TextView deviceAddress;
-    }
-}

--- /dev/null
+++ b/src/com/dryerzinia/aprs_x/test/RecentPacketsActivity.java
@@ -1,1 +1,113 @@
+package com.dryerzinia.aprs_x.test;
 
+import com.dryerzinia.aprs_x.R;
+import com.dryerzinia.aprs_x.data_model.packet.RawPacket;
+import com.dryerzinia.aprs_x.db.APRSContentProvider;
+import com.dryerzinia.aprs_x.db.table.RawPacketTable;
+
+import android.app.Activity;
+import android.app.LoaderManager;
+import android.content.Context;
+import android.content.CursorLoader;
+import android.content.Loader;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CursorAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+public class RecentPacketsActivity extends Activity implements LoaderManager.LoaderCallbacks<Cursor> {
+
+    private RawPacketCursorAdapter mPacketListCursorAdapter;
+    private ListView mPacketListView;
+
+    public class RawPacketCursorAdapter extends CursorAdapter {
+
+    	private LayoutInflater mInflater;
+
+		public RawPacketCursorAdapter(Context context, Cursor c, int flags) {
+			super(context, c, flags);
+			mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+		}
+
+		@Override
+		public View newView(Context context, Cursor cursor, ViewGroup parent) {
+			return mInflater.inflate(R.layout.packet_list_item, parent, false);
+		}
+
+		@Override
+		public void bindView(View view, Context context, Cursor cursor) {
+			TextView content = (TextView) view.findViewById(R.id.packet_list_item);
+			byte data[] = cursor.getBlob(cursor.getColumnIndex(RawPacketTable.COLUMN_RAW_DATA));
+			content.setText(RawPacket.fromRawData(data).toString());			
+		}
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+
+    	super.onCreate(savedInstanceState);
+        setContentView(R.layout.recent_packets);
+
+        // Sets up UI references.
+        mPacketListView = (ListView) findViewById(R.id.recent_packet_list);
+
+        // Set-up loader
+        getLoaderManager().initLoader(0, null, this);
+        mPacketListCursorAdapter = new RawPacketCursorAdapter(this, null, 0);
+        mPacketListView.setAdapter(mPacketListCursorAdapter);
+
+        getActionBar().setTitle("Recent Packets");
+        getActionBar().setDisplayHomeAsUpEnabled(true);
+
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch(item.getItemId()) {
+            case android.R.id.home:
+                onBackPressed();
+                return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+	@Override
+	public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+		String[] projection = { RawPacketTable.COLUMN_ID, RawPacketTable.COLUMN_RAW_DATA };
+		CursorLoader cursorLoader = new CursorLoader(this,
+			APRSContentProvider.RAW_PACKET_URI, projection, null, null, null);
+	    return cursorLoader;
+	}
+
+	@Override
+	public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+		mPacketListCursorAdapter.swapCursor(data);
+	}
+
+	@Override
+	public void onLoaderReset(Loader<Cursor> loader) {
+		mPacketListCursorAdapter.swapCursor(null);		
+	}
+
+}
+

--- a/src/com/dryerzinia/aprs_x/ui/MainActivity.java
+++ b/src/com/dryerzinia/aprs_x/ui/MainActivity.java
@@ -10,7 +10,9 @@
 

 import com.dryerzinia.aprs_x.R;

 import com.dryerzinia.aprs_x.services.DataManager;

-import com.dryerzinia.aprs_x.test.DeviceScanActivity;

+import com.dryerzinia.aprs_x.test.BLETNCMonitor;

+import com.dryerzinia.aprs_x.test.BLETNCScanner;

+import com.dryerzinia.aprs_x.test.RecentPacketsActivity;

 

 public class MainActivity extends Activity {

 

@@ -55,7 +57,15 @@
 			return true;

 		}

 		if (id == R.id.ble_scan) {

-			Intent intent = new Intent(this, DeviceScanActivity.class);

+			Intent intent = new Intent(this, BLETNCScanner.class);

+			startActivity(intent);

+		}

+		if (id == R.id.ble_monitor) {

+			Intent intent = new Intent(this, BLETNCMonitor.class);

+			startActivity(intent);

+		}

+		if (id == R.id.recent_packets) {

+			Intent intent = new Intent(this, RecentPacketsActivity.class);

 			startActivity(intent);

 		}

 		return super.onOptionsItemSelected(item);