/*
Copyright (C) 2013, TecVis LP, support@tecvis.co.uk
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation as version 2.1 of the License.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.airs.handlerUIs;
import java.util.ArrayList;
import java.util.List;
import com.airs.R;
import com.airs.database.AIRS_database;
import android.app.Activity;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.res.Configuration;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
/**
* Activity to show a hitlist of most visited APs, called from the {@link LocationHandlerUI} preference
*/
public class LocationHandlerAPs extends Activity implements OnClickListener, OnItemSelectedListener, OnItemLongClickListener
{
private static final long FULL_DAY = 1000*60*60*24; // milliseconds per day
private static final long FULL_WEEK = FULL_DAY * 7; // milliseconds per week
private static final long FULL_MONTH = FULL_DAY * 31; // milliseconds per month
// Layout Views
private Button ok_button;
private Spinner duration_spinner;
private ListView hitlist;
private ProgressBar progress;
// adapter for the AP list
private ArrayAdapter<String> mAPsArrayAdapter;
// list of visited APs and their counters
private List<Integer> WiFiCounter;
private List<String> WiFiNames;
private StringBuffer storedWifis;
private String[] adaptiveWifis;
private AsyncTask<String, Long, Long> task;
private long duration = FULL_WEEK;
private long startedTime;
// database stuff
private Cursor values = null;
private AIRS_database database_helper;
private SQLiteDatabase airs_storage;
// preferences
private SharedPreferences settings;
/** Called when the activity is first created.
* @param savedInstanceState a Bundle of the saved state, according to Android lifecycle model
*/
@Override
public void onCreate(Bundle savedInstanceState)
{
// Set up the window layout
super.onCreate(savedInstanceState);
// current time in milliseconds
startedTime = System.currentTimeMillis();
try
{
// get database
database_helper = new AIRS_database(getApplicationContext());
airs_storage = database_helper.getWritableDatabase();
// store pointer to preferences
settings = PreferenceManager.getDefaultSharedPreferences(this);
// retrieve WiFis marked for adaptive GPS
storedWifis = new StringBuffer(settings.getString("LocationHandler::AdaptiveGPS_WiFis", ""));
adaptiveWifis = storedWifis.toString().split("::");
requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
// set content of View
setContentView(R.layout.adaptivehitlist);
getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_title);
// get window title fields
TextView mTitle = (TextView) findViewById(R.id.title_left_text);
mTitle.setText(R.string.AdaptiveGPS);
// get buttons and set onclick listener
ok_button = (Button)findViewById(R.id.hitlist_OK);
ok_button.setOnClickListener(this);
duration_spinner = (Spinner)findViewById(R.id.hitlist_duration);
duration_spinner.setOnItemSelectedListener(this);
progress = (ProgressBar)findViewById(R.id.hitlist_progress);
// populate list of APs now
hitlist = (ListView)findViewById(R.id.hitlist_list);
hitlist.setOnItemLongClickListener(this);
hitlist.setItemsCanFocus(false);
hitlist.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
mAPsArrayAdapter = new ArrayAdapter<String>(this.getApplicationContext(), android.R.layout.simple_list_item_multiple_choice);
// Find and set up the ListView for paired devices
hitlist.setAdapter(mAPsArrayAdapter);
// reserve memory for the counting lists
WiFiCounter = new ArrayList<Integer>();
WiFiNames = new ArrayList<String>();
}
catch(Exception e)
{
finish();
}
}
/** Called when the activity is paused.
*/
@Override
public synchronized void onPause()
{
super.onPause();
}
/** Called when the activity is stopped.
*/
@Override
public void onStop()
{
super.onStop();
}
/** Called when the activity is destroyed.
*/
@Override
public void onDestroy()
{
super.onDestroy();
}
/** Called when the configuration of the activity has changed.
* @param newConfig new configuration after change
*/
@Override
public void onConfigurationChanged(Configuration newConfig)
{
super.onConfigurationChanged(newConfig);
}
/** Called when the Options menu is opened
* @param menu Reference to the {@link android.view.Menu}
*/
@Override
public boolean onPrepareOptionsMenu(Menu menu)
{
MenuInflater inflater;
menu.clear();
inflater = getMenuInflater();
inflater.inflate(R.menu.options_about, menu);
return true;
}
/** Called when an option menu item has been selected by the user
* @param item Reference to the {@link android.view.MenuItem} clicked on
*/
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
switch (item.getItemId())
{
case R.id.main_about:
// call about dialogue
Toast.makeText(getApplicationContext(), R.string.AdaptiveGPSAbout, Toast.LENGTH_LONG).show();
return true;
}
return false;
}
/** Called when a button has been clicked on by the user
* @param v Reference to the {@link android.view.View} of the button
*/
@Override
public void onClick(View v)
{
switch (v.getId())
{
case R.id.hitlist_OK:
// write the selection to the persistent storage
writeNewAPs();
// ...then finish the activity
finish();
break;
}
}
/**
* Called when nothing is selected in spinner
* @param parent Reference to the parent {@link android.view.View}
*/
public void onNothingSelected(AdapterView<?> parent)
{
}
/**
* Called when item in spinner is clicked
* @param parent Reference to the parent {@link android.view.View}
* @param view Reference to the current {@link android.view.View}
* @param pos position of the spinner
* @param id not needed
* @return true if click is consumed
*/
public boolean onItemLongClick(AdapterView<?> parent, View view, int pos, long id)
{
Toast.makeText(getApplicationContext(), "Number of visits at this WiFi AP: " + String.valueOf(WiFiCounter.get(pos)), Toast.LENGTH_LONG).show();
return true;
}
/**
* Called when item in spinner is clicked
* @param parent Reference to the parent {@link android.view.View}
* @param view Reference to the current {@link android.view.View}
* @param pos position of the spinner
* @param id not needed
*/
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id)
{
// change duration
switch(pos)
{
case 0:
duration = FULL_WEEK;
// now gather hit list (again)
task = new GatherTask();
task.execute("");
break;
case 1:
duration = FULL_MONTH;
// now gather hit list (again)
task = new GatherTask();
task.execute("");
break;
}
}
private class GatherTask extends AsyncTask<String, Long, Long>
{
protected Long doInBackground(String... params)
{
int i, j, number_values, reading_index, current_counter;
int v_column;
String query;
String reading;
// issue query to the database
query = new String("SELECT Value from 'airs_values' WHERE Timestamp BETWEEN " + String.valueOf(startedTime - duration) + " AND " + String.valueOf(startedTime) + " AND Symbol='WI'");
values = airs_storage.rawQuery(query, null);
if (values == null)
return Long.valueOf(-1);
// get column index for timestamp and value
v_column = values.getColumnIndex("Value");
if (v_column == -1)
return Long.valueOf(-1);
number_values = values.getCount();
// are there any values?
if (number_values != 0)
{
// move to first row to start
values.moveToFirst();
// go through all values
for (i=0;i<number_values;i++)
{
// get current reading
reading = values.getString(v_column);
// split the value into the different SSIDs
String[] devices = reading.split("\n");
// go through all SSIDs of that reading
for(j=0;j<devices.length;j++)
{
// try to find the reading in the list of already counted APs
reading_index = WiFiNames.indexOf(devices[j]);
// nothing found?
if (reading_index == -1)
{
// skip the " " reading which means no WiFi AP around!!
if (devices[j].compareTo("") != 0)
{
// add SSID to our list of read APs and add a counter with 1 visit
WiFiNames.add(devices[j]);
WiFiCounter.add(1);
}
}
else
{
// get counter of the AP that we found
current_counter = WiFiCounter.get(reading_index);
// one more visit detected
current_counter++;
// write it back to list of counters
WiFiCounter.set(reading_index, current_counter);
}
}
// now move to next row
values.moveToNext();
}
// sort AP list based on visit
sortAPList();
// return task and show values
return Long.valueOf(0);
}
else
return Long.valueOf(-1);
}
protected void onPreExecute()
{
progress.setVisibility(View.VISIBLE);
}
protected void onPostExecute(Long result)
{
int i, j;
String currentWiFi;
// free database memory
values.close();
values = null;
if (result.longValue() == 0)
{
Toast.makeText(getApplicationContext(), R.string.AdaptiveGPS_Hitlist_Click, Toast.LENGTH_LONG).show();
mAPsArrayAdapter.clear();
// now write the names of the visited WiFis into the listview adapter
for (i=0;i<WiFiNames.size();i++)
mAPsArrayAdapter.add(WiFiNames.get(i));
// notify UI thread to redraw
mAPsArrayAdapter.notifyDataSetChanged();
// now set checkmarks in the listview depending on whether the entry is already marked for adaptive GPS
for (i=0;i<WiFiNames.size();i++)
{
// get current WiFi name
currentWiFi = WiFiNames.get(i);
// now see if the WiFi name is already in the adaptive GPS list
for (j=0;j<adaptiveWifis.length;j++)
if (adaptiveWifis[j].compareTo(currentWiFi) == 0)
hitlist.setItemChecked(i, true);
}
// let the progress bar disappear
progress.setVisibility(View.INVISIBLE);
}
else
{
Toast.makeText(getApplicationContext(), R.string.AdaptiveGPS_NothingFound, Toast.LENGTH_LONG).show();
finish();
}
}
}
/**
* sorts the AP list that was crawled by the GatherTask
*/
private void sortAPList()
{
int max, i, j;
int max_counter = 0, total_counters, current_counter;
String copyString;
// get total number of counted APs
total_counters = WiFiCounter.size();
for (i=0;i<total_counters;i++)
{
max = Integer.MIN_VALUE;
// find maximum value of the remaining array
for (j=i;j<total_counters;j++)
{
if (WiFiCounter.get(j) > max)
{
max = WiFiCounter.get(j);
max_counter = j;
}
}
// get the value of the current array position
current_counter = WiFiCounter.get(i);
// copy the current array position to where the maximum value was stored
WiFiCounter.set(max_counter, current_counter);
// now store the maximum value at the current position
WiFiCounter.set(i, max);
// now swap the AP names, too
copyString = WiFiNames.get(max_counter);
WiFiNames.set(max_counter, WiFiNames.get(i));
WiFiNames.set(i, copyString);
}
}
/**
* Checks the list of APs as to which ones need to be deleted from the stored list and which ones to be added
*/
private void writeNewAPs()
{
int i, j;
String currentWiFi;
boolean found;
// go through the entire list to remove anything to be removed
for (i=0;i<hitlist.getCount();i++)
{
// is the current item in the list not checked?
if (hitlist.isItemChecked(i) == false)
{
// get current WiFi name
currentWiFi = WiFiNames.get(i);
found = false;
storedWifis = new StringBuffer("");
// now create a new list without the one to be removed
for (j=0;j<adaptiveWifis.length;j++)
if (adaptiveWifis[j].compareTo(currentWiFi) != 0)
{
if (found == true)
storedWifis.append("::");
storedWifis.append(adaptiveWifis[j]);
found = true;
}
// now re-create the individual marked WiFis again
adaptiveWifis = storedWifis.toString().split("::");
}
}
// now go through the entire list again in order to add what needs to be added
for (i=0;i<hitlist.getCount();i++)
{
// is the current item in the list checked?
if (hitlist.isItemChecked(i) == true)
{
// get current WiFi name
currentWiFi = WiFiNames.get(i);
found = false;
// now see if the WiFi name is already in the adaptive GPS list
for (j=0;j<adaptiveWifis.length;j++)
if (adaptiveWifis[j].compareTo(currentWiFi) == 0)
found = true;
// not found marked already in the stored list -> add to the persistent list
if (found == false)
storedWifis.append("::" + currentWiFi);
}
}
// now write changes to persistent settings
Editor editor = settings.edit();
editor.putString("LocationHandler::AdaptiveGPS_WiFis", storedWifis.toString());
editor.commit();
}
}