Wednesday, July 15, 2015

Custom Tiled Background Theme in Android Activity

Making a tiled background in Android can be made simply by xml.

But, what if you need enough flexibility to manage a background theme where you can change color and pattern by user interaction, as in our free secure instant messaging app SHADO?

Since custom themes are immutable in Android, you can use our approach as a starting point for building your own runtime changeable themed UI with less code and xml resources, too.

First of all, you need a handful of patterns to use as backgrounds. They must be PNG images with transparency otherwise you won't see any change in the background color. Here are some examples:


Name them as you prefer (background_pattern_0.png, background_pattern_1.png, and so on) and put your patterns under the appropriate res/drawable resolution folder (res/drawable-hdpi, for example).
The second step would be to prepare your custom drawable resource using LayerList (and put it under the folder res/drawable) as follows:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
    <item android:id="@+id/shape_color">
        <shape android:shape="rectangle" >
            <solid android:color="@color/black" />
        </shape>
    </item>
    <item android:id="@+id/shape_pattern"
        android:bottom="0px"
        android:left="0px"
        android:right="0px"
        android:top="0px">
        <bitmap
            android:antialias="true"
            android:src="@drawable/background_pattern_0"
            android:tileMode="repeat" />
    </item>
</layer-list>

Third, define your colors in the res/values/colors.xml file and, fourth, define the layout for your Activity as in the following skeleton:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/activity_layout_root"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/background_activity" >

    <!-- Here you can put your further layouts and controls -->

</RelativeLayout>

Fifth, in your Activity source code (or in an helper Java class), you can set the background theme programmatically as below:

private void setBackgroundTheme(RelativeLayout rlActivity) {
    LayerDrawable ldTheme = (LayerDrawable) rlActivity.getBackground();
    GradientDrawable shape = (GradientDrawable) 
        ldTheme.findDrawableByLayerId(R.id.shape_color);
    shape.setColor(getResources().getColor(BACKGROUND_COLORS[currentColor]));
    BitmapDrawable bmp = (BitmapDrawable) ResourcesCompat.getDrawable(getResources(), 
        BACKGROUND_PATTERNS[currentPattern], null);
    bmp.setAntiAlias(true);
    bmp.setTileModeXY(Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
    ldTheme.setDrawableByLayerId(R.id.shape_pattern, bmp);
}

To use ResourceCompat you must import the Android Support API v4.

currentColor and currentPattern are two static variabiles you read promptly when the user make a selection on a Color or on a Pattern spinner, for example. Remember to save the user choice in SharedPreferences as soon as possible to keep track of it when app is restarted.

BACKGROUND_COLORS and BACKGROUND_PATTERNS are two final static int arrays linking to appropriate resources, for example:

private static final int[] BACKGROUND_COLORS = {
    R.color.blue_dark,
    R.color.green_dark,
    R.color.red_dark
};
private static final int[] BACKGROUND_PATTERNS = {
    R.drawable.background_pattern_0,
    R.drawable.background_pattern_1,
    R.drawable.background_pattern_2
};

Finally, you have to call setBackgroundTheme in the Activity's onCreate method and pass it the RelativeLayout root of the Activity layout, for example in this way:

setBackgroundTheme((RelativeLayout) findViewById(R.id.activity_layout_root));

Following is a background theme example based on the above code. You can download the complete code for this example here.



Have fun!

Tuesday, March 31, 2015

Service Intent Must Be Explicit

Soon after releasing our new secure instant messaging app called SHADO on the Google Play Store we got a discrete number of this exception which caused the app to crash on Android Lollipop and up:

java.lang.IllegalArgumentException: Service Intent must be explicit: Intent { act=com.android.vending.licensing
.ILicensingService }

This exception concerns the new way how service intents are started or bound on the Android API 21+. Implicit service intents are no longer supported and indeed raise an exception. In this specific case, which is very common, the problem is with the Google Play Licensing API, which, for some strange reasons, Google didn't update to explicit intents since October 2014, when the bug was reported first.

To quickly solve it, you must go with your own rebuilt version of the API, as I will explain below.

First, when you imported the API from <Android-SDK>\extras\google
\play_licensing\library in Eclipse you should have checked it to make a workspace copy of the library. If not, delete the project on Eclipse but do not delete it on the disk, otherwise you will remove it from the Android-SDK local repository, and import again the API checking for making a workspace copy of it.

Second, find the class LicenseChecker.java under the package com.google.android.vending.licensing.

Third, find the following lines of code:

boolean bindResult = mContext
  .bindService(
    new Intent(
      new String(
        Base64.decode("Y29tLmFuZHJvaWQudmVuZGluZy5saWNlbnNpbmcuSUxpY2Vuc2luZ1NlcnZpY2U="))),
    this, // ServiceConnection.
    Context.BIND_AUTO_CREATE);

And replace them with the following:

boolean bindResult;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
  bindResult = mContext
    .bindService(
      new Intent(
        new String(
          Base64.decode("Y29tLmFuZHJvaWQudmVuZGluZy5saWNlbnNpbmcuSUxpY2Vuc2luZ1NlcnZpY2U=")))
          .setPackage("com.android.vending"),
      this, // ServiceConnection.
      Context.BIND_AUTO_CREATE);
} else {
  bindResult = mContext
    .bindService(
      new Intent(
        new String(
          Base64.decode("Y29tLmFuZHJvaWQudmVuZGluZy5saWNlbnNpbmcuSUxpY2Vuc2luZ1NlcnZpY2U="))),
      this, // ServiceConnection.
      Context.BIND_AUTO_CREATE);
}

At the end, Lint will alert you that LOLLIPOP is not a valid value for android.os.Build.VERSION_CODES. Don't worry. You must open the Properties window of the API project and change the Project Build Target under the Android tab to Android 5.1 to have it rebuilt without problems.

That's all. If you added the API reference to the Properties of your app project, it will import the rebuilt version of the Google Play Licensing API without pain.

Have fun!

Tuesday, April 23, 2013

Enabling Android Logcat on a Huawei Smartphone

Getting your debug to work on a Huawei smartphone is a bit tricky, since the LogCat is disabled by default for (useless) security reasons.

First of all, to enable the LogCat you must take all the usual steps to debug an Android app on a real device under Eclipse. Then, you must take the following further steps to get the work done:


  1. on the smartphone press the number *#*#2846579#*#* which will show you a secret menu, quite common to find on android devices;
  2. choose ProjectMenu >> Background setting >> Log setting;
  3. now you will see 2 entries: "Log switch" and "Log level setting"; just choose the second one to set the minimum debugging level shown and confirm on the popup dialog, then choose "Log ON" on the first entry and confirm again;
  4. reboot the device.

Connect the Huawei to your PC via USB and... ta-daa! Eclipse will show you the LogCat.

Have fun!

Thursday, January 31, 2013

Connecting an Android App to a Tomcat Localhost

While working on the development of an Android client for one of our web apps we ran into the task of connecting our client app to a java servlet running on a local instance of Tomcat under Eclipse.

It was then that we realized that the standard way to connect to localhost or 127.0.0.1 didn't work with Android at all.

The reason is that both an AVD emulator and a real Android device will interpret the localhost as the machine which is running the app, that is, the Android emulator/device itself.

So, if you are trying to make your Android app to connect to a java servlet (or any web app) running on Tomcat under Eclipse, you must:

  1. change the IP part of the URL you're trying to connect to 10.0.2.2 (http://127.0.0.1:8080/myapp/myservlet become http://10.0.2.2:8080/myapp/myservlet for example) as explained in  the official Android documentation.
  2. if (1) for some reason it doesn't work, try setting the IP address of the machine which is running Eclipse as the IP part of the servlet URL; you can get it by running ipconfig under the start >> execute on Windows-based OSs.
  3. if (1) and (2) won't work, try to close and restart Eclipse.
  4. if none of the solutions above will work, man, it can only be a bug in your networking source code; debug it carefully and then try the previous steps again.

Just to be clear, this is only for testing purposes on localhost: restore the original URL address to connect to before to deploy your Android app or it won't work.

Have fun!

Friday, March 16, 2012

A Working Facebook OAuth Logout URL

Last week I worked on the Facebook integration of a web app of ours. The starting point was the Facebook Connect button. We need to authenticate Facebook users with our app (single sign-on), get and manage their data, and logout them from both our app and Facebook (single sign-off). All that by using the new OAuth 2.0 protocol and Java Servlets.

Not a difficult task for the first part of the process, that is, single sign-on. Problems emerged when working on the logout part. According to Facebook terms and conditions for apps, all external web apps must implement the single sign-off (SSO) method to logout Facebook users. Reasons for that are quite obvious: if you logout from an external web app without SSO the Facebook session remains active with all the security risks arising from it.

Unfortunately, official documentation on logout is poor or focused on SDKs. I found loads of forum and blog posts on how to accomplish the single sign-off. It took me about a week to check almost all proposed solutions. Crap. All crap. Found algorithms, cowboys and aliens, but none worked for me (and for the most part of the Facebook app developers as I saw on comments).

After a very long search, I found a post from a guy whose Javascript SDK-based solution worked for me. However, since our web app is going to be cross-device, I wondered about doing Facebook authentication by means of Java and Servlets for compatibility reasons with our web app, since Javascript can be disabled on the desktop browser and isn't always available on the mobile one.

Finally, I found and tested the right URL for logging out Facebook users:

https://www.facebook.com/logout.php?next=[YourAppURL]&access_token=[ValidAccessToken]

You must have a valid Facebook access_token and the next parameter must be your app URL (field URL of your web site in the tab Web Site of the Facebook App configuration). More parameters can be added, but those are the most important to get the work done.

Just a few words on the access_token. It’s in the form of:

XXXXXX|YYYYYYYYYYYY|ZZZZZZ

You must take it as a whole. Not only the Y part as I read on some posts. And that parameter is NOT the session_key.

Remember to encode URL before to pass it as the parameter next, too. In Java it's:

URLEncoder.encode([yourAppURL], “UTF-8”)

Have fun!