Working with ButterKnife in Android

logo

The following tutorial will help you understand Butter Knife implementation in Android

Why to use Butter Knife for Android?

Butter Knife in short is used in case for method binding of Android Views. Butter Knife is mainly used to make coding clean and simple especially in cases where where you deal with complex layout. Usually if you aren’t using Butter Knife you’ll have to eventually use findViewById() method for each view that you create in your layout, in cases where your application deals with many TextView’s, EditText’s, Button’s , ImageView’s the lines of code you write extends. In such cases Butter Knife comes in handy, using which you can reduce many lines of code and simply avoid methods such as findViewById().

Does Butter Knife make your App to slow down ?

No. Butter Knife doesn’t slow down your App, it gives the same result as when you declare your views using findViewById. The reason behind it is ButterKnife automatically generates findViewById calls at compile time itself thus, making use of “Annotation Processing”.

Butter Knife in Action :

Usage in xml :

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="15dp"
android:id="@+id/butterknifeLayout"
android:layout_marginLeft="@dimen/pager_margin"
android:layout_marginRight="16dp"
android:weightSum="2">

<EditText
android:id="@+id/butterknifeEdittext"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textSize="13sp"
android:hint="First Name"
android:singleLine="true"
android:layout_weight="1"/>
 
</LinearLayout>;
 

Usage in Java class.
 
@InjectView(R.id.butterknifeLayout)
LinearLayout linearLayout;
@InjectView(R.id.butterknifeText)
EditText edittext;
 
//Just use the below code for setting a OnclickListener. That’s it. you don’t need to use findViewById multiple times
 
@OnClick(R.id.butterknifeLayout)
void OnLayoutClicked(View view) {
 
//Do Your Stuff here
}

Learn more about butterknife at : http://jakewharton.github.io/butterknife/

Using Stetho to debug Android database

Facebook’s Stetho project enables you to use Chrome debugging tools to troubleshoot network traffic, database files, and view layouts. With this library, you need to have an active emulator or device running, and you use will Chrome to connect to the device by typing chrome://inspect.

For network traffic, you can use the Network Inspector:

Any SQLite database can also be inspected using the Resources -> Web SQL tab:

Note!

The third-party Android Async Http Client library uses the Apache HTTP Client, which is not currently supported by Stetho as noted in this issue. Troubleshooting networking issues works best with OkHttp or Retrofit. Regardless, you can still use this library for SQLite database inspection.

Setup

Setup your app/build.gradle file:

// Gradle dependency on Stetho
  dependencies {
    compile 'com.facebook.stetho:stetho:1.3.1'
  }

Next, initialize Stetho inside your Application object:

public class StethoApplication extends Application {
  public void onCreate() {
    super.onCreate();
    Stetho.initializeWithDefaults(this);
  }
}

If you are also using Stetho with the OkHttp or Retrofit, make sure to include the OkHttp library as well:

dependencies {
    // add below Stetho main dependency
    compile 'com.facebook.stetho:stetho-okhttp3:1.3.1' // for OkHttp library
}

You will also need to add the StethoInterceptor when constructing the OkHttpClient instance:

OkHttpClient client = new OkHttpClient.Builder()
    .addNetworkInterceptor(new StethoInterceptor())
    .build();

Start your emulator or device. Then visit chrome://inspect on your Chrome desktop and your emulator device should appear. Click on Inspect to launch a new window.

inspector-discovery

Click on the Network tab. Now you can start watching network traffic between your emulator or device in real-time!

Inspecting SharedPrefs

To view your app’s SharedPreferences, open the Resources tab of the Developer Tools window and select LocalStorage. You will see the names of the files your app uses to store the preferences. Clicking a file displays the key-value pairs stored in that file.

You can even edit the values stored in a file.

Credits:

https://github.com/codepath/android_guides/wiki/Debugging-with-Stetho/8debb39f4fbeefe3c99daa512caa16331a0e8a34

That’s it for now!
Be sure to leave your comment below regarding any queries or suggestions.

 

 

Lambda expressions in Android

What are Lambda expressions

Lambda Expressions are one of the most important features added to Java 8. Prior to Lambda Expressions, implementing functional interfaces i.e interfaces with only one abstract method has been done using syntax that has a lot of boilerplate code in it.
In cases like this, what we are trying to do is pass a functionality as an argument to a method, such as what happens when a button is clicked.

Lambda expressions enables you to do just that, in a way that is much more compact and clear.

Syntax of Lambda Expressions

A lambda expression consist of the following:

  • A comma separated list of formal parameters enclosed in parentheses. The data types of the parameters in a lambda expression can be omitted. Also the parenthesis can be omitted if there is only one parameter. For example:
TextView tView = (TextView) findViewById(R.id.tView);
tView.setOnLongClickListener(v -> System.out.println("Testing Long Click"));
  • The arrow token ->
  • A body which contains a single expression or a statement block. If a single expression is specified, the java runtime evaluates the expression and then return its value. To specify a statement block, enclose statements in curly braces "{}"

Lambda Expressions in Android

To use Lambda Expressions and other Java 8 features in Android, you need to use the Jack tool-chain. Open your module level build.gradle file and add the following:

android {
  ...
  defaultConfig {
    ...
    jackOptions {
      enabled true
    }
  }
  compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  }
}

Sync your build.gradle file and if you are having any issue with build tools, you may need to update buildToolsVersion in your build.gradle file to "24rc4" or just download the latest Android SDK Build-tools from the SDK Manager, under the Tools (Preview channel).

Example

Adding a click listener to a button

without lambda expression

Button button = (Button)findViewById(R.id.button);
button.setOnClickListener(button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Toast.makeText(this, "Button clicked", Toast.LENGTH_LONG).show();
    }
}););

with lambda expressions It is as simple as:

Button button = (Button)findViewById(R.id.button);
button.setOnClickListener(v -> Toast.makeText(this, "Button clicked", Toast.LENGTH_LONG).show(););

As we can see above, using lambda expressions makes implementing a functional interface clearer and compact. Standard functional interfaces can be found in the java.util.function package [included in Java 8]. These interfaces can be used as target types for lambda expressions and method references.

Credits : https://mayojava.github.io/android/java/using-java8-lambda-expressions-in-android/

Another way to have Java 8 features in your Android app is using the RetroLambda plugin.

Working with ConstraintLayout in Android

Few months ago, during the Google I/O conference, Google introduced a new set of tools for Android developers. Among them is a new Layout editor and a new layout called the ConstraintLayout.

I’ll be highlighting the key points in this brand new layout.

ConstraintLayout is available in a new Support Library that’s compatible with Android 2.3 (Gingerbread) and higher, but the new layout editor is available only in Android Studio 2.2 Preview.

Layout Editor & Constraints Overview.

The new layout editor in Android Studio 2.2 Preview is specially built for the ConstraintLayout. You can specify the constraints manually, or automatically reference within the layout editor.

Overview of Constraints?

A constraint is the description of how a view should be positioned relative to other items, in a layout. A constraint is typically defined for one or more sides by connecting the view to:

  • An anchor point, or another view,
  • An edge of the layout,
  • Or An invisible guide line.

Since each view within the layout is defined by associations to other views within the layout, it’s easier to achieve flat hierarchy for complex layouts.

In principle, the ConstraintLayout works very similar to the RelativeLayout, but uses various handles (or say anchors) for the constraints.

  • Resize handle. The resize handle is the alt text seen in the corners of the figure above, and it’s used to resize the view.
  • Side handle. The side handle is the alt text in the figure above, and it’s used to specify the location of a widget. E.g using the left side handle to always be aligned to the right of another view, or the left of the ConstraintLayout itself.
  • Baseline handle. The baseline handle is the alt text in the figure above. It is used to align the text of a view by the baseline of the text on another view.

Getting started with ConstraintLayout

Setup

Ensure that you’re running the AS 2.2 preview, and Android Support Repository version 32 or higher, it’s required before you can use the ConstraintLayout. Let’s get started.

  • First, you need to add the constraint layout library to your app’s dependencies within your build.gradle file:
dependencies {
    compile 'com.android.support.constraint:constraint-layout:1.0.0-alpha1'
}
  • Sync your project.

Add Constraints

There are typically two ways to create ConstraintLayout in AS. You can create a new XML layout and select the root element to be a ConstraintLayout or convert an existing layout into a ConstraintLayout as shown in the image below:

Once you have the ConstraintLayout setup, what is next is to add the constraints to the views within that layout.

As an example, drag an ImageView to the layout. The new layout builder will immediately ask to add a drawable or resource, select one from the options and press ok. Also drag a TextView unto the layout.

To create a constraint, drag the top side handle on the ImageView to the top of the ConstraintLayout. You can also drag from the top side handle of the TextView to the bottom handle of the ImageView

Using the Inspector Pane

Now that we’re able to add constraints, we will need to use the inspector. It’s on the right hand side of the layout builder and it lists various properties of the selected widget. Typically, it looks as shown below:

You can use the sliders to move the view by percentage along the x and y axes. You can also control the dimensions of the view from the inspector pane, by altering the values corresponding to the layout_width and layout_height fields.

Taking a closer look at the square widget on the inspector pane. It contains some more control over the dimensions of the views.

There are other modes of controlling the size of the view. Clicking on the inner lines in the image above help you cycle through the other modes.

  • Fixed mode: alt text This allows you specify the width and height of the view.
  • Any size: alt text This mode allows the image to fill up all the space required to fulfill that constraint. You can look at this like “match constraint”
  • Wrap content: alt text This just expands to fill the content of the view. E.g text or image

Using Auto-connect to add constraints.

Autoconnect as the name suggests, automatically creates connections between views/widgets. It tries to create connections to neighboring views/widgets.
To enable autoconnect, look out for the alt text icon on the top bar of the layout editor.

Thats’s almost it for the constraint layout.
If you want, you can head over to the official documentation on it at http://tools.android.com/tech-docs/layout-editor

 

Credits : https://segunfamisa.com/posts/constraint-layout-in-androidhttps://segunfamisa.com/posts/constraint-layout-in-android

Cheers.

Allowing web-user on apache server to run scripts as root

Allowing web-user on apache server to run scripts as root

If you are new to this, you might be wondering, what the hell is a web user anyways?

So let’s say that you need a server which hosts a simple web page and does a particular task based on data entered into that web-page.

The normal way of doing this is to navigate to /var/www/html and place the web page you want to host here.

You also need to put your php script in this folder so that it is accessible from the website.
This php script will take in the data from your web-page and run the necessary commands that you need to be executed on the server.( I am assuming you are not using “The Real Dev Language” for now. :p )

I will be using a simple web page and script that I have made for this post.

<html>
<head>
  <title>Apk Generator</title>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
  <link href='https://fonts.googleapis.com/css?family=Roboto:400,100' rel='stylesheet' type='text/css'>
  <link href="css/main.css" rel="stylesheet">
</head>
<body>
<div class="container"><br><br>
<form name="htmlform" id="form" enctype="multipart/form-data" class="col-md-offset-4 col-xs-offset-2 col-xs-8 col-md-4 form-group generator_form" >
  <label for="name">Email</label>
      <input type="email" class="form-control" id="Email" name="Email">
      <br>
      <input type="hidden" id="theme" name="theme" value="light">

      <label for="name">App's Name</label>
      <input type="text" class="form-control" id="App_Name" name="App_Name">
      <br>
      <label> Choose your data source </label>
      <ul style="list-style-type:none">
        <li><input type="radio" name="datasource" value="jsonupload"> Upload your own JSON files </input></li>
        <li><input type="radio" name="datasource" value="eventapi"> API endpoint of event on OpenEvent </input></li>
      </ul>
      <br>
      <section id="eventapi-input" style="display:none;">
        <label for="apiendpoint">Link to Open Event API endpoint</label>
        <input type="url" class="form-control"
        id="Api_Link" name="Api_Link">
      </section>
      <br>
      <section id="jsonupload-input" style="display:none;">
        <input type="file" name="uploadZip" id="uploadZip" class="form-control"/>
        <br>
      </section>
      <br>
      <input type="hidden" name="assetmode" value="download">
      <center>
        <br>
        <div id="status"></div>
        <br>
<tr>
 <td colspan="5" style="text-align:center">
  <button type="submit">Generate and Download app</button>
</td>
</tr>
</table>
</form>
<script src="https://www.gstatic.com/firebasejs/live/3.0/firebase.js"></script>
<script src="https://code.jquery.com/jquery-3.1.0.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.0/jquery-ui.js"></script>
<script>
  $('input:radio[name="datasource"]').change(
  function() {
    if ($(this).is(':checked')) {

      if ($(this).val() === 'mockjson') {
        $('#jsonupload-input').hide(100);
        $('#eventapi-input').hide(100);
      }

      if ($(this).val() === 'jsonupload') {
        $('#jsonupload-input').show(100);
        $('#eventapi-input').hide(100);
      }

      if ($(this).val() === 'eventapi') {
        $('#eventapi-input').show(100);
        $('#jsonupload-input').hide(100);
      }
    }
  });
  var $ = jQuery;
  var timestamp = Number(new Date());
  var form = document.querySelector("form");
      form.addEventListener("submit", function(event) {
        event.preventDefault();
        var ary = $(form).serializeArray();
        var obj = {};
        for (var a = 0; a < ary.length; a++) obj[ary[a].name] = ary[a].value;
          console.log("JSON",obj);
        if(obj.Email == "" || obj.App_Name ==""){
          alert("It seems like you forgot to fill up your email address or the app's name");
          setTimeout("location.reload(true);", 1);
        }
        else{
	alert("Please wait while we generate the app, meanwhile you can stick around to directly download it.The app will also be emailed to you."); 
            $.ajax({
              type: "POST",
              url: "/test.php",
              data: { timestamp : timestamp },
              success: function(response){
                console.log("Success",response);
                window.location = response;
              }
            });
        }
      });
    </script>
    </div>
</body>
</html>

This is basically a web page with some inputText widgets which accept response and send it to a php file named test.php on the server via an AJAX post.

<?php
if(isset($_POST['timestamp']))
{
    $uid = escapeshellcmd($_POST['timestamp']);
    exec("sudo sh /var/www/email.sh $uid");
}
?>

This php script will call a bash script which in turns an email to me with the user’s timestamp as the subject.

Well, here is where the problem arises, as I am trying to run the bash file as root.

You might wonder as to why is this such a big issue?
Why can’t we do that?

Well, we can surely do that on the server but the point to be noted here is that we are not running this script directly from the server.

We are running it from a web page which is hosted on a server.

So our user here is a web user aka www-data rather than being a root user.

The web user is not provided root access by default, but there are ways to get this done.

Solution 1 :

Allow the web user to run only specific scripts as root.

Please note that this is not a ideal workaround.
Ideally your web user should not have root access in any case.
Since that’s cleared up, lets proceed.

This can be done by editing your sudoers list and adding www-data to it.
Open up your terminal and enter the following command.

sudo visudo

Next up, navigate to the end of the file and add the following command there

www-data = (root) NOPASSWD: /path/to/script.sh

In case you have to execute one script as root which in turn executes some more scripts as root, you don’t need to set the path to all of them over here.
Doing it only for the parent script will do the job.

Solution 2 :

Using SuExec

DigitalOcean blog has a very good article on how to execute python scripts as root via the web user through cgi.

You can go through the article here :https://www.digitalocean.com/community/tutorials/how-to-use-suexec-in-apache-to-run-cgi-scripts-on-an-ubuntu-vps

Well, that was all about my findings on properly handling sudo requirements for your web user on your apache server.

I’ll be adding more solutions as I find them along the way.
Meanwhile feel free to comment below your thoughts, suggestions and queries.

Cheers.

Creating an API in PHP

One of the key components of my GSoC Project was to have a POST API for the Android App generator.

This was required so that the app generator could be plugged into the server and can be called directly instead of someone manually visiting the webpage and entering his/her details.

It takes in a JSON input and compiles and emails the app to the organizer based on his email address in the input JSON.

The input to the API will look something like this :

{
“email”: “[email protected]”,
“app_name”: “MyApp”,
“endpoint”: “https://open-event-dev.herokuapp.com/api/v2
}

Once the data is sent, on the server I have a php file which intercepts the requests and performs an action based on the request.

<?php
function sendResponse($data) {
    header('Content-type: application/json');
    echo json_encode($data);
    die();
}
/* If the request isn't a POST request then send an error message*/
if ($_SERVER['REQUEST_METHOD'] != 'POST') {
    sendResponse([
        "status"=>"error",
        "code"=>405,
        "message"=>"Method Not Allowed",
    ]);
}
/* Store the input received in a variable named body */
$body = json_decode(file_get_contents('php://input'), true);
/* If the user is nissing any important input parameters, don't process the request */
if (!array_key_exists('email', $body) || !array_key_exists('app_name', $body) || !array_key_exists('endpoint', $body)) {
    sendResponse([
        "status"=>"error",
        "code"=>422,
        "message"=>"Unprocessable entity",
    ]);
}
$uid = mt_rand(1000,9999). "_" .time();  //A random User ID
/* Extracting variables from the body */
$email = escapeshellcmd($body['email']);
$appName = escapeshellcmd($body["app_name"]); 
$endpoint = escapeshellcmd($body["endpoint"]);

/* Run a script based on the input parameters */
exec("sudo python /var/www/html/api/appgenserver.py $email $appName $endpoint");

The code above is pretty much self explanatory.

So basically, first we check for a valid request (GET/POST) and throw an error if it is invalid.

Next up, for a valid request we store the body into a variable and then execute a followup script as per our needs using data from this response.

This PHP file should be located in the public-html (/var/www/data) of the server so as to be accessible from outside of the server.

You can test out this API by calling it directly by prepending the server’s ip address to the name of php file containing this code.

Something like :

domain-name.com/api/api.php

You can also use Postman for Chrome or RESTClient for Firefox for making API calls easily.

Well, that’s it then!

You can easily modify the PHP code provided and modify it to suite your needs for making you own API.

Let me know your thoughts and your queries in the “response” 😉 below.

Until next time.

Creating a Widget for your Android App

Having a widget for your app, not only helps it to stand out among its alternatives but also provides user information on the go without having to open the app. Keeping this thought in mind, I decided to make a widget for my GSoC project. Let’s go through the steps involved.

Step 1:

Creating a new widget from Android Studio.

Open up your project for which you need a widget and navigate to the project’s Java source. Create a new sub-package there named widget. Right click on the newly created sub-package and select the New->Widget option from there.

new_widget

Follow the instructions on the next screen.

screenshot-area-2016-07-30-002554
Most of the fields here are pretty much self explanatory. After doing this and running the app in your device, you will be able to see a widget for your app in the widget picker.
Screenshot_20160730-003515_01

 

Just kidding, this was the easy part, off to more harder things now!

Step 2:

Populating the widget with data.

Now, there can be 2 broad type of widgets Information Widgets and Collection Widgets.

Information widgets are simple widgets that are used to display an information that changes with time, for example Weather Widget or a Clock Widget.

Whereas, collection widgets are widgets which display a collection of data, for example the GMail widget is a collection widget.
These are relatively complex and harder than the Information Widgets.

In this post, we will focus on making a Collection Widget.

For Collection widgets, we need two layout files, one for the widget and one for each item in the widget collection.

Go ahead and create the two layout files. The wizard automatically generates the widget_layout.xml for you, you just need to edit it up.

stock_layout.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:id="@+id/widget_toolbar"
        android:layout_height="?android:attr/actionBarSize"
        android:background="@color/colorPrimary">

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_gravity="center"
            android:src="@drawable/stock_up"
            android:contentDescription="@string/stock_widget" />

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_gravity="center"
            android:src="@drawable/stock_down"
            android:contentDescription="@string/stock_widget" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:layout_marginStart="32dp"
            android:gravity="center_vertical"
            android:text="@string/your_stocks"
            android:textAppearance="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title"
            android:layout_marginLeft="32dp" />
    </LinearLayout>

    <ListView
        android:id="@+id/widget_listView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/backGround"></ListView>

</LinearLayout>
list_item.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="72dp"
    android:gravity="center_vertical"
    android:orientation="horizontal"
    android:paddingLeft="16dp"
    android:paddingRight="16dp"
    >
  <TextView
      android:id="@+id/stock_symbol"
      style="@style/StockSymbolTextStyle"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:gravity="start|center_vertical"
      tools:text="List Item"
      />
</LinearLayout>

Next up, having a look at the modified files, we can see that the Widget creation wizard added some stuff into out AndroidManifest.xml and created a new java file.

Upon taking a closer look at the manifest, we can see that the widget’s java class has been registered as a <receiver/>

Next, opening up the NewAppWidget.java, we will see that it extends AppWidgetProvider and some methods are already overridden for you.

Time to edit up this file to reference to the layouts we have just created.

import android.annotation.TargetApi;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.support.annotation.NonNull;
import android.widget.RemoteViews;

/**
 * Implementation of App Widget functionality.
 */
public class StockWidgetProvider extends AppWidgetProvider {

    private static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
                                        int appWidgetId) {
        // Construct the RemoteViews object which defines the view of out widget
        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
        // Instruct the widget manager to update the widget
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            setRemoteAdapter(context, views);
        } else {
            setRemoteAdapterV11(context, views);
        }
        /** PendingIntent to launch the MainActivity when the widget was clicked **/
        Intent launchMain = new Intent(context, MainActivity.class);
        PendingIntent pendingMainIntent = PendingIntent.getActivity(context, 0, launchMain, 0);
        views.setOnClickPendingIntent(R.id.widget, pendingMainIntent);
        appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId,R.id.widget_listView);
        appWidgetManager.updateAppWidget(appWidgetId, views);
    }

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        // There may be multiple widgets active, so update all of them
        for (int appWidgetId : appWidgetIds) {
            updateAppWidget(context, appWidgetManager, appWidgetId);
        }

        super.onUpdate(context, appWidgetManager, appWidgetIds);
    }

    @Override
    public void onEnabled(Context context) {
        // Enter relevant functionality for when the first widget is created
    }

    @Override
    public void onDisabled(Context context) {
        // Enter relevant functionality for when the last widget is disabled
    }

  /** Set the Adapter for out widget **/

    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    private static void setRemoteAdapter(Context context, @NonNull final RemoteViews views) {
        views.setRemoteAdapter(R.id.widget_listView,
                new Intent(context, StockWidgetService.class));
    }

    
    /** Deprecated method, don't create this if you are not planning to support devices below 4.0 **/
    @SuppressWarnings("deprecation")
    private static void setRemoteAdapterV11(Context context, @NonNull final RemoteViews views) {
        views.setRemoteAdapter(0, R.id.widget_listView,
                new Intent(context, StockWidgetService.class));
    }

}

Now, create a WidgetDataProvider which will provide us with data to be displayed inside the widget.

You can use a static data for now (like a prefilled ArrayList, but make sure that this data should be dynamic for making the widget meaningful)

import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.os.Binder;
import android.widget.RemoteViews;
import android.widget.RemoteViewsService;

/**
 * Created by the-dagger on 24/7/16.
 */

public class WidgetDataProvider implements RemoteViewsService.RemoteViewsFactory {

    private Context context;
    private Cursor cursor;
    private Intent intent;

    //For obtaining the activity's context and intent
    public WidgetDataProvider(Context context, Intent intent) {
        this.context = context;
        this.intent = intent;
    }

    private void initCursor(){
        if (cursor != null) {
            cursor.close();
        }
        final long identityToken = Binder.clearCallingIdentity();    
        /**This is done because the widget runs as a separate thread 
        when compared to the current app and hence the app's data won't be accessible to it
        because I'm using a content provided **/
        cursor = context.getContentResolver().query(QuoteProvider.Quotes.CONTENT_URI,
                new String[]{QuoteColumns._ID, QuoteColumns.SYMBOL, QuoteColumns.BIDPRICE,
                        QuoteColumns.PERCENT_CHANGE, QuoteColumns.CHANGE, QuoteColumns.ISUP},
                QuoteColumns.ISCURRENT + " = ?",
                new String[]{"1"},null);
        Binder.restoreCallingIdentity(identityToken);
    }

    @Override
    public void onCreate() {
        initCursor();
        if (cursor != null) {
            cursor.moveToFirst();
        }
    }

    @Override
    public void onDataSetChanged() {
        /** Listen for data changes and initialize the cursor again **/
        initCursor();
    }

    @Override
    public void onDestroy() {
    cursor.close();
    }

    @Override
    public int getCount() {
        return cursor.getCount();
    }

    @Override
    public RemoteViews getViewAt(int i) {
        /** Populate your widget's single list item **/
        RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.list_item_quote);
        cursor.moveToPosition(i);
        remoteViews.setTextViewText(R.id.stock_symbol,cursor.getString(cursor.getColumnIndex(QuoteColumns.SYMBOL)));
        remoteViews.setTextViewText(R.id.bid_price,cursor.getString(cursor.getColumnIndex(QuoteColumns.BIDPRICE)));
        remoteViews.setTextViewText(R.id.change,cursor.getString(cursor.getColumnIndex(QuoteColumns.CHANGE)));
        if (cursor.getString(cursor.getColumnIndex(QuoteColumns.ISUP)).equals("1")) {
            remoteViews.setInt(R.id.change, "setBackgroundResource", R.drawable.percent_change_pill_green);
        } else {
            remoteViews.setInt(R.id.change, "setBackgroundResource", R.drawable.percent_change_pill_red);
        }
        return remoteViews;
    }

    @Override
    public RemoteViews getLoadingView() {
        return null;
    }

    @Override
    public int getViewTypeCount() {
        return 1;
    }

    @Override
    public long getItemId(int i) {
        return i;
    }

    @Override
    public boolean hasStableIds() {
        return true;
    }
}

Let’s also create a service that invokes the WidgetDataProvider after a fixed interval

import android.content.Intent;
import android.widget.RemoteViewsService;

/**
 * Created by the-dagger on 24/7/16.
 */

public class StockWidgetService extends RemoteViewsService {
    @Override
    public RemoteViewsFactory onGetViewFactory(Intent intent) {
        return new WidgetDataProvider(this,intent);
    }
}

Phew.. almost done with this now.

Finally edit up the widget_info.xml located inside /res/values/xml/ of your project.

Edit it to reference the time after which your widget will be updated, the preview image which should show up in the widget picker and minimum width and height of the widget.

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:initialKeyguardLayout="@layout/app_widget"
    android:initialLayout="@layout/app_widget"
    android:minHeight="110dp"
    android:minWidth="170dp"
    android:previewImage="@drawable/example_appwidget_preview"
    android:resizeMode="horizontal|vertical"
    android:updatePeriodMillis="86400000"
    android:widgetCategory="home_screen"></appwidget-provider>

Well, once this is done, go ahead and fire up your app. You will be able to see the newly created and updated widget in your homescreen.

 widget

Pretty awesome right!
Congratulations on making your first widget.

For now the app only opens a specific activity on clicking it, but you can read up a bit on how to execute a separate task on clicking each item on the list by using a pendingIntent.

Working with GCM Task Service on Android

The GCM Network Manager enables apps to register services that perform network-oriented tasks.
The API helps with scheduling these tasks, allowing Google Play services to batch network operations across the system.

Hence, multiple tasks get executed in a go, rather than executing each of them individually.
This in turns save battery as the mobile radio is awoken only once rather than waking up the device multiple times.

Using GCM Network Manager in our app

Add the dependency to build.gradle

dependencies {
    ...
    compile 'com.google.android.gms:play-services-gcm:8.4.0'
    ...
}

Next, declare a new Service in the Android Manifest which will make the network calls:

<service android:name=".GCMService"
        android:permission="com.google.android.gms.permission.BIND_NETWORK_TASK_SERVICE"
        android:exported="true">
   <intent-filter>
       <action android:name="com.google.android.gms.gcm.ACTION_TASK_READY"/>
   </intent-filter>
</service>

The name of the service (GCMService) is the name of the class that will extend GcmTaskService, which is the core class for dealing with GCM Network Manager. This service will handle the running of a task.

Next we will define the GCMSerice class

public class GCMService extends GcmTaskService {
        ...
}

On implementing the methods from GcmTaskService, we have something like this :

@Override
public int onRunTask(TaskParams taskParams) {
        switch (taskParams.getTag()) {
                case TAG_TASK_ONEOFF_LOG:
                        //bundle with other tasks and then execute
                        return GcmNetworkManager.RESULT_SUCCESS;
                case TAG_TASK_PERIODIC_LOG:
                        //run this periodically 
                        return GcmNetworkManager.RESULT_SUCCESS;
                default:
                        return GcmNetworkManager.RESULT_FAILURE;
        }
}

Once this is set up, we need to schedule tasks from our Activity.

This can be done easily by first creating an object of GcmNetworkManager and class and hook up tasks to be executed from the above defined service

private GcmNetworkManager mGcmNetworkManager;

@Override
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ...
        gcmNetworkManager = GcmNetworkManager.getInstance(this);
}

The GcmNetworkManager object will be used to schedule tasks, so one way to do this is to hold a reference as a member variable in onCreate . Alternatively, we can just get an instance of the GcmNetworkManager when we need it for scheduling.

Scheduling Tasks

Scheduling a OneOff Task

We provide the task with a window of execution, and the scheduler will determine the actual execution time. Since tasks are non-immediate, the scheduler can batch together several network calls to preserve battery life.

The scheduler will consider network availability, network activity and network load. If none of these matter, the scheduler will always wait until the end of the specified window.

Now, here’s how we would schedule a one-off task:

Task task = new OneoffTask.Builder()
              .setService(GCMService.class)
              .setExecutionWindow(0, 30)
              .setTag(LogService.TAG_TASK_ONEOFF_LOG)
              .setUpdateCurrent(false)
              .setRequiredNetwork(Task.NETWORK_STATE_CONNECTED)
              .setRequiresCharging(false)
              .build();

mGcmNetworkManager.schedule(task);

Using the builder pattern, we define all the aspects of our Task:

  • Service: The specific GcmTaskService that will control the task. This will allow us to cancel it later.
  • Execution window: The time period in which the task will execute. First param is the lower bound and the second is the upper bound (both are in seconds).
  • Tag: We’ll use the tag to identify in the onRunTask method which task is currently being run. Each tag should be unique, and the max length is 100.
  • Update Current: This determines whether this task should override any pre-existing tasks with the same tag. By default, this is false.
  • Required Network: Sets a specific network state to run on. If that network state is unavailable, then the task won’t be executed until it becomes available.
  • Requires Charging: Whether the task requires the device to be connected to power in order to execute.

All together, the task is built, scheduled on the GcmNetworkManager instance, and then eventually runs when the time is right.

Scheduling a periodic task

Here’s what a periodic task looks like:

Task task = new PeriodicTask.Builder()
                        .setService(GCMService.class)
                        .setPeriod(30)
                        .setFlex(10)
                        .setTag(LogService.TAG_TASK_PERIODIC_LOG)
                        .setPersisted(true)
                        .build();

mGcmNetworkManager.schedule(task);

It seems pretty similar, but there are a few key differences:

  • Period: Specifies that the task should recur once every interval at most, where the interval is the input param in seconds.
  • Flex: Specifies how close to the end of the period (set above) the task may execute. With a period of 30 seconds and a flex of 10, the scheduler will execute the task between the 20-30 second range.
  • Persisted: Determines whether the task should be persisted across reboots. Defaults to true for periodic tasks, and is not supported for one-off tasks. Requires “Receive Boot Completed” permission, or the setter will be ignored.

Cancelling Tasks

We saw how to schedule tasks, so now we should also take a look at how to cancel them. There’s no way to cancel a task that is currently being executed, but we can cancel any task that hasn’t yet run.

We can cancel all tasks for a given GcmTaskService:

  • gcmNetworkManager.cancelAllTasks(GCMService.class);

    And we can also cancel a specific task by providing its tag and GcmTaskService:

    gcmNetworkManager.cancelTask(
            GCMService.TAG_TASK_PERIODIC_LOG,
            GCMService.class
    );

    Well, we took a deep look at the GCM Network Manager and how to use it to conserve battery life, optimize network performance, and perform batched work using Tasks.
    I would urge you to try this out in your app for a streamlined yet robust approach on making networking calls.

Getting fired up with Firebase Database

As you might’ve noticed, in my Open Event Android Project, we are asking the user to enter his/her details and then using these details at the backend for generating the app according to his/her needs.

One thing to wonder is how did we transmit the details from webpage to the server.

Well, this is where Firebase comes to the rescue one more time!

If you’ve read my previous post on Firebase Storage, you might have started to appreciate what an awesometastic service Firebase is.

So without any further adieu, lets get started with this.

Step 1 :

Add your project to Firebase from the console.

 newProj
Click on the Blue button

Step 2 :

Add Firebase to your webapp

Open the project, you’ve just created and click on the bright red button that says, “ Add Firebase to your web app”

 addFirebase

Copy the contents from here and paste it after your HTML code.

Step 3 :

Next up, navigate to the Database section in your console and move to the Rules tab.

 screenshot-area-2016-07-18-204133.png

For now, let us edit the rules to allow anyone to read and write to the database.

 screenshot-area-2016-07-18-204437

Almost all set up now.

Step 4 :

Modify the HTML to allow entering data by the user

This looks something like this :

Now let us setup our javascript to extract this data and store this in Firebase Database.

We are almost finished with uploading the data to the database.

Enter data inside the fields and press submit.

If everything went well, you will be able to see the newly entered data inside your database.

screenshot-area-2016-07-18-205651.png

Now on to retrieving this data on the server.

Our backend runs on a python script, so we have a library known as python-firebase which helps us easily fetch the data stored in the Firebase database.

The code for it goes like this

The data will be returned in JSON format, so you can manipulate and store it as you wish.

Well, that’s it!

You now know how to store and retrieve data to and from Firebase.
It makes the work a lot simpler as there is no Database schema or tables that need to be defined, firebase handles this on its own.

I hope that you found this tutorial helpful, and if you have any doubts regarding this feel free to comment down below, I would love to help you out.

Cheers.

Push your apk to your GitHub repository from Travis

In this post I’ll guide you on how to directly upload the compiled .apk from travis to your GitHub repository.

Why do we need this?

Well, assume that you need to provide an app to your testers after each commit on the repository, so instead of manually copying and emailing them the app, we can setup travis to upload the file to our repository where the testers can fetch it from.

So, lets get to it!

Step 1 :

Link Travis to your GitHub Account.

Open up https://travis-ci.org.

Click on the green button in the top right corner that says “Sign in with GitHub”

screenshot-area-2016-07-15-205733.png<

Step 2 :

Add your existing repository to Travis

Click the “+” button next to your Travis Dashboard located on the left.

screenshot-area-2016-07-15-210630.png<

Choose the project that you want to setup Travis from the next page

screenshot-area-2016-07-15-210916.png
Toggle the switch for the project that you want to integrate

Click the cog here and add an Environment Variable named GITHUB_API_KEY.
Proceed by adding your Personal Authentication Token there.
Read up here on how to get the Token.

 screenshot-area-2016-07-15-213931.png<

Great, we are pretty much done here.

Let us move to the project repository that we just integrated and create a new file in the root of repository by clicking on the “Create new file” on the repo’s page.

Name it .travis.yml and add the following commands over there

language: android 
jdk:
  - oraclejdk8
android:
  components:
    - tools
    - build-tools-24.0.0
    - android-24
    - extra-android-support
    - extra-google-google_play_services
    - extra-android-m2repository
    - extra-google-m2repository
    - addon-google_apis-google-24
 before_install:
 - chmod +x gradlew
 - export JAVA8_HOME=/usr/lib/jvm/java-8-oracle
 - export JAVA_HOME=$JAVA8_HOME
 after_success:
 - chmod +x ./upload-gh-pages.sh
 - ./upload-apk.sh
 script:
 - ./gradlew build

Next, create a bash file in the root of your repository using the same method and name it upload-apk.sh

  #create a new directory that will contain out generated apk
  mkdir $HOME/buildApk/ 
  #copy generated apk from build folder to the folder just created
  cp -R app/build/outputs/apk/app-debug.apk $HOME/android/
  #go to home and setup git
  cd $HOME
  git config --global user.email "[email protected]"
  git config --global user.name "Your Name" 
  #clone the repository in the buildApk folder
  git clone --quiet --branch=master  https://user-name:[email protected]/user-name/repo-name  master > /dev/null
  #go into directory and copy data we're interested
  cd master  cp -Rf $HOME/android/* .
  #add, commit and push files
  git add -f .
  git remote rm origin
  git remote add origin https://user-name:[email protected]/user-name/repo-name.git
  git add -f .
  git commit -m "Travis build $TRAVIS_BUILD_NUMBER pushed"
  git push -fq origin master > /dev/null
  echo -e "Donen"

Once you have done this, commit and push these files, a Travis build will be initiated in few seconds.
You can see it ongoing in your Dashboard at https://travis-ci.org/.

After the build has completed, you will can see an app-debug.apk in your Repository.

IMPORTANT NOTE :

You might be wondering as to why did I write [skip ci] in the commit message.

Well the reason for that is, Travis starts a new build as soon as it detects a commit made on the master branch of your repository.

So once the apk is uploaded, that will trigger another build in Travis and hence forming an infinite loop.

We can prevent this in 2 ways :

First, simply write [skip ci] somewhere in the commit message and it will cause Travis to ignore the commit.

Or, push the apk to any other branch which is not configured for Travis build.

So well, that’s almost it.

I hope that you found this tutorial helpful, and if you have any doubts regarding this feel free to comment down below, I would love to help you out.

Cheers.