Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 50 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
50
Dung lượng
2,14 MB
Nội dung
326 CHAPTER 12 Putting it all together–the Field Service Application capable device and toolbox. To help in the discus- sion of the different features available to the user on this screen, take a look at figure 12.10. The layout is very straightforward but this time we have some Button s and we will be changing the textual description depending on the condition of a particular job’s status. A Text- View is used to present job details such as address, product requiring service, and com- ments. The third Button will have the text property changed, depending on the status of the job. If the job’s status is marked as CLOSED, the functionality of the third button will change. To support the functionality of this Activity , first the code needs to launch a new Activity to show a map of the job’s address, as shown in fig- ure 12.11. The second button, Get Product Info, launches a browser window to assist the user in learning more about the product he is being called upon to work with. Figure 12.12 shows this in action. The third requirement is to allow the user to close the job or to view the signature if it is already closed, the details of which are covered in the next section on the CloseJob Activity . More on Bundles We need to pass the selected job to the ShowJob Activity , but we cannot casually pass an object from one Activity to another. We don’t want the ShowJob Activity to have to parse the list of jobs again; otherwise we could simply pass back an index to the selected job by using the integer storage methods of a Bundle . Perhaps we could store the currently selected JobEntry (and JobList for that matter) in a global data member of the Application object, should we have chosen to implement one. If you recall in chapter 1 when we discussed the ability of Android to dispatch In- tent s to any Activity registered on the device, we want to keep the ability open to an application other than our own to perhaps pass a job to us. If that were the case, using a global data member of an Application object would never work! Never mind for the moment the likelihood of such a step being low, particularly considering how the data is stored in this application. This chapter’s sample application is an exercise of evaluating some different mechanisms one might employ to solve data movement around Android. The chosen solution is to package the data fields of the JobEntry in a Bundle F (in listing 12.15) to move a JobEntry from one Activity to another. In the strictest sense, we are moving not a real JobEntry object but a representation of a JobEntry ’s data members. The net of this long discussion is that this method creates a new Bundle by using the toBundle() method of the JobEntry . Figure 12.10 An example of a job shown in the ShowJob Activity Licensed to Deborah Christiansen <pedbro@gmail.com> Download at Boykma.Com 327Digging deeper into the code Fortunately, the steps required for the first two operations are quite simple with Android—thanks to the Intent . Listing 12.16 and the accompanying descriptions show you how. package com.msi.manning.UnlockingAndroid; // multiple imports omitted for brevity, see full source public class ShowJob extends Activity { Prefs myprefs = null; JobEntry je = null; final int CLOSEJOBTASK = 1; public void onCreate(Bundle icicle) { super.onCreate(icicle); setContentView(R.layout.showjob); myprefs = new Prefs(this.getApplicationContext()); StringBuilder sb = new StringBuilder(); String details = null; Intent startingIntent = getIntent(); if (startingIntent != null) { Bundle b = startingIntent.getExtas(); if (b == null) { details = "bad bundle?"; } else { je = JobEntry.fromBundle(b); sb.append("Job Id: " + je.get_jobid() + " (" + je.get_status()+ ➥ ")\n\n"); sb.append(je.get_customer() + "\n\n"); sb.append(je.get_address() + "\n" + je.get_city() + "," + ➥ je.get_state() + "\n" ); sb.append("Product : "+ je.get_product() + "\n\n"); Listing 12.16 ShowJob.java Figure 12.11 Viewing a job address in the Maps application Figure 12.12 Get Product Info takes the user to a web page specific to this job. Get Intent Extract the Bundle from the Intent Licensed to Deborah Christiansen <pedbro@gmail.com> Download at Boykma.Com 328 CHAPTER 12 Putting it all together–the Field Service Application sb.append("Comments: " + je.get_comments() + "\n\n"); details = sb.toString(); } } else { details = "Job Information Not Found."; TextView tv = (TextView) findViewById(R.id.details); tv.setText(details); return; } TextView tv = (TextView) findViewById(R.id.details); tv.setText(details); Button bmap = (Button) findViewById(R.id.mapjob); bmap.setOnClickListener(new Button.OnClickListener() { public void onClick(View v) { // clean up data for use in GEO query String address = je.get_address() + " " + je.get_city() + " " + ➥ je.get_zip(); String cleanAddress = address.replace(",", ""); cleanAddress = cleanAddress.replace(' ','+'); try { Intent geoIntent = new Intent("android.intent.action.VIEW",android.net.Uri.parse("geo:0,0?q=" + ➥ cleanAddress)); startActivity(geoIntent); } catch (Exception ee) { } } }); Button bproductinfo = (Button) findViewById(R.id.productinfo); bproductinfo.setOnClickListener(new Button.OnClickListener() { public void onClick(View v) { try { Intent productInfoIntent = new Intent("android.intent.action.VIEW", ➥ android.net.Uri.parse(je.get_producturl())); startActivity(productInfoIntent); } catch (Exception ee) { } } } ) ; Button bclose = (Button) findViewById(R.id.closejob); if (je.get_status().equals("CLOSED")) { bclose.setText("Job is Closed. View Signature"); } bclose.setOnClickListener(new Button.OnClickListener() { public void onClick(View v) { if (je.get_status().equals("CLOSED")) { Intent signatureIntent = new Intent("android.intent.action.VIEW", ➥ android.net.Uri.parse(myprefs.getServer() + "sigs/" + ➥ je.get_jobid() + ".jpg")); startActivity(signatureIntent); } else { Intent closeJobIntent = new Intent(ShowJob.this,CloseJob.class); Bundle b = je.toBundle(); closeJobIntent.putExtras(b); startActivityForResult(closeJobIntent,CLOSEJOBTASK); Update UI upon error and return Build and launch a geo query Obtain product information via URL Selectively update Button label Show Signature for CLOSED JobEntrys Initiate CloseJob Activity Licensed to Deborah Christiansen <pedbro@gmail.com> Download at Boykma.Com 329Digging deeper into the code } } } ) ; Log.d("CH12","Job status is :" + je.get_status()); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case CLOSEJOBTASK: if (resultCode == 1) { this.setResult(1, "", data.getExtras()); finish(); } break; } } } Upon completion of the CloseJob Activity , the onActivityResult callback is invoked. When this situation occurs, this method receives a Bundle containing the data elements for the recently closed JobEntry B . If you recall, the ShowJob Activ- ity was launched “for result.” The requirement is to propagate this JobEntry data back up to the calling Activity , ManageJobs . Calling setResult() and passing the Bundle (obtained with getExtras() ) fulfills this requirement. Despite the simple appearance of some text and a few easy-to-hit buttons, the ShowJob Activity provides a significant amount of functionality to the user. All that remains is to capture the signature to close out the job. To do this requires an exami- nation of the CloseJob Activity . 12.4.4 CloseJob Our faithful mobile technician has just completed the maintenance operation on the part and is ready to head off to lunch before stopping for another job on the way home, but first he must close out this job with a signature from the customer. To accomplish this, the Field Service Application presents a blank screen, and the cus- tomer uses a stylus (or a mouse in the case of the Android Emulator) to sign the device, acknowledging that the work has been completed. Once the signature has been captured, the data is submitted to the server. The proof of job completion has been captured, and the job can now be billed. Figure 12.13 demonstrates this sequence of events. This Activity can be broken down into two basic functions. The first is the capture of a signature. The second is transmittal of job data to the server. Of interest is that this Activity ’s UI has no layout resource. All of the UI elements in this Activity are gen- erated dynamically, as shown in listing 12.17. In addition, the ProgressDialog intro- duced in the RefreshJobs Activity is brought back for an encore, to let our mobile technician know that the captured signature is being sent when the Sign & Close menu Handle newly closed JobEntry B Licensed to Deborah Christiansen <pedbro@gmail.com> Download at Boykma.Com 330 CHAPTER 12 Putting it all together–the Field Service Application option is selected. If the user selects Cancel, the ShowJob Activity resumes control. Note that the signature should be made prior to selecting the menu option. package com.msi.manning.UnlockingAndroid; // multiple imports omitted for brevity, see full source public class CloseJob extends Activity { ProgressDialog myprogress; Handler progresshandler; Message msg; JobEntry je = null; private closejobView sc = null; @Override public void onCreate(Bundle icicle) { Listing 12.17 CloseJob.java—GUI setup Figure 12.13 The CloseJob Activity capturing a signature and sending data to the server Local queuing One element not found in this sample application is the local queuing of the signa- ture. Ideally this would be done in the event that data coverage is not available. The storage of the image is actually quite simple; the perhaps more challenging piece is the logic on when to attempt to send the data again. Considering all of the develop- ment of this sample application is done on the Android Emulator with near-perfect connectivity, it is of little concern here. However, in the interest of best preparing you to write real-world applications, it is worth the reminder of local queuing in the event of communications trouble in the field. Licensed to Deborah Christiansen <pedbro@gmail.com> Download at Boykma.Com 331Digging deeper into the code super.onCreate(icicle); Intent startingIntent = getIntent(); if (startingIntent != null) { Bundle b = startingIntent.getExtras() if (b != null) { je = JobEntry.fromBundle(b); } } sc = new closejobView(this); setContentView(sc); if (je == null) { finish(); } } @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); menu.add(0,0,"Sign & Close"); menu.add(0,1,"Cancel"); return true; } public boolean onOptionsItemSelected(Menu.Item item) { Prefs myprefs = new Prefs(CloseJob.this.getApplicationContext()); switch (item.getId()) { case 0: try { myprogress = ProgressDialog.show(this, "Closing Job ", ➥ "Saving Signature to Network",true,false); progresshandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case 0: myprogress.setMessage("" + (String) msg.obj); b r e a k ; case 1: myprogress.cancel(); finish(); b r e a k ; } super.handleMessage(msg); } } ; Thread workthread = new Thread(new DoCloseJob(myprefs)); workthread.start(); } catch (Exception e) { Log.d("closejob",e.getMessage()); msg = new Message(); msg.what = 1; progresshandler.sendMessage(msg); } return true; case 1: finish(); return true; Instantiate instance of closejobView B Define available menus C Handle selected menu D Start Thread to CloseJob E Licensed to Deborah Christiansen <pedbro@gmail.com> Download at Boykma.Com 332 CHAPTER 12 Putting it all together–the Field Service Application } return false; } Unlike previous activities in this chapter, the UI does not come from a design time–defined layout, but rather an instance of a closejobView B is the primary UI. The closejobView is defined in listing 12.18. The onCreateOptionsMenu method C is an override of the base View ’s method, allowing a convenient way to add menus to this screen. Note that two menus are added, one for Sign & Close and one for Cancel. The onOptionsItemSelected method D is invoked when the user selects a menu item. A ProgressDialog and accompanying Handler are instantiated when the menu to close a job is selected. Once the progress-reporting mechanism is in place, a new Thread is created and started in order to process the steps required to actually close the job E . Note that an instance of Prefs is passed in as an argument to the constructor, as that will be needed to store a signature, as we’ll show in listing 12.19. The UI at this point is only partially set up; we need a means to capture a signature on the screen of our Android device. Listing 12.18 implements the class closejob- View , which is an extension of the View class. public class closejobView extends View { Bitmap _bitmap; Canvas _canvas; final Paint _paint; int lastX; int lastY; public closejobView(Context c) { super(c); _paint = new Paint(); _paint.setColor(Color.BLACK); lastX = -1; } public boolean Save(OutputStream os){ try { _canvas.drawText("Unlocking Android", 10, 10, _paint); _canvas.drawText("http://manning.com/ableson", 10, 25, _paint); _canvas.drawText("http://android12.msi-wireless.com", 10, 40, _paint); _bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os); invalidate(); return true; } catch (Exception e) { return false; } } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { Bitmap img = Bitmap.createBitmap(w, h,Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(); Listing 12.18 CloseJob.java—closejobView class B closejobView extends the base class View Required classes for drawing C Initialize drawing classes Save method persists signature D E Add contextual data to image Convert image to JPEG Bitmap initialization code Licensed to Deborah Christiansen <pedbro@gmail.com> Download at Boykma.Com 333Digging deeper into the code canvas.setBitmap(img); if (_bitmap != null) { canvas.drawBitmap(img, 0, 0, null); } _bitmap = img; _canvas = canvas; _canvas.drawColor(Color.WHITE); } @Override protected void onDraw(Canvas canvas) { if (_bitmap != null) { canvas.drawBitmap(_bitmap, 0, 0, null); } } @Override public boolean onTouchEvent(MotionEvent event) { int action = event.getAction(); int X = (int)event.getX(); int Y = (int)event.getY(); switch (action ) { case MotionEvent.ACTION_UP: // reset location lastX = -1; break; case MotionEvent.ACTION_DOWN: if (lastX != -1){ if ((int) event.getX() != lastX) { _canvas.drawLine(lastX, lastY, X, Y, _paint); } } lastX = (int)event.getX(); lastY = (int)event.getY(); break; case MotionEvent.ACTION_MOVE: if (lastX != -1){ _canvas.drawLine(lastX, lastY, X, Y, _paint); } lastX = (int)event.getX(); lastY = (int)event.getY(); break; } invalidate(); return true; } } } The closejobView extends the base View class B . The Bitmap and Canvas classes C work together to form the drawing surface for this Activity . Note the call to the Canvas.drawColor method, which sets the background color to WHITE. When the onDraw() method is invoked, the canvas draws its associated bitmap with a call to drawBitmap() F . The logic for where to draw relies on the onTouchEvent method G , which receives an instance of the MotionEvent class. The MotionEvent class tells what happened and Draw image on screen F Handle Touch Events (i.e., capture signature!) G Licensed to Deborah Christiansen <pedbro@gmail.com> Download at Boykma.Com 334 CHAPTER 12 Putting it all together–the Field Service Application where. ACTION_UP , ACTION_DOWN , and ACTION_MOVE are the events captured, with some logic to guide when and where to draw. Once the signature is complete, the Save method D is responsible for converting the contents of the image to a form usable for submission to the server. Note that additional text is drawn on the signature E . In this case, it is little more than a shameless plug for this book’s webpage; however, this could also be location-based data. Why is this important? Imagine someone forging a signature. Could happen, but it would be more challenging and of less value to a rogue mobile technician if the GPS/location data were actually stamped on the job, along with the date and time. When converting the image to our desired JPEG format, there is an additional input argument to this method—an OutputStream , used to store the image data. This OutputStream reference was actually an input argument to the Save method. Now that the UI has been created and a signature drawn on the screen, let’s look at the code used to close the job. Closing the job involves capturing the signature and send- ing it to the server via an HTTP POST . The class DoCloseJob is shown in listing 12.19. class DoCloseJob implements Runnable { Prefs _myprefs; DoCloseJob(Prefs p) { _myprefs = p; } public void run() { try { FileOutputStream os = getApplication().openFileOutput("sig.jpg", 0); sc.Save(os); os.flush(); os.close(); // reopen to so we can send this data to server File f = new File(getApplication().getFileStreamPath("sig.jpg").toString()); long flength = f.length(); FileInputStream is = getApplication().openFileInput("sig.jpg"); byte data[] = new byte[(int) flength]; int count = is.read(data); if (count != (int) flength) { // bad read? } msg = new Message(); msg.what = 0; msg.obj = (Object)("Connecting to Server"); progresshandler.sendMessage(msg); URL url = new URL(_myprefs.getServer() + "/closejob.php?jobid=" + je.get_jobid()); URLConnection conn = url.openConnection(); conn.setDoOutput(true); BufferedOutputStream wr = new BufferedOutputStream(conn.getOutputStream()); Listing 12.19 CloseJob.java—DoCloseJob class Constructor uses Prefs instance Open a file for storing signature B Construct storage URL C Write data to server D Licensed to Deborah Christiansen <pedbro@gmail.com> Download at Boykma.Com 335Digging deeper into the code wr.write(data); wr.flush(); wr.close(); msg = new Message(); msg.what = 0; msg.obj = (Object)("Data Sent"); progresshandler.sendMessage(msg); BufferedReader rd = new BufferedReader(new ➥ InputStreamReader(conn.getInputStream())); String line = ""; Boolean bSuccess = false; while ((line = rd.readLine()) != null) { if (line.indexOf("SUCCESS") != -1) { bSuccess = true; } } wr.close(); rd.close(); if (bSuccess) { msg = new Message(); msg.what = 0; msg.obj = (Object)("Job Closed Successfully"); progresshandler.sendMessage(msg); je.set_status("CLOSED"); CloseJob.this.setResult(1,"",je.toBundle()); } else { msg = new Message(); msg.what = 0; msg.obj = (Object)("Failed to Close Job"); progresshandler.sendMessage(msg); CloseJob.this.setResult(0); } } catch (Exception e) { Log.d("CH12","Failed to submit job close signature: " + e.getMessage()); } msg = new Message(); msg.what = 1; progresshandler.sendMessage(msg); } } At this point, we have a signature on the screen and need to capture it. A new File- OutputStream B is obtained for a file on the local filesystem, and the signature is writ- ten to this file. We are now ready to transmit this file to the server—remember, we want to bill the client as soon as possible for work completed! In preparation for sending the signature to the server, the signature file contents are read into a byte array via an instance of a FileInputStream . Using the Prefs instance to get specific configuration information, a URL C is constructed in order to POST data to the server. The query String of the URL contains the jobid and the POST data contains the image itself. A BufferedOutputStream D is employed to POST data, which consists of the captured signature in JPEG format. Read server response E Check for successful processing F Update local JobEntry status G H Set result and store updated JobEntry Licensed to Deborah Christiansen <pedbro@gmail.com> Download at Boykma.Com [...]... pullandroid.bat adb pull /system/lib/libdl.so m: \android\ system\lib libdl.so, dynamic loading adb pull /system/lib/libthread_db.so m: \android\ system\lib adb pull /system/lib/libc.so m: \android\ system\lib libc.so, C runtime adb pull /system/lib/libm.so m: \android\ system\lib libm.so, math library adb pull /system/lib/libGLES_CM.so m: \android\ system\lib libGLES_CM.so, adb pull /system/lib/libssl.so m: \android\ system\lib... language In this chapter we use the term Android/ Linux to refer to the Linux underpinnings of the Android platform We also use the term Android/ Java to refer to a Java application built using the Android SDK and Eclipse 341 Download at Boykma.Com Licensed to Deborah Christiansen 342 CHAPTER 13 Hacking Android We demonstrate the steps of building an Android/ Linux application commencing... server Ultimately any application built for Android/ Linux needs to bring value to the user in some form In an effort to meet this objective, it is desirable that Android/ Java be able to interact in a meaningful manner with our Android/ Linux application To that end we will build a traditional Android application using Java in Eclipse to interact with the Android/ Linux server application Let’s get started... own Android application, and of course the full source code is provided for the Android and server applications discussed in this chapter Now that we have shown what can be accomplished when exercising a broad range of the Android SDK, the next chapter takes a decidedly different turn as we explore the underpinnings of Android a little deeper and look at building native C applications for the Android. .. behind us, let’s build the obligatory Hello Android application to run in the Linux foundation of the Android Emulator 13.1.2 Building an application The first thing we want to accomplish with our journey into Android/ Linux development is to print something to the screen of the emulator to demonstrate that we are running something on the platform outside the Android SDK and its Java application environment... big-picture architecture of the Android Emulator running essentially on top of Linux, as shown in figure 13.1 Figure 13.1 Android runs atop a Linux kernel Download at Boykma.Com Licensed to Deborah Christiansen The Android/ Linux:junction 345 As presented in the early chapters of this book, there is a Linux kernel running underneath the pretty, graphical face of Android There exist device... of the code: it also means that using Android resident code libraries is a bigger challenge Let’s dig deeper to understand why In order to do this, we have to look at the filesystem of Android/ Linux System libraries in Android/ Linux are stored in the directory /system/lib This directory contains important functionality, such as OpenGL, SQLite, C standard routines, Android runtime, UI routines, and much... the Android/ Linux server application Let’s get started with an examination of the requirements of building our first C application for Android 13.1 The Android/ Linux:junction Applications for Android/ Linux are markedly different from applications constructed with the Android SDK Applications built with Eclipse and the context-sensitive Java syntax tools make for a comfortable learning environment In... that enables us as much flexibility as possible to employ the Android system Download at Boykma.Com Licensed to Deborah Christiansen 350 CHAPTER 13 Hacking Android libraries To that end, listing 13.4 shows the build script for building a dynamic version of Hello Android Listing 13.4 Build script for dynamically linked Android application arm-none-linux-gnueabi-gcc -c hello.c -o hello.o... arm-none-linux-gnueabi-ld entry=_start dynamic-linker /system/bin/linker nostdlib -rpath /system/lib -rpath-link \android\ system\lib -L \android\ system\lib -l c -l android_ runtime -l sqlite -o executablefile csourcefile.o crt0.o At this point, we are comfortable that we can build applications for Android/ Linux, so it’s time to build something useful The next section walks through the construction of a daytime . our first C application for Android. 13.1 The Android/ Linux:junction Applications for Android/ Linux are markedly different from applications con- structed with the Android SDK. Applications. of the Android SDK, the next chapter takes a decidedly different turn as we explore the underpinnings of Android a little deeper and look at building native C applica- tions for the Android. foundation layer of Android. To accomplish this, we are going to use the C programming language. In this chapter we use the term Android/ Linux to refer to the Linux underpinnings of the Android platform.