2

I need to create a free and paid version of the app and therefore I am using a library project with the common activities and resources in. However I was wondering how you would go about organising this:

A shared (library) activity MyActivity.java launches a DialogFragment when a user clicks a buttons. The DialogFragment will be different for the free and paid apps. For the free app we will launch FreeDialog.java and for paid we will launch PaidDialog.java. These two dialog fragments will not be in the library project as they are not shared; they will be in the free and paid projects separately.

The only thing I can think is to make MyActivity.java abstract and to extend it in the free and paid projects. The extensions will launch their respective DialogFragments.

Another way could be to overload the Fragments in each of the projects. However, I am not sure if this is possible.

James Cross
  • 7,799
  • 8
  • 24
  • 30

1 Answers1

3

Theoretically, you could make MyActivity abstract, and subclass it in both paid and free app projects. But, Activity classes are a little different than normal Java classes, which complicates this. They are listed in AndroidManifest.xml, and then also are not created by simply newing them up. They're normally created with Intents. So, I think I would stay away from making the Activity an abstract class.

I think what you really want is to use is something like the Factory pattern to create an instance of DialogFragment, where your common library does not know which DialogFragment will be created. You can get pretty fancy with a factory, and you can read all about that elsewhere, but a simple one might work for you:

public class FragmentFactory {

   private static String _dialogFragmentClassName;

   public static void registerFragment(String className) {
      // you might choose to assert if _dialogFragmentClassName != null
      _dialogFragmentClassName = className;
   }

   public static DialogFragment createFragment() {
      try {
         return (DialogFragment) Class.forName(_dialogFragmentClassName).newInstance();
      } catch (Exception e) {
         return null;
      }
   }
}

Then, in your free and paid app project code, you would issue calls like this at startup (e.g. in a main Activity):

FragmentFactory.registerFragment(com.mycompany.free.FreeDialogFragment.class.getName());

and

FragmentFactory.registerFragment(com.mycompany.paid.PaidDialogFragment.class.getName());

Finally, in the common library code, you can create an instance of the fragment by calling

DialogFragment fragment = FragmentFactory.createFragment();
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.fragment_content, fragment);
ft.commit(); 
Nate
  • 31,017
  • 13
  • 83
  • 207
  • Wow, a very elegant solution. Thank you for that. – James Cross Jun 24 '12 at 12:24
  • @Styx, You're welcome. I built little pieces of it to convince myself of the general idea, but didn't build a full system of lib, and two apps. So, if anything doesn't work right, please let me know and I'll update the post. Oh, and you probably shouldn't catch all Exceptions in createFragment(), but I figure you can hunt down which Exception subclasses that code can actually throw. Good luck! – Nate Jun 24 '12 at 12:30
  • 2
    I made a slight alteration using Class> objects instead of Strings: http://pastebin.com/7UjT9v45 – James Cross Jun 24 '12 at 14:00
  • @Styx, yep, i think in general, that's the best way to go. i probably picked strings for simplicity, but a Class is really what you're registering – Nate Jun 24 '12 at 20:00
  • This is very nice, but to be perfectly candid I wouldn't go to the trouble, as it is very simple just to test the package name in the library and then put up one dialog vs. the other (with both dialogs defined in the library code). True, it is a little extra bloat in your app to have to store both dialogs, but unless you have a strong reason to *not* want to even include the paid dialog in the free app (e.g., proprietary content), it is the rock bottom simplest solution. A case that really would make a difference would be raw resource files, since they are often quite large. – Carl Jul 01 '12 at 07:49
  • @Carl, if you re-read the question, the problem was posed with the constraint that his two Dialogs are **not** in the same project. So, if you accept that constraint, your suggestion is out. Also, I find it unlikely that the poster (or most people reading this question) will really be in the situation where the **only** difference between a free/paid app version is one `Dialog`. Usually, there's more than that ... maybe some image files, etc. And as the title of his question even included the word *architecture*, I think using a more scalable solution is appropriate. – Nate Jul 01 '12 at 09:12
  • @Carl, in addition, the solution I posted adds like 5 sloc, all of which I spoon feed to you above. That qualifies as "trouble"? I also strongly disagree with the tendency of developers, especially on Android (where so many users have devices with limited storage for apps), to allow bloat in their apps. Size matters. If it's just one Dialog class, sure, it's not a big deal. But, as I said before, usually there's more than that. Using libraries is a scalable solution, and should **usually** be considered *best practice*. – Nate Jul 01 '12 at 09:18
  • @Nate: Those are all good points, and my comment was not intended as a criticism of your answer. I'm also aware that my approach does not address the issue but rather circumvents it; it's a comment, not an attempt to answer the OP's question as such. It may be that my comment reflects disappointment at not yet having found a way to do something similarly nice for raw resource files, which is on my own to do list at the moment :-). – Carl Jul 01 '12 at 12:03
  • Looking over your answer again, it is a very straightforward and proper approach. I regret and retract my earlier remarks; it's worth finding a proper approach because, as you say, you may have to scale it to deal with multiple dialog variants or other app-specific differences. And incorporating both dialogs into the library would of course violate modularity. – Carl Jul 01 '12 at 12:40
  • @Carl, no need to regret/retract :). I simply made no attempt to assess the validity of using a lib in the first place, because that wasn't how the question was posed, and the poster may not have included all the rationale they had for picking a lib architecture. However, I'd say that your comment would be a better comment **on the question itself**, or perhaps a second **answer**. If you suggest that for apps with few differences between versions, and no bloat concerns, another simpler approach would be what you described. If you qualified it that way, I'd even upvote that answer! – Nate Jul 02 '12 at 20:42
  • @Nate: Inspired in part by this discussion, I left some comments here that touch upon those very issues: http://stackoverflow.com/questions/11158164/android-free-and-paid-with-different-functionality/11219200#11219200 – Carl Jul 03 '12 at 11:52