Friday, July 31, 2015

iOS Settings Bundle - Registering default values

By default, iOS Settings bundle does not register the default values from the preferences. In order to make sure the default values are present, application needs to use the registerDefaults method. This can be either done one by one key and value like below or using the bundle.plist reading and getting the key and registering it. 

// Set the application defaults
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDictionary *appDefaults = [NSDictionary dictionaryWithObject:@"YES"
                                                        forKey:@"enableRotation"];
[defaults registerDefaults:appDefaults];

[defaults synchronize];

references:
http://useyourloaf.com/blog/2010/05/18/adding-a-settings-bundle-to-an-iphone-app.html

Geo fencing Practicals

Below given snippet. API client connection need to be done before starting the geo fencing request. 

public int onStartCommand(Intent intent, int flags, int startId)
            {
                
                if(mGeofenceList == null)
                {
                    mGeofenceList = new ArrayList();
                }
                mGeofenceList.add(new Geofence.Builder()
                                  // Set the request ID of the geofence. This is a string to identify this
                                  // geofence.
                                  .setRequestId("HOME_GF")
                                  .setCircularRegion(12.841022,
                                                     77.6480388,
                                                     100
                                                     )
                                  .setExpirationDuration(600000 * 6)
                                  .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER |
                                                      Geofence.GEOFENCE_TRANSITION_EXIT)
                                  .build());
                
                buildGoogleApiClient();
                mGoogleApiClient.connect();
                return START_REDELIVER_INTENT;

            }

private PendingIntent getGeofencePendingIntent() {
    // Reuse the PendingIntent if we already have it.
    if (mGeofencePendingIntent != null) {
        return mGeofencePendingIntent;
    }
    Intent intent = new Intent(this, GeoFenceTransitionsIntentService.class);
    // We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when
    // calling addGeofences() and removeGeofences().
    return PendingIntent.getService(this, 0, intent, PendingIntent.
                                    FLAG_UPDATE_CURRENT);
}

/**
 * Runs when a GoogleApiClient object successfully connects.
 */
@Override
public void onConnected(Bundle connectionHint) {
    Log.i(LOG_TAG, "Connected to GoogleApiClient");
    Toast.makeText(AppUtils.getAppContext(), "Connected to GoogleApiClient", Toast.LENGTH_SHORT).show();
    LocationServices.GeofencingApi.addGeofences(
                                                mGoogleApiClient,
                                                getGeofencingRequest(),
                                                getGeofencePendingIntent()
                                                ).setResultCallback(this);
}

@Override
public void onConnectionFailed(ConnectionResult result) {
    // Refer to the javadoc for ConnectionResult to see what error codes might be returned in
    // onConnectionFailed.
    Log.i(LOG_TAG, "Connection failed: ConnectionResult.getErrorCode() = " + result.getErrorCode());
    String info = "Connection failed: ConnectionResult.getErrorCode() = " + result.getErrorCode();
    Toast.makeText(AppUtils.getAppContext(), info , Toast.LENGTH_SHORT).show();
}

@Override
public void onConnectionSuspended(int cause) {
    // The connection to Google Play services was lost for some reason.
    Log.i(LOG_TAG, "Connection suspended");
    Toast.makeText(AppUtils.getAppContext(), "Connection suspended" , Toast.LENGTH_SHORT).show();
    
    // onConnected() will be called again automatically when the service reconnects
}
References:



Android Sending SMS in background.

Sending an SMS in background is quite simple. below is the code that will let one do that

public void sendSMS(String message,String destination)
{
    SmsManager smsManager = SmsManager.getDefault();
    if( smsManager != null )
    {
        PendingIntent sentPI = PendingIntent.getBroadcast(AppUtils.getAppContext(), 0,
                                                          new Intent(AppUtils.getAppContext(), SMSSentStatusReceiver.class), 0);
        PendingIntent deliveredPI = PendingIntent.getBroadcast(AppUtils.getAppContext(), 0,
                                                               new Intent(AppUtils.getAppContext(), SMSDeliveryStatusReceiver.class), 0);
        smsManager.sendTextMessage(destination,null,message,sentPI,deliveredPI);
    }
}

Note that there are two receivers one is SMSSentStatusReceiver and SMSDeliveryStatusReceiver which will receive the callbacks to update the status of sent and delivery respectively.

References:

iOS View Controller Containment

A best example of Container view controllers are Tab View Controllers. In essence, this is about containing other controllers inside one controller. Before iOS 5.0 view controller containers were existing only in the framework classes. 

When designing a container, we are establishing a parent child relation ship between container and its child controllers. A container can add the content views of other view controllers in its own view hierarchy Whenever child view is displayed in containers view, the Controller also establishes a connection to the child view controller and ensure that all appropriate view controller events are sent to the child. 

Below is the code to add a child view controller to a Customer Container view controller 

-(void) addChildViewController:(UIViewController*) content 
{
[self addChildViewController:content];
content.view.frame = self.view.frame;
[self.view addSubView:content.view];
[content didMoveToParentViewController:self];
}

When a child view Controller is added to a controller, its willMoveToParentViewController will be called automatically. 

To remove a child controller from a Container view controller, below can be used

-(void) removeChildViewController:(UIViewController*) content
{
[content willMoveToParentViewController:nil];
[self.view removeFromSuperView:content.view];
[content removeFromParentViewController:nil];
}

For a container with essentially static contents, adding and removing view controllers is simple like above. Sometimes we would want to animate a view into the scene while simultaneously removing another child. 

-(void) tranitionToNewViewController:(UIViewController*)newVC oldVC:(UIViewController*)oldVC
{
[oldVC willMoveToParentViewController:nil];
[self addChildViewController:newVC];
self transitionFromViewController:oldVC toNewViewController:newVC
duration:0.25 options:0
animations:^{
newVC.view.frame = oldVC.view.frame;
oldVC.view.frame = endFrame;
}
completion:^{
[oldVC removeFromParentViewController];
[newVC didMoveToParentViewController:self];
}
}


References:


iOS Coredova Plugin - An Overview

The call exec(, , , ,[args]); from javascript code effectively marshals the request from UIWebView to the native iOS side calling the method on the service class passing the arguments to the action method. 

The plugin needs to be defined as a Feature tag in the XML file. The plugin xml file needs to specify the tags and these tags gets injected into the config.xml file. 

Plugins can utilize the pluginInitialize method to start up the services. Plugins should also implement the onReset method to stop the services if there are long running tasks. This method get called whenever the WebView changes the focus of the page. 

The general format of the cordova plugin method is like below 

-(void) pluginMethodSample:(CDVInvokeUrlCommand*)command
{
CDVPluginResult *pluginResult = nil;
NSString argument1 = [command.arguments objectAtIndex:0];
if(argument1 == nil)
{
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@“command was nil”];
}
else 
{
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
}
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}

For the return type, we can have String, Int, Double, Bool, Array, Dictionary, ArrayBuffer and multipart types. 

The complex return types include, 

messageAsArrayBuffer expects as NSData and converts as ArrayBuffer in the javascript callback. Likewise, ArrayBuffer sent from javascript is converted into NSData in iOS argument 
messageAsMultiPart expects and NSArray containing any of the other supported types, and sends the entire array as the arguments to the Javascript callback. This way, all the arguments are 
serialized and deserialized as necessary. It is safe to result back NSData as as mutlipart but not as Array/Dictionary. 

References:

Wednesday, July 29, 2015

Android Geo Fencing Basics

Geo fencing work by marking a lat and long as the center and a radius as the proximity. Or in other words, the latitude, longitude and radius define a geofence. 
There can be multiple geo fences active with a limit of 100 per device user. For each geofence, we can ask location services to send the entrance and exit events or can specify the duration within the geo fence area to wait or dwell before triggering an event. We can limit the duration of any geo fence by specifying an expiration duration in milliseconds. Afer the geofence expires, Location services automatically removes it. 

Set up for Geo fence Monitoring 
Ignored for this to work, application needs to have ACCESS_FINE_LOCATION permission in the application manifest file. 

Ignored to listen to the geo fence transitions, need to have a geo fence intent service in the manifest file as well. 
Below is what taken from the application manifest for e.g. 

To Create the Geo fence, Application needs to use the Builder class available in the location APIs.To handle the intents sent from Location services when geofence transitions occur, application can specify a PendingIntent  

Below are the main steps involved 

1. Define Geofence
2. Monitory State changes 

Defining the Geofence is in following 3 three steps 
1. Create Geofence objects by specifying lat, long, radius, Id, expiration duration, transition types. Use GeoFence.Builder for this
2. Make a geofencing request - Use GeoFencingRequest.Builder for this purpose. need to add the geo fences created in step #1 to this and set the Initial triggers 
3. Define an intent for the geo fencing transitions - This is a normal PendingIntent 
4. Now add the geo fences with the configurations to the GeoFencingApi 

LocationServices.GeoFencingApi.addGeoFences(mGoogleApiClient, GeoFencingRequestObject, GeoFencingPendingIntent).setResultCallback(this);

References:

Monday, July 27, 2015

WiFi Connectivity State Monitoring

In order to get the WiFi State changes, need to register a broadcast receiver. I registered a broadcast receiver in the Service like below and it all worked very well.

public int onStartCommand(Intent intent, int flags, int startId)
{
    Log.v(LOG_TAG,"Network connection sensor is starting");
    //start the wifi sensor right now
    mWifiReceiver = new WifiReceiver();
    IntentFilter mIntentFilter = new IntentFilter();
    mIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
    mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
    mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
    registerReceiver(mWifiReceiver, mIntentFilter);
    return START_REDELIVER_INTENT;

}

private class WifiReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        Log.v("WifiReceiver", "onReceive() is calleld with " + intent);
        if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
            notifyScanResult();
        } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
            mWifiNetworkInfo =
            (NetworkInfo) intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
            Log.v(LOG_TAG, "mWifiNetworkInfo: " + mWifiNetworkInfo.toString());
            if (mWifiNetworkInfo.getState() == NetworkInfo.State.CONNECTED) {
                mBssid = intent.getStringExtra(WifiManager.EXTRA_BSSID);
                Log.v(LOG_TAG,"Connected SSID is :"+mBssid);
                WifiManager wifiManager = (WifiManager) getSystemService (Context.WIFI_SERVICE);
                WifiInfo info = wifiManager.getConnectionInfo ();
                String bssid2 = info.getBSSID();
                Log.v(LOG_TAG,"connected SSID "+info.getSSID());
                Log.v(LOG_TAG,"Connected SSID bssid2 :"+bssid2);
                AppUtils.infoLog("Connected SSID:"+info.getSSID()+":BSSID :"+bssid2+":bSSID :"+mBssid);
                notifyWifiState();
            } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
                mWifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
                                                WifiManager.WIFI_STATE_UNKNOWN);
                notifyWifiState();
            }
            else {
                return;
            }
        }

    }

References:
http://alvinalexander.com/java/jwarehouse/android/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java.shtml