External Services CSE 5236 Mobile Application Development Course
External Services CSE 5236: Mobile Application Development Course Coordinator: Dr. Rajiv Ramnath Instructor: Dr. Adam C. Champion Reading: Big Nerd Ranch Guide, Chap. 30 (Web. View); 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 // Help. Fragment. java private void launch. Browser( String url) { Uri the. Uri = Uri. parse(url); Intent Launch. Browser. Intent = new Intent(Intent. ACTION_VIEW, the. Uri); start. Activity( Launch. Browser. Intent); } Kotlin // Help. Fragment. kt private fun launch. Browser( url: String) { val the. Uri = Uri. parse(url) val Launch. Browser. Intent = Intent(Intent. ACTION_VIEW, the. Uri) start. Activity( Launch. Browser. Intent) } URL: http: //en. wikipedia. org/wiki/Tictactoe Note: Activity stacking due to re-launch of browser on mobile page 3
Embedded Web. View - Layout <? xml version="1. 0" encoding="utf-8"? > <Scroll. View. . . > <Linear. Layout. . . > <Web. View android: id="@+id/helpwithwebview" android: layout_width="match_parent" android: layout_height="200 dip" android: layout_weight="1. 0"/> <Button. . . android: text="Exit"/> </Linear. Layout> </Scroll. View> 4
Embedded Web. View: Java // Help. Web. View. Fragment. java public View on. Create. View(. . . ) { View v = inflater. inflate(R. layout. fragment_help_webview, . . . ); Web. View help. In. Web. View = v. find. View. By. Id(R. id. helpwithwebview); m. Progress. Bar = v. find. View. By. Id(R. id. webviewprogress); m. Progress. Bar. set. Max(100); View button. Exit = v. find. View. By. Id(R. id. button_exit); button. Exit. set. On. Click. Listener(this); Bundle extras = get. Activity(). get. Intent(). get. Extras(); if (extras != null) { m. Url = extras. get. String(ARG_URI); /*. . . */ } //. . . help. In. Web. View. load. Url(m. Url); return v; } 5
Embedded Web. View: Kotlin // Help. Web. View. Fragment. kt override fun on. Create. View(. . . ): View? { val v = inflater. inflate(R. layout. fragment_help_webview, . . . ) val help. In. Web. View = v. find. View. By. Id(R. id. helpwithwebview) m. Progress. Bar = v. find. View. By. Id(R. id. webviewprogress) m. Progress. Bar. apply { max = 100 } val button. Exit = v. find. View. By. Id<Button>(R. id. button_exit) button. Exit. set. On. Click. Listener(this) val extras = activity. intent. extras if (extras != null) { m. Url = extras. get. String(ARG_URI) } /*. . . */ help. In. Web. View. load. Url(m. Url) 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 Google. Map with a Fragment. Activity • 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 Open. GL ES v 2: <uses-feature android: gl. Es. Version="0 x 00020000” 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=". Maps. Activity" android: name="com. google. android. gms. maps. Support. Map. Fragment"/> • Google Maps uses Fragments • UI Fragment loaded dynamically 9
Map Fragment Code: Java (1) // Maps. Fragment. java public class Maps. Fragment extends Support. Map. Fragment implements On. Map. Ready. Callback { private Google. Map m. Map; private Google. Api. Client m. Api. Client; private static final String[] LOCATION_PERMISSIONS = new String[] { Manifest. permission. ACCESS_FINE_LOCATION, Manifest. permission. ACCESS_COARSE_LOCATION }; private Fused. Location. Provider. Client m. Fused. Location. Provider. Client; private Location m. Location; private Lat. Lng m. Default. Location; private static final int REQUEST_LOCATION_PERMISSIONS = 0; private boolean m. Location. Permission. Granted = false; . . . // Maps. Activity. java just uses a Single. Fragment. Activity 10
Map Fragment Code: Java (2) // Maps. Fragment. java (continued) @Override public void on. Create(Bundle saved. Instance. State) { super. on. Create(saved. Instance. State); set. Has. Options. Menu(true); m. Api. Client = new Google. Api. Client. Builder(get. Activity()). add. Api(Location. Services. API). add. Connection. Callbacks(new Google. Api. Client. Connection. Callbacks() { @Override public void on. Connected(@Nullable Bundle bundle) { get. Activity(). invalidate. Options. Menu(); } @Override public void on. Connection. Suspended(int i) { /*. . . */ } }). build(); get. Map. Async(this); } @Override public void on. Resume() { super. on. Resume(); set. Up. Eula(); 11
Map Fragment Code: Java (3) // Maps. Fragment. java (continued) @Override public void on. Start() { m. Api. Client. connect(); } @Override public void on. Stop() { m. Api. Client. disconnect(); } private void find. Location() { update. Location. UI(); if (has. Location. Permission()) { m. Fused. Location. Provider. Client = Location. Services. get. Fused. Location. Provider. Client( get. Activity()); m. Default. Location = new Lat. Lng(40. 0, -83. 0); Location. Request location. Request = Location. Request. create(); /* Set location request parameters */ Fused. Location. Provider. Client location. Provider = Location. Services. get. Fused. Location. Provider. Client(get. Activity()); Task location. Result = location. Provider. get. Last. Location(); location. Result. add. On. Complete. Listener(get. Activity(), new On. Complete. Listener() { @Override public void on. Complete(@Non. Null Task task) { if (task. is. Successful()) {/* Move camera */ } } else { /*. . . */ } }}); } 12 // Else request permissions, end of method.
Map Fragment Code: Java (4) // Maps. Fragment. java (continued) private void set. Up. Eula() { m. Settings = get. Activity(). get. Shared. Preferences(get. String(R. string. prefs), 0); boolean is. Eula. Accepted = m. Settings. get. Boolean(get. String(R. string. eula_accepted_key), false); if (!is. Eula. Accepted) { Dialog. Fragment eula. Dialog. Fragment = new Eula. Dialog. Fragment(); eula. Dialog. Fragment. show(get. Activity(). get. Support. Fragment. Manager(), "eula"); } } @Override public void on. Create. Options. Menu(Menu menu, Menu. Inflater inflater) { //. . . inflater. inflate(R. menu. maps_menu, menu); } @Override public boolean on. Options. Item. Selected(Menu. Item item) { switch (item. get. Item. Id()) { case R. id. menu_showcurrentlocation: Log. d(TAG, "Showing current location"); if (has. Location. Permission()) { find. Location(); } else { /* Request permissions */ } break; } return true; } 13
Map Fragment Code: Java (5) // Maps. Fragment. java (continued) @Override public void on. Request. Permissions. Result(. . . ) { m. Location. Permission. Granted = false; switch (request. Code) { case REQUEST_LOCATION_PERMISSIONS: { // If request is cancelled, the result arrays are empty. if (grant. Results. length > 0 && grant. Results[0]==Package. Manager. PERMISSION_GRANTED) { m. Location. Permission. Granted = true; } } } update. Location. UI(); } private void update. Location. UI() { if (m. Map == null) { return; } if (m. Location. Permission. Granted) { /* Enable location */ } else { /* Disable location, request permissions */ } } @Override public void on. Map. Ready(Google. Map google. Map) { m. Map = google. Map; m. Map. add. Marker(new Marker. Options(). position(new Lat. Lng(40. 0, -83. 0)). title("O. S. U. ")); } private boolean has. Location. Permission() { /*. . . */ } 14
Map Fragment Code: Kotlin (1) // Maps. Fragment. kt class Maps. Fragment : Support. Map. Fragment(), On. Map. Ready. Callback { private lateinit var m. Map: Google. Map private lateinit var m. Api. Client: Google. Api. Client private lateinit var m. Fused. Location. Provider. Client: Fused. Location. Provider. Client private var m. Location: Location? = null private var m. Default. Location: Lat. Lng? = null private var m. Location. Permission. Granted = false private var m. Map. Ready = false companion object { private val LOCATION_PERMISSIONS = array. Of( Manifest. permission. ACCESS_FINE_LOCATION, Manifest. permission. ACCESS_COARSE_LOCATION) private val REQUEST_LOCATION_PERMISSIONS = 0 } 15
Map Fragment Code: Kotlin (2) // Maps. Fragment. kt (continued) override fun on. Create(saved. Instance. State: Bundle? ) { super. on. Create(saved. Instance. State) set. Has. Options. Menu(true) m. Api. Client = Google. Api. Client. Builder(activity). add. Api(Location. Services. API). add. Connection. Callbacks(object: Google. Api. Client. Connection. Callbacks { override fun on. Connected(bundle: Bundle? ) { /*. . . */ } override fun on. Connection. Suspended(i: Int) { /*. . . */ } }). build() get. Map. Async(this) } override fun on. Resume() { super. on. Resume() set. Up. Eula() find. Location() } 16
Map Fragment Code: Kotlin (3) // Maps. Fragment. kt (continued) override fun on. Start() { m. Api. Client. connect() } override fun on. Stop() { m. Api. Client. disconnect() } private fun find. Location() { update. Location. UI() if (has. Location. Permission()) { m. Fused. Location. Provider. Client = Location. Services. get. Fused. Location. Provider. Client(activity) m. Default. Location = Lat. Lng(40. 0, -83. 0) val location. Request = Location. Request. create() /* Set location. Request properties */ val location. Provider = Location. Services. get. Fused. Location. Provider. Client(activity) val location. Result = location. Provider. last. Location location. Result. add. On. Complete. Listener(activity, object: On. Complete. Listener<Location> { override fun on. Complete(task: Task<Location>) { if (task. is. Successful) { // Set map's camera position to current device location } else { /* Disable location */ } } }) } // Else request permissions, end of method. 17
Map Fragment Code: Kotlin (4) // Maps. Fragment. kt (continued) private fun set. Up. Eula() { m. Settings = activity. get. Shared. Preferences(get. String(R. string. prefs), 0) val is. Eula. Accepted = m. Settings!!. get. Boolean(get. String(R. string. eula_accepted_key), false) if (!is. Eula. Accepted) { val eula. Dialog. Fragment = Eula. Dialog. Fragment() eula. Dialog. Fragment. show(activity. support. Fragment. Manager, "eula") } } override fun on. Create. Options. Menu(menu: Menu? , inflater: Menu. Inflater? ) { super. on. Create. Options. Menu(menu, inflater) inflater? . inflate(R. menu. maps_menu, menu) } override fun on. Options. Item. Selected(item: Menu. Item? ): Boolean { when (item? . item. Id) { R. id. menu_showcurrentlocation -> { if (has. Location. Permission()) { find. Location() } else { request. Permissions(LOCATION_PERMISSIONS, REQUEST_LOCATION_PERMISSIONS) } } } 18 }
Map Fragment Code: Kotlin (5) // Maps. Fragment. kt (continued) override fun on. Request. Permissions. Result(request. Code: Int, permissions: Array<String>, grant. Results: Int. Array) { m. Location. Permission. Granted = false when (request. Code) { REQUEST_LOCATION_PERMISSIONS -> { // If request is cancelled, the result arrays are empty. if (grant. Results. is. Not. Empty() && grant. Results[0] == Package. Manager. PERMISSION_GRANTED) { m. Location. Permission. Granted = true } } } update. Location. UI() } private fun update. Location. UI() { if (m. Map. Ready) { if (m. Location. Permission. Granted) { /* Enable location */ } } else { /* Disable location, request permissions */ } } override fun on. Map. Ready(google. Map: Google. Map) { m. Map = google. Map /* Prepare map; set m. Map. Ready to true */ } 19 private fun has. Location. Permission(): Boolean { /*. . . */ }
Map Menu Layout <menu <!-- Details omitted --> > <item android: id="@+id/ menu_showcurrentlocation" android: order. In. Category="100" android: title="@string/show_location" android: icon="@android: drawable/ ic_menu_mylocation" app: show. As. Action="if. Room"/> </menu> 20
License Agreement Fragment: Java • We need to get the user’s consent public class Eula. Dialog. Fragment extends Dialog. Fragment { public void set. Eula. Accepted() { Shared. Preferences prefs = get. Activity(). get. Shared. Preferences( get. String(R. string. prefs), 0); Shared. Preferences. Editor editor = prefs. edit(); editor. put. Boolean(get. String(R. string. eula_accepted_key), true). apply(); } @Override public Dialog on. Create. Dialog(Bundle saved. Instance. State) { Alert. Dialog. Builder builder = new Alert. Dialog. Builder(get. Activity()); builder. set. Title(R. string. eula_title). set. Message(Html. from. Html(get. String(R. string. eula))). set. Positive. Button(R. string. accept, new Dialog. Interface. On. Click. Listener() { /* Call set. Eula. Accepted() */ } }). set. Negative. Button(R. string. decline, new Dialog. Interface. On. Click. Listener() { /* Cancel dialog, finish activity */ return builder. create(); } } 21
License Agreement Fragment: Kotlin class Eula. Dialog. Fragment : Dialog. Fragment() { fun set. Eula. Accepted() { val prefs = activity. get. Shared. Preferences(get. String(R. string. prefs), 0) val editor = prefs. edit() editor. put. Boolean(get. String(R. string. eula_accepted_key), true). apply() } override fun on. Create. Dialog(saved. Instance. State: Bundle? ): Dialog { // Use the Builder class for convenient dialog construction val builder = Alert. Dialog. Builder(activity) builder. set. Title(R. string. about_app). set. Message(Html. from. Html(get. String(R. string. eula))). set. Positive. Button(R. string. accept) { /* Call set. Eula. Accepted() */ }. set. Negative. Button(R. string. decline) { /* Cancel dialog, finish activity */ } return builder. create() } } 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 (3 rd ed. ) • http: //developer. android. com/reference/android/webkit/packagesummary. html • http: //developer. android. com/reference/android/webkit/Web. View. 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
- Slides: 25