0

I have developed an app capable of detecting BLE signals and others parameters. I use a BaseAdapter to develop the ListView for showing each item. The problem is that I want to save those data in a xml file when the scan has finished (after a period of time I have established) but I don´t know how to do it.

In this class I do the scan of BLE and is where I want to initiate the process of saving the List when it has passed the time of scanning:

public class ScanBleActivity extends ScanBaseActivity {

private BluetoothAdapter mBluetoothAdapter;
private boolean mScanning;
private Handler mHandler = new Handler();
//private List<BluetoothDevice> mydata;

// Stops scanning after 10 seconds.
private static final long SCAN_PERIOD = 20000;

/* (non-Javadoc)
 * @see com.zishao.bletest.ScanBaseActivity#initScanBluetooth()
 */
protected void initScanBluetooth() {
    BluetoothManager manager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
    mBluetoothAdapter = manager.getAdapter();
    startScanLen(true);
}

@Override
protected void onDestroy() {
    super.onDestroy();
    if (mScanning) {
        startScanLen(false);
    }
}

/**
 * 
 * @param enable
 */
private void startScanLen(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);
                try {
                    savedata(true);
                } catch (FileNotFoundException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

            }
        }, SCAN_PERIOD);

        mScanning = true;
        mBluetoothAdapter.startLeScan(mLeScanCallback);
    } else {
        mScanning = false;
        mBluetoothAdapter.stopLeScan(mLeScanCallback);
    }
}

And here is my Adapter:

public class LeDeviceListAdapter extends BaseAdapter {
public List<BluetoothDevice> data;
private Activity context;
private final HashMap<BluetoothDevice, Integer> rssiMap = new HashMap<BluetoothDevice, Integer>();



public LeDeviceListAdapter(Activity context, List<BluetoothDevice> data) {
    this.data = data;
    this.context = context;

}
//public static List<BluetoothDevice> getAllData() {
//  return data;
//}

public synchronized void addDevice(BluetoothDevice device, int rssi) {
    if(!data.contains(device) ){
    data.add(device);
    }
    rssiMap.put(device, rssi);
}

@Override
public int getCount() {
    return data.size();
}

@Override
public Object getItem(int position) {
    return data.get(position);
}

@Override
public long getItemId(int position) {
    return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    if (null == convertView) {
        LayoutInflater mInflater =
            (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = mInflater.inflate(R.layout.leaf_devices_list_item, null);
        convertView.setTag(new DeviceView(convertView));
    }
    DeviceView view = (DeviceView) convertView.getTag();
    view.init((BluetoothDevice) getItem(position));
    return convertView;
}

public class DeviceView {



    private TextView title;
    private TextView status;
    private TextView type;
    private TextView address;
    private TextView rssivalue;

    public DeviceView(View view) {
        title = (TextView) view.findViewById(R.id.device_name);
        status = (TextView) view.findViewById(R.id.device_status_txt);
        type = (TextView) view.findViewById(R.id.device_type_txt);
        address = (TextView) view.findViewById(R.id.device_address_txt);
        rssivalue = (TextView) view.findViewById(id.signal_intensity_txt);
    }

    public void init(BluetoothDevice device) {
        title.setText(device.getName());
        address.setText(device.getAddress());
        setType(device.getType());
        setStatus(device.getBondState());
        rssivalue.setText(""+rssiMap.get(device)+" dBm");

    }

    public void setType(int status) {
        switch(status) {
        case BluetoothDevice.DEVICE_TYPE_CLASSIC:
            type.setText("Bluetooth Signal");
            break;
        case BluetoothDevice.DEVICE_TYPE_LE:
            type.setText("BLE Signal");
            break;
        case BluetoothDevice.DEVICE_TYPE_DUAL:
            type.setText("Dual Mode - BR/EDR/LE");
            break;
        case BluetoothDevice.DEVICE_TYPE_UNKNOWN:
            type.setText("Device Unknown");
            break;
        }
    }

    public void setStatus(int s) {
        switch(s) {
        case BluetoothDevice.BOND_NONE:
            status.setText("Not Bonded");
            break;
        case BluetoothDevice.BOND_BONDED:
            status.setText("Bonded");
            break;
        case BluetoothDevice.BOND_BONDING:
            status.setText("Bonding");
            break;
        }
    }


}

I want to save the title, address, type, status and rssivalue (shown in the code above) of each BLE signal that has been found during the scan to be saved in a xml file. I have provided only a part of the project but if it is necessarly I will edit and put the code that is missing.

Does anyone know how to do it?? Please help!!!!!

New code: This corresponds to the class ScanBaseActivity:

abstract public class ScanBaseActivity extends ListActivity {

protected LeDeviceListAdapter mLeDeviceListAdapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_devices_scan);
    mLeDeviceListAdapter = new LeDeviceListAdapter(this, new ArrayList<BluetoothDevice>());
    this.setListAdapter(mLeDeviceListAdapter);
    initScanBluetooth();
}

/**
 * Start Scan Bluetooth
 * 
 */
abstract protected void initScanBluetooth();

@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
    BluetoothDevice device = (BluetoothDevice) mLeDeviceListAdapter.getItem(position);
    ParcelUuid[] uuids = device.getUuids();
    String uuidString = "Getting UUID's from " + device.getName() + ";UUID:";
    if (null != uuids && uuids.length > 0) {
        uuidString += uuids[0].getUuid().toString();
    } else {
        uuidString += "empty";
    }
    Toast.makeText(this, uuidString, Toast.LENGTH_LONG).show();
}

/**
 * @param device
 */
protected synchronized void addDevice(final BluetoothDevice device, final int rssi) {
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            mLeDeviceListAdapter.addDevice(device, rssi);
            mLeDeviceListAdapter.notifyDataSetChanged();
        }
    });
}

protected void savedata(boolean enable) throws FileNotFoundException{

        String filename = "file.txt";

        FileOutputStream fos;
        Bundle extras = getIntent().getExtras();
        long timestamp = extras.getLong("currentTime");
        try {
        fos= openFileOutput(filename, Context.MODE_PRIVATE);
        ObjectOutputStream out = new ObjectOutputStream(fos);
        out.write((int) timestamp);
        out.writeObject(mLeDeviceListAdapter);
        out.write(null);
        out.close();
        Toast.makeText(this, R.string.list_saved, Toast.LENGTH_SHORT).show();
        savedata(false);
        } catch (FileNotFoundException e){
            e.printStackTrace();
        } catch (IOException e){
            e.printStackTrace();
        }

    }
}

New!!: I have edited ScanBaseActivity and ScanBleActivity to introduce the xml saving but when I run the app, when the scan stops it causes an error (moment when the list has to be saved in the sml file). Does anyone know how to solve it or correct it??!!!

  • possible duplicate of [Create xml file and save it in internal storage android](http://stackoverflow.com/questions/11687074/create-xml-file-and-save-it-in-internal-storage-android) – Merlevede Mar 06 '14 at 20:44
  • it is not a duplicate. I need to save all items parameters from the list and also to know how to launch the operation of saving when the scan stops. Do you know how to implement this? – user3365807 Mar 06 '14 at 20:49

2 Answers2

0

Well, it's not that you are going to be saving it from the adapter, it's that the adater is going to be 'adapting' a data set that you will put into preferences.

as a feild:

private SharedPreferences saveHash;

in onCreate:

saveHash = getSharedPreferences( getString( R.string.save_hash ), MODE_PRIVATE );

then later:

public void onFinishedLoading(){
    super.onPause();
    SharedPreferences.Editor editor = saveHash.edit();
    editor.clear();
    for( String s: myData){
        editor.putString(x, s);
    }
    editor.commit();
}

edit: realized you want to make a hash from a list; what do you want as the key?

  • Sorry but I don´t understand it clearly. Is this going to create a xml file? Where I should put the code above? – user3365807 Mar 06 '14 at 20:56
  • oh yes =] It creates a key/value hashmap in an internal xml file =] Now, if you want access to that file independent of the SharePreferences, follow Meleverde's link, but start that process at the end of the async task that is loading that list – anthropic android Mar 06 '14 at 20:59
  • ...because if loading that list takes any amount of time, you've probably moved it off the UI thread onto an asyncTask, and have a onPostExecute method to play with – anthropic android Mar 06 '14 at 21:01
  • I see but I still don´t where I should implement this code or how to import the list of the items. In my app, I used a MainActivity that checks if the bluetooth is enabled and if it can supports the ble. After, it calls the class ScanBleActivity that is the one that is listed above. However, I use another class called ScanBaseActivity that extends from ListActivity that is the one that sets the Adapter and launches the scan from the ScanBleActivity. I will put now the other class so you understand more – user3365807 Mar 06 '14 at 21:11
  • I have uploaded the class that was missing. Where I should modify it and how to implement the code you are saying¿? – user3365807 Mar 06 '14 at 21:16
  • just to be clear, the usual pattern is such that you initialize an adapter with a empty data set, set the adapter on a listview or some such, then later populate that dataset and adapter.notifyDataSetChanged ftw. In that pattern( and in yours )whichever class has the dataset already has the list of items, no need to import. That is the class that will know when this dataset has been populated, and that is the class that can write the data to your xml file – anthropic android Mar 06 '14 at 21:19
  • aaaah, yes, my advice, is to not use the adapter as a data modulation tool, do that in your ScanBaseActivity, that way you can divorce the scanning from the UI in an asyncTask – anthropic android Mar 06 '14 at 21:23
  • also, the constructor method for your adapter will probably need a call to super(context, resId, List) where resId is a layout.row of some sort – anthropic android Mar 06 '14 at 21:25
  • I am very sorry sorry for the inconvinience that I am causing but I still don´t get it. Can you tell what I must put/change in each class that I have put in the thread and where (if it is the case that I have to introduce new code)? It would be great if you could put the code with the changes if it is not a problem for you. I am still a newby in the Android/Java programming. I am sorry for the matters and thanks for your help – user3365807 Mar 06 '14 at 21:48
  • kk, will make another answer, b/c my first was assuming too much. – anthropic android Mar 06 '14 at 21:49
0

OK, first, you need to re-shape how you are dealing with your adapter, after that, everything should fall into place.

so for that, I'm going to outsource to vogella, the cardinal of good android design patterns http://www.vogella.com/tutorials/AndroidListView/article.html

you could read up through section 3, eat some copypasta and come back here, but every extra line you grok is a good thing =]

Now you have an activity that has a list of data, and an adapter that takes that list and--somewhat dumbly in comparison to your code--applies it to a view of some sort. When you want to update that data, you do so by modifying the List object in the activity by your method somewhere that gets a list of Bluetoothdevices--and I'd take that process off thread with an AsyncTask.

Since you are passing a List to the adapter, you can wait until you've populated the data in your activity, then do an adapter.notifyDataSetChanged. You do not want to adapter.add

then you have a nice List of your data in the activity; you don't need to worry if it's the right list, because--given the update pattern--it's the only list!

then follow the link Merleverde posted to serialize that data into an xml, maybe in a utility class, but in your activity is fine.

edit: here is a perfectly good adapter, this is part of the larger pattern that displays dynamically changing data:

public class AnchorListAdapter extends ArrayAdapter<String>
{

private final List<String> anchorNames;
public AnchorListAdapter(Context context, int textviewId, List<String> anchors){
    super(context, textviewId, anchors);
    anchorNames = anchors;
}

@Override
public String getItem( int i ) {
    return anchorNames.get(i).toString();
}

}
  • This is how I have understand so far and correct me if I am mistaken. In my class ScanBaseActivity is where I am creating a list with the same pattern of each item. We do the scanning in ScanBleActivity and the ScanBaseActivity passes to the Adapter the list that has been created with the device found. In the adapter, it fills the information for the item of the list depending on the device found and it returns the updated list to the ScanBaseActivity. It continues until it finishes the scan period and after that my ultimate list is in ScanBaseActivity. I am right? – user3365807 Mar 06 '14 at 22:20
  • almost =] ScanBleActivity[note: this could be a service class or an asyncTask inner class to ScanBaseActivity] returns a list to ScanBaseActivity. ScanBase has constructed an adapter with an empty dataset, and then assigned the adapter to a listview. The adapter basically correlates the list to the view with getCount returning the size of the List, and getView returning List.get(position)( so far, I think we're in agreement ) The adapter returns absolutely nothing; it is modifying a view with strings during getView/Item. – anthropic android Mar 06 '14 at 22:30
  • See edit for a typical adapter for me. ScanBase Activity has gone no-where( it is providing the entire context for these operations, and is presenting the view( e.g. setContentView ) the list is in( e.g. ListView listView = ( ListView )findViewById( R.id.data_list_view ) ); yes, there is a list in the adapter, but that is a slave to the one in ScanBaseActivity, the command to comply is adapter.notifyDataSetChanged(). – anthropic android Mar 06 '14 at 22:36
  • So if I need to save the final list, what I should do is to create a public class after addDevice in ScanBaseActivity, where it is saved the list and work from it with the link that Merlevede has provided, right?? – user3365807 Mar 06 '14 at 22:36
  • =] I'd put that static method in a util class, yeah. – anthropic android Mar 06 '14 at 22:39
  • but inside addDevice or after the bracket that finally closes addDevice?? And sorry for asking again silly questions but "static method in a util class"? I don´t understand what it means, sorry :( – user3365807 Mar 06 '14 at 22:45
  • A static method is one where you don't want to have to make a new object to run it; it depends on no instance variables (feilds), which is pretty much what the xml writer is doing--the same thing every time. There's a lot of little methods that we write that are just supposed to do one thing given certain inputs, regardless of the application's state. I put those in a utils class I define, and make them static. A nice feature of a static method is you just need to say Utils.method(params) without having to make a Utils object – anthropic android Mar 06 '14 at 23:23
  • as for where to do that, I would call it when you know you are done adding things to the list, but before you lose control of the program( onPause is fairly choice, but you'll probably have a point where you clean up and turn off the high-energy search for bluetooth, and that's a good spot, too ) it's not good to be writing to a file over and over again. – anthropic android Mar 06 '14 at 23:27
  • In my opinion, I would implement the class after the final bracket of the addDevice [in ScanBaseActivity] (in here we implement the xml file saving) and in the run method (from ScanBleActivity), when the scanning has stopped, intialize that class so we are sure that has stopped in that moment the scan. Can it be done in this way? If it is so, how can I execute the class in the run method corresponding to other activity? Thanks for your help – user3365807 Mar 07 '14 at 00:28
  • Unless you are talking about an asyncTask you've written, we generally do not execute classes, we call methods in a class; if they are not static methods, we must create an instance of that class--an object--first e.g. String string = new String("string"); When the scanning has stopped, there should either be a callback or a onPostExecute method in which you can call the save method, wherever that is. We are talking about some fundamental concepts of OO vs procedural code here; putting a method call after a method does not mean one will follow the other, in fact, the opposite is true. – anthropic android Mar 07 '14 at 02:01
  • tl;dr: before the final bracket – anthropic android Mar 07 '14 at 02:01
  • can you edit the code from my thread or copy and modify it in a new answer in the way you are saying, pleasseeee?? – user3365807 Mar 07 '14 at 10:01
  • I have modified my code to introduce the xml file saving of the list but it launches an error when it stops scanning. After launching the error executes again the scanning and after the scan period, it causes again the same error and again and again. Do you know what is the problem and how to solve it?? I fit is possible, can you correct the mistake pleasee!!! – user3365807 Mar 07 '14 at 12:23
  • since this site is organized per-question, and not per-incident, how about you post it as a new question and link it from here? please include your logcat so I can tell what error it is – anthropic android Mar 07 '14 at 20:17
  • I posted this question some hours ago: http://stackoverflow.com/questions/22249246/problems-saving-a-list-in-a-xml-file-in-android. I hope that this will help – user3365807 Mar 07 '14 at 20:29
  • That seems like the samn problem from yesterday, remember, keep the adapter simple; don't add data to it. Also, your logcat. you can get that from adb logcat( use adb --help for the exact verbage ) or, better, you can goto the play store and get logcat extreme. that does a nice overlay of the log as you run your app, and can export the log to a file, which you can post here so I know what is wrong. Logcat should be your first stop for all problems, and everyone here asks for it. I'm sure the logcat in your adt bundle can be put to a file, too. – anthropic android Mar 07 '14 at 21:16