Friday, December 20, 2013

Android: Getting to Wifi scan results without consuming additional battery

In previous post, I have shown how we can start the scanning of wifi and receive the results using BroadcastReceiver. The problem with this approach is it consumes battery each time scanning is started. To avoid this there is another way: Listen to scan results initiated by other applications or by OS.
You need to add following in manifest.
<receiver android:name="com.example.receivers.WifiScanCompleteReceiver" >
 <intent-filter>
  <action android:name="android.net.wifi.SCAN_RESULTS" />
 </intent-filter>
</receiver>

Code for WifiScanCompleteReceiver.java is as follows:
package com.example.receivers;

//imports

public class WifiScanCompleteReceiver extends BroadcastReceiver {

 public WifiScanCompleteReceiver() {}

 @
 Override
 public void onReceive(Context context, Intent intent) {
  Log.d(HelperUtils.getInstance().getApplicationName(), "Wifi scan result received.");

  Vector < String > WifiNames = new Vector < String > ();
  WifiManager manager = (WifiManager) Main.getInstance().getContext().getSystemService(Context.WIFI_SERVICE);
  List < ScanResult > scanResults = manager.getScanResults();
  if (scanResults != null) {
   for (int i = scanResults.size() - 1; i >= 0; i--) {
    ScanResult result = scanResults.get(i);
    //Use result.SSID, result.level or result as it suits to your requirements
   }
  }
 }
}
This receiver will be called even if the application is killed.

Thursday, December 12, 2013

Formatting and showing code in Blogger

Android: Adding and removing an alarm event (to be repeated each day)

Following code shows how to add an alarm for a given time along with setting a number as extra to identify which alarm was triggered.


ProfileSettings.java is a class containing following fields with getter setter.
Class ProfileSettings {
    private long _time;
    private int _type;
    private int _id;
}


Code to add the alarm is as follows:
public void addAlarmEvent(ProfileSettings setting) {
    Context context = Main.getInstance().getContext();
    AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    Intent intent = new Intent(context, AlarmBroadcastReceiver.class);
    intent.putExtra(AlarmBroadcastReceiver.PROFILE_CODE, setting.getProfileCode());
    PendingIntent pendingIntent = PendingIntent.getBroadcast(context, setting.getEventId(), intent, PendingIntent.FLAG_UPDATE_CURRENT);
    Long time = setting.getTime();
    alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, time, AlarmManager.INTERVAL_DAY, pendingIntent);
    Log.d(getApplicationName(), "addAlarmEvent(): Added alarm for id:" + setting.getEventId() + " Time:" + new Date(setting.getTime()).toString() + " Type:" + setting.getProfileCode());
}
AlarmBroadcastReceiver.java will receive the alarm event.
intent.putExtra will add a code which will be received in the intent, once the alarm triggers.
Passing setting.getEventId() to PendingIntent.getBroadcast will make help us in cancelling the specific alarm later.
FLAG_UPDATE_CURRENT tells to update the same Intent, if is already present.


Following is the method for cancelling the previously added alarm.
public void cancelAlarm(int id) {
    Context context = Main.getInstance().getContext();
    AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);

    Intent intent = new Intent(context, AlarmBroadcastReceiver.class);
    PendingIntent pendingIntent = PendingIntent.getBroadcast(context, id, intent, PendingIntent.FLAG_UPDATE_CURRENT);
    alarmManager.cancel(pendingIntent);
    Log.d(getApplicationName(), "cancelAlarm(): Cancelled alarm for id: " + id);
}

"id" passed to cancelAlarm is same as setting.getEventId() used for adding the Alarm.
Please note that the context used to add the alarm must be the same for cancelling the alarm.


AlarmBroadcastReceiver.java shall receive the events triggered by AlarmManager. The code for this class is given below.
package com.example.receivers;
public class AlarmBroadcastReceiver extends BroadcastReceiver {

    public static final String PROFILE_CODE = "profile";

    public AlarmBroadcastReceiver() {}

    @Override
    public void onReceive(Context context, Intent intent) {
        int code = intent.getExtras().getInt(PROFILE_CODE, -1);
        switch (code) {
            case 1:
                Log.d(HelperUtils.getInstance().getApplicationName(), "OnReceive(): CODE_NORMAL");
                break;
            default:
                Log.d(HelperUtils.getInstance().getApplicationName(), "OnReceive(): Invalid code received");
                break;
        }
    }
}


Finally the AndroidManifest.xml entry:
<application ...>
    <receiver android:name="com.example.receivers.AlarmBroadcastReceiver">
    </receiver>
</application>

Thursday, November 28, 2013

Android: Starting Wifi scan and using ScanResult

For scanning wifi access points at interval of 30 seconds, I have the following code. However I strongly discourage frequent scanning as it will drain the battery very fast.
Following code registers for a broadcast receiver and requests the Wifi scan.

new Thread( ) {
 public void run( ) {
  WifiManager manager = (WifiManager) Main.getInstance( ).getContext( )
  .getSystemService( Context.WIFI_SERVICE );
  while ( true ) {
   IntentFilter i = new IntentFilter( );
   i.addAction( WifiManager.SCAN_RESULTS_AVAILABLE_ACTION );
   Main.getInstance( ).getContext( ).registerReceiver( new WifiScanCompleteReceiver( ), i );
   boolean a = manager.startScan();
   try {
    Thread.sleep( 30000 );
   }
   catch ( InterruptedException e ) {
   }
   Log.d( HelperUtils.getInstance( ).getApplicationName( ), "New Wifi scan started." );
  }
 }
}
.start( );
In this code Main.getInstance( ).getContext( ) returns _context = getApplicationContext( ).
Next write a broadcast receiver to listen the results available event.

public class WifiScanCompleteReceiver extends BroadcastReceiver {
    
    public WifiScanCompleteReceiver( ) {
    }
    
    @Override
    public void onReceive( Context context, Intent intent ) {
        Log.d( HelperUtils.getInstance( ).getApplicationName( ), "Wifi scan result received." );
        Main.getInstance( ).getContext( ).unregisterReceiver( this );
        
        Vector< String > WifiNames = new Vector< String >( );
        WifiManager manager = (WifiManager) Main.getInstance( ).getContext( ).getSystemService( Context.WIFI_SERVICE );
        List< ScanResult > scanResults = manager.getScanResults( );
        if ( scanResults != null ) {
            for ( int i = scanResults.size( ) - 1; i >= 0; i-- ) {
                ScanResult result = scanResults.get( i );
                WifiNames.add( result.SSID );
                Log.d( HelperUtils.getInstance( ).getApplicationName( ),
                        "Scanned SSID is:" + showOnlySSIDAndLevel( result.SSID, result.level ) );
            }
        }
    }
    
    private String showOnlySSIDAndLevel( String name, int strength ) {
        int level = WifiManager.calculateSignalLevel( strength, 5 ) + 1;
        String content = name + " \tSignal strength: " + level;
        return content;
    }
}
Its necessary to unregister the broadcast receiver using the same Context, otherwise Android will throw exception saying BroadcastReceiver leaked.

Finally add the following line in manifest.
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>

A good link for Android developers. Power usage by various components in Android.

https://source.android.com/devices/tech/power.html

Types of coroutine scopes
https://vladsonkin.com/android-coroutine-scopes-how-to-handle-a-coroutine/

Friday, October 4, 2013

Handling long string not visible in one entry of list field in Blackberry (show ...@ end)

This code returns string which can be shown in a field having width=width, 5 pixel margin on left and 5 pixel on right.
    private String getTextShown( String completeString, int width ) {
        if ( completeString == null )
            return null;
        width = width - 10;
        Font font = listfield.getFont( );
        if ( font.getAdvance( completeString ) < width )
            return completeString;
        for ( int i = completeString.length( ) - 1; i > 0; i-- ) {
            completeString = completeString.substring( 0, i );
            StringBuffer tempBuffer = new StringBuffer( completeString );
            tempBuffer.append( "..." );
            String temp = tempBuffer.toString( );
            if ( font.getAdvance( temp ) < width ) {
                return temp;
            }
        }
        return completeString;
    }
This can be called from

     protected void paint( Graphics graphics ) {
         String stringToDraw = getTextShown( "This is very long text. this will not fit in one line on the blackberry device", w );
         graphics.drawText( stringToDraw, x, y );
     }

Android aar deployment in Maven - 2022

Introduction If you are working on android library project, you might be wondering how to publish it on Maven like this . Earl...