se 390: software engineering for mobile devices week 12: geolocation copyright © steven w. johnson...
TRANSCRIPT
SE 390: Software Engineering for Mobile Devices
Week 12: GeolocationCopyright © Steven W. Johnson
February 1, 2013
Mobile devices are about portability
Portability is about knowing where you are
Mobile devices, geolocation are a natural fit
Geolocation starts with GPS
Movie project:
makes sense if device is aware of location
movie theatres within 50km of me
movies playing in my area
Primer on GPS:
4
Global Positioning System
Original: US military for navigation (Navstar)
Original implementation: 1973 (fixes TRANSIT)
Made available to public in 1983 (degraded system)
May 2000, selective availability removed
Other GPS systems: Russia, China, EU
Primer on GPS:
5
Primer on GPS:
6
*up to 31http://www.modern.nl/catalog/page.php?id=3751
24 satellites* in known positions
4 satellites in 6 different orbits
Theoretical: up to 8 satellites may see you
Orbits at 12,600 miles (20,200 km)
Low Earth Orbit (below 22,236 mi)
Primer on GPS:
7http://www.superbwallpapers.com
Space shuttle:
Orbits at 200 to 400 miles
15.9 orbits per day
Primer on GPS:
8http://www.kowoma.de/en/gps/orbits.htm
Six orbital paths:
orbit not geostationary
2 orbits a day
base orbit at 55°, others at ± 60°
View in blue
Distance from each satellite determines position
Consumer GPS: good to 10 meters
Satellite sends its time, device calculates distance
Device calculates its latitude and longitude
Primer on GPS:
10
349875124 349875133
349875131
349875128
349875139
156
11
8
Primer on GPS:
11
Latitude(ladder – merdiven)
Latitude (enlem)
Longitude (boylam)
Minutes
seconds
Longitude(boylam)
Primer on GPS:Our purposes today:
assume 1° of latitude, longitude are same
1° of latitude is constant everywhere
Turkey: longitude is 70% of latitude
Equator
45° N latitude
North Pole
24,900
17,600 or 70.5%
0
Primer on GPS:
13
Precision of degrees (derece) latitude:
1 degree69.16 miles 111.31 km
1 minute 1.15 miles 1.86 km
1 second 0.02 miles 0.03 km
GPS coordinates of Izmir:
Decimal: 34.422028° N, 27.129042° E
Sexagesimal:38° 24’ 26” N / 27° 09’ 01” E
Primer on GPS:
14
40,075.16 km to Earth’s circumference
1° of latitude (anywhere) = 111.3198 km
27.129042 deci
mete
rs
mete
rs
kilo
mete
rs
centi
mete
rs
11
1 k
ilom
ete
rs
Primer on GPS:
15
Distance: absolute difference between two points
Works well for short distances
.
.
Lat:Long:
34.422028 27.129042
34.448268 27.142674
Lat:Long:
dist x2
dist y2dist z
2
𝑧=√|𝑥2|+¿ 𝑦2∨¿¿
Primer on GPS:
16
.
.
Lat:Long:
34.422028 27.129042
34.448268 27.142674
Lat:Long:
dist x2
dist y2dist z
2
𝑧=√|𝑥2|+¿ 𝑦2∨¿¿Longitude
27.12904227.142674
-0.01363
2
0.013632
Latitude34.44826834.422028
0.026240
0.0262402
0.0136322
0.0006885376
0.0001858314
0.0008743690
𝑧=√0.00087436900.029569733.29km
Primer on GPS:
18
Calculating the great circle route:
//radians = degrees * PI/180;
var R = 6371; // kmvar dLat = (lat2-lat1).toRad(); var dLon = (lon2-lon1).toRad(); var lat1 = lat1.toRad(); var lat2 = lat2.toRad(); var a = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2); var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); var d = R * c;
GPS and Android:
19
How does a mobile device know its position?
1st alternative:
GPS (fine location)
2nd alternative:
use cell phone towers (coarse location)
3rd alternative:
use WiFi hotspot (desperation??)
GPS and Android:
20
GPS is a common feature in smartphones
Ubiquitous (her yerde birden bulunan) mobile devices
Organizations providing map data:
Google Maps
OpenStreetMap
Android Studio:
Google Map Template
GPS and Android:
21
Permissions must be turned on:
Internet
GPS
<uses-permissions> are children of <manifest>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.steve.johnson.gps" android:versionCode="1" android:versionName="1.0" > <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
GPS and Andriod:
22
Two main elements:
location manager (web services)
location providers (technologies)
http://developer.android.com/reference/android/location/LocationManager.html
GPS and Andriod:
23http://developer.android.com/reference/android/location/LocationManager.html
Location loc = locationManager.getLastKnownLocation(provider); .getGpsStatus(GpsStatus status); .addProximityAlert(); .getBestProvider(Criteria criteria, boolean enabledOnly);
Location manager:
provides access to location services
determines current location
Some locationManager methods
Browsers have similar idea: ‘navigator’
Lab: GPS
24
Purpose: place a GPS location on emulator
Problem: emulator does not have GPS
Eclipse allowed for finer control than Studio
Studio works in decimal only*
Lab: GPS
27
Opens Android console:
geo fix longitude latitude
Coordinates for IEU
geo fix 27.044622 38.388369
Lab: GPS
28
Turn on your emulator
Alternative methods:
Tools – Open Terminal
click ‘Terminal’ in lower left corner of IDE
Lab: GPS
29
Turn on your emulator
In the terminal:
Now told where emulator is located
Microsoft Windows [Version 6.1.7601]Copyright (c) 2009 Microsoft Corporation. All rights reserved.
C:\Users\Steve\Desktop\IMAX\app>
Microsoft Windows [Version 6.1.7601]Copyright (c) 2009 Microsoft Corporation. All rights reserved.
C:\Users\Steve\Desktop\IMAX\app>telnet localhost 5554
Microsoft Windows [Version 6.1.7601]Copyright (c) 2009 Microsoft Corporation. All rights reserved.
C:\Users\Steve\Desktop\IMAX\app>telnet localhost 5554
Android Console: type 'help' for a list of commandsOK
Microsoft Windows [Version 6.1.7601]Copyright (c) 2009 Microsoft Corporation. All rights reserved.
C:\Users\Steve\Desktop\IMAX\app>telnet localhost 5554
Android Console: type 'help' for a list of commandsOK geo fix 27.044622 38.388369OK
Lab: location
31
Get a key from ‘google_maps_api.xml’:<resources> <string name="google_maps_key_instructions" templateMergeStrategy="replace"> <!--
TODO: Before you run your application, you need a Google Maps API key.
To get one, follow this link, follow the directions and press "Create" at the end:
https://console.developers.google.com/flows/enableapi?apiid=maps_android_ backend&keyType=CLIENT_SIDE_ANDROID&r=74:76:97:C7:4F:25:91:43:6E:77:E1:FA :12:A1:5A:41:D5:A2:17:D0%3Bcom.example.sjohnson.location
You can also add your credentials to an existing key, using this line: 74:76:97:C7:4F:25:91:43:6E:77:E1:FA:12:A1:5A:41:D5:A2:17:D0;com.example .sjohnson.location
Once you have your key (it starts with "AIza"), replace the "google_maps_key" string in this file.-->
<resources> <string name="google_maps_key_instructions" templateMergeStrategy="replace"> <!--
TODO: Before you run your application, you need a Google Maps API key.
To get one, follow this link, follow the directions and press "Create" at the end:
https://console.developers.google.com/flows/enableapi?apiid=maps_android_ backend&keyType=CLIENT_SIDE_ANDROID&r=74:76:97:C7:4F:25:91:43:6E:77:E1:FA :12:A1:5A:41:D5:A2:17:D0%3Bcom.example.sjohnson.location
You can also add your credentials to an existing key, using this line: 74:76:97:C7:4F:25:91:43:6E:77:E1:FA:12:A1:5A:41:D5:A2:17:D0;com.example .sjohnson.location
Once you have your key (it starts with "AIza"), replace the "google_maps_key" string in this file.-->
Copy/paste into browser:
Lab: location
36
Copy ‘API Key’ and paste into string for key
Can also place key in ‘strings’ and call in name
--> </string>
<string name="google_maps_key" templateMergeStrategy="preserve"> YOUR_KEY_HERE </string></resources>
--> </string>
<string name="google_maps_key" templateMergeStrategy="preserve"> AIzaSyBLAC9HJdk-kfI599kKGY7nRgR2Z0RVq8E </string></resources>
Lab: location
37
Manifest already has permissions built-in
package="com.example.steve.location" >
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" /><!-- The ACCESS_COARSE/FINE_LOCATION permissions are not required to use Google Maps Android API v2, but are recommended.--> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
private void setUpMap() { mMap.addMarker(new MarkerOptions().position(new LatLng(0, 0)) .title("Marker"));}
Lab: location
39
Add coordinates and marker
‘setUpMap’ = define markers
Method at bottom of ‘Sourcecode’
private void setUpMap() { LatLng Izmir = new LatLng(38.4189, 27.1287); mMap.addMarker(new MarkerOptions() .position(Izmir) .title("Marker") );}
Lab: location
41
Add zoom to map:
generates map without marker
Method at bottom of ‘Sourcecode’
private void setUpMap() { LatLng Izmir = new LatLng(38.4189, 27.1287); mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(Izmir, 15));}
Lab: location
43
Zoom:
starts from 0: Earth fits in ≈256 dp
each size up doubles pixels required
1: 512 dp to show globe (10 cm)
2: 1,024 dp to show globe
3: 2,048 dp to show globe
Zoom method:
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(Izmir, 15));
Lab: locationChange MAP_TYPE_. Four alternatives:
terrain/normal (same appearance/look)
satellite
hybrid
none
Method at bottom of ‘Sourcecode’
private void setUpMap() { LatLng Izmir = new LatLng(38.4189, 27.1287); mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(Izmir, 15)); mMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE);
}
Lab: location
47
Marker attributes:
private void setUpMap() { LatLng Izmir = new LatLng(38.4189, 27.1287); mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(Izmir, 15)); mMap.setMapType(GoogleMap.MAP_TYPE_TERRAIN); mMap.addMarker(new MarkerOptions() .icon(BitmapDescriptorFactory.fromResource(R.drawable.turkeymarker)) .anchor(0.0f, 1.0f) // Anchors the marker on the bottom left .draggable(true) .alpha(0.5f) .rotation(90) .position(Izmir));
Lab: location
54
Adding ‘MyLocation’ layer of Google Map
Method at bottom of ‘Sourcecode’
private void setUpMap() { mMap.addMarker(new MarkerOptions().position(new LatLng(0, 0)).title("Marker"));}
private void setUpMap() { LatLng Izmir = new LatLng(38.4189, 27.1287); mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(Izmir, 15)); mMap.setMyLocationEnabled(true); mMap.addMarker(new MarkerOptions() .icon(BitmapDescriptorFactory.fromResource(R.drawable.turkeymarker)) .position(Izmir));
Lab: location
55
‘MyLocation’
button (top right) centers map
location is:
blue dot (stationary)
chevrons (moving)
Lab: location
56
Locating yourself: (not in emulator; no GPS)
private void setUpMap() { mMap.setMyLocationEnabled(true); LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE); Criteria criteria = new Criteria(); String provider = locationManager.getBestProvider(criteria, true); Location location = locationManager.getLastKnownLocation(provider); double latitude = location.getLatitude(); double longitude = location.getLongitude(); LatLng MyHome = new LatLng(latitude, longitude); mMap.setMapType(GoogleMap.MAP_TYPE_TERRAIN); mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(MyHome, 15));}
Lab: location
58
‘Sourcecode’:
much the same plus ‘setUpMap’ methods
private void setUpMapIfNeeded() { …builds the map setUpMap()}
private void setUpMap() { get location zoom display marker}
Lab: location
59
Two big ideas:
map what is being viewed
camera position/conditions of viewing
Move the camera or the map
Think of the camera as starting at (0, 0)
Lab: location
60
Camera position defines middle of map:
moveCamera place camera (no animation)
animateCamera place camera (with animation)
Lab: location
61
Camera position defines middle of map:
cameraUpdate
where and how camera moves
could be by lat/long or by pixels
changes an existing camera
cameraUpdateFactory “do the cameraUpdate”
Lab: location
63
This method:
private void setUpMap() { LatLng Izmir = new LatLng(38.4189, 27.1287); mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(Izmir, 15)); mMap.setMapType(GoogleMap.MAP_TYPE_TERRAIN); mMap.addMarker(new MarkerOptions() .icon(BitmapDescriptorFactory.fromResource(R.drawable.turkeymarker)) .anchor(0.0f, 1.0f) // Anchors the marker on the bottom left .draggable(true) .alpha(0.5f) .rotation(90) .position(Izmir));
Images in Android
65
A test:
created an image of 350 x 130 @ 72ppi
copied using each resolution of ‘drawable’
added each image to a layout
Images in Android
66
Results:‘drawable’
‘ldpi’
‘mdpi’
‘hdpi’
‘xhdpi’
‘xxhdpi’
450px
450px
600px
150px
225px
300px
Images in Android
67
Considerations:
desktop monitors render at 96ppi
default Photoshop setting is 72ppi
Bucket Size Pixels per 160dp
Launcher Icon size
Renders like
Impact toDesktop
‘drawable’ 96ppi 0
ldpi 120 36x36 72ppi +33%
mdpi 160 48x48 96ppi 0
hdpi 240 72x72 144ppi -33%
xhdpi 320 96x96 192ppi -50%
xxhdpi 480 144x144 288ppi -67%
Android L: Android Lollipop (API 21)
Covers versions 5.0 and 5.1
Biggest change: material design
Dalvik replaced by ART (Android RunTime)
Two new widgets:
RecyclerView
CardView
RecyclerView and CardView
68http://www.itpro.co.uk/mobile/22562/android-lollipop-release-date-specs
RecyclerView and CardView
69
CardView:
a container that holds images, text
a repeated element; a <li>
RecyclerView:
container that holds cards
like a <ul>
Both are new with Lollipop (API 21)
Support down to API 7 with libraries
RecyclerView and CardView
70
RecyclerView
CardView
CardView
Title ADataDataData
Title BDataDataData
Lab: Area Cinemas
72
To complete:
list all theatres within 50 km of ‘your location’
connect to MySQL database (imaxdb)
read out latitude and longitude
compare to IEU latitude/longitude:
longitude: 27.044622
latitude: 38.388369
1° of difference = 111.3198 km
sort by distance ascending (yükselen)http://code.tutsplus.com/tutorials/getting-started-with-recyclerview-and-cardview-on-android--cms-23465
Lab: Area Cinemas
73
Output of the app:
http://code.tutsplus.com/tutorials/getting-started-with-recyclerview-and-cardview-on-android--cms-23465
Lab: Area Cinemas
74
Lab uses two full activities and two helper classes
StructurecodeAdaptercodeCardcodeRecyclercode
recyclerlayout cardlayout
http://code.tutsplus.com/tutorials/getting-started-with-recyclerview-and-cardview-on-android--cms-23465
Lab: Area Cinemas
75
Turn on your web server
Open your ‘www’ folder
Add ‘getCinemaList.php’ (in-class files)
Lab: Area Cinemas
76
Create the web service:
send current coordinates to web service
use coordinates of theaters in DB
criteria:
distance (km) = 111.3198 *
distance <=50
sort ascending (yükselen)
name of file: getCinemaList.php
Lab: Area Cinemas
77
Connect to the database
Capture location of device<?php $host = "localhost"; $username = "your_user"; $password = "your_pass"; $database = "imaxdb"; $con = mysqli_connect($host, $username, $password, $database) or trigger_error(mysqli_error(),E_USER_ERROR);
$latPosition = mysqli_real_escape_string($con, $_GET['latitude']); $longPosition= mysqli_real_escape_string($con, $_GET['longitude']);
$query = "SELECT theatrename, latitude, longitude, theatrepix, streetaddress, citystatezip FROM theatres"; $recordset = mysqli_query($con, $query); $count = mysqli_num_rows($recordset);
Lab: Area Cinemas
78
Recordset holds every theatre in world
SELECT ‘regional’ movie theatres<?php $host = "localhost"; $username = "your_user"; $password = "your_pass"; $database = "imaxdb"; $con = mysqli_connect($host, $username, $password, $database) or trigger_error(mysqli_error(),E_USER_ERROR);
$latPosition = mysqli_real_escape_string($con, $_GET['latitude']); $longPosition= mysqli_real_escape_string($con, $_GET['longitude']);
$query = "SELECT theatrename, latitude, longitude, theatrepix, streetaddress, citystatezip FROM WHERE lat and long within ½° of current location; $recordset = mysqli_query($con, $query); $count = mysqli_num_rows($recordset);
Lab: Area Cinemas
80
Refine the list to within 50km:
for ($i=0; $i<$count; $i++) { $row=mysqli_fetch_assoc($recordset); $latDistance = abs($row['latitude'] - $latPosition); $longDistance = abs($row['longitude'] - $longPosition); $latSquare = pow($latDistance, 2); $longSquare = pow($longDistance, 2); $Distance = 111.3198 * sqrt($latSquare + $longSquare); $Distance = number_format($Distance, 1); if ($Distance <= 50) {
$data = array("theatrename" => $row['theatrename'], "theatrepix" =>$row['theatrepix'], "streetaddress" => $row['streetaddress'], "citystatezip" => $row['citystatezip'], "distance" => $Distance." km");
$output[] = $data; }}
Lab: Area Cinemas
81
Order the cinemas by distance from device
Send final output back to the devicefunction cust_sort($a,$b) { return strtolower($a[distance]) > strtolower($b[distance]);}usort($output, 'cust_sort');mysqli_free_result($row);mysqli_close($con);echo json_encode($output, JSON_UNESCAPED_UNICODE);?>
Lab: Area Cinemas
82
Test ‘getCinemaList’ in browser
Should get 4 returns (Foça, Ceşme too far away)
http://localhost/getCinemaList.php?latitude=38.388369&longitude=27.044622
Lab: Area Cinemas
84
Recent upgrade included API 22
Potential rendering problems with ‘Design View’
Solution:
change API from 22 to 21
put back to 22 if you want
Lab: CinemaList
87
Create a class for the Recycler adapter
Open ‘Java’ – rt. Click on Package (NOT –TEST)
New – Java Class
Lab: CinemaList
88
Create second class for the Card’s content
Open ‘Java’ – rt. Click on Package (NOT –TEST)
New – Java Class
90
Update ‘build.gradle (Module: app)’:
Add bottom two ‘compiles’
Sync the project
‘+’ on end is not good, but not horrible either
means ‘get most current’
dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:recyclerview-v7:21.0.+' compile 'com.android.support:cardview-v7:21.0.+'}
Lab: Area Cinemas
91
Update ‘AndroidManifest’:
Permissions to use the Internet
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.sjohnson.areacinemas" > <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
Lab: Area Cinemas
<activity android:name="com.example.steve.areacinemas.Cardcode" android:label="app_name" > </activity>
92
Update ‘strings’:
Lab: Area Cinemas
<resources> <string name="app_name">Area Cinemas</string> <string name="altText">Image of Theatre Marquee</string></resources>
93
Update ‘dimens’:
Lab: Area Cinemas
<resources> <!-- Default screen margins--> <dimen name="dp16">16dp</dimen> <dimen name="dp8">8dp</dimen> <dimen name="dp5">5dp</dimen> <dimen name="sp16">16sp</dimen> <dimen name="sp12">12sp</dimen></resources>
94
Create ‘colors.xml’:
Lab: Area Cinemas
<?xml version="1.0" encoding="utf-8"?><resources>
<color name="cardcolor">#ffefe8da</color> <color name="recyclercolor">#fff1f8ff</color>
</resources>
96
Open ‘recyclerlayout’: (the RecyclerView)
delete ‘hello world’
change ‘RelativeLayout’ to ‘LinearLayout’
update ‘LinearLayout’:
layout_width & height: match_parent
background: @color/recyclercolor’
padding (all): ‘@dimen/dp8’
Lab: Area Cinemas
97
Add a ‘CustomView’
Click on ‘Custom’ and search of ‘RecyclerView’
Select and ‘OK’
Click on ‘LinearLayout’ to add
Lab: Area Cinemas
98
Open ‘recyclerlayout’: (the RecyclerView)
get a rendering error
can’t be previewed in ‘design’ window
potential fixes: not worth it
Lab: Area Cinemas
99
Update the ‘RecyclerView’
Copy/paste this code in place of ‘view’
Done with ‘recyclerlayout’
<android.support.v7.widget.RecyclerView android:layout_width="match_parent" android:layout_height="match_parent“ android:id="@+id/recycler" />
Lab: Area Cinemas
100
cardlayout holds the CardView
Describes one instance of a card
CardView: like rubber stamp
Recycler: the repeater
Data fed into CardView using Adapter (like list)
Lab: Area Cinemas
RecyclerView
CardView
CardView
CardView
101
Open ‘cardlayout’: (the CardView)
delete ‘hello world’
change to ‘LinearLayout’
Lab: Area Cinemas
102
Update the ‘LinearLayout’:
layout_width: match_parent
layout_height: wrap_content
orientation: vertical
background: ‘@color/cardcolor’
elevation: ‘dimen/dp5’
padding (all): ‘@dimen/dp8’
Lab: Area Cinemas
104
Update the CardView on
‘listing.xml’
copy/paste above
‘LinearLayout’
And below </LinearLayout>:
Lab: Area Cinemas
<?xml version="1.0" encoding="utf-8"?> <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:card_view="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="@dimen/dp8" card_view:cardCornerRadius="@dimen/dp8" android:id="@+id/cardview" >
</android.support.v7.widget.CardView>
105
Current ‘cardlayout’
Lab: Area Cinemas
<?xml version="1.0" encoding="utf-8"?> <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:card_view="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="@dimen/dp8" card_view:cardCornerRadius="@dimen/dp8" android:id="@+id/cardview" > <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.steve.areacinemas.Cardcode" android:orientation="vertical" android:background="@color/cardcolor" android:elevation="@dimen/dp5" android:padding="@dimen/dp8"> </LinearLayout> </android.support.v7.widget.CardView>
107
Update the ‘ImageView’
layout_width: match_parent
layout_height: wrap_content
contentDescription: ‘@string/altText’
id: theatrePix
scaleType: fitStart
src: (none)
Lab: Area Cinemas
112
Update ‘Recyclercode’
calls the recycler (which calls the cards)
connects to web service
translates transmitted data
feeds the adapter
Delete ‘OptionsMenu’ and ‘ItemSelected’ menu
Lab: Area Cinemas
113
Update ‘Recyclercode’ (red currently undefined)public class Recyclercode extends Activity {
private List<Theatre> movieHouses; private RecyclerView recycler;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.recyclerlayout);
recycler=(RecyclerView)findViewById(R.id.recycler);
LinearLayoutManager llm = new LinearLayoutManager(this); recycler.setLayoutManager(llm); recycler.setHasFixedSize(true);
initializeData(); initializeAdapter(); }
Lab: Area Cinemas
114
Update ‘Recyclercode’ (connect to web service)
private void initializeData(){ movieHouses = new ArrayList<>();
if (android.os.Build.VERSION.SDK_INT > 9) { StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); StrictMode.setThreadPolicy(policy); }
String latitude = "38.5"; String longitude = "27.0";
String url = "http://10.0.2.2/getCinemaList.php?latitude= +latitude+"&longitude="+longitude;
Lab: Area Cinemas
115
Update ‘Recyclercode’ (extract data from service)
try { JSONArray data = new JSONArray(getJSONUrl(url)); int arrSize = data.length(); String a, b, c, d, e; //done to save vertical space
for (int i = 0; i < arrSize; i++) { JSONObject blob = data.getJSONObject(i); a = blob.getString("theatrename"); b = blob.getString("streetaddress"); c = blob.getString("theatrepix"); d = blob.getString("citystatezip"); e = blob.getString("distance"); int pixID = getResources().getIdentifier(c, "drawable", getPackageName()); movieHouses.add(new Theatre(a, b, pixID, d, e)); }} catch (JSONException e) { e.printStackTrace();}}
Lab: Area Cinemas
116
Update ‘Recyclercode’ (extract data from service)
private void initializeAdapter(){ Adaptercode adapter = new Adaptercode(movieHouses); recycler.setAdapter(adapter);}
Lab: Area Cinemas
117
Update ‘Recyclercode’ (extract data from service)
public String getJSONUrl(String url) { StringBuilder str = new StringBuilder(); HttpClient client = new DefaultHttpClient(); HttpGet httpGet = new HttpGet(url); try { HttpResponse response = client.execute(httpGet); StatusLine statusLine = response.getStatusLine(); int statusCode = statusLine.getStatusCode(); if (statusCode == 200) { HttpEntity entity = response.getEntity(); InputStream content = entity.getContent(); BufferedReader reader = new BufferedReader(new InputStreamReader(content)); String line; while ((line = reader.readLine()) != null) { str.append(line); } } else { Log.e("Log", "Failed to download result.."); } } catch (IOException e) { e.printStackTrace(); } return str.toString();}
Lab: Area Cinemas
118
Update ‘Cardcode’: Defines views used in card
Lab: Area Cinemas
public class Cardcode extends Activity {
TextView theatreName; TextView theatreAddress; ImageView theatrePix; TextView theatreCity; TextView theatreDistance;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.cardlayout);
theatreName = (TextView)findViewById(R.id.theatreName); theatreAddress = (TextView)findViewById(R.id.theatreAddress); theatrePix = (ImageView)findViewById(R.id.theatrePix); theatreCity = (TextView)findViewById(R.id.theatreCity); theatreDistance = (TextView)findViewById(R.id.distance);
}}
119
Update ‘Structurecode’: defines list ‘Theatre’
Lab: Area Cinemas
class Theatre { String name; String address; int pix; String city; String distance;
Theatre(String name, String address, int pix, String city, String distance) { this.name = name; this.address = address; this.pix = pix; this.city = city; this.distance = distance; }}
120
Update ‘Adaptercode’:
Lab: Area Cinemas
public class Adaptercode extends RecyclerView.Adapter<Adaptercode.PersonViewHolder> {
public static class PersonViewHolder extends RecyclerView.ViewHolder {
CardView cardview; TextView theatreName; TextView theatreAddress; ImageView theatrePix; TextView theatreCity; TextView theatreDistance;
PersonViewHolder(View itemView) { super(itemView); cardview = (CardView)itemView.findViewById(R.id.cardview); theatreName = (TextView)itemView.findViewById(R.id.theatreName); theatreAddress = (TextView)itemView.findViewById(R.id.theatreAddress); theatrePix = (ImageView)itemView.findViewById(R.id.theatrePix); theatreCity = (TextView)itemView.findViewById(R.id.theatreCity); theatreDistance = (TextView)itemView.findViewById(R.id.distance); } }
121
Update ‘Adaptercode’:
Lab: Area Cinemas
List<Theatre> movieHouses; Adaptercode(List<Theatre> movieHouses){ this.movieHouses = movieHouses; }
@Override public void onAttachedToRecyclerView(RecyclerView recyclerView) { super.onAttachedToRecyclerView(recyclerView); }
@Override public PersonViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) { View v = LayoutInflater.from(viewGroup.getContext()).inflate (R.layout.cardlayout, viewGroup, false); PersonViewHolder pvh = new PersonViewHolder(v); return pvh; }
122
Update ‘Adaptercode’:
Lab: Area Cinemas
@Override public void onBindViewHolder(PersonViewHolder personViewHolder, int i) { personViewHolder.theatreName.setText(movieHouses.get(i).name); personViewHolder.theatreAddress.setText(movieHouses.get(i).address); personViewHolder.theatrePix.setImageResource(movieHouses.get(i).pix); personViewHolder.theatreCity.setText(movieHouses.get(i).city); personViewHolder.theatreDistance.setText(movieHouses.get(i).distance); }
@Override public int getItemCount() { return movieHouses.size(); }}
1. How many satellites does it take to position you on Earth?
A. 1B. 2C. 3D. 4E. At least 6F. At least 8
Quiz:
124
2. What type of orbit are GPS satellites in?
A. Low Earth OrbitB. High Earth OrbitC. Geostationary OrbitD. Parabolic OrbitE. Circular Orbit
Quiz:
125
3. The precision of the civilian GPS system is to the nearest:
A. 1 centimeterB. 1 meterC. 10 metersD. 100 meters
Quiz:
126
4. One degree of latitude (at equator) is how many kilometers?
A. 1 kmB. 10 kmC. 100 kmD. 1,000 km
Quiz:
127
5. A latitude of 15 degrees and a longitude of 40 degrees is on which continent?
A. AsiaB. AfricaC. EuropeD. North AmericaE. South AmericaF. AustraliaG. Antarctica
Quiz:
128
SE 390: Software Engineering for Mobile Devices
Week 12: GeolocationCopyright © Steven W. Johnson
February 1, 2013
Place all the cinemas on the map
Use ‘placeCinemas.php’ web service (very similar)
Use one activity
Lab: Every Theatre
133
Theatre name
Get/test coordinates
Calculate distance
‘placeMarkers.php’ (in-class files)
turn on web server
copy ‘placeMarkers.php’ to web server
Lab: Every Theatre
134
Lab: Every Theatre
135
<?php $host = "localhost"; $username = "your_user"; $password = "your-pass"; $database = "imaxdb"; $con = mysqli_connect($host, $username, $password, $database) or trigger_error(mysqli_error(),E_USER_ERROR);
$latPosition = mysqli_real_escape_string($con, $_GET['latitude']); $longPosition= mysqli_real_escape_string($con, $_GET['longitude']);
$query = "SELECT theatrename, latitude, longitude, theatrepix FROM theatres"; $recordset = mysqli_query($con, $query); $count = mysqli_num_rows($recordset);
for ($i=0; $i<$count; $i++) { $row=mysqli_fetch_assoc($recordset); $latDistance = abs($row['latitude'] - $latPosition); $longDistance = abs($row['longitude'] - $longPosition); $latSquare = pow($latDistance, 2); $longSquare = pow($longDistance, 2); $Distance = 111.3198 * sqrt($latSquare + $longSquare); $Distance = number_format($Distance, 1); if ($Distance <= 50) { $data = array("theatrename" => $row['theatrename'], "latitude" => $row['latitude'], "longitude" => $row['longitude'], "distance" => $Distance. " km"); $output[] = $data; } } mysqli_free_result($row); mysqli_close($con); echo json_encode($output, JSON_UNESCAPED_UNICODE);?>
Lab: Every Theatre
138
Get a key from ‘google_maps_api.xml’:<resources> <string name="google_maps_key_instructions" templateMergeStrategy="replace"> <!--
TODO: Before you run your application, you need a Google Maps API key.
To get one, follow this link, follow the directions and press "Create" at the end:
https://console.developers.google.com/flows/enableapi?apiid=maps_android_ backend&keyType=CLIENT_SIDE_ANDROID&r=74:76:97:C7:4F:25:91:43:6E:77:E1:FA :12:A1:5A:41:D5:A2:17:D0%3Bcom.example.sjohnson.location
You can also add your credentials to an existing key, using this line: 74:76:97:C7:4F:25:91:43:6E:77:E1:FA:12:A1:5A:41:D5:A2:17:D0;com.example .sjohnson.location
Once you have your key (it starts with "AIza"), replace the "google_maps_key" string in this file.-->
<resources> <string name="google_maps_key_instructions" templateMergeStrategy="replace"> <!--
TODO: Before you run your application, you need a Google Maps API key.
To get one, follow this link, follow the directions and press "Create" at the end:
https://console.developers.google.com/flows/enableapi?apiid=maps_android_ backend&keyType=CLIENT_SIDE_ANDROID&r=74:76:97:C7:4F:25:91:43:6E:77:E1:FA :12:A1:5A:41:D5:A2:17:D0%3Bcom.example.sjohnson.location
You can also add your credentials to an existing key, using this line: 74:76:97:C7:4F:25:91:43:6E:77:E1:FA:12:A1:5A:41:D5:A2:17:D0;com.example .sjohnson.location
Once you have your key (it starts with "AIza"), replace the "google_maps_key" string in this file.-->
Copy/paste into browser:
Add ‘drawable’ markers:
location (red)
mylocation (orange)
copy image and paste into ‘drawable’
Lab: Every Theatre
144
Determine present location (mobile)
Add IEU location (emulator)
Lab: Every Theatre
145
private void setUpMap() { double latitude = 0.0d; double longitude = 0.0d; mMap.setMyLocationEnabled(true); LocationManager locationManager = (LocationManager)getSystemService(LOCATION_SERVICE); Criteria criteria = new Criteria(); String provider = locationManager.getBestProvider(criteria, true); Location location = locationManager.getLastKnownLocation(provider);
if (location == null) { latitude = 38.388369; longitude = 27.044622; } else { latitude = location.getLatitude(); longitude = location.getLongitude(); }
Determine present location (mobile)
Add IEU location (emulator)
Lab: Every Theatre
146
LatLng position = new LatLng(latitude, longitude);mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(position, 11));mMap.addMarker(new MarkerOptions() .icon(BitmapDescriptorFactory.fromResource(R.drawable.mylocation)) .title("My Location") .snippet("Lat: " + latitude + " Long: " + longitude) .position(position));}
Get the locations of the cinemas
Process:
read next theater out of array
place marker on screen
no need for array or list
Lab: Every Theatre
147
Connect to the web service:
Lab: Every Theatre
148
if (android.os.Build.VERSION.SDK_INT > 9) { StrictMode.ThreadPolicy policy = newStrictMode.ThreadPolicy.Builder() .permitAll().build(); StrictMode.setThreadPolicy(policy);}
String url = "http://10.0.2.2/placeMarkers.php?latitude=" +latitude+ "&longitude=" +longitude;
Lab: Every Theatre
149
try { JSONArray data = new JSONArray(getJSONUrl(url)); int arrSize = data.length(); String a, b, c, d; for (int i = 0; i < arrSize; i++) { JSONObject blob = data.getJSONObject(i); a = blob.getString("theatrename"); b = blob.getString("latitude"); c = blob.getString("longitude"); d = blob.getString("distance"); double readlat = Double.parseDouble(b); double readlong = Double.parseDouble(c); LatLng pos = new LatLng(readlat, readlong); mMap.addMarker(new MarkerOptions() .icon(BitmapDescriptorFactory .fromResource(R.drawable.location)) .title(a) .snippet(d) .position(pos)); }} catch (JSONException e) { e.printStackTrace();}}
Lab: Every Theatre
150
public String getJSONUrl(String url) { StringBuilder str = new StringBuilder(); HttpClient client = new DefaultHttpClient(); HttpGet httpGet = new HttpGet(url); try { HttpResponse response = client.execute(httpGet); StatusLine statusLine = response.getStatusLine(); int statusCode = statusLine.getStatusCode(); if (statusCode == 200) { HttpEntity entity = response.getEntity(); InputStream content = entity.getContent(); BufferedReader reader = new BufferedReader(new InputStreamReader(content)); String line; while ((line = reader.readLine()) != null) { str.append(line); } } else { Log.e("Log", "Failed to download result.."); } } catch (IOException e) { e.printStackTrace(); } return str.toString();}
HttpClient class deprecated as of API 22
Can ‘fix’ deprecated problem by compiling in 21
Lab: Every Theatre
151http://developer.android.com/reference/android/net/http/AndroidHttpClient.html
Lab starts from IEU or your location
Click on map and get coordinates of location
To complete:
have the ‘normal’ addMarker method
add ‘onClickListener’
clear old ‘addMarker’
add new ‘addMarker’
Lab: My Location
154http://developer.android.com/reference/android/net/http/AndroidHttpClient.html
Lab: My Location
156
Get a key from ‘google_maps_api.xml’:<resources> <string name="google_maps_key_instructions" templateMergeStrategy="replace"> <!--
TODO: Before you run your application, you need a Google Maps API key.
To get one, follow this link, follow the directions and press "Create" at the end:
https://console.developers.google.com/flows/enableapi?apiid=maps_android_ backend&keyType=CLIENT_SIDE_ANDROID&r=74:76:97:C7:4F:25:91:43:6E:77:E1:FA :12:A1:5A:41:D5:A2:17:D0%3Bcom.example.sjohnson.location
You can also add your credentials to an existing key, using this line: 74:76:97:C7:4F:25:91:43:6E:77:E1:FA:12:A1:5A:41:D5:A2:17:D0;com.example .sjohnson.location
Once you have your key (it starts with "AIza"), replace the "google_maps_key" string in this file.-->
<resources> <string name="google_maps_key_instructions" templateMergeStrategy="replace"> <!--
TODO: Before you run your application, you need a Google Maps API key.
To get one, follow this link, follow the directions and press "Create" at the end:
https://console.developers.google.com/flows/enableapi?apiid=maps_android_ backend&keyType=CLIENT_SIDE_ANDROID&r=74:76:97:C7:4F:25:91:43:6E:77:E1:FA :12:A1:5A:41:D5:A2:17:D0%3Bcom.example.sjohnson.location
You can also add your credentials to an existing key, using this line: 74:76:97:C7:4F:25:91:43:6E:77:E1:FA:12:A1:5A:41:D5:A2:17:D0;com.example .sjohnson.location
Once you have your key (it starts with "AIza"), replace the "google_maps_key" string in this file.-->
Copy/paste into browser:
Open ‘Sourcecode’: (setUpMap)
Lab: My Location
163
private void setUpMap() { double latitude = 0.0; double longitude = 0.0; mMap.setMyLocationEnabled(true); LocationManager locationManager = (LocationManager)getSystemService(LOCATION_SERVICE); Criteria criteria = new Criteria(); String provider = locationManager.getBestProvider(criteria, true); Location location = locationManager.getLastKnownLocation(provider);
if (location == null) { latitude = 38.388369; longitude = 27.044622; } else { latitude = location.getLatitude(); longitude = location.getLongitude(); }
Open ‘Sourcecode’: (setUpMap)
Lab: My Location
164
LatLng position = new LatLng(latitude, longitude); mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(position, 11)); mMap.addMarker(new MarkerOptions() .icon(BitmapDescriptorFactory .fromResource(R.drawable.location)) .title("My Location") .snippet("Lat: " + latitude + " Long: " + longitude) .position(position));
Open ‘Sourcecode’: (onClickListener)
Lab: My Location
mMap.setOnMapClickListener(new GoogleMap.OnMapClickListener() {
@Override public void onMapClick(LatLng latLng) { MarkerOptions markerOptions = new MarkerOptions(); markerOptions.position(latLng); double a = latLng.latitude; double newLat = (double) Math.round(a*1000000)/1000000; double b = latLng.longitude; double newLong = (double) Math.round(b*1000000)/1000000; markerOptions.title("This location"); markerOptions.snippet("Lat: " +newLat+ " Long: "+newLong); markerOptions.icon(BitmapDescriptorFactory .fromResource(R.drawable.location));
mMap.clear(); mMap.animateCamera(CameraUpdateFactory.newLatLng(latLng)); mMap.addMarker(markerOptions); }});
1. 2. 3.
Geolocate five locations with these businesses
Map must show all five locations
Make database to define location, type, etc.
Make a marker for each type of business
2 fast food restaurants
two ATMs
1 mall
use your name as the snippet on each one
Assignment: Businesses
167