Sunday, June 26, 2016

registerReceiver (BroadcastReceiver, IntentFilter) Android Example

This example is about using registerReceiver and unregisterReceiver methods to use BroadcastReceiver programmatically. When BroadcastReceiver is registered in this way it's not declared in the AndroidManifest.xml file.

This application project has one Activity file and it register BroadcastReceiver to obtain battery level change information. Broadcast action ACTION_BATTERY_CHANGED is used by Android system to broadcast battery level changing information. Therefore that is used in this project.

Below source code is about implementation of the class BatteryChangedReceiver that extends from BroadcastReceiver. It receive battery change information and extra data on that intent are got by using the class BatteryManager. BatteryManager class has definitions of extra data that are sent with aforementioned broadcast action. Once onReceive(Context, Intent) method receives that data they are shown as Toast.

BatteryChangedReceiver Implementation

import android.content.BroadcastReceiver; 
import android.content.Context; 
import android.content.Intent; 
import android.os.BatteryManager; 
import android.widget.Toast; 
 
public class BatteryChangedReceiver extends BroadcastReceiver { 
   public BatteryChangedReceiver() {
   } 
 
   @Override
   public void onReceive(Context context, Intent intent) { 
      if (intent.getAction().equals(Intent.ACTION_BATTERY_CHANGED)) { 
         int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); 
         int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
         Toast.makeText(context, "Level = "+level+", 
                        Scale = "+scale, Toast.LENGTH_LONG).show();
      }
   }
}

Above BroadcastReceiver is registered in the class MainActivity. That source code is this.

MainActivity Implementation

public class MainActivity extends AppCompatActivity { 
 
   private BatteryChangedReceiver batteryChangedReceiver; 
   private IntentFilter intentFilter; 
 
   @Override
   protected void onCreate(Bundle savedInstanceState) { 
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main); 
 
      batteryChangedReceiver = new BatteryChangedReceiver(); 
      intentFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
   } 
 
   @Override
   protected void onResume() { 
      super.onResume();
      registerReceiver(batteryChangedReceiver, intentFilter);
   } 
 
   @Override
   protected void onPause() {
      unregisterReceiver(batteryChangedReceiver);
      super.onPause();
   }
}

An instance of the BatteryChangedReceiver is created to be used as first argument to registerReceiver() method. This method needs IntentFilter object for second argument to register BroadcastReceiver provided with first argument for a specific Intent. So both objects for first and second arguments are created in onCreate() method.
 
   batteryChangedReceiver = new BatteryChangedReceiver(); 
   intentFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);

The method registerReceiver() is called in onResume() to register BroadcastReceiver. 
 
protected void onResume() { 
   super.onResume();
   registerReceiver(batteryChangedReceiver, intentFilter);
} 

As the BroadcastReceiver is registered in the onResume() it must be unregistered onPause(). Otherwise error will be occurred for leaking BroadcastReceiver. A BroadcastReceiver must be unregistered as many as it is registered.
 
@Override 
protected void onPause() {
   unregisterReceiver(batteryChangedReceiver); 
   super.onPause();
}

If onStop() is used to register BroadcastReceiver it must be unregistered in onStop().

Reference
  • https://developer.android.com/reference/android/content/Context.html#registerReceiver(android.content.BroadcastReceiver,%20android.content.IntentFilter)
  • https://developer.android.com/reference/android/content/Context.html#unregisterReceiver(android.content.BroadcastReceiver)
  • https://developer.android.com/reference/android/content/Intent.html#ACTION_BATTERY_CHANGED
  • https://developer.android.com/reference/android/os/BatteryManager.html

Sunday, June 19, 2016

Android BroadcastReceiver

Android applications need some mechanism to know when some event is occurred. For example some applications need to perform tasks when Android OS booting is completed. Android SDK provide BroadcastReceiver class for that purpose. This blog post content is about using BroadcastReceiver to get notification about connecting and disconnecting Android OS running device from external power source.
Name of this application is ConDisConInfo. So it has the main package with name com.blogspot.nipunswritings.ConDisConInfo. To create a BroadcastReceiver follow these steps.
  • Right Click on Main package
  • New
  • Other
  • Broadcast Receiver
  • Give name and click Finish
Once Finish is clicked a new java file can be seen in main package with the name PowerConnInfoReceiver.java. It has a PowerConnInfoReceiver class which extends from super class BroadcastReceiver. PowerConnInfoReceiver has below method which receive power connection/disconnection information which is sent by system.

@Overridepublic void onReceive(Context context, Intent intent) {
    
}

After  PowerConnInfoReceiver is created AndroidManifest.xml file has been updated with receiver element. That is registering a Broadcast Receiver. The receiver element in AndroidManifest.xml file is this.

<receiver 
     android:name=".PowerConnInfoReceiver" 
     android:enabled="true"
     android:exported="true">
</receiver>

Though that element is in the AndroidManifest.xml file PowerConnInfoReceiver will not receive any broadcast. A BroadcastReceiver to receive broadcasts it must be specified what broadcasts it must be received through the intent-filter element. Therefore receiver element is updated like below to receive broadcasts as mentioned above.

<receiver 
    android:name=".PowerConnInfoReceiver" 
    android:enabled="true"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
        <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
    </intent-filter>
</receiver>

Notice the actions inside intent-filter. Without those actions this application doesn't work as expected. 

Method onReceive(Context, Intent) is updated to filter received information via Intent object and to display information to user as a Toast. So updated onReceive(Context, Intent) method is this.

@Overridepublic void onReceive(Context context, Intent intent) {
    if (intent.getAction().equals(Intent.ACTION_POWER_CONNECTED)){
        Toast.makeText(context, "Power Connected", Toast.LENGTH_LONG).show();
    } else if (intent.getAction().equals(Intent.ACTION_POWER_DISCONNECTED)) {
        Toast.makeText(context, "Power Disconnected", Toast.LENGTH_LONG).show();
    }
}

Run this app on a emulator. This application can be tested with battery section in extended controls of emulator. When charger connection is toggled between AC Charger and None corresponding Toast is displayed.



Reference:
  • https://developer.android.com/reference/android/content/BroadcastReceiver.html
  • https://developer.android.com/reference/android/content/Intent.html#ACTION_POWER_CONNECTED
  • https://developer.android.com/reference/android/content/Intent.html#ACTION_POWER_DISCONNECTED

Sunday, June 12, 2016

HelloCharts for Android Example

This example is using HelloCharts for Android to plot line chart for function |sin(x)|. The value range for x axis is from 0 to 360 degrees. The x values are given like 0, 15, 30, 45 ... 360. So there is difference of 15 degrees between  two points. But X axis is drawn with 30 degree scale. Therefore it goes like 0, 30, 60, 90, ..., 180, ...360. Y axis is drawn with scale of 0.25 from 0 to 1.0 to make Y axis range 0, 0.25, 0.5, 1.

At the end of this example developed application creates the below chart.

Add hello-charts library to app by adding below line to dependencies block of build.gradle file of app module. Then sync the project by using Android Studio.

compile 'com.github.lecho:hellocharts-library:1.5.8@aar'


Project has one Activity file with the name MainActivity.java. It's layout resource file is activity_main.xml.

This is the content of activity_main.xml. It has RelativeLayout as root element and RelativeLayout contain TextView and chart definition.

<TextView 
 android:id="@+id/chartLbl" 
 android:gravity="center" 
 android:layout_width="match_parent" 
 android:layout_height="wrap_content" 
 android:text="|sin(x)|" 
 android:layout_alignParentTop="true"     
 android:layout_alignParentStart="true"     
 android:layout_alignParentLeft="true"     
 android:layout_margin="5dp" 
 android:textStyle="bold" />

<lecho.lib.hellocharts.view.LineChartView 
 android:id="@+id/chart"     
 android:layout_width="match_parent" 
 android:layout_height="match_parent" 
 android:padding="10dp" 
 android:layout_below="@+id/chartLbl"/>

The element lecho.lib.hellocharts.view.LineChartView is xml definition of the line chart.

Now reference to this LineChartView can be made with findViewById(int) method. To hold reference to that view, field of type LineChartView has been defined in the MainActivity.java like this.
 
private LineChartView lineChartView;

Line chart has been created with below method. That is called in onCreate(Bundle) method.
 
public void drawSinAbsChart()

Method definition of  drawSinAbsChart()

public void drawSinAbsChart() {
    String decimalPattern = "#.##";
    DecimalFormat decimalFormat = new DecimalFormat(decimalPattern);

    lineChartView = (LineChartView) findViewById(R.id.chart);

    List<PointValue> values = new ArrayList<PointValue>();

    PointValue tempPointValue;
    for (float i = 0; i <= 360.0; i+= 15.0f) {
        tempPointValue = new PointValue(i, Math.abs((float)Math.sin(Math.toRadians(i))));
        tempPointValue.setLabel(decimalFormat
                   .format(Math.abs((float)Math.sin(Math.toRadians(i)))));
        values.add(tempPointValue);
    }

    Line line = new Line(values)
                   .setColor(Color.BLUE)
                   .setCubic(false)
                   .setHasPoints(true).setHasLabels(true);
    List<Line> lines = new ArrayList<Line>();
    lines.add(line);

    LineChartData data = new LineChartData();
    data.setLines(lines);

    List<AxisValue> axisValuesForX = new ArrayList<>();
    List<AxisValue> axisValuesForY = new ArrayList<>();
    AxisValue tempAxisValue;
    for (float i = 0; i <= 360.0f; i += 30.0f){
        tempAxisValue = new AxisValue(i);
        tempAxisValue.setLabel(i+"\u00b0");
        axisValuesForX.add(tempAxisValue);
    }

    for (float i = 0.0f; i <= 1.00f; i += 0.25f){
        tempAxisValue = new AxisValue(i);
        tempAxisValue.setLabel(""+i);
        axisValuesForY.add(tempAxisValue);
    }

    Axis xAxis = new Axis(axisValuesForX);
    Axis yAxis = new Axis(axisValuesForY);
    data.setAxisXBottom(xAxis);
    data.setAxisYLeft(yAxis);


    lineChartView.setLineChartData(data);


}

Code Explanation

    String decimalPattern = "#.##";
    DecimalFormat decimalFormat = new DecimalFormat(decimalPattern);

Values that are obtained must be shown only up to two decimal places in chart point labels. Object decimalFormat is created for that purpose.

    lineChartView = (LineChartView) findViewById(R.id.chart);

Above jave statement is to obtain the LineChartView object that is declared in the layout xml resource.

    List<PointValue> values = new ArrayList<PointValue>();

    PointValue tempPointValue;
    for (float i = 0; i <= 360.0; i+= 15.0f) {
        tempPointValue = new PointValue(i, Math.abs((float)Math.sin(Math.toRadians(i))));
        tempPointValue.setLabel(decimalFormat
                   .format(Math.abs((float)Math.sin(Math.toRadians(i)))));
        values.add(tempPointValue);
    }

Tha values that must be displayed in the line chart must be in the List of type List<PointValue>. That is doing here. That list contains points which are drawn in the chart. Point is represented with PointValue Class. for loop create those points and set it label that are shown at the points.

    Line line = new Line(values)
                   .setColor(Color.BLUE)
                   .setCubic(false)
                   .setHasPoints(true).setHasLabels(true);
    List<Line> lines = new ArrayList<Line>();
    lines.add(line);

    LineChartData data = new LineChartData();
    data.setLines(lines);

Line chart can  contain many lines, Above code shows how to create new line with the values which was generated function |sin(x)|. Method setColor() is used to define color of the line. This chart has straight line as setCubic() has been provided false value. If setHasPoints() called with argument false value points wouldn't be displayed.When setHasLabels() is provided with argument true value labels that was set during PointValue object are created is shown. The lines variable of type List<Line> holds all the lines that is drawn in the chart. This example chart has only one line so it has been added. Finally that lines object is added to LineChartData object with setLines().

    List<AxisValue> axisValuesForX = new ArrayList<>();
    List<AxisValue> axisValuesForY = new ArrayList<>();
    AxisValue tempAxisValue;
    for (float i = 0; i <= 360.0f; i += 30.0f){
        tempAxisValue = new AxisValue(i);
        tempAxisValue.setLabel(i+"\u00b0");
        axisValuesForX.add(tempAxisValue);
    }

    for (float i = 0.0f; i <= 1.00f; i += 0.25f){
        tempAxisValue = new AxisValue(i);
        tempAxisValue.setLabel(""+i);
        axisValuesForY.add(tempAxisValue);
    }

    Axis xAxis = new Axis(axisValuesForX);
    Axis yAxis = new Axis(axisValuesForY);
    data.setAxisXBottom(xAxis);
    data.setAxisYLeft(yAxis);

Java statements that are shown creating X and Y axis of the chart. First loop creates X axis which is started from 0 and goes upto 360 with increment 15.0 from previous x value. Values of axis is represented with object of type List<AxisValue> and AxisValue objects are used represent a location in the axis. AxisValue has setLabel() to display developer preferred label in the chart. In same way Y axis also has been created. After thata x & y axis is created as Axis objects. X axis is set to bottom with setAxisXBottom() and Y Axis is set to left with setAxisYLeft().

Chart data is provided to LineChartView with below statement.

    lineChartView.setLineChartData(data);

If axis values is cut off this link might be helpful. I had similar problem but I could resolve it without that link by adding 5dp padding around LineChartView.

Reference:
  • https://github.com/lecho/hellocharts-android

Saturday, June 4, 2016

Renaming Android Studio Project and Main Package

Content of this blog post has been tested with Android Studio 2.0.

Changing Project Name
  • Open Project Window (View → Tool Windows → Project or Alt + 1)
  • By default Project window shows the Android view change it to Project view with the drop down menu at top left. 


  •  Right click on project root → Show in Explorer.
  • Close Android Studio.
  • Go into project folder.
  • Change .iml file to new name that you want to have for the project.
  •  Change the project folder name to new name which is same name of .iml file.
  • Start Android Studio
  • Click on “Open an existing Android Studio project”
  • Find & select the renamed project, then “Ok”
Changing Package name
  • Right Click on package name (I tried this on main package)
  • Refactor
  • Rename
  • Rename Package
  • Type new name & click Refactor
  • Click on Do Refactor 
After doing this I have observed changing the main package name successfully renamed other two packages (androidTest & Test) during refactoring process. Although Generated files like AndroidManifest.xml also had correct values strings.xml file had old project name for the string element with attribute “app_name”.
Above experiment was done on a very small project.