I've had to come up with a solution for this, so I figured I would write a tutorial for anyone else who has had this problem.
Problem:
You have an Android App which you are ready to put on the Play Store, but you would like 2 versions of the app, one which you will charge for (usually 99 cents) and one which is free but has ads. Having two versions of the app requires you to have two separate apps, which means managing two separate code bases. Managing two separate code bases means doubling your maintenance efforts.
Solution:
In this tutorial, we will discuss how to reduce your maintenance efforts by using a third project as an "Android Library Project". We will be utilizing 3 projects:
1. The Library Project (TestApp), which will contain the majority of your app's user interface.
2. The Ad Supported App (TestAdApp), which will contain your app's user interface, with banner ads along the top of the activities.
3. The Paid App (TestPaidApp), which will contain your app's user interface without any ads
Requirements:
1. Eclipse (Tutorial uses Version: 4.3.2)
2. Installed Android Development Kit (Tutorial uses API Level: 19)
3. Installed Android Emulator (Tutorial uses an emulator created using the AVD Manager)
4. The Google Play Services extra installed
5. A valid Ad Unit Id
Setup:
We begin by creating our three Android projects. I will gloss over this, since there are many tutorials out there to show you how to do this.
Creating TestApp:
1. Go to New > Other > Android > Android Application Project
2. Enter the desired Application Name (TestApp), and change the Project Name and Package Name according to your preferences. Ensure the Minimum Required SDK is no lower than 11 (we will be using Fragments). Make the Target API 19 (or a lower API level which is still compatible). Press Next.
3. Check the "Mark this project as a library" box and press Next
4. Modify the icons or just press Next
5. In the Create Activity screen, select Blank Activity with Fragment and press Next
6. Name the new Activity (MainActivity), adjust the names of the layout and fragment layout as you see fit, and press the Finish button
Creating 2 Other Projects:
1. Repeat steps 1 and 2 from above
2. Uncheck the "Create activity" box and the "Create custom launcher icon" box
Adding The TestApp Library:
Now that we have all 3 projects created, we need to add the TestApp Library Project to the TestAdApp and TestPaidApp projects. To do this, right-click on the TestAdApp/TestPaidApp project in the Package Explorer and go down to Properties. Go to the Android section and in the Library section, press the Add... button. Select TestLibrary from the list and press OK. Press Apply, and OK.
Preparing MainActivity:
The MainActivity.java file in the TestApp library should currently look like this:
We need to do a quick modification to include the ability to support both versions of our app. We will create a new method called setAdvertisements() and call that method in our onCreate() method:package com.example.testlibrary; import android.app.Activity; import android.app.Fragment; import android.os.Bundle; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (savedInstanceState == null) { getFragmentManager().beginTransaction().add(R.id.container, new PlaceholderFragment()).commit(); } } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } /** * A placeholder fragment containing a simple view. */ public static class PlaceholderFragment extends Fragment { public PlaceholderFragment() { } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_main, container, false); return rootView; } } }
... public void setAdvertisements() { } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); setAdvertisements(); if (savedInstanceState == null) { getFragmentManager().beginTransaction().add(R.id.container, new PlaceholderFragment()).commit(); } } ...
Modifying The TestPaidApp Project:
The paid app is the easy one to modify. All you have to do for this one is add your activities to the manifest. The MainActivity is inherited from the TestApp library and, since the setAdvertisements() method is empty, we don't actually have to modify anything. So, in the AndroidManifest.xml of the TestPaidApp project, we simply need to add our MainActivity:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.testpaidapp" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="19" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name="com.example.testlibrary.MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
Modifying the TestAdApp Project:
This is the project we need to heavily modify. First, we need to create an activity to override each of our other activites from the TestApp library.
Now we need to include the Google Play Service library in our TestAdApp project. Follow the steps provided in this tutorial to set that up: https://developer.android.com/google...ces/setup.html
Assuming you want all the AdViews to work the same, we can create a new class MyAdView in the TestAdApp project:
NOTE: You need an Ad Unit Id and the Hashed Id of your test device before you can successfully run advertisements. Also, remember to set the AdRequest to use the real device when you are ready to deploy.package com.example.test; import android.app.Activity; import android.widget.FrameLayout; import com.google.android.gms.ads.AdRequest; import com.google.android.gms.ads.AdSize; import com.google.android.gms.ads.AdView; public class MyAdView { /** The view to show the ad. */ private AdView adView; /* Your ad unit id. Replace with your actual ad unit id. */ private static final String AD_UNIT_ID = "INSERT_YOUR_AD_UNIT_ID_HERE"; public MyAdView(Activity context, int viewId) { this.adView = new AdView(context); this.adView.setAdSize(AdSize.BANNER); this.adView.setAdUnitId(AD_UNIT_ID); // Add the AdView to the view hierarchy. The view will have no size // until the ad is loaded. FrameLayout layout = (FrameLayout) context.findViewById(viewId); layout.addView(this.adView); // Create an ad request. Check logcat output for the hashed device ID to // get test ads on a physical device. AdRequest adRequest = new AdRequest.Builder().addTestDevice(AdRequest.DEVICE_ID_EMULATOR).addTestDevice("INSERT_YOUR_HASHED_DEVICE_ID_HERE").build(); // Start loading the ad in the background. this.adView.loadAd(adRequest); } public AdView getAdView() { return this.adView; } }
Then, we create a class which extends MainActivity. In this class, we will provide code for our setAdvertisements() methods, and we will handle the way the ad responds to activity state changes. We will create our AdView with the help of our MyAdView class. We will call our new class: MainActivityOverride
This pattern will need to be repeated for every activity you wish to include the banner ad on.package com.example.test; import com.example.testlibrary.MainActivity; import com.example.testlibrary.R; import com.google.android.gms.ads.AdView; public class MainActivityOverride extends MainActivity { private AdView adView; @Override public void setAdvertisements() { MyAdView myAdView = new MyAdView(this, R.id.container); this.adView = myAdView.getAdView(); } @Override public void onResume() { super.onResume(); if (this.adView != null) { this.adView.resume(); } } @Override public void onPause() { if (this.adView != null) { this.adView.pause(); } super.onPause(); } /** Called before the activity is destroyed. */ @Override public void onDestroy() { // Destroy the AdView. if (this.adView != null) { this.adView.destroy(); } super.onDestroy(); } }
The last thing we need to do is modify the manifest for our TestAdApp project. We will add the Activities the same way we did on the free version. However, we also need to add user-permissions to the app, as well as AdActivity configurations and Google Play Service metadata, in order for our advertisements to work. The updated AndroidManifest will look like this:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.testadapp" android:versionCode="1" android:versionName="1.0" > <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.INTERNET" /> <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="21" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name="com.example.test.MainActivityOverride" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name="com.google.android.gms.ads.AdActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"/> <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" /> </application> </manifest>
Conclusion:
You should now be ready to deploy and test your Ad-Free and Ad-Supported app versions.
So, what advantage does this provide? Well, from a maintenance standpoint, you now have significantly less work on your hands. You will still need to remember to update the AndroidManifests when you create a new Activity, and you will need to remember to create an override for that new activity in the Ad-Supported version, but you are not rewriting the entire user interface for each version. You can ensure the user interfaces between your two app versions are consistent, with the only difference being one having a banner ad, and the other not. When modifying existing activities, you simply need to modify your app's library project and you will be ready to deploy both versions at the same time without any additional code changes. You have also ensured the Paid version does not have any unnecessary dependencies (such as the Google Play Service) or extra code, which only the Ad-Supported version needs.
NOTE: This tutorial is still being revised to ensure proper accuracy and details. Please tell me if there is something I left out or if something is unclear.