PROGRAMMING IN ANDROID IOANNIS (JOHN) PAPAVASILEIOU MARCH 31 2014 1 papabasile@engr.uconn.edu WHAT IS IT • Software platform • Operating system • Key apps • Developers: • Google • Open Handset Alliance 2 • Open Source (http://source.an droid.com/) SOFTWARE STACK DESIGN • Android ≠ Linux!!! • The kernel is based on Linux kernel version 3.x (source Wikipedia) • Written in C/C++ • Apps written in Java 3 • Use of Dalvik VM • Runs .dex files (dalvic executables) API CAPABILITIES • Rich UI components • Threads and Background processing • Full network stack (Http, JSON) • Database and file system access • Access to hardware (Camera, Phone, GPS, sensors..) 4 • Plenty of libraries and services Many of the APIs provided map 1:1 to the underlying HAL crossing of process boundaries & call of system services code Includes window and notification managers Media services for playing and Interface that allows recording media the Android system Kernel, includes to call device drivers device drivers and memory management Source: http://source.android.com/devices/im ages/system-architecture.png 5 LOW LEVEL ARCHITECTURE APPLICATION COMPONENTS Every app may have: • Activities • • A single screen with a UI. E.g. compose, read, list e-mails Services • • Run in the background to perform long-running operations. E.g. play music, fetch data over the network Content Providers • • Manage a shared set of app data. Other apps can query or modify the data. E.g. contacts Broadcast Receivers • Respond to a system-wide broadcast announcement. E.g. screen turned off, low-battery. May initiate services or notifications Intents • Processes and threads 6 • APP FUNDAMENTALS Every app lives in its own security sandbox: • Every app runs its own Linux process • Each process has it’s own Java VM • UI thread (managed by the system) • Worker threads Violates the rule: “do not access the Android UI toolkit from outside the UI” Will run on the UI thread public public void void onClick(View onClick(View v) v) {{ new Thread(new Runnable() new Thread(new Runnable() {{ public public void void run() run() {{ final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png"); Bitmap b = loadImageFromNetwork("http://example.com/image.png"); mImageView.post(new Runnable() { mImageView.setImageBitmap(b); public void run() { } mImageView.setImageBitmap(bitmap); }).start(); } } }); } }).start(); } http://developer.android.com/guide/components/fundame ntals.html 7 Every process can have multiple threads: ACTIVITIES • The basis of android applications • A viewable screen • the actions, not the layout • Can have multiple per application • Each is a separate entity • They have a structured life cycle 8 • Different events in their life happen either via the user touching buttons or programmatically ACTIVITY LIFECYCLE The system can drop the activity from memory by just killing the process 9 It might not reach this point! ACTIVITY RESTART • When there is a configuration change the app will be restarted by the system • Screen orientation • keyboard availability • Language • Etc. • To save the state of the Activity the developer has to use the onSaveInstanceState() method. • onPause() is guaranteed to be called in any case 10 • onCreate() or onRestoreInstancestate() will give you access to the restored state • We can prevent the restart of the activity by handling the configuration change manually SERVICES • Started • Can continue even if Activity that started it dies • Something needs to be done while the user is not interacting with application • Otherwise, a thread is probably more applicable • Bound • Multiple activities can bind to it • Allows multiple applications to communicate with it via a common interface • Client-server interface • Needs to be declared in manifest file 11 • Like Activities, has a structured life cycle SERVICES LIFECYCLE One component calls stopService() or service calls To stop it calls stopSelf() unbindService(), Multiple clients can bind to the same service 12 One component calls startService() One component calls bindService(), It can communicate with the Ibinder interface CONTENT PROVIDERS • Encaptulate data provide data security • Share data between processes • Android has providers for Audio Video Images Contact information 13 • • • • PROJECT COMPONENTS • src – your source code • gen – auto-generated code (usually just R.java) • Included libraries • Resources • Drawables (like .png images) • Layouts (define the UI) • Values (like strings) 14 • Manifest file AndroidManifest.Xml Configuration file for your application • Permissions required from the app: • E.g. access contacts, internet, vibration, camera, phone history, DB access Contains characteristics about your application • • Activities • Services • Content providers • Broadcast receivers Defines external libraries, like Google Maps API 15 • <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android=http://schemas.android.com/apk/res/android package="com.abimed.ecg“ android:versionCode="5“ android:versionName="1.2.2"> <uses-sdk android:minSdkVersion="8"/> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.READ_CONTACTS"/> <uses-permission android:name="android.permission.WRITE_CONTACTS"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> <application android:label="@string/app_name“ android:icon="@drawable/logo“ android:debuggable="false"> <uses-library android:name="com.google.android.maps"/> <activity android:name="com.abimed.ecg.EcgActivity“ android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <activity android:name="com.abimed.ecg.Preferences" android:label="@string/set_preferences"/> <activity android:name="com.abimed.ecg.About" android:label="@string/aboutActivity"/> <activity android:name="com.abimed.ecg.Report" android:label="@string/reportActivity"/> <activity android:name="com.abimed.ecg.GraphInfo" android:label="@string/graphInfoActivity"/> <activity android:name=".patients.Patients" android:label="@string/patients"/> <activity android:name=".patients.ViewPatient" android:label="@string/patient"/> <activity android:name=".patients.PatientInfo" android:theme="@android:style/Theme.NoTitleBar"/> <activity android:name=".patients.PatientEdit" android:theme="@android:style/Theme.NoTitleBar"/> </application> </manifest> 16 MANIFEST FILE EXAMPLE STRINGS.XML Nice way to localize apps: • res/values/strings.xml • res/values-en/strings.xml • res/values-fr/strings.xml • res/values-ja/strings.xml Application wide available strings Promotes good software engineering UI components made in the UI editor should have text defined in strings.xml 17 Strings are just one kind of ‘Value’ there are many others TOOLS TO DEVELOP • Phone or a Virtual Device • Android Studio (http://developer.android.com/sdk/installing/studio.html) Provides: 18 • Development environment (based on IntelliJ IDEA) • SDK • Virtual devices http://developer.android.com/training/basics/firstapp/buildi ng-ui.html 19 HELLO WORLD LAYOUTS • Declare UI elements in XML • Separates presentation from actual code • Visualize easy to debug • Almost Every Development Environment has nice Drag n Drop editor • Instantiate layout elements at runtime (through Java code) • Fast and easy when the layout is simple: Has a few View elements Has only a surface vew like GLSurfaceView 20 • • R CLASS • Auto-generated: you shouldn’t edit it • Pairs Layout files with Activities • Use findViewById and Resources object to get access to the resources 21 • Ex. Button buttonInfo = (Button)findViewById(R.id.buttonInfo); buttonInfo.setOnClickListener(buttonInfoClickListener); • Ex. helloStr = getResources().getString(R.string.hello)); R CLASS R.java Layout.xml 22 Activity.java HELLO WORLD LAYOUT 23 <!-- mainLayout.xml - -> <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal"> Only one root <EditText android:id="@+id/edit_message" android:layout_weight="1" element android:layout_width="0dp" android:layout_height="wrap_content" Additional elements android:hint=“Enter a message" /> <Button as children android:layout_width="wrap_content" android:layout_height="wrap_content" android:text=“Send “ android:onClick="sendMessage" /> </LinearLayout> MAIN ACTIVITY public class MainActivity extends Activity { public final static String EXTRA_MESSAGE = "com.example.myfirstapp.MESSAGE"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.mainLayout); // Make sure we're running on Honeycomb or higher to use ActionBar APIs if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { // Show the Up button in the action bar. getActionBar().setDisplayHomeAsUpEnabled(true); } } /** Called when the user clicks the Send button */ public void sendMessage(View view) { Intent intent = new Intent(this, DisplayMessageActivity.class); EditText editText = (EditText) findViewById(R.id.edit_message); String message = editText.getText().toString(); intent.putExtra(EXTRA_MESSAGE, message); startActivity(intent); } 24 } DISPLAY MESSAGE ACTIVITY public class DisplayMessageActivity extends Activity { @SuppressLint("NewApi") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Get the message from the intent Intent intent = getIntent(); String message = intent.getStringExtra(MainActivity.EXTRA_MESSAGE); // Create the text view TextView textView = new TextView(this); textView.setTextSize(40); textView.setText(message); // Set the text view as the activity layout setContentView(textView); } 25 } INTENTS • Start new activity • Send user to another App: Implicit intent: • • Uri number = Uri.parse("tel:5551234"); Intent intent = new Intent(Intent.ACTION_DIAL, number); Uri location = Uri.parse("geo:0,0?q=1600+Amphitheatre+Parkway,+Mountai n+View,+California"); intent = new Intent(Intent.ACTION_VIEW, location); • • startActivity(intent); Start a service • • Perform an operation in the background Deliver a broadcast • Something happened that other apps need to know http://developer.android.com/training/basics/intents/sendi ng.html 26 • When multiple apps can handle the intent APP CHOOSER Intent intent = new Intent(Intent.ACTION_SEND); ... 27 // Always use string resources for UI text. // This says something like "Share this photo with" String title = getResources().getString(R.string.chooser_title); // Create and start the chooser Intent chooser = Intent.createChooser(intent, title); startActivity(chooser); 28 STORAGE OPTIONS.. SHARED PREFERENCES • • Store key-value sets Good for small sets public class Calc extends Activity { public static final String PREFS_NAME = "MyPrefsFile"; @Override protected void onCreate(Bundle state){ super.onCreate(state); ... // Restore preferences SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0); boolean silent = settings.getBoolean("silentMode", false); setSilent(silent); } @Override protected void onStop(){ super.onStop(); // Commit the edits! editor.commit(); } }http://developer.android.com/guide/topics/data/datastorage.html 29 // We need an Editor object to make preference changes. // All objects are from android.context.Context SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0); SharedPreferences.Editor editor = settings.edit(); editor.putBoolean("silentMode", mSilentMode); FILE STORAGE Internal External • Always available • • Files accesible only • by the app • When the app is uninstalled, files are deleted http://developer.android.com/guide/topics/data/datastorage.html World-readable When app is uninstalled, files will be deleted if the directory is gotten from getExternalFilesDir() 30 • Not always available INTERNAL STORAGE String FILENAME = “myfile.txt"; String string = "hello world!"; FileOutputStream fos = openFileOutput(FILENAME, Context.MODE_PRIVATE); fos.write(string.getBytes()); fos.close(); http://developer.android.com/guide/topics/data/datastorage.html 31 • When there is a need to cache some files use method: File createTempFile (String prefix, String suffix) EXTERNAL STORAGE <manifest ... > <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> ... </manifest> /* Checks if external storage is available to at least read */ public boolean isExternalStorageReadable() { String state = Environment.getExternalStorageState(); if (Environment.MEDIA_MOUNTED.equals(state) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state) ){ return true; } return false; } http://developer.android.com/guide/topics/data/datastorage.html public File getAlbumStorageDir(Context context, String albumName) { // Get the directory for the app's private pictures directory. File file = new File(context.getExternalFilesDir( Environment.DIRECTORY_PICTURES), albumName); if (!file.mkdirs()) { Log.e(LOG_TAG, "Directory not created"); } return file; } 32 /* Checks if external storage is available for read and write */ public boolean isExternalStorageWritable() { String state = Environment.getExternalStorageState(); if (Environment.MEDIA_MOUNTED.equals(state)) { return true; } return false; } CREATE DATABASE public class DictionaryOpenHelper extends SQLiteOpenHelper { private static final int DATABASE_VERSION = 2; private static final String DICTIONARY_TABLE_NAME = "dictionary"; private static final String DICTIONARY_TABLE_CREATE = "CREATE TABLE " + DICTIONARY_TABLE_NAME + " (" + KEY_WORD + " TEXT, " + KEY_DEFINITION + " TEXT);"; @Override public void onCreate(SQLiteDatabase db) { db.execSQL(DICTIONARY_TABLE_CREATE); } } 33 DictionaryOpenHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } PUT INFO INTO THE DATABASE / Gets the data repository in write mode SQLiteDatabase db = mDbHelper.getWritableDatabase(); // Create a new map of values, where column names are the keys ContentValues values = new ContentValues(); values.put(FeedEntry.COLUMN_NAME_ENTRY_ID, id); values.put(FeedEntry.COLUMN_NAME_TITLE, title); values.put(FeedEntry.COLUMN_NAME_CONTENT, content); 34 // Insert the new row, returning the primary key value of the new row long newRowId; newRowId = db.insert( FeedEntry.TABLE_NAME, FeedEntry.COLUMN_NAME_NULLABLE, values); SENSORS • Types: • Motion sensors • Environmental sensors • Position sensors • Sensor Framework: Get available sensors Get sensor capabilities Acquire raw data for a given minimum rate Register for sensor event listeners http://developer.android.com/guide/topics/sensors/sensor s_overview.html 35 • • • • MAGNETOMETER AVAILABILITY CHECK 36 private SensorManager mSensorManager; ... mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); if (mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD) != null){ // Success! There's a magnetometer. } else { // Failure! No magnetometer. } GET DATA EVENTS FROM THE LIGHT SENSOR public class SensorActivity extends Activity implements SensorEventListener { private SensorManager mSensorManager; private Sensor mLight; @Override public final void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); mLight = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT); } @Override public final void onAccuracyChanged(Sensor sensor, int accuracy) { // Do something here if sensor accuracy changes. } @Override public final void onSensorChanged(SensorEvent event) { // The light sensor returns a single value. // Many sensors return 3 values, one for each axis. float lux = event.values[0]; // Do something with this sensor value. } @Override protected void onPause() { super.onPause(); mSensorManager.unregisterListener(this); } } 37 @Override protected void onResume() { super.onResume(); mSensorManager.registerListener(this, mLight, SensorManager.SENSOR_DELAY_NORMAL); } CONNECTIVITY Several APIs to let the app connect and interact with other devices: • Bluetooth • NFC • Wi-Fi P2P • USB http://developer.android.com/guide/topics/connectivity/ind ex.html 38 • SIP CHECK NETWORK CONNECTIVITY private void checkNetworkConnection() { ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo activeInfo = connMgr.getActiveNetworkInfo(); if (activeInfo != null && activeInfo.isConnected()) { wifiConnected = activeInfo.getType() == ConnectivityManager.TYPE_WIFI; mobileConnected=activeInfo.getType()==ConnectivityManager.TYPE_MOBILE; if(wifiConnected) { Log.i(TAG, "Wifi connection"); } else if (mobileConnected){ Log.i(TAG, "Mobile connection"); } } else { Log.i(TAG, "Not connected"); } 39 } USER LOCATION <manifest ... > <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> ... </manifest> // Acquire a reference to the system Location Manager LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE); // Define a listener that responds to location updates LocationListener locationListener = new LocationListener() { public void onLocationChanged(Location location) { // Called when a new location is found by the network location provider. makeUseOfNewLocation(location); } public void onStatusChanged(String provider, int status, Bundle extras) {} public void onProviderEnabled(String provider) {} // Register the listener with the Location Manager to receive location updates locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener); http://developer.android.com/guide/topics/location/strategi es.html 40 public void onProviderDisabled(String provider) {} }; TOAST Context context = getApplicationContext(); CharSequence text = "Hello toast!"; int duration = Toast.LENGTH_SHORT; http://developer.android.com/guide/topics/ui/notifiers/toast s.html 41 Toast toast = Toast.makeText(context, text, duration); toast.show(); SEND SMS /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.main); btnSendSMS = (Button) findViewById(R.id.btnSendSMS); txtPhoneNo = (EditText) findViewById(R.id.txtPhoneNo); txtMessage = (EditText) findViewById(R.id.txtMessage); btnSendSMS.setOnClickListener(new View.OnClickListener() { public void Onclick(View v) { String phoneNo = txtPhoneNo.getText().toString(); String message = txtMessage.getText().toString(); if (phoneNo.length()>0 && message.length()>0) sendSMS(phoneNo, message); else Toast.makeText(getBaseContext(), "Please enter both phone number and message.",Toast.LENGTH_SHORT).show(); }}); } http://developer.android.com/reference/android/telephony/ SmsManager.html 42 <manifest ... > <uses-permission android:name="android.permission.SEND_SMS"/> ... </manifest> SEND SMS private void sendSMS(String phoneNumber, String message) { SmsManager sms = SmsManager.getDefault(); sms.sendTextMessage(phoneNumber, null, message, null, null); 43 } GOOGLE LOCATION SERVICES • Fused location provider: • • Gives the bests location according to the needs • “high accuracy”, “low power”, etc. Geofencing APIs: • • • • • • • In a vehicle On a bicycle On foot Still Tilting Unknown http://developer.android.com/reference/android/telephony/ SmsManager.html 44 • App will receive notifications when the user enters specific geographic boundaries Activity recognition: 45 MY APP 46 THANKS!