External Services
CSE 5236: Mobile Application Development
Course Coordinator: Dr. Rajiv Ramnath
Instructor: Dr. Adam C. Champion
Reading: Big Nerd Ranch Guide, Chap. 30 (WebView);
Chaps. 33, 34 (Google Play services, maps)
1
External Services
• Viewing websites
• Location- and map-based functionality
• REST-based Web services invocation
2
Invoking Browser App
Java Kotlin
// [Link] // [Link]
private void launchBrowser( private fun launchBrowser(
String url) { url: String) {
Uri theUri = [Link](url); val theUri = [Link](url)
Intent LaunchBrowserIntent = val LaunchBrowserIntent =
new Intent(Intent.ACTION_VIEW, Intent(Intent.ACTION_VIEW,
theUri); theUri)
startActivity( startActivity(
LaunchBrowserIntent); LaunchBrowserIntent)
} }
URL: [Link]
3
Note: Activity stacking due to re-launch of browser on mobile page
Embedded WebView - Layout
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
...>
<LinearLayout ...
...>
<WebView
android:id="@+id/helpwithwebview"
android:layout_width="match_parent"
android:layout_height="200dip"
android:layout_weight="1.0"/>
<Button
...
android:text="Exit"/>
</LinearLayout>
</ScrollView>
4
Embedded WebView: Java
// [Link]
public View onCreateView(. . .) {
View v = [Link]([Link].fragment_help_webview, ...);
WebView helpInWebView = [Link]([Link]);
mProgressBar = [Link]([Link]);
[Link](100);
View buttonExit = [Link]([Link].button_exit);
[Link](this);
Bundle extras = getActivity().getIntent().getExtras();
if (extras != null) {
mUrl = [Link](ARG_URI); /* . . . */
}
// . . .
[Link](mUrl);
return v;
}
5
Embedded WebView: Kotlin
// [Link]
override fun onCreateView( . . . ): View? {
val v = [Link]([Link].fragment_help_webview, . . .)
val helpInWebView = [Link]([Link])
mProgressBar = [Link]([Link])
[Link] { max = 100 }
val buttonExit = [Link]<Button>([Link].button_exit)
[Link](this)
val extras = [Link]
if (extras != null) {
mUrl = [Link](ARG_URI)
}
/* . . . */
[Link](mUrl)
return v
}
6
Location-Based Applications
These mix-and-match the following actions:
– Opening a map
– Invoking a geocoding service on a point of
interest
– Navigating the map to a position or make a
map-based calculation
– Determining the user’s geolocation from the
device (latitude, longitude)
7
Additional Requirements for Maps
• Use built-in GoogleMap with a FragmentActivity
• Link against Google APIs for Android (not “Android SDK”)
• Request permissions: <uses-permission
android:name="<permission>"/>, where <permission>
includes ACCESS_NETWORK_STATE,
ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION,
WRITE_EXTERNAL_STORAGE READ_GSERVICES
• Request a Map API key: [Link]
documentation/android/start
• Need OpenGL ES v2: <uses-feature
android:glEsVersion="0x00020000”
android:required="true"/>
8
Map Fragment Layout
<fragment
xmlns:tools="[Link]
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/map"
tools:context=".MapsActivity"
android:name="[Link].
SupportMapFragment"/>
• Google Maps uses Fragments
• UI Fragment loaded dynamically
9
Map Fragment Code: Java (1)
// [Link]
public class MapsFragment extends SupportMapFragment implements
OnMapReadyCallback {
private GoogleMap mMap;
private GoogleApiClient mApiClient;
private static final String[] LOCATION_PERMISSIONS = new String[] {
[Link].ACCESS_FINE_LOCATION,
[Link].ACCESS_COARSE_LOCATION };
private FusedLocationProviderClient mFusedLocationProviderClient;
private Location mLocation;
private LatLng mDefaultLocation;
private static final int REQUEST_LOCATION_PERMISSIONS = 0;
private boolean mLocationPermissionGranted = false;
. . .
// [Link] just uses a SingleFragmentActivity
10
Map Fragment Code: Java (2)
// [Link] (continued)
@Override
public void onCreate(Bundle savedInstanceState) {
[Link](savedInstanceState);
setHasOptionsMenu(true);
mApiClient = new [Link](getActivity())
.addApi([Link])
.addConnectionCallbacks(new [Link]() {
@Override
public void onConnected(@Nullable Bundle bundle) {
getActivity().invalidateOptionsMenu(); }
@Override
public void onConnectionSuspended(int i) { /*...*/ } }).build();
getMapAsync(this);
}
@Override
public void onResume() {
[Link]();
setUpEula(); 11
findLocation(); }
Map Fragment Code: Java (3)
// [Link] (continued)
@Override
public void onStart() { [Link](); }
@Override
public void onStop() { [Link](); }
private void findLocation() {
updateLocationUI();
if (hasLocationPermission()) {
mFusedLocationProviderClient = [Link](
getActivity());
mDefaultLocation = new LatLng(40.0, -83.0);
LocationRequest locationRequest = [Link]();
/* Set location request parameters */
FusedLocationProviderClient locationProvider =
[Link](getActivity());
Task locationResult = [Link]();
[Link](getActivity(), new OnCompleteListener() {
@Override
public void onComplete(@NonNull Task task) {
if ([Link]()) {/* Move camera */ } } else { /* ... */ } }});}
12
// Else request permissions, end of method.
Map Fragment Code: Java (4)
// [Link] (continued)
private void setUpEula() {
mSettings = getActivity().getSharedPreferences(getString([Link]), 0);
boolean isEulaAccepted = [Link](getString([Link].eula_accepted_key),
false);
if (!isEulaAccepted) {
DialogFragment eulaDialogFragment = new EulaDialogFragment();
[Link](getActivity().getSupportFragmentManager(), "eula"); }
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { // . . .
[Link]([Link].maps_menu, menu); }
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch ([Link]()) {
case [Link].menu_showcurrentlocation:
Log.d(TAG, "Showing current location");
if (hasLocationPermission()) { findLocation(); }
else { /* Request permissions */ }
break; }
return true; 13
}
Map Fragment Code: Java (5)
// [Link] (continued)
@Override
public void onRequestPermissionsResult(. . .) {
mLocationPermissionGranted = false;
switch (requestCode) {
case REQUEST_LOCATION_PERMISSIONS: {
// If request is cancelled, the result arrays are empty.
if ([Link] > 0 &&
grantResults[0]==PackageManager.PERMISSION_GRANTED) {
mLocationPermissionGranted = true; } } }
updateLocationUI(); }
private void updateLocationUI() {
if (mMap == null) { return; }
if (mLocationPermissionGranted) { /* Enable location */ }
else { /* Disable location, request permissions */ }
}
@Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
[Link](new MarkerOptions().position(new LatLng(40.0,-83.0)).title("O.S.U."));
}
14
private boolean hasLocationPermission() { /* . . . */ }
Map Fragment Code: Kotlin (1)
// [Link]
class MapsFragment : SupportMapFragment(), OnMapReadyCallback {
private lateinit var mMap: GoogleMap
private lateinit var mApiClient: GoogleApiClient
private lateinit var mFusedLocationProviderClient: FusedLocationProviderClient
private var mLocation: Location? = null
private var mDefaultLocation: LatLng? = null
private var mLocationPermissionGranted = false
private var mMapReady = false
companion object {
private val LOCATION_PERMISSIONS = arrayOf(
[Link].ACCESS_FINE_LOCATION,
[Link].ACCESS_COARSE_LOCATION)
private val REQUEST_LOCATION_PERMISSIONS = 0
}
15
Map Fragment Code: Kotlin (2)
// [Link] (continued)
override fun onCreate(savedInstanceState: Bundle?) {
[Link](savedInstanceState)
setHasOptionsMenu(true)
mApiClient = [Link](activity)
.addApi([Link])
.addConnectionCallbacks(object: [Link] {
override fun onConnected(bundle: Bundle?) { /* ... */ }
override fun onConnectionSuspended(i: Int) { /* ... */ }
}).build()
getMapAsync(this)
}
override fun onResume() {
[Link]()
setUpEula()
findLocation()
}
16
Map Fragment Code: Kotlin (3)
// [Link] (continued)
override fun onStart() { [Link]() }
override fun onStop() { [Link]() }
private fun findLocation() {
updateLocationUI()
if (hasLocationPermission()) {
mFusedLocationProviderClient =
[Link](activity)
mDefaultLocation = LatLng(40.0, -83.0)
val locationRequest = [Link]()
/* Set locationRequest properties */
val locationProvider = [Link](activity)
val locationResult = [Link]
[Link](activity, object:OnCompleteListener<Location> {
override fun onComplete(task: Task<Location>) {
if ([Link]) {
// Set map's camera position to current device location
} else { /* Disable location */ } } }) }
// Else request permissions, end of method.
17
Map Fragment Code: Kotlin (4)
// [Link] (continued)
private fun setUpEula() {
mSettings = [Link](getString([Link]), 0)
val isEulaAccepted = mSettings!!.getBoolean(getString([Link].eula_accepted_key),
false)
if (!isEulaAccepted) {
val eulaDialogFragment = EulaDialogFragment()
[Link]([Link], "eula") }
}
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
[Link](menu, inflater)
inflater?.inflate([Link].maps_menu, menu)
}
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
when (item?.itemId) {
[Link].menu_showcurrentlocation -> {
if (hasLocationPermission()) { findLocation() }
else { requestPermissions(LOCATION_PERMISSIONS,REQUEST_LOCATION_PERMISSIONS) }
}
}
} 18
Map Fragment Code: Kotlin (5)
// [Link] (continued)
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>,
grantResults: IntArray) {
mLocationPermissionGranted = false
when (requestCode) {
REQUEST_LOCATION_PERMISSIONS -> {
// If request is cancelled, the result arrays are empty.
if ([Link]() &&
grantResults[0] == PackageManager.PERMISSION_GRANTED) {
mLocationPermissionGranted = true } } }
updateLocationUI() }
private fun updateLocationUI() {
if (mMapReady) {
if (mLocationPermissionGranted) { /* Enable location */ }
else { /* Disable location, request permissions */ }
} }
override fun onMapReady(googleMap: GoogleMap) {
mMap = googleMap /* Prepare map; set mMapReady to true */ } 19
Map Menu Layout
<menu
<!-- Details omitted --> >
<item
android:id="@+id/
menu_showcurrentlocation"
android:orderInCategory="100"
android:title="@string/show_location"
android:icon="@android:drawable/
ic_menu_mylocation"
app:showAsAction="ifRoom"/>
</menu>
20
License Agreement Fragment: Java
• We need to get the user’s consent
public class EulaDialogFragment extends DialogFragment {
public void setEulaAccepted() {
SharedPreferences prefs = getActivity().getSharedPreferences(
getString([Link]), 0);
[Link] editor = [Link]();
[Link](getString([Link].eula_accepted_key), true)
.apply(); }
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
[Link] builder = new [Link](getActivity());
[Link]([Link].eula_title)
.setMessage([Link](getString([Link])))
.setPositiveButton([Link], new [Link]() {
/* Call setEulaAccepted() */ } })
.setNegativeButton([Link], new [Link]() {
/* Cancel dialog, finish activity */
return [Link](); }
}
21
License Agreement Fragment: Kotlin
class EulaDialogFragment : DialogFragment() {
fun setEulaAccepted() {
val prefs = [Link](getString([Link]), 0)
val editor = [Link]()
[Link](getString([Link].eula_accepted_key), true).apply()
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
// Use the Builder class for convenient dialog construction
val builder = [Link](activity)
[Link]([Link].about_app)
.setMessage([Link](getString([Link])))
.setPositiveButton([Link]) { /* Call setEulaAccepted() */ }
.setNegativeButton([Link]) {
/* Cancel dialog, finish activity */
}
return [Link]()
}
}
22
Location Determination Practices
• Ways to get location:
– GPS
– Cellular
– Wi-Fi
• Best practices for location-based apps:
– Check for connectivity
– Use threading to ensure responsiveness (Note:
Threading built-in for many SDK components)
23
Thank You
Questions and comments?
24
References
• Chapter 10: “Channeling the Outside World through your Android
Device” from Android SDK 3 Programming for Dummies
• Chapters 33–34: “Locations and Play Services” and “Maps” from
Android Programming: The Big Nerd Ranch Guide (3rd ed.)
• [Link]
[Link]
• [Link]
• [Link]
• [Link]
• [Link]
25