Need help testing your android app? Want to test it on-demand across several devices? Check http://www.testingandroid.com
Recently I was faced with an issue: one of my android users had a problem and wasted 2 hours with me debugging it, he didn't ask me for anything but I though it would be nice if I gave him the full version of the free app he helped me fix.
However, there's no way of giving a user a copy of a priced app on the Android Market ( at least that I know of), so I ended up giving him the APK for that product. Unfortunately, this approach has 2 big drawbacks:
- The user has to turn on the settings for "Install from Unknown sources"
- The user will NOT receive automatic updates for the app (since it wasn't deployed through the market)
Normally when I design an app I usually follow this pattern:
- Design the full app first (the paid one)
- Design the free app by restricting access to some functionalities
As you can imagine the source code is exactly the same, only a small option in my AndroidManifest file is changed (I know that it isn't the best approach but works for me as is).
In the retail world if you want to give someone a product for free you give him a coupon to trade for the product, so I decided to follow the exact same approach.
So the requirements I set to myself to fulfill were:
- be able to give away coupons for paid apps to anyone I wanted to
- be able to cancel any coupon at any give time
- be able to disable coupons for a specific app if required
As you imagine this project has 2 main components: the coupon controller on the free Android App and the backend to control which coupons are valid and which apps have coupons.
This is a very simple project so I ended up using AppEngine to deploy my backend code.
Android Coupon Controller Overview
Let's start by having a look at the coupon controller on the Android App, there're two ways for redeeming a coupon:
a) In your app have a "Redeem Coupon" option somewhere
b) When the user tries to access priced app functionalities, fire a dialog for him to buy the app or use a coupon:
If the user presses "Redeem coupon" a new dialog will popup with the instruction to insert a coupon:
Obviously, a different dialog will be shown if the user inserts a valid or invalid coupon (or if there's no internet connection available):
Android Coupon Controller Installation and usage:It's very easy to use the coupon controller on your Android App, just follow these steps:
- Create a libs directory on your project (if it doesn't exist)
- Copy coupon.jar to that folder
- Right-Click on "libs", press "Refresh"
- Go to Project -> Properties -> Java Build Path -> Libraries -> Add Jar
After adding the library to your project is time to integrate your code with the coupon controller, here's a brief description of the controller's code:
- interface ProcessCoupon - your activity should implements this
- public void launchCoupon() - should call Coupon.createCouponDialog(..) to create the coupon dialog
- public void processInvalidCoupon() - is called after an invalid coupon is inserted
- public void processValidCoupon() - is called after an invalid coupon is inserted (should call CouponPreferences.markUnblocked(Context) )
- CouponPreferences - utility class
- public static boolean isUnblocked(Context context) - Returns true if a valid coupon was already inserted
- public static void markUnblocked(Context context) - transforms the app in it's full version
Coupon class - UI for the coupon controller
- public static void createForbiddenDialog(final ProcessCoupon parent, final String warning, final String packageNameFull ) - should be called when the user tries to access a feature that is only available in the full version: informs the user that he can't access that feature (described by the warning parameter) giving him the options to: Buy the App (with the pname assigned to ' packageNameFull') or to 'Redeem the coupon'
- public static void createCouponDialog(final Context context,final ProcessCoupon parent,final String appname, final String host, final String deviceId) - This method shows the user a dialog to insert his coupon, it will then communicate with the backend and inform the user if the coupon is valid, invalid or if there's no internet connection. The parent param should handle those cases, the host param stands for the URL for your backend (more or this later) and the deviceId of the user's cellphone (which gets registered on server-side )
Android Coupon Backend Installation and usage:
The backend is where you create applications and coupons for them. To disable specific coupons or applications you will need to use the AppEngine dashboard and Datastore Viewer
To install your backend on AppEngine, you can start by cloning this git repo:
- https://github.com/zemariamm/DroidCoupons
- Go to appengine.google.com and create a new application
- Edit app.yaml and replace droidcoupons with your Application Identifier (you check get from the previous step)
Deploy it on AppEngine ( see: http://code.google.com/appengine/docs/python/gettingstarted/uploading.html )
Afterwards you will need to create coupons for your application, to do that just open (on your browser) the following link:
http://your-application-identifier.appspot.com/random/your-app-name -> replace your-app-name with the same string you on param "appname" when calling createCouponDialog on your java code, replace "your-application-identifier" with your application identifier
(If the application doesn't exist yet, it will be created, you don't have to worry about that)
After opening the bellow URL you will be asked for your google credentials and afterwards you will see something like this (if you open:
http://droidcoupons.appspot.com/random/testcoupons ):
testcoupons - dkbm1ha5
Which means:appname - coupon
E.g. dkbm1ha5 is the string you will insert in your android app
NOTE: It's required that the "host" parameter mentioned on "createCouponDialog" matches: "http://your-application-identifier.appspot.com/check/"
To disable a specific coupon, open your appengine dashboard for the application identifier you just created, click Datastore Viewer and follow these steps:
To disable all coupons for a app you need to follow a similar proccess:
Example:In order to explain better how the library works and to give a working sample, I'll walk you through a working app (available at:
https://github.com/zemariamm/TestCoupon)The goal is to start out with a limited app and having it's
behavior changed upon inserting the coupon:
The app consist of one TextView and 2 Buttons: one button to
redeem the coupon and the other one to perform a "paid" app action
Here's the screenshot:
Where the user clicks the "Click Here" button he will see this message:
When he clicks Redeem coupon:
And here're the printscreens after inserting a valid coupon:
After inserting the Coupon if the user presses the "Click Here" button he's now able to execute it's full functionality, which means, displaying the following dialog:
The code for this test application is very simple (and available at:
https://github.com/zemariamm/TestCoupon), the source code for the Main Activity follows:
The Main Activity (which handles coupons) implements ProcessCoupon, the
behavior of each button is defined according to the value of CouponPreferences.isUnblocked (which will return true if a valid coupon was inserted previously), the Activity implements the required methods: launchCoupon (to show the Coupon dialog), processInvalidCoupon (which gets called when an invalid coupon is inserted) and processValidCoupon (which gets called when a valid coupon is inserted). It's also important to show that when the user tries to access paid app features that we call createForbiddenDialog giving him the option to buy the full app or redeem a coupon in order to have access to that feature.
Note: All the code is released in a "It works for me" state. :)
Source code:
Android Library - https://github.com/zemariamm/Coupons
AppEngine Backend - https://github.com/zemariamm/DroidCoupons
Example App - https://github.com/zemariamm/TestCoupon
Need help testing your android app? Want to test it on-demand across several devices? Check http://www.testingandroid.com