Thursday, September 7, 2017

Advanced Android App Concepts

Advanced Android App Concepts

Following are some of the Android advanced concepts
Threading and Asynchronous Processing
                  Android SDK provides two easy ways to manage offload processing from the main UI thread: the AsyncTask class and the standard Java Thread class.

AsyncTask

The important callbacks are:
  • The onPreExecute() method runs on the UI thread before background processing begins.
  • The doInBackground() method runs in the background on a separate thread and is where all the real work is done.
  • The publishProgress() method, called from the doInBackground() method, periodically informs the UI thread about progress of the background process. This method sends information to the UI process. Use this opportunity to send updated information to a progress bar that the user can see.
  • The onProgressUpdate() method runs on the UI thread whenever the doInBackground() method calls publishProgress(). This method receives information from the background process. Use this opportunity to update a ProgressBarcontrol that the user can see or to update the UI in other ways.
  •  The onPostExecute() method runs on the UI thread once the background processing is completed.
Services

                 An Android Service might be used to perform functions in the background that do not require user input or to supply information to other applications.
  • First, a Service can mean a background process that performs some useful operation at regular intervals. 
  • Second, a Service can be an interface for a remote object, called from within an application.
To connect to a Service, interested applications use the Context.bindService() method to obtain a connection. If that Service is not running, the Service is created at that time. 

If a Service is started by the system with a call to the Context.startService() method, the onCreate() method is called just before the onStart() or onStartCommand() methods. 


Implementing a remote interface
    
  To define a remote interface, you must declare the interface in an AIDL file, implement the interface, and then return an instance of the interface when the onBind() method is called.

You can use only primitive types and objects that implement the Parcelable (android.os.Parcelable) protocol with remote Servicecalls. This is because these calls may cross process boundaries where memory can’t be shared. 

package com.advancedandroidbook.services;

interface IRemoteInterface {
    Location getLastLocation();
}
Implementaion
private final IRemoteInterface.Stub
    mRemoteInterfaceBinder = new IRemoteInterface.Stub() {
        public Location getLastLocation() {
            Log.v("interface", "getLastLocation() called");
            return lastLocation;
        }
    };      

The Service code has already stored the last location received as a member variable, so we can simply return that value. With the interface implemented, it needs to be returned from the onBind() method of the Service:

@Override
public IBinder onBind(Intent intent) {
    // we only have one, so no need to check the intent
    return mRemoteInterfaceBinder;
}

The Service can now be used through this interface. This is done by implementing a ServiceConnection object and calling the bindService() method. When finished, the unbindService() method must be called so the system knows that the application has finished using the Service. The connection remains even if the reference to the interface is gone.

public void onServiceConnected(ComponentName name,
    IBinder service) {

    mRemoteInterface = IRemoteInterface.Stub.asInterface(service);
    Log.v("ServiceControl", "Interface bound.");
}

public void onServiceDisconnected(ComponentName name) {
    mRemoteInterface = null;
    Log.v("ServiceControl", "Remote interface no longer bound");
}

Intentservice class
  • The IntentService class (android.app.IntentService) is a simple type of Service that can be used to handle such tasks asynchronously by way of Intent requests. 
  • Each Intent is added to the work queue associated with that IntentService and is handled sequentially. 
  • You can send data back to the application by simply broadcasting the result as an Intentobject and using a broadcast receiver to catch the result and use it within the application.
Loaders
      Loader class, which helps asynchronously load data for an Activity or Fragment from a data source such as a content provider or the network.
To use a Loader, take the following steps:
  1. Use your Activity or Fragment class’s LoaderManager to initialize a Loader.
  2. Provide an implementation of the LoaderManager.LoaderCallbacks.
  3. The onCreateLoader() method is used to return a new Loader instance, typically a CursorLoader that queries a content provider from which the Activity or Fragmentwants to display data.
  4. The onLoadFinished() method signals that all data has been loaded and is ready for use. Typically, your screen contains some sort of control, such as a ListView, that leverages the CursorAdapter associated with the CursorLoader, so you want to swap the old and new Cursor objects in the adapter at this time.
  5. The onLoaderReset() method is used to signify that the data is unavailable, and thus the Cursor used by the adapter is no longer valid. Typically, you need to swap out the Cursor again at this time. 
Strictmode
           StrictMode is a method developers can use to detect operations that should not be performed on the main thread. 
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().detectAll().penaltyDeath().build();
StrictMode.setThreadPolicy(policy);
SQLite Application Databases
Creating a SQLite Database Instance Using the Application Context
import android.database.sqlite.SQLiteDatabase;
...
SQLiteDatabase mDatabase;
mDatabase = openOrCreateDatabase("my_sqlite_database.db",
                SQLiteDatabase.CREATE_IF_NECESSARY,
                null);

CREATE TABLE tbl_authors (
id INTEGER PRIMARY KEY AUTOINCREMENT,
firstname TEXT,
lastname TEXT);
mDatabase.execSQL(CREATE_AUTHOR_TABLE);

Adding Data to DB
ContentValues values = new ContentValues();
values.put("firstname", "J.K.");
values.put("lastname", "Rowling");
long newAuthorID = mDatabase.insert("tbl_authors", null, values);

Update Data to DB
  ContentValues values = new ContentValues();
    values.put("title", newtitle);
    mDatabase.update("tbl_books",
        values, "id=?", new String[] { bookId.toString() });

Delete
mDatabase.delete("tbl_authors", null, null);

Working with Cursors


When results are returned from a SQL query, you often access them using a Cursor found in the android.database.Cursor class. Cursor objects are like file pointers; they allow random access to query results.

// SIMPLE QUERY: select * from tbl_books
Cursor c = mDatabase.query("tbl_books", null, null, null, null, null, null);
// Do something quick with the Cursor here...
c.close();

Building Android Content Providers
Applications can access data in other applications on the Android system through content provider interfaces, and they can expose internal application data to other applications by becoming content providers. Typically, a content provider is backed by a SQLite database where the underlying data is stored.

public static final Uri CONTENT_URI =
    Uri.parse("content://com.advancedandroidbook.ssp." +
                "SimpleFieldnotesContentProvider/fieldnotes_provider");

Broadcasting and Receiving Intents
The Android framework supports two kinds of broadcasts. 

  • Normal broadcasts are delivered to all receivers and completed asynchronously in an undefined order. 
  • Ordered broadcasts are delivered to each receiver in priority order; the receiver can pass the event on to the next appropriate receiver in the queue or abort the broadcast before all receivers get it.
  • Broadcasts can also be sticky. This means that the Intent associated with the broadcast stays around after the broadcast has been completed, so that the broadcast receivers can retrieve valid Intent data from the registerReceiver() method return value. Both normal and ordered broadcasts can be sticky.

Intent i = new Intent("ACTION_DO_A_LITTLE_DANCE");
sendBroadcast(i);

To send a normal sticky broadcast, simply use the sendStickyBroadcast() method of the Context class instead of the sendBroadcast() method.

To send an ordered broadcast, simply create the appropriate Intent object as normal and then dispatch it to the system using the sendOrderedBroadcast() method of the Context class.

class MyDancingBroadcastReceiver extends BroadcastReceiver
{
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "Get Down and Boogie!",
            Toast.LENGTH_LONG).show();
    }
}
Registering to Receive Broadcasts Dynamically

To register for specific broadcasts at runtime, use the registerReceiver() and unregisterReceiver() methods of the Context class.

public class SimpleBroadcastsActivity extends Activity {
    public static String ACTION_DANCE =
        "com.advancedandroidbook.simplebroadcasts.ACTION_DANCE";
 MyDancingBroadcastReceiver mReceiver;

@Override
    protected void onResume() {
        super.onResume();
        IntentFilter danceFilter =
            new IntentFilter(ACTION_DANCE);
        registerReceiver(mReceiver, danceFilter);
    }
}

Registering to Receive Broadcasts Statically
<receiver android:name=

        "com.advancedandroidbook.simplebroadcasts

        .SimpleBroadcastsActivity$MyDancingBroadcastReceiver" >
    <intent-filter>
        <action
            android:name=
                "com.advancedandroidbook.simplebroadcasts.ACTION_DANCE" />
    </intent-filter>
</receiver>

Notifications
NotificationManager notifier = (NotificationManager)
    getSystemService(Context.NOTIFICATION_SERVICE);
        NotificationCompat.Builder(getApplicationContext());
notifyBuilder.setTicker("Hello!");
notifyBuilder.setWhen(System.currentTimeMillis());
        "android.resource://com.advancedandroidbook.simplenotifications/"
        + R.raw.fallbackring),
        AudioManager.STREAM_NOTIFICATION);

NotificationCompat.Builder notifyBuilder = new
notifyBuilder.setSmallIcon(R.drawable.ic_launcher);

notifyBuilder.setVibrate(new long[] {0, 200, 200, 600, 600});
notifyBuilder.setLights(Color.GREEN, 1000, 1000);

Using Android Networking APIs

Using HttpURLConnection

HttpURLConnection retrieves some information about the resource referenced by the URL object, including HTTP status and header information.

import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

// ...

URL text = new URL("http://api.flickr.com/services/feeds/photos_public.gne" +
                   "?id=26648248@N04&lang=en-us&format=atom");

HttpURLConnection http = (HttpURLConnection) text.openConnection();
Log.i("Net", "length = " + http.getContentLength());
Log.i("Net", "respCode = " + http.getResponseCode());
Log.i("Net", "contentType = "+ http.getContentType());
Log.i("Net", "content = " + http.getContent());
Retrieving Android Network Status
import android.net.ConnectivityManager;
import android.net.NetworkInfo;

// ...

ConnectivityManager cm = (ConnectivityManager)
    getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo ni = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
boolean isWifiAvail = ni.isAvailable();
boolean isWifiConn = ni.isConnected();
ni = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
boolean isMobileAvail = ni.isAvailable();
boolean isMobileConn = ni.isConnected();

status.setText("WiFi\nAvail = "+ isWifiAvail +
    "\nConn = " + isWifiConn +
    "\nMobile\nAvail = "+ isMobileAvail +
    "\nConn = " + isMobileConn);
Android Web APIs
<WebView
    android:id="@+id/web_holder"
    android:layout_height="match_parent"
    android:layout_width="match_parent" />

final WebView wv = (WebView) findViewById(R.id.web_holder);
wv.loadUrl("http://www.android.com/");

Android Multimedia APIs

Capturing Still Images Using the Camera
The Camera object controls the camera on devices that have camera support enabled. The preview feature of the camera relies on the assignment of a SurfaceHolder of an appropriate type. This enables applications to control the placement and size of the preview area that the camera can use.
1.     Create a new class extending SurfaceView and implement SurfaceHolder.Callback.
2.     In the surfaceCreated() method, get an instance of the Camera object.
3.     In the surfaceChanged() method, configure and apply the Camera.Parameters;, then call the startPreview() method.
4.      Add a method in CameraSurfaceView for capturing images.
5.      Add the CameraSurfaceView to an appropriate layout.
6.      Implement a PictureCallback class to handle storing of the captured image.
7.      Release the Camera object in the surfaceDestroyed() method.

·       import android.hardware.Camera;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

private class CameraSurfaceView extends SurfaceView
    implements SurfaceHolder.Callback {

    private SurfaceHolder mHolder;
    private Camera camera = null;

    public CameraSurfaceView(Context context) {
        super(context);
        mHolder = getHolder();
        mHolder.addCallback(this);
    }

    public void surfaceChanged(SurfaceHolder holder,
        int format, int width, int height) {
·         List<Camera.Size> sizes = params.getSupportedPreviewSizes();

    Camera.Size pickedSize = getBestFit(sizes, width, height);
    if (pickedSize != null) {
        params.setPreviewSize(pickedSize.width, pickedSize.height);
        camera.setParameters(params);
    }
    camera.startPreview();
    }

    public void surfaceCreated(SurfaceHolder holder) {
·        camera = Camera.open();
    camera.setPreviewDisplay(mHolder);
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
·         camera.stopPreview();
    camera.release();
    camera = null;
    }

    public boolean capture(Camera.PictureCallback
        jpegHandler) {
·          if (camera != null) {
        camera.takePicture(null, null, jpegHandler);
        return true;
    } else {
        return false;
    }
    }
}

Video

public void onClick(View v) {
    if (videoRecorder == null) {
        videoRecorder = new MediaRecorder();
    }
    String pathForAppFiles =
        getFilesDir().getAbsolutePath();
    pathForAppFiles += RECORDED_FILE;

    videoRecorder.setVideoSource(
        MediaRecorder.VideoSource.CAMERA);

    videoRecorder.setOutputFormat(
        MediaRecorder.OutputFormat.MPEG4 );

    videoRecorder.setVideoSize(640, 480);
    videoRecorder.setVideoFrameRate(30);
    videoRecorder.setVideoEncoder(
        MediaRecorder.VideoEncoder.H264);

    videoRecorder.setOutputFile(pathForAppFiles);
    videoRecorder.setPreviewDisplay(surface);

    videoRecorder.prepare();
    videoRecorder.start();

    // button handling and other behavior here
}

Playing Video
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.moving);

    VideoView vv = (VideoView) findViewById(R.id.video);
    MediaController mc = new MediaController(this);
    Uri video = Uri.parse(MOVIE_URL);

    vv.setMediaController(mc);
    vv.setVideoURI(video);
}
Using Android Telephony APIs

Requesting Call State


TelephonyManager telManager = (TelephonyManager)
    getSystemService(Context.TELEPHONY_SERVICE);

String opName = telManager.getNetworkOperatorName();
Log.i("telephony", "operator name = " + opName);

String phoneNumber = telManager.getLine1Number();
Log.i("telephony", "phone number = " + phoneNumber);

String providerName = telManager.getSimOperatorName();
Log.i("telephony", "provider name = " + providerName);

SMS


final SmsManager sms = SmsManager.getDefault();
sms.sendTextMessage("9995551212", null, "Hello!", null, null);

Making Phone Calls


Button call = (Button) findViewById(R.id.call_button);
call.setOnClickListener(new View.OnClickListener() {
    public void onClick(View v) {
        Uri number = Uri.parse("tel:" + numberEntry.getText().toString());
        Intent dial = new Intent(Intent.ACTION_DIAL, number);
        startActivity(dial);
    }
});

Accessing Android’s Hardware Sensors

SensorManager sensors =
    (SensorManager) getSystemService(Context.SENSOR_SERVICE);

TYPE_ACCELEROMETER: Measures acceleration in three directions (values are in SI units (m/s2))
 TYPE_AMBIENT_TEMPERATURE: Measures temperature
 TYPE_GYROSCOPE: Measures angular orientation in three directions (values are angles in degrees)
 TYPE_LIGHT: Measures ambient light (values are in SI lux units)
 TYPE_MAGNETIC_FIELD: Measures magnetism in three directions; the compass (values are in micro-Teslas (μT))
 TYPE_PRESSURE: Measures barometric pressure
 TYPE_PROXIMITY: Measures the distance to an object (values are in centimeters, or near versus far)
 TYPE_RELATIVE_HUMIDITY: Measures the relative humidity
 TYPE_STEP_COUNTER: Measures the total number of steps recorded since Servicecreation (API Level 19)
 TYPE_STEP_DETECTOR: Measures each step by recording a timestamp and a value of 1.0 (API Level 19)

Reading Sensor Data


Sensor accelSensor = sensors.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
boolean isAvailable = sensors.registerListener(SensorsActivity.this,
    accelSensor, SensorManager.SENSOR_DELAY_NORMAL);

@Override
public void onSensorChanged(SensorEvent event) {
    StringBuilder sensorMessage =
        new StringBuilder(event.sensor.getName()).append(" new values: ");

    for (float value : event.values) {
        sensorMessage.append("[").append(value).append("]");
    }

    sensorMessage.append(" with accuracy ").append(event.accuracy);
    sensorMessage.append(" at timestamp ").append(event.timestamp);

    sensorMessage.append(".");

    Log.i(DEBUG_TAG, sensorMessage);
}

Monitoring the battery
<uses-permission
    android:name="android.permission.BATTERY_STATS" />
registerReceiver(batteryRcv,
    new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
batteryRcv = new BroadcastReceiver() {

    public void onReceive(Context context, Intent intent) {
        int level =
            intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
        int maxValue =
            intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
        int batteryStatus =
            intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
        int batteryHealth =
            intent.getIntExtra(BatteryManager.EXTRA_HEALTH, -1);
        int batteryPlugged =
            intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
        String batteryTech =
            intent.getStringExtra(BatteryManager.EXTRA_TECHNOLOGY);
        int batteryIcon =
            intent.getIntExtra(BatteryManager.EXTRA_ICON_SMALL, -1);
        float batteryVoltage =
            (float) intent.getIntExtra(BatteryManager.EXTRA_VOLTAGE,
                -1) / 1000;
        boolean battery =
            intent.getBooleanExtra(BatteryManager.EXTRA_PRESENT,
                false);
        float batteryTemp =
            (float) intent.getIntExtra(
                BatteryManager.EXTRA_TEMPERATURE, -1) / 10;
        int chargedPct = (level * 100)/maxValue ;

        String batteryInfo = "Battery Info:\nHealth=" +
            (String)healthValueMap.get(batteryHealth)+"\n" +
            "Status="+(String)statusValueMap.get(batteryStatus)+"\n" +
            "Charged % = "+chargedPct+"%\n"+
            "Plugged = " + pluggedValueMap.get(batteryPlugged) + "\n" +
            "Type = " + batteryTech + "\n"      +
            "Voltage = " + batteryVoltage + " volts\n" +
            "Temperature = " + batteryTemp + "°C\n"+
            "Battery present = " + battery + "\n";

        status.setText(batteryInfo);
        icon.setImageResource(batteryIcon);

        Toast.makeText(Battery.this, "Battery state changed",
            Toast.LENGTH_LONG).show();
    }

};

Bluetooth
For Classic Bluetooth, the APIs are divided into several useful classes, including the following:

  •  The BluetoothAdapter class represents the Bluetooth radio hardware on the local device.
  •  The BluetoothDevice class represents a remote Bluetooth device.
  •  The BluetoothServerSocket class is used to open a socket to listen for incoming connections and provides a BluetoothSocket object when a connection is made.
  •  The BluetoothSocket class is used by the client to establish a connection to a remote device. After the device is connected, the BluetoothSocket object is used by both sides to handle the connection and retrieve the input and output streams.
For Bluetooth LE, the APIs differ and are divided into their own classes:

  •  The BluetoothManager class is used to acquire the BluetoothAdapter using the getSystemService() method passing in the Context.BLUETOOTH_SERVICE value.
  •  The BluetoothAdapter class requires implementing the LeScanCallback interface to access the BluetoothDevice object using the onLeScan() method call.
  •  The BluetoothDevice class requires calling the connectGatt() method to connect to a peripheral Bluetooth LE device.
BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
if (btAdapter == null) {
    Log.d(DEBUG_TAG, "No bluetooth available.");
    // ...
} else {
    // bt available
}
ANDROID BEAM
Just get an instance of the NfcAdapter class and call the setNdefPushMessageCallback() method with a valid CreateNdefMessageCallback instance. For example:
mNfcAdapter.setNdefPushMessageCallback(new CreateNdefMessageCallback() {
    @Override
    public NdefMessage createNdefMessage(NfcEvent event) {
        Time time = new Time();
        time.setToNow();
        String message = messageToBeam.getText().toString();
        String text = (message + " \n[Sent @ "
                + time.format("%H:%M:%S") + "]");
        byte[] mime = MIMETYPE.getBytes(Charset.forName("US-ASCII"));
        NdefRecord mimeMessage = new NdefRecord(
            NdefRecord.TNF_MIME_MEDIA, mime,
            new byte[0], text.getBytes());
        NdefMessage msg = new NdefMessage(
            new NdefRecord[] { mimeMessage,NdefRecord.
            createApplicationRecord("com.advancedandroidbook.simplewireless") });
        return msg;
    }
}, this);

WI-FI

Using Wi-Fi Direct on Android is fairly straightforward; start by checking out the peer-to-peer Wi-Fi package android.net.wifi.p2p. Using the WifiP2pManager class (android.net.wifi.p2p.WifiP2pManager), you configure several callback classes that are used to asynchronously get the status of requests you make.

WifiManager wifi =
    (WifiManager) getSystemService(Context.WIFI_SERVICE);

wifi.startScan();
registerReceiver(rcvWifiScan,
    new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));

rcvWifiScan = new BroadcastReceiver() {
    public void onReceive(Context context, Intent intent) {
        List<ScanResult> resultList = wifi.getScanResults();
        int foundCount = resultList.size();

        Toast.makeText(WiFi.this,
            "Scan done, " + foundCount + " found",
            Toast.LENGTH_SHORT).show();
        ListIterator<ScanResult> results = resultList.listIterator();
        String fullInfo = "Scan Results : \n";
        while (results.hasNext()) {
            ScanResult info = results.next();
            String wifiInfo = "Name: " + info.SSID +
                "; capabilities = " + info.capabilities +
                "; sig str = " + info.level + "dBm";

            Log.v("WiFi", wifiInfo);

            fullInfo += wifiInfo + "\n";
        }
        status.setText(fullInfo);
    }
};

Using Location and Map APIs

import android.location.*;
...
LocationManager locationManager =
    (LocationManager)getSystemService(Context.LOCATION_SERVICE);

public void onLocationChanged(Location location) {
    String locInfo = String.
        format("Current loc = (%f, %f) @ (%f meters up)",
        location.getLatitude(), location.getLongitude(),
        location.getAltitude() );
    if (lastLocation != null) {
        float distance = location.distanceTo(lastLocation);
        locInfo += String.
            format("\n Distance from last = %f meters", distance);
    }
    lastLocation = location;
    status.setText(locInfo);
}
Share:

Popular Posts

Contact Form

Name

Email *

Message *