Developing Android Apps Barry Burd Drew University Barry@Burd.org Slides available at http://allmycode.com/tcf This presentation © 2014 Barry Burd 1 Agenda • About Android • Demo: Creating a "Hello, Android" App • Making calls, sending text messages, visiting Web sites, etc. • Publishing an Android App • ?&! Today at TCF 10:15-11:10 Android 1 11:20-12:15 Android 2 12:25-1:30 Google Glass Time? Book signing 2 ABOUT ANDROID 3 Android • • • • Purchased in mid-2000s by Google "Open" Based on Linux Versions: Astro Boy, Bender, R2-D2, Cupcake (1.5), Donut (1.6), Éclair (2.0-2.1), Froyo (2.2), Gingerbread (2.3), Honeycomb (3.x), Ice Cream Sandwich (4.0), Kit-Kat (4.1-4.3) • Also Cyanogenmod 4 U.S. Market Share - 2013 Source: http://thedroidguy.com/2013/07/androidmarket-share-vs-apple-blackberrywindows-symbian/ 5 World Market Share - 2013 Source: http://www.dazeinfo.com/2013/08/23/sam sung-android-app-usage-advertisersreport/ 6 CREATING AN ANDROID APP 7 What you need • Java (java.com) • Eclipse adt bundle (developer.android.com/sdk) – includes Eclipse with ADT – includes the Android SDK • Need to create at least one Android Virtual Device (AVD) 8 Create an Android project 9 Project name, package name, SDKs used 10 About the app's Activity... 11 The newly created app's folders 12 The newly created activity 13 The main activity's layout 14 Drag and drop 15 Name the button's event handler 16 Create event handler code 17 Run As... Android Application 18 Running the app The emulator takes a long time to start. Make changes to your code and rerun without stopping and restarting the emulator! 19 Testing on a real phone • <application android:debuggable="true"... • On the phone: Settings → Applications → Development → USB debugging (on) • Setup USB on the computer: – Windows: Install an Android USB driver for the phone – Mac: /* Do nothing */ – Linux: Add a rules file containing USB configuration info • Plug the phone into the computer • Run the project (To run on an emulator with the phone plugged in, select Run As... Run Configurations) 20 MAKING CALLS, SENDING TEXT MESSAGES, VISITING WEB SITES, ETC. 21 Dialing <activity android:name=”.DialerActivity”> <intent-filter> <action android:name=”android.intent.action.DIAL” /> <category android:name=”android.intent.category.DEFAULT” /> <data android:scheme=”tel” /> </intent-filter> </activity> 22 Dialing boolean deviceIsAPhone() { TelephonyManager manager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); return manager.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE; } intent.setData(Uri.parse("tel:234-555-6789")); or intent.setData(Uri.parse("tel:")); startActivity(intent); 23 Sending a text message SmsManager smsMgm = SmsManager.getDefault(); smsMgm.sendTextMessage("2345556789", null, "Hello world", null, null); The second parameter, a Java string, is a service center address . null means "I don't care which service center." The fourth parameter’s broadcast notifies the system when the message is sent. The fifth parameter’s broadcast notifies the system when the message is received. 24 Using device sensors sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); magFieldSensor = sensorManager.getDefaultSensor(TYPE_MAGNETIC_FIELD); accelerometer = sensorManager.getDefaultSensor(TYPE_ACCELEROMETER); sensorManager.registerListener (sensorListener, magFieldSensor, SensorManager.SENSOR_DELAY_UI); sensorManager.registerListener (sensorListener, accelerometer, SensorManager.SENSOR_DELAY_UI); locationManager.requestLocationUpdates (LocationManager.GPS_PROVIDER, 0, 0, locationListener); 25 Using device sensors public void onSensorChanged(SensorEvent event) { if (sensorEventType == Sensor.TYPE_ACCELEROMETER) { System.arraycopy(event.values, 0, gravityValues, 0, 3); } else if (sensorEventType == Sensor.TYPE_MAGNETIC_FIELD) { System.arraycopy(event.values, 0, geoMagnetValues, 0, 3); ... if (SensorManager.getRotationMatrix (rotationMatrix, null, gravityValues, geoMagnetValues)) { SensorManager.getOrientation(rotationMatrix, orientation); orientationView.setText ("Yaw: " + orientation[0] + "\n" + "Pitch: " + orientation[1] + "\n" + "Roll: " + orientation[2]); } 26 Using device sensors public void onLocationChanged(Location location) { locationView.setText ("Latitude: " + location.getLatitude() + "\n" + "Longitude: " + location.getLongitude()); } 27 public boolean onTouch(View view, MotionEvent event) { switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: numberOfFingers = 1; oldX[0] = event.getX(0); oldY[0] = event.getY(0); break; case MotionEvent.ACTION_POINTER_DOWN: numberOfFingers = 2; oldX[1] = event.getX(1); oldY[1] = event.getY(1); break; case MotionEvent.ACTION_MOVE: handleMove(event); break; case MotionEvent.ACTION_POINTER_UP: case MotionEvent.ACTION_UP: numberOfFingers--; break; } view.invalidate(); return true; } Handling Touch Events 28 Getting an image from the Web class MyAsyncTask extends AsyncTask<String, Integer, Bitmap> { . . . @Override protected Bitmap doInBackground(String... urlArray) { try { URL url = new URL(urlArray[0]); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setDoInput(true); connection.connect(); progress += 50; publishProgress(progress); InputStream input = connection.getInputStream(); Bitmap bitmap = BitmapFactory.decodeStream(input); progress += 50; publishProgress(progress); return bitmap; } catch (IOException e) { e.printStackTrace(); return null; } } 29 Getting real weather data (from World Weather Online) public Weather getWeather(String location) { URL url; Weather weather = null; try { url=new URL(WORLD_WEATHER_ONLINE_URL+location.replace(" ", "%20")); SAXParser parser = SAXParserFactory.newInstance().newSAXParser(); XMLReader reader = parser.getXMLReader(); MySaxHandler saxHandler = new MySaxHandler(); reader.setContentHandler(saxHandler); reader.parse(new InputSource(url.openStream())); weather = saxHandler.getWeather(); } catch (Exception e) { e.printStackTrace(); } return weather; } 30 Getting real weather data (from World Weather Online) public class MySaxHandler extends DefaultHandler { @Override public void startElement(String namespaceURI, String localName, String qName, Attributes attributes) throws SAXException { if (localName.equals(CURRENT_CONDITIONS)) { current_conditions = true; } else { if (current_conditions) { if (localName.equals(TEMP_F)) { temp_f = true; } else if (localName.equals(CONDITION)) { weatherDescription = true; } } } } 31 Getting real weather data (from World Weather Online) @Override public void characters(char chars[], int start, int length) throws SAXException { if (temp_f) { weather.setTemperature(Integer.parseInt(String.valueOf(chars, start, length))); } else if (weatherDescription) { weather.setCondition(String.valueOf(chars, start, length)); } } @Override public void endElement(String namespaceURI, String localName, String qName) throws SAXException { if (localName.equals(CURRENT_CONDITIONS)) { current_conditions = false; } else { if (localName.equals(TEMP_F)) { temp_f = false; } else if (localName.equals(CONDITION)) { weatherDescription = false; } } } 32 Using sqlite (for an app's own data) helper = new DBHelper(this, "simple_db", null, 1); db = helper.getWritableDatabase(); values = new ContentValues(); values.put("name", "Barry"); values.put("amount", "100"); db.insert("simpletable", "", values); values.clear(); values.put("name", "Harriet"); values.put("amount", "300"); db.insert("simpletable", "", values); addToTextView(); values.clear(); values.put("amount", "500"); db.update("simpletable", values, "name='Barry'", null); addToTextView(); db.delete("simpletable", "1", null); addToTextView(); 33 Doing a query void addToTextView() { cursor = db.rawQuery("SELECT * FROM simpletable;", null); if (cursor != null && cursor.moveToFirst()) { String name; do { String _id = cursor.getString(0); name = cursor.getString(1); int amount = cursor.getInt(2); textView.append(_id + " " + name + " " + amount + "\n"); } while (cursor.moveToNext()); } textView.append("-----------\n"); } 34 My DBHelper Class public class DBHelper extends SQLiteOpenHelper { public DBHelper(Context context, String dbName, SQLiteDatabase.CursorFactory factory, int version) { super(context, dbName, factory, version); } @Override public void onCreate(SQLiteDatabase db) { String createString = "CREATE TABLE IF NOT EXISTS simpletable " + "( _id INTEGER PRIMARY KEY AUTOINCREMENT, " + "name TEXT NOT NULL, " + "amount INTEGER NOT NULL);"; db.execSQL(createString); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { String dropString = "DROP TABLE IF EXISTS simpletable;"; db.execSQL(dropString); onCreate(db); } } 35 DEPLOYING AN ANDROID APP 36 Where to publish an Android app from http://www.coderanch.com/how-to/java/AndroidFaq • • • • • • • • • • • • Android Market: http://www.android.com/market/ Voeveo: http://www.voeveo.com/ SlideME: http://www.slideme.org/ AndAppStore: http://andappstore.com/ AndroLib: http://www.androlib.com/ Cyrket: http://www.cyrket.com/ AndroidPIT (in German): http://www.androidpit.de/ AndSpot: http://andspot.com/ AppBrain: http://www.appbrain.com/ Aproov: http://www.aproov.com/ Amazon appstore (in beta): https://developer.amazon.com/ as an .apk file on your own website 37 Preparing for Google's Android Market • Test, test, test – Wide variety of devices, conditions, resolutions, network delays, etc. • [optional] Add End User License Agreement and support for Android Market Licensing (network-based license check at startup time) • Specify icon and label <application android:icon="drawable resource" android:label="string resource" ... > ... </application> 38 Preparing for Google's Android Market • Remove development-only stuff – <application android:debuggable="true" ... – Remove logging code, test data, etc. • Create a version <manifest android:versionCode="integer" android:versionName="string" ... – The only versionCode rule is that later versions must have higher version numbers – Users typically don't see the versionCode – Users typically see versionName – A versionName example: 1.1.2 (major.minor.point) 39 Compile the app in release mode and sign the app 40 Compile the app in release mode and sign the app 41 Compile the app in release mode and sign the app Must be valid at least until Oct. 22, 2033 42 Compile the app in release mode and sign the app Note: Eclipse automatically performs zipalign on your app. When you do it manually, the zipalign step must come after signing. Note: To use Google's Maps API you must also have a Maps API key. 43 To start publishing, visit http://market.android.com/publish/Home 44 45 After clicking Upload buttons 46 Icons • UI Guidelines developer.android.com/guide/practices/ui_guidelines/icon_design.html Includes guidelines for icon naming; icon types (launcher, menu, tab, etc.), icon densities (hdpi, mdpi, ldpi), etc. • Android Icon Templates Pack developer.android.com/shareables/icon_templates-v2.3.zip • 512x512 pixels, 32-bit PNG (or JPEG?) with alpha, max size of 1024KB 47 48 49 50 For testing, click Save instead of Publish. 51 Your app is saved 52 ?&! Barry Burd Drew University Barry@Burd.org Slides available at http://allmycode.com/tcf Today at TCF 10:15-11:10 Android 1 11:20-12:15 Android 2 12:25-1:30 Google Glass Time? Book signing 53