05 ExternalServices
05 ExternalServices
05 ExternalServices
1
External Services
• Viewing websites
• Location- and map-based functionality
• REST-based Web services invocation
2
Invoking Browser App
Java Kotlin
// HelpFragment.java // HelpFragment.kt
private void launchBrowser( private fun launchBrowser(
String url) { url: String) {
Uri theUri = Uri.parse(url); val theUri = Uri.parse(url)
Intent LaunchBrowserIntent = val LaunchBrowserIntent =
new Intent(Intent.ACTION_VIEW, Intent(Intent.ACTION_VIEW,
theUri); theUri)
startActivity( startActivity(
LaunchBrowserIntent); LaunchBrowserIntent)
} }
URL: http://en.wikipedia.org/wiki/Tictactoe
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
// HelpWebViewFragment.java
public View onCreateView(. . .) {
View v = inflater.inflate(R.layout.fragment_help_webview, ...);
WebView helpInWebView = v.findViewById(R.id.helpwithwebview);
mProgressBar = v.findViewById(R.id.webviewprogress);
mProgressBar.setMax(100);
View buttonExit = v.findViewById(R.id.button_exit);
buttonExit.setOnClickListener(this);
Bundle extras = getActivity().getIntent().getExtras();
if (extras != null) {
mUrl = extras.getString(ARG_URI); /* . . . */
}
// . . .
helpInWebView.loadUrl(mUrl);
return v;
}
5
Embedded WebView: Kotlin
// HelpWebViewFragment.kt
override fun onCreateView( . . . ): View? {
val v = inflater.inflate(R.layout.fragment_help_webview, . . .)
val helpInWebView = v.findViewById(R.id.helpwithwebview)
mProgressBar = v.findViewById(R.id.webviewprogress)
mProgressBar.apply { max = 100 }
val buttonExit = v.findViewById<Button>(R.id.button_exit)
buttonExit.setOnClickListener(this)
val extras = activity.intent.extras
if (extras != null) {
mUrl = extras.getString(ARG_URI)
}
/* . . . */
helpInWebView.loadUrl(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: https://developers.google.com/maps/
documentation/android/start
• Need OpenGL ES v2: <uses-feature
android:glEsVersion="0x00020000”
android:required="true"/>
8
Map Fragment Layout
<fragment
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/map"
tools:context=".MapsActivity"
android:name="com.google.android.gms.maps.
SupportMapFragment"/>
9
Map Fragment Code: Java (1)
// MapsFragment.java
public class MapsFragment extends SupportMapFragment implements
OnMapReadyCallback {
private GoogleMap mMap;
private GoogleApiClient mApiClient;
private static final String[] LOCATION_PERMISSIONS = new String[] {
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION };
private FusedLocationProviderClient mFusedLocationProviderClient;
private Location mLocation;
private LatLng mDefaultLocation;
private static final int REQUEST_LOCATION_PERMISSIONS = 0;
private boolean mLocationPermissionGranted = false;
. . .
10
Map Fragment Code: Java (2)
// MapsFragment.java (continued)
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
mApiClient = new GoogleApiClient.Builder(getActivity())
.addApi(LocationServices.API)
.addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
@Override
public void onConnected(@Nullable Bundle bundle) {
getActivity().invalidateOptionsMenu(); }
@Override
public void onConnectionSuspended(int i) { /*...*/ } }).build();
getMapAsync(this);
}
@Override
public void onResume() {
super.onResume();
setUpEula(); 11
findLocation(); }
Map Fragment Code: Java (3)
// MapsFragment.java (continued)
@Override
public void onStart() { mApiClient.connect(); }
@Override
public void onStop() { mApiClient.disconnect(); }
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { // . . .
inflater.inflate(R.menu.maps_menu, menu); }
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_showcurrentlocation:
Log.d(TAG, "Showing current location");
if (hasLocationPermission()) { findLocation(); }
else { /* Request permissions */ }
break; }
return true; 13
}
Map Fragment Code: Java (5)
// MapsFragment.java (continued)
@Override
public void onRequestPermissionsResult(. . .) {
mLocationPermissionGranted = false;
switch (requestCode) {
case REQUEST_LOCATION_PERMISSIONS: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0 &&
grantResults[0]==PackageManager.PERMISSION_GRANTED) {
mLocationPermissionGranted = true; } } }
updateLocationUI(); }
@Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
mMap.addMarker(new MarkerOptions().position(new LatLng(40.0,-83.0)).title("O.S.U."));
}
14
private boolean hasLocationPermission() { /* . . . */ }
Map Fragment Code: Kotlin (1)
// MapsFragment.kt
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(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION)
private val REQUEST_LOCATION_PERMISSIONS = 0
}
15
Map Fragment Code: Kotlin (2)
// MapsFragment.kt (continued)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
mApiClient = GoogleApiClient.Builder(activity)
.addApi(LocationServices.API)
.addConnectionCallbacks(object: GoogleApiClient.ConnectionCallbacks {
override fun onConnected(bundle: Bundle?) { /* ... */ }
override fun onConnectionSuspended(i: Int) { /* ... */ }
}).build()
getMapAsync(this)
}
grantResults: IntArray) {
mLocationPermissionGranted = false
when (requestCode) {
REQUEST_LOCATION_PERMISSIONS -> {
// If request is cancelled, the result arrays are empty.
if (grantResults.isNotEmpty() &&
grantResults[0] == PackageManager.PERMISSION_GRANTED) {
mLocationPermissionGranted = true } } }
updateLocationUI() }
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(R.string.prefs), 0);
SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean(getString(R.string.eula_accepted_key), true)
.apply(); }
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(R.string.eula_title)
.setMessage(Html.fromHtml(getString(R.string.eula)))
.setPositiveButton(R.string.accept, new DialogInterface.OnClickListener() {
/* Call setEulaAccepted() */ } })
.setNegativeButton(R.string.decline, new DialogInterface.OnClickListener() {
/* Cancel dialog, finish activity */
return builder.create(); }
}
21
License Agreement Fragment: Kotlin
class EulaDialogFragment : DialogFragment() {
fun setEulaAccepted() {
val prefs = activity.getSharedPreferences(getString(R.string.prefs), 0)
val editor = prefs.edit()
editor.putBoolean(getString(R.string.eula_accepted_key), true).apply()
}
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
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.)
• http://developer.android.com/reference/android/webkit/package-summ
ary.html
• http://developer.android.com/reference/android/webkit/WebView.html
• https://developers.google.com/maps/documentation/android/start
• https://developer.android.com/guide/components/fragments.html
• https://developer.android.com/training/basics/fragments/creating.html
25