Short description of the project Download

Transcript
Friend Finder Application
Evgeny Erlihman
[email protected]
Roman Nassimov
[email protected]
Supervisor
Viktor Kulikov
[email protected]
Software Systems Lab
Department of Electrical Engineering,
Technion - Israel Institute of Technology
1
SHORT DESCRIPTION OF THE PROJECT...............................................................3
INTRODUCTION.............................................................................................................4
MOTIVATION.....................................................................................................................4
Android 1.6 ......................................................................................................................4
PROJECT GOAL..................................................................................................................5
THE SOLUTION...............................................................................................................6
INTRODUCTION..................................................................................................................6
REQUIREMENTS LIST.........................................................................................................7
SERVER PACKAGE .............................................................................................................9
Architecture Overview ..................................................................................................9
Use case Sscenarios....................................................................................................11
Class Diagram............................................................................................................14
USER AGENT PACKAGE...................................................................................................15
Architecture Overview ................................................................................................15
Main Classes...............................................................................................................15
Class Diagram............................................................................................................17
2
Short description of the project
The project goal is to build a client-server application witch will allow it user to
find each other geographically using GPS receiver.
The client application will run on an Andriod OS based cellular device witch will
include at least a GPS receiver and some sort of internet connection (3G or WiFi). The
device will transmit it’s own geographical coordinates to a predefined server, according
to privacy rules as set by the application user.
The server application will run on a PC machine with an internet connection, it’s
role is to store online user’s coordinates and transmit them if requested by other users of
the application.
Both sides of the application are implemented using standard java libraries on the
devices. The Android side is off course implemented using the java libraries that are
present on the device (v1.6).
3
Introduction
Motivation
In the recent time the abilities of the mobile devices are growing and with it the
variety of applications that they can run. The mobiles have more hardware functions as
other hardware devices such as GPS locators. The key of successful applications these
days is integration. Why use mobile phone and GPS device if you can use both in a single
device. The device will have the ability to locate yourself on the map, locate your friends
on the map and more important will allow the communication with those friends
according to their location or without it. This application will provide an easy way to find
the whereabouts of your friends and the way to get to them.
Android 1.6
Is the platform on witch this applications is developed, what means that the
application will run on any version of android above and including 1.6. The Android
platform has a quick progress and is being updated each couple of months. Providing the
3rd party developers an user friendly API for developing new applications and a good
solutions for the existing problems of the older versions.
4
Project Goal
The main goals of this project are:

Developing an independent Server witch will answer to request coming from the
Android Clients.

Developing Android Client witch will be able to send self coordinates to the
server, receive coordinates of other users, and display a GUI to the user.
As Educational project further goals are:

Learning to use SQL server in a java program. A complicated task that involves
the learning of the apache SQL server API etc.

Learning the API for 3rd party developers for Android based platforms.
5
The Solution
FFinder application designed to give it’s user a clear whereabouts of his friends relatively
to his own, and some direction of getting to them using a certain GUI. The whole project
is written using the java language, the client side uses android java libraries and the
server side uses the standard java .
In the next part we list the requirements of the project. These requirements will be listed
and described by different scenarios that describe how our application should behave.
Then we shall describe the design of solution to give a good feeling of the system
architecture.
6
Functional Requirements
Server requirements
1. Receive XML format data from handsets and store in SQL based Database. We
shall refer to data as to user’s location, his IP address, actual phone number and
online/offline status.
2. Send appropriate data to handset upon request.
3. Manage blacklist for each user.
4. Display logs to screen (incoming/outgoing messages in XML format)
Technology used
Java DB
Java DB's embedded mode makes it simple to develop, deploy, and use applications,
while allowing easy migration to client/server mode. Java DB is 100% pure Java, so it
fits seamlessly with any Java development model.
Java DB supports the standards we are already familiar with -- Java, JDBC, ANSI SQL.
Under development for over a decade, Java DB is robust and stable.
Java DB technology adheres to the JDBC and ANSI/ISO SQL standards. This means
Java DB provides the functionality expected of a sophisticated relational database,
including SQL syntax, transaction management, concurrency, triggers, and backups. It
also means that it is easy to migrate an application using Java DB to and from other
standards-based databases, such as PostgreSQL, Oracle and DB2.
Java DB is Sun's supported distribution of the open source Apache Derby database. Java
DB is written in Java, providing "write once, run anywhere" portability. Its ease of use,
standards compliance, full feature set, and small footprint make it the ideal database for
Java developers. It can be embedded in Java applications, requiring zero administration
by the developer or user. It can also be used in client server mode. Java DB is fully
transactional and provides a standard SQL interface as well as a JDBC 4.0 compliant
driver. The Apache Derby project has a strong and growing community that includes
7
developers from large companies such as Sun Microsystems and IBM as well as
individual contributors.
Java DB is ideal for departmental Java client-server applications that need up to 24 x 7
support and the sophistication of a transactional SQL database that protects against data
corruption without requiring a database administrator.
Java DB is extremely easy to use, making it ideal for Java application development and
testing. It can run on a laptop, is available at no cost under the Apache license, and is also
full-featured.
You can also use Java DB in embedded applications where there is no need for the
developer or the end-user to buy, download, install, administer - or even be aware of - the
database separately from the application.
Java DB is highly portable. And, because Java DB is fully standards-compliant, it is easy
to migrate an application between Java DB and other open standard databases.
Dom XML parser
The DOM is a W3C (World Wide Web Consortium) standard.
The DOM defines a standard for accessing documents like XML and HTML:
"The W3C Document Object Model (DOM) is a platform and language-neutral interface
that allows programs and scripts to dynamically access and update the content, structure,
and style of a document."
The DOM is separated into 3 different parts / levels:



Core DOM - standard model for any structured document
XML DOM - standard model for XML documents
HTML DOM - standard model for HTML documents
The DOM defines the objects and properties of all document elements, and the
methods (interface) to access them.
We used DOM in order to parse incoming and construct outgoing messages.
8
Client Application
The Client application will constantly update the server with it’s current GPS
coordinates, receive updated coordinates of the friend that the user seeks and display the
calculated information to the user.
The client application will manage the user settings as privacy, blacklist, findebilty etc.
Architecture overview
Server is divided to 2 singleton always working threads (database thread and
communicator thread) and to unlimited number processing threads (called
DataReceivers).
Database thread is the only thread that has access to DB and can perform updates to
tables stored at DB.
Communicator thread listens on socket for user requests to connect and whenever there is
one – DataReceiver thread is opened.
DataReceiver – receives user queries and updates the DB using Database thread. After
update, or data retrievals the DataReceiver thread is closed.
Client Package
The client package implements the client application. Is separated into several threads,
each responsible for a certain aspect of the client functionality.
As the android programming principles dictates, no heavy operations should be executed
in the GUI thread. Therefore the client is divided to number of threads that approximately
match the number of client’s functionalities.
9
10
Architecture Overview
The Model
The application is divided to two different processes. The GPG process runs
independently from the others, it’s purpose is to update the GUI process (if it is running)
and the remote server about new coordinates. The process can be turned on and off from
the GUI process. The GUI process has several threads in it to ensure smooth presentation
to the user while commencing network operations and other calculations in the
background.
GUI
GPS
2 threads
1 thread
The following activity is the first activity the user sees when he opens the application.
This activity is designed to give the User the ability of launching other acivities and
services that the user might need, using the application
public class firstScreen extends Activity {
private LinkedList<Contact> contactsList;
private LinkedList<String> contactsNameList;
private Communicator myCommunicator;
private Intent myPathActivity;
private Intent myBlackListActivity;
private String myPhoneNumber;
/** Called when the activity is first created. */
@Override
We create and initialize all the activity’s variables and structures when it is first created.
public void onCreate(Bundle savedInstanceState) {
11
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
contactsList = new LinkedList<Contact>();
contactsNameList = new LinkedList<String>();
myCommunicator = new Communicator(Defenitions.SEND_COM);
myPathActivity = new Intent(this,PathActivity.class);
myBlackListActivity = new Intent(this,BlackListActivity.class);
TelephonyManager telephony = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
myPhoneNumber = telephony.getLine1Number();
myPhoneNumber = "528027455";
}
/* Creates the menu items */
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0, Defenitions.MENU_BLACK_LIST, 0, "Manage Black List").setIcon(R.drawable.equalizer);
menu.add(0, Defenitions.MENU_FIND_FRIEND, 0, "Find a Friend ").setIcon(R.drawable.magnet);
menu.add(0, Defenitions.MENU_GO_OFFLINE, 0, "Go Offline").setIcon(R.drawable.disc);
menu.add(0, Defenitions.MENU_GO_ONLINE, 0, "Go Online").setIcon(R.drawable.globe);
return true;
}
/* Handles item selections */
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case Defenitions.MENU_FIND_FRIEND:
findFriend();
return true;
case Defenitions.MENU_GO_ONLINE:
goOnline();
return true;
case Defenitions.MENU_GO_OFFLINE:
goOffline();
return true;
case Defenitions.MENU_BLACK_LIST:
manageBlackList();
return true;
}
return false;
}
public void findFriend(){
Get all friends from local contact list.
// Form an array specifying which columns to return.
String[] projection = new String[] {
People._ID,
People.NAME,
People.NUMBER
};
// Get the base URI for the People table in the Contacts content provider.
Uri contacts = People.CONTENT_URI;
// Make the query.
Cursor managedCursor = managedQuery(contacts,
projection, // Which columns to return
null,
// Which rows to return (all rows)
null,
// Selection arguments (none)
// Put the results in ascending order by name
People.NAME + " ASC");
12
if (managedCursor.moveToFirst()) {
String name;
String phoneNumber;
int nameColumn = managedCursor.getColumnIndex(People.NAME);
int phoneColumn = managedCursor.getColumnIndex(People.NUMBER);
do {
// Get the field values
name = managedCursor.getString(nameColumn);
phoneNumber = managedCursor.getString(phoneColumn);
// Do something with the values.
contactsList.addLast(new Contact(name,phoneNumber));
contactsNameList.addLast(name);
} while (managedCursor.moveToNext());
}
ListView myListView = (ListView) findViewById(R.id.ContactsList);
ArrayAdapter<String> myArrayAdapter= new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,contactsNameList);
myListView.setAdapter(myArrayAdapter);
myListView.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,long arg3) {
Toast toast = Toast.makeText(getApplicationContext(), "You chose: " + (String)contactsNameList.get(arg2), oast.LENGTH_LONG);
toast.show();
Check if the wanted user is available to be found.
Contact findy = contactsList.get(arg2);
//if(myCommunicator.checkifOnline(findy.getPhone())){
if(myCommunicator.checkifOnline(myPhoneNumber,"528027455")){
myPathActivity.putExtra("friend_name", findy.getPhone());
//myCommunicator.closeServerSocket();
startActivity(myPathActivity);
}
}
});
}
public void goOnline(){
//SelfLocationUpdater will send our current location to the server as soon as it starts, so we need not notify the server from here
Intent i = new Intent();
i.setClassName( "com.FFinder","com.FFinder.SelfLocationUpdater" );
startService(i);
}
public void goOffline(){
Intent i = new Intent();
i.setClassName( "com.FFinder","com.FFinder.SelfLocationUpdater" );
stopService(i);
}
public void manageBlackList(){
startActivity(myBlackListActivity);
}
}
13
The next service is designed to run in a separate procces, get updates from the GPS and
send them to the server.
public class SelfLocationUpdater extends Service{
final RemoteCallbackList<IRemoteServiceCallback> mCallbacks = new RemoteCallbackList<IRemoteServiceCallback>();
private LocationManager mgr;
private myLocationListenerClass myLocationListener;
private Location myLocation;
private Communicator myCommunicator;
private String myPhoneNumber;
A listener for location changes updates. It notifies the GUI to make a change.
private class myLocationListenerClass implements LocationListener{
@Override
public void onLocationChanged(Location location) {
Log.v("SelfLocationUpdater", "New Location: " + location.getLatitude() + location.getLongitude());
// Broadcast to all clients the new value.
final int N = mCallbacks.beginBroadcast();
for (int i=0; i<N; i++) {
try {
mCallbacks.getBroadcastItem(i).locationChanged(location);
} catch (RemoteException e) {
// The RemoteCallbackList will take care of removing
// the dead object for us.
}
}
mCallbacks.finishBroadcast();
myLocation = location;
// Send new Self Location to the server
myCommunicator.sendNewSelfLocation(location,myPhoneNumber);
}
@Override
public void onProviderDisabled(String provider) {
// TODO Auto-generated method stub
}
@Override
public void onProviderEnabled(String provider) {
// TODO Auto-generated method stub
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
// TODO Auto-generated method stub
}
}
We create and initialize all the activity’s variables and structures when it is first created.
Also, this is where the service notifies the OS that it should receive updates about
location changes.
@Override
public void onCreate() {
Log.v("SelfLocationUpdater", "Launched");
myLocationListener = new myLocationListenerClass();
myCommunicator = new Communicator(Defenitions.SEND_COM);
14
TelephonyManager telephony = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
myPhoneNumber = telephony.getLine1Number();
myPhoneNumber = "528027455";
mgr = (LocationManager) getSystemService(LOCATION_SERVICE);
Criteria criteria = new Criteria();
String best = mgr.getBestProvider(criteria, true);
Log.v("SelfLocationUpdater", "Best provider is: " + best);
mgr.requestLocationUpdates(best, 15000, 1, myLocationListener);
Location location = mgr.getLastKnownLocation(best);
if(location == null) return; //protection in case there is no last known location - may be impossible unless emulator
// Send Self Current Location to the server
myCommunicator.sendNewSelfLocation(location,myPhoneNumber);
Log.v("SelfLocationUpdater", "Locations (starting with last known): " + location.getLatitude() + location.getLongitude());
}
@Override
public void onStart( Intent intent, int startId ) {
super.onStart(intent, startId);
Log.v("SelfLocationUpdater", "onStart");
For us to be able to actually stop this service, we need the OS to think that it is active
rather then hung on listener events. So a simple single thread is used for this purpose, it
just sleeps.
Thread t = new Thread() {
public void run() {
for(int i = 0; ; i++)
{
// this is done in order to keep the service "alive" and give us the ability to actually stop it
SystemClock.sleep(500000000);
}
}
};
t.start();
}
@Override
public void onDestroy() {
Log.v("SelfLocationUpdater", "onDestroy");
mgr.removeUpdates(myLocationListener);
// Send "we go offline" notification to the server
myCommunicator.sendOffLineNotification(myPhoneNumber);
}
@Override
public IBinder onBind(Intent arg0) {
return mBinder;
}
Next part is for IPC.
private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
15
public void registerCallback(IRemoteServiceCallback cb) {
if (cb != null) mCallbacks.register(cb);
}
public void unregisterCallback(IRemoteServiceCallback cb) {
if (cb != null) mCallbacks.unregister(cb);
}
@Override
public Location getCurrentLocation() throws RemoteException {
return myLocation;
}
};
}
The next activity is responsible for showing the user a kind of GUI. It’s inputs are, local
GPS and remote server location updates of the wanted client.
public class PathActivity extends Activity{
IRemoteService mService = null;
private Location myLocation = null;
private Location hisLocation = null;
private Communicator myCommunicator;
private String myPhoneNumber;
// Need handler for callbacks to the UI thread
final Handler mHandler = new Handler();
// Create runnable for posting
final Runnable mUpdateResults = new Runnable() {
public void run() {
updateView(myLocation,hisLocation);
}
};
Upon creation of this activity, it gets some input parameters from the activity that started
it, and creates the GUI object.
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
myCommunicator = new Communicator(Defenitions.GET_COM);
TelephonyManager telephony = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
myPhoneNumber = telephony.getLine1Number();
// get current location of the person we want to find
String findy_phone = getIntent().getStringExtra("friend_name");
Communicator temp_Communicator = new Communicator(Defenitions.SEND_COM);
hisLocation = temp_Communicator.getCurrentLocation(myPhoneNumber,findy_phone);
Director mv = new Director(this);
mv.setSelfLocation(myLocation,hisLocation);
setContentView(mv);
}
16
When the activity is actually started it opens an IPC protocol line with the GPS procces in
order to get updates of location changes.
@Override
protected void onStart() {
super.onStart();
Intent i = new Intent();
i.setClassName( "com.FFinder","com.FFinder.SelfLocationUpdater" );
bindService(i,mConnection, Context.BIND_AUTO_CREATE);
// THe purpose of this thread is to wait for other location updates from the Server
Thread t = new Thread() {
public void run() {
while(true)
{
hisLocation = myCommunicator.waitForNewLocation();
mHandler.post(mUpdateResults);
}
}
};
t.start();
}
public void updateView(Location location1,Location location2){
Director mv = new Director(this);
mv.setSelfLocation(location1,location2);
setContentView(mv);
Log.v("PathFinder", "New Location: ");
}
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
// This is called when the connection with the service has been
// established, giving us the service object we can use to
// interact with the service. We are communicating with our
// service through an IDL interface, so get a client-side
// representation of that from the raw service object.
mService = IRemoteService.Stub.asInterface(service);
// We want to monitor the service for as long as we are
// connected to it.
try {
mService.registerCallback(mCallback);
//myLocation = mService.getCurrentLocation();
} catch (RemoteException e) {
// In this case the service has crashed before we could even
// do anything with it; we can count on soon being
// disconnected (and then reconnected if it can be restarted)
// so there is no need to do anything here.
}
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mService = null;
17
}
};
/**
* This implementation is used to receive callbacks from the remote
* service.
*/
private IRemoteServiceCallback mCallback = new IRemoteServiceCallback.Stub() {
/**
* This is called by the remote service regularly to tell us about
* new values. Note that IPC calls are dispatched through a thread
* pool running in each process, so the code executing here will
* NOT be running in our main thread like most other things -- so,
* to update the UI, we need to use a Handler to hop over there.
*/
@Override
public void locationChanged(Location location){
myLocation = location;
mHandler.post(mUpdateResults);
Log.v("PathFinder", "got New Location b");
}
@Override
public void currentLocation(Location location) throws RemoteException {
myLocation = location;
};
};
}
The next class is being used in the previous activity for presenting the GUI to the user.
package com.FFinder;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.location.Location;
import android.util.AttributeSet;
import android.view.View;
public class Director extends View {
private float direction = 45.0f;
//private Double myLatitude = 32.775812;
//private Double myLongatiude = 35.025063;
//private Double hisLatitude = 32.777752;
//private Double hisLongatiude = 35.021743;
private Location myLocation = null;
private Location hisLocation = null;
public float getDirection() {
return direction;
}
public void setDirection(float direction) {
this.direction = direction;
18
invalidate();
}
public void setSelfLocation(Location location1,Location location2){
if(location1 != null) myLocation = location1;
if(location2 != null) hisLocation = location2;
if((myLocation != null) & (hisLocation!= null)){
invalidate();
}
}
public Director(Context context) {
super(context);
myLocation = new Location("mock");
myLocation.setLatitude(Location.convert("32:46:33.17"));
myLocation.setLongitude(Location.convert("35:1:30.28"));
hisLocation = new Location("mock");
hisLocation.setLatitude(Location.convert("32:46:39.82"));
hisLocation.setLongitude(Location.convert("35:1:18.24"));
initCompassView();
}
public Director(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initCompassView();
}
public Director(Context context, AttributeSet attrs) {
super(context, attrs);
initCompassView();
}
private void initCompassView() {
invalidate();
}
@Override
protected void onDraw(Canvas cv) {
// this is done for the case that we have not obtained the latest location, self or friend
if(myLocation == null) return;
if(hisLocation == null) return;
int x = cv.getWidth();
int y = cv.getHeight();
Paint p = new Paint();
p.setColor(android.graphics.Color.MAGENTA);
draw_arrow(cv, p, x, y);
}
/*private void draw_arrow(Canvas canvas, Paint p, int width, int height) {
p.setColor(android.graphics.Color.BLUE);
p.setStrokeWidth(10);
final float centre_x = width*0.5f;
final float centre_y = height*0.5f;
//canvas.rotate(-direction, width * 0.5f, height * 0.5f);
canvas.drawLine(centre_x, centre_y-(height*0.3f), centre_x, centre_y + (height*0.3f), p);
p.setColor(android.graphics.Color.RED);
19
//canvas.drawLine(centre_x - width*0.1f, centre_y-(height*0.2f), centre_x + width*0.1f, centre_y-(height*0.2f), p);
//canvas.drawLine(centre_x - width*0.1f, centre_y-(height*0.2f), centre_x, centre_y-(height*0.3f), p);
//canvas.drawLine(centre_x + width*0.1f, centre_y-(height*0.2f), centre_x, centre_y-(height*0.3f), p);
}*/
private void draw_arrow(Canvas canvas, Paint p, int width, int height) {
p.setColor(android.graphics.Color.BLUE);
p.setStrokeWidth(10);
final float centre_x = width*0.5f;
final float centre_y = height*0.5f;
float deg_brng = myLocation.bearingTo(hisLocation);
canvas.drawText("Bearing:" + deg_brng + "\u00b0", 32, 32, p);
canvas.rotate(deg_brng, width * 0.5f, height * 0.5f);
canvas.drawLine(centre_x, centre_y-(height*0.3f), centre_x, centre_y + (height*0.3f), p);
p.setColor(android.graphics.Color.RED);
canvas.drawLine(centre_x - width*0.1f, centre_y-(height*0.2f), centre_x + width*0.1f, centre_y-(height*0.2f), p);
canvas.drawLine(centre_x - width*0.1f, centre_y-(height*0.2f), centre_x, centre_y-(height*0.3f), p);
canvas.drawLine(centre_x + width*0.1f, centre_y-(height*0.2f), centre_x, centre_y-(height*0.3f), p);
}
}
The next class is responsible for the communication between the clients and the server.
It offers a kind of abstraction for it’s user that do not need to know how exactly the data
is being transferred, what is the format and the physical link.
public class Communicator {
private ServerSocket mySS;
private Socket myCS;
private int myType;
private XMLHandler myXMLHandler;
public Communicator(int ntype){
myXMLHandler = new XMLHandler();
myType = ntype;
try {
switch(myType)
{
case Defenitions.GET_COM: mySS = new ServerSocket(Defenitions.srcPort);
break;
case Defenitions.SEND_COM:
//myCS = new Socket(Defenitions.dstName,Defenitions.dstPort);
break;
case Defenitions.ALL_COM: mySS = new ServerSocket(Defenitions.srcPort);
//myCS = new Socket(Defenitions.dstName,Defenitions.dstPort);
break;
default:
}
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
20
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void sendMsg(String msg){
}
public Location waitForNewLocation(){
Socket temp_socket;
byte[] incoming_bytes = new byte[256];
try {
temp_socket = mySS.accept();
temp_socket.getInputStream().read(incoming_bytes);
temp_socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NullPointerException e) {
e.printStackTrace();
}
return myXMLHandler.getLocationFromReply(new String(incoming_bytes));
}
public void sendNewSelfLocation(Location location,String myNumber){
String str = myXMLHandler.newLoactionToString(location,myNumber);
try {
myCS = new Socket(Defenitions.dstName,Defenitions.dstPort);
myCS.getOutputStream().write(str.getBytes());
myCS.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void sendOffLineNotification(String phone){
String str = myXMLHandler.closureNotification(phone);
try {
myCS = new Socket(Defenitions.dstName,Defenitions.dstPort);
myCS.getOutputStream().write(str.getBytes());
myCS.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public boolean checkifOnline(String mphone,String phone){
String str = myXMLHandler.isPersonOnlinQuery(mphone,phone);
byte[] incoming_bytes = new byte[256];
try {
myCS = new Socket(Defenitions.dstName,Defenitions.dstPort);
myCS.getOutputStream().write(str.getBytes());
myCS.getInputStream().read(incoming_bytes);
myCS.close();
21
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
str = myXMLHandler.getIsOnlineReply(new String(incoming_bytes));
if(str.equalsIgnoreCase("true")) return true;
return false;
}
public Location getCurrentLocation(String mphone,String phone){
String str = myXMLHandler.getCurrentLocationQuery(mphone,phone);
byte[] incoming_bytes = new byte[256];
try {
myCS = new Socket(Defenitions.dstName,Defenitions.dstPort);
myCS.getOutputStream().write(str.getBytes());
myCS.getInputStream().read(incoming_bytes);
myCS.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return myXMLHandler.getLocationFromReply(new String(incoming_bytes));
}
public LinkedList<String> getBlackListFriends(String mPhone){
String str = myXMLHandler.BlackListRequestString(mPhone);
byte[] incoming_bytes = new byte[256];
try {
myCS = new Socket(Defenitions.dstName,Defenitions.dstPort);
myCS.getOutputStream().write(str.getBytes());
myCS.getInputStream().read(incoming_bytes);
myCS.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// the following is for testing without actually recieve the string
//LinkedList<String> tempList = new LinkedList<String>();
//tempList.add("1-545-4");
return myXMLHandler.parseBlackList(new String(incoming_bytes));
}
public void addPersonToBlackList(String myPhone,String hisPhone){
String str = myXMLHandler.addPersonToBlackList(myPhone, hisPhone);
try {
myCS = new Socket(Defenitions.dstName,Defenitions.dstPort);
myCS.getOutputStream().write(str.getBytes());
myCS.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void removePersonFromBlackList(String myPhone,String hisPhone){
String str = myXMLHandler.removePersonFromBlackList(myPhone, hisPhone);
try {
myCS = new Socket(Defenitions.dstName,Defenitions.dstPort);
myCS.getOutputStream().write(str.getBytes());
22
myCS.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void closeServerSocket(){
try {
mySS.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
The following class dictates the XML format standard for server client communication. It
is responsible for creating and parsing incoming and outgoing messages.
package com.FFinder;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.LinkedList;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;
import org.xmlpull.v1.XmlSerializer;
import android.location.Location;
import android.util.Xml;
public class XMLHandler {
public XMLHandler(){
}
public String writeXml(String str){
XmlSerializer serializer = Xml.newSerializer();
StringWriter writer = new StringWriter();
try {
serializer.setOutput(writer);
serializer.startDocument("UTF-8", true);
serializer.startTag("", "message");
serializer.text("updatecoord");
//serializer.attribute("", "cmd", "updatecoord");
//serializer.attribute("", "dataX", str);
//serializer.attribute("", "dataY", str);
serializer.endTag("", "message");
serializer.endDocument();
return writer.toString();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public String readXml(String str) {
String str1 = "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?><isOnlineReply>true</isOnlineReply>";
String str2;
23
try {
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
XmlPullParser xpp = factory.newPullParser();
xpp.setInput( new StringReader ( str1 ) );
int eventType = xpp.getEventType();
str2 = xpp.getAttributeValue(0);
while (eventType != XmlPullParser.END_DOCUMENT) {
if(eventType == XmlPullParser.START_DOCUMENT) {
System.out.println("Start document");
} else if(eventType == XmlPullParser.END_DOCUMENT) {
System.out.println("End document");
} else if(eventType == XmlPullParser.START_TAG) {
System.out.println("Start tag "+xpp.getName());
System.out.println("Start tag "+xpp.getText());
//System.out.println("Start tag "+xpp.getAttributeValue("", "cmd"));
} else if(eventType == XmlPullParser.END_TAG) {
System.out.println("End tag "+xpp.getName());
} else if(eventType == XmlPullParser.TEXT) {
System.out.println("Text "+xpp.getText());
}
eventType = xpp.next();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return str2;
}
@SuppressWarnings("static-access")
public String newLoactionToString(Location location,String myNumber){
XmlSerializer serializer = Xml.newSerializer();
StringWriter writer = new StringWriter();
try {
serializer.setOutput(writer);
serializer.startDocument("UTF-8", true);
serializer.startTag("", "NewSelfLocation");
serializer.startTag("", "Phone");
serializer.text(myNumber);
serializer.endTag("", "Phone");
serializer.startTag("", "Lat");
serializer.text(location.convert(location.getLatitude(), Location.FORMAT_SECONDS));
serializer.endTag("", "Lat");
serializer.startTag("", "Lon");
serializer.text(location.convert(location.getLongitude(), Location.FORMAT_SECONDS));
serializer.endTag("", "Lon");
serializer.endTag("", "NewSelfLocation");
serializer.endDocument();
return writer.toString();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public String closureNotification(String phone){
24
XmlSerializer serializer = Xml.newSerializer();
StringWriter writer = new StringWriter();
try {
serializer.setOutput(writer);
serializer.startDocument("UTF-8", true);
serializer.startTag("", "ClosureNotification");
serializer.startTag("", "Close");
serializer.text(phone);
serializer.endTag("", "Close");
serializer.endTag("", "ClosureNotification");
serializer.endDocument();
return writer.toString();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public String isPersonOnlinQuery(String mPhone,String nPhone){
XmlSerializer serializer = Xml.newSerializer();
StringWriter writer = new StringWriter();
try {
serializer.setOutput(writer);
serializer.startDocument("UTF-8", true);
serializer.startTag("", "isOnline");
serializer.startTag("", "myPhone");
serializer.text(mPhone);
serializer.endTag("", "myPhone");
serializer.startTag("", "hisPhone");
serializer.text(nPhone);
serializer.endTag("", "hisPhone");
serializer.endTag("", "isOnline");
serializer.endDocument();
return writer.toString();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public String getCurrentLocationQuery(String mPhone,String nPhone){
XmlSerializer serializer = Xml.newSerializer();
StringWriter writer = new StringWriter();
try {
serializer.setOutput(writer);
serializer.startDocument("UTF-8", true);
serializer.startTag("", "GetLocation");
serializer.startTag("", "myPhone");
serializer.text(mPhone);
serializer.endTag("", "myPhone");
serializer.startTag("", "hisPhone");
serializer.text(nPhone);
serializer.endTag("", "hisPhone");
25
serializer.endTag("", "GetLocation");
serializer.endDocument();
return writer.toString();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public String getIsOnlineReply(String str) {
String str2 = null;
try {
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
XmlPullParser xpp = factory.newPullParser();
xpp.setInput( new StringReader ( str ) );
int eventType = xpp.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if(eventType == XmlPullParser.START_DOCUMENT) {
//System.out.println("Start document");
} else if(eventType == XmlPullParser.END_DOCUMENT) {
//System.out.println("End document");
} else if(eventType == XmlPullParser.START_TAG) {
//System.out.println("Start tag "+xpp.getName());
if(xpp.getName().equalsIgnoreCase("isOnlineReply")){
xpp.next();
str2 = xpp.getText();
return str2;
}
System.out.println("Start tag "+xpp.getText());
//System.out.println("Start tag "+xpp.getAttributeValue("", "cmd"));
} else if(eventType == XmlPullParser.END_TAG) {
//System.out.println("End tag "+xpp.getName());
} else if(eventType == XmlPullParser.TEXT) {
//System.out.println("Text "+xpp.getText());
}
eventType = xpp.next();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return str2;
}
public Location getLocationFromReply(String str) {
String Lat = null;
String Lon = null;
try {
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
XmlPullParser xpp = factory.newPullParser();
xpp.setInput( new StringReader ( str ) );
int eventType = xpp.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if(eventType == XmlPullParser.START_DOCUMENT) {
//System.out.println("Start document");
} else if(eventType == XmlPullParser.END_DOCUMENT) {
// System.out.println("End document");
} else if(eventType == XmlPullParser.START_TAG) {
//System.out.println("Start tag "+xpp.getName());
26
if(xpp.getName().equalsIgnoreCase("Lat")){
xpp.next();
Lat = xpp.getText();
}else
if(xpp.getName().equalsIgnoreCase("Lon")){
xpp.next();
Lon = xpp.getText();
}
System.out.println("Start tag "+xpp.getText());
//System.out.println("Start tag "+xpp.getAttributeValue("", "cmd"));
} else if(eventType == XmlPullParser.END_TAG) {
//System.out.println("End tag "+xpp.getName());
} else if(eventType == XmlPullParser.TEXT) {
//System.out.println("Text "+xpp.getText());
}
eventType = xpp.next();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
Location t = new Location("mock");
t.setLatitude(Location.convert(Lat));
t.setLongitude(Location.convert(Lon));
return t;
}
public String addPersonToBlackList(String mPhone,String nPhone){
XmlSerializer serializer = Xml.newSerializer();
StringWriter writer = new StringWriter();
try {
serializer.setOutput(writer);
serializer.startDocument("UTF-8", true);
serializer.startTag("", "AddToBlackList");
serializer.startTag("", "myPhone");
serializer.text(mPhone);
serializer.endTag("", "myPhone");
serializer.startTag("", "hisPhone");
serializer.text(nPhone);
serializer.endTag("", "hisPhone");
serializer.endTag("", "AddToBlackList");
serializer.endDocument();
return writer.toString();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public String removePersonFromBlackList(String mPhone,String nPhone){
XmlSerializer serializer = Xml.newSerializer();
StringWriter writer = new StringWriter();
try {
serializer.setOutput(writer);
serializer.startDocument("UTF-8", true);
serializer.startTag("", "RemoveFromBlackList");
serializer.startTag("", "myPhone");
serializer.text(mPhone);
serializer.endTag("", "myPhone");
27
serializer.startTag("", "hisPhone");
serializer.text(nPhone);
serializer.endTag("", "hisPhone");
serializer.endTag("", "RemoveFromBlackList");
serializer.endDocument();
return writer.toString();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public String BlackListRequestString(String mPhone){
XmlSerializer serializer = Xml.newSerializer();
StringWriter writer = new StringWriter();
try {
serializer.setOutput(writer);
serializer.startDocument("UTF-8", true);
serializer.startTag("", "BlackListRequest");
serializer.startTag("", "myPhone");
serializer.text(mPhone);
serializer.endTag("", "myPhone");
serializer.endTag("", "BlackListRequest");
serializer.endDocument();
return writer.toString();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public LinkedList<String> parseBlackList(String str) {
LinkedList<String> tmpList = new LinkedList<String>();
String tmpName = null;
try {
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
XmlPullParser xpp = factory.newPullParser();
xpp.setInput( new StringReader ( str ) );
int eventType = xpp.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if(eventType == XmlPullParser.START_DOCUMENT) {
//System.out.println("Start document");
} else if(eventType == XmlPullParser.END_DOCUMENT) {
// System.out.println("End document");
} else if(eventType == XmlPullParser.START_TAG) {
//System.out.println("Start tag "+xpp.getName());
if(xpp.getName().equalsIgnoreCase("BadMan")){
xpp.next();
tmpName = xpp.getText();
tmpList.add(tmpName);
}
System.out.println("Start tag "+xpp.getText());
//System.out.println("Start tag "+xpp.getAttributeValue("", "cmd"));
} else if(eventType == XmlPullParser.END_TAG) {
//System.out.println("End tag "+xpp.getName());
} else if(eventType == XmlPullParser.TEXT) {
//System.out.println("Text "+xpp.getText());
}
eventType = xpp.next();
28
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return tmpList;
}
}
The ext activity enables the user to manage his black list, it can choose any person from
his contact list and mark it as a person that he want him to see himself or not.
package com.FFinder;
import java.util.LinkedList;
import com.iconList.IconifiedText;
import com.iconList.IconifiedTextListAdapter;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Contacts.People;
import android.telephony.TelephonyManager;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.AdapterView.OnItemClickListener;
public class BlackListActivity extends Activity{
private LinkedList<Contact> contactsList;
private LinkedList<String> contactsNameList;
private Communicator myCommunicator;
private String myPhoneNumber;
private IconifiedTextListAdapter myArrayAdapter;
private ListView myListView;
@Override
protected Dialog onCreateDialog(final int id) {
return new AlertDialog.Builder(this).setTitle(R.string.select_dialog).setItems(R.array.select_dialog_items, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
/* User clicked so do some stuff */
//String[] items = getResources().getStringArray(R.array.select_dialog_items);
if(which == 0){
myArrayAdapter.replaceItem(id, new IconifiedText(contactsNameList.get(id), getResources().getDrawable(R.drawable.vidoop)));
myListView.setAdapter(myArrayAdapter);
myCommunicator.addPersonToBlackList(myPhoneNumber,(String)contactsList.get(id).getPhone());
}else{
myArrayAdapter.replaceItem(id, new IconifiedText(contactsNameList.get(id), getResources().getDrawable(R.drawable.favicon)));
myListView.setAdapter(myArrayAdapter);
myCommunicator.removePersonFromBlackList(myPhoneNumber,(String)contactsList.get(id).getPhone());
}
//new AlertDialog.Builder(BlackListActivity.this).setMessage("You selected: " + which + " , " + items[which]).show();
}
}).create();
29
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
contactsList = new LinkedList<Contact>();
contactsNameList = new LinkedList<String>();
myCommunicator = new Communicator(Defenitions.SEND_COM);
TelephonyManager telephony = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
myPhoneNumber = telephony.getLine1Number();
// Form an array specifying which columns to return.
String[] projection = new String[] {
People._ID,
People.NAME,
People.NUMBER
};
// Get the base URI for the People table in the Contacts content provider.
Uri contacts = People.CONTENT_URI;
// Make the query.
Cursor managedCursor = managedQuery(contacts,
projection, // Which columns to return
null,
// Which rows to return (all rows)
null,
// Selection arguments (none)
// Put the results in ascending order by name
People.NAME + " ASC");
// create a linked list for display
myArrayAdapter = new IconifiedTextListAdapter(this);
if (managedCursor.moveToFirst()) {
String name;
String phoneNumber;
int nameColumn = managedCursor.getColumnIndex(People.NAME);
int phoneColumn = managedCursor.getColumnIndex(People.NUMBER);
LinkedList<String> tempList = myCommunicator.getBlackListFriends(myPhoneNumber);
do {
// Get the field values
name = managedCursor.getString(nameColumn);
phoneNumber = managedCursor.getString(phoneColumn);
if(tempList.contains(phoneNumber)){
myArrayAdapter.addItem(new IconifiedText(name, getResources().getDrawable(R.drawable.vidoop)));
}else{
myArrayAdapter.addItem(new IconifiedText(name, getResources().getDrawable(R.drawable.favicon)));
}
contactsList.addLast(new Contact(name,phoneNumber));
contactsNameList.addLast(name);
} while (managedCursor.moveToNext());
}
myListView = (ListView) findViewById(R.id.ContactsList);
30
// Display it
myListView.setAdapter(myArrayAdapter);
myListView.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,long arg3) {
//Toast toast = Toast.makeText(getApplicationContext(), "You chose: " +
(String)contactsNameList.get(arg2), Toast.LENGTH_LONG);
//toast.show();
showDialog(arg2);
}
});
}
}
Use case scenarios
The application includes several client server sessions:
1. Self location Update. The Client changes position physically and sends it’s new
whereabouts to the server – XML formatted. One way transmition.
2. Black List:
a. Get current Black List. The Client request the current black list as it is
stored on the server. The Server transmits the list back to the client.
b. Add a person to a black list. The Client sends a contact details to the
server, that should be added to the Client’s Black List.
c. Remove a person to a black list. The Client sends a contact details to the
server, that should be Removed to the Client’s Black List.
31
3. Request another Client whereabouts. The client requests from the server the
whereabouts of another client, if the requesting client is not in a black list of the
requested Client, it gets his last location.
4. Go On/Off Line. The Client notifies the server if it can be found or not for other
Clients not in his black list.
32