Paid and Free App using Android Library Project - How to code effectively? - android

I am planning to release a paid version of my free android app using the android library project approach.
http://developer.android.com/tools/projects/index.html
My app has several stand-alone (non-user interface) classes and resources that are easy to reuse between the paid and free version.
My question is about the best way to manage the user interface logic (code in Activity classes). Let's say my free app has one button and my paid app has two buttons in the same activity. Is the best way to achieve this is to have the following setup?
Android library project
1)Layout with one button
2) an Ativity.java file containing logic for when the button is clicked
Free app
Use layout and source code from the library project
Paid app
1) A new layout file with two buttons
2) A new Activity.java which has the exact same code for handling button1 clicks and new code for handling button2 clicks.
This does not seem right because button1's logic in paid app seems to be a wasteful copy ... Is there a better way to do this?

You can make a single project library with all the functionality
Just you need a one method that can identify that if application is paid or free
For that follow the steps
1) Create a new application suppose testFree
2) Create a new Application Class as follow in the library project
package com.example.testlib;
import android.app.Application;
public class App extends Application{
private static App mInstance;
public App() {
mInstance = this;
}
public static App getInstance() {
return mInstance;
}
public boolean isFree()
{
return true;
}
}
3) create a new application suppose testPaid
4) create a new Application class in the testPaid Application as Follow
package com.example.testpaid;
import com.example.testlib.App;
public class AppPaid extends App {
#Override
public boolean isFree() {
// TODO Auto-generated method stub
return false;
}
}
5) set Application name on testFree app to Application class that we created on the library class and also set main and launcher activity from library class
<application
android:name="com.example.testlib.App"
....
<activity
android:name="com.example.testlib.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>
6) now set the application name to create application class of testPaid app and also main and launcher activity from library project as follows
<application
android:name="com.example.testpaid.AppPaid"
....
<activity
android:name="com.example.testlib.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>
7) All you have set now in any class of library project you have a method that will check if you app is free or paid you can check as following way and based on that you can make visible some paid functionality to paid app and some free to free
if(App.getInstance().isFree())
{
Toast.makeText(getActivity(), "Free App", Toast.LENGTH_SHORT).show();
}
else
{
Toast.makeText(getActivity(), "Paid App", Toast.LENGTH_SHORT).show();
}
Let me know if you still find any problem.....

Related

App not being recognized by Android Auto

So, I followed the official guide here https://developer.android.com/training/auto/start/index.html to create a very basic Android Auto Audio App. For the moment it does nothing, other then declaring what needs to be declared in the manifest and implementing empty onGetRoot() and onLoadChildren().
Problem is, that it is not being recognized by the Android Auto app.
Any idea where to get a working example? What could be wrong?
Manifest:
<service
android:name=".MyService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name=
"android.media.browse.MediaBrowserService"/>
</intent-filter>
</service>
automotive_app_desc.xml:
<automotiveApp>
<uses name="media" />
</automotiveApp>
service:
public class MyService extends MediaBrowserServiceCompat {
public static final String MEDIA_ID_ROOT = "__ROOT__";
#Override
public BrowserRoot onGetRoot(String clientPackageName, int clientUid,
Bundle rootHints) {
//TODO: check if the client is allow access
return new BrowserRoot(MEDIA_ID_ROOT, null);
}
#Override
public void onLoadChildren(final String parentMediaId,
final Result<List<MediaBrowserCompat.MediaItem>> result) {
// Assume for example that the music catalog is already loaded/cached.
List<MediaBrowserCompat.MediaItem> mediaItems = new ArrayList<>();
// Check if this is the root menu:
if (MEDIA_ID_ROOT.equals(parentMediaId)) {
// build the MediaItem objects for the top level,
// and put them in the mediaItems list
} else {
// examine the passed parentMediaId to see which submenu we're at,
// and put the children of that menu in the mediaItems list
}
result.sendResult(mediaItems);
}
You have to go to Android Auto settings, tap many times on the Version entry (the last one) to unlock Developer settings. Then tap on Developer settings menu item and enable Unknown sources. Restart Android Auto and if your app it's ok it will be listed. Worked for me
I didn't see this included in your snippet from the manifest, but double check that this line is also there.
<application>
...
<meta-data android:name="com.google.android.gms.car.application"
android:resource="#xml/automotive_app_desc"/>
...
</application>
I created a sample app matching everything you have (plus the line above), and it appears in Android Auto on the mobile device, as well as the Desktop Head Unit.

Branch.io and Instant Apps

I have an app that's using Branch.io and am in process of updating it to support Instant Apps. Part of this change will require using app links to navigate from one part of app to another.
The Activity that I'm opening is configured to use Branch.io (e.g. using instructions in https://dev.branch.io/marketing-channels/android-instant-apps/guide/) and includes following in it's onStart() method. However I'm not seeing parameters I'm including in deep link in referringParams.
Branch.getInstance().initSession(new Branch.BranchReferralInitListener() {
#Override
public void onInitFinished(JSONObject referringParams, BranchError error) {
Log.d("Branch", "onInitFinished() with deep link data: " + referringParams);
}
});
Do I need to trigger opening of the branch link in particular way from Android code for this to work?
BTW referringParams above does show +clicked_branch_link being false.
UPDATE
Just to clarify a few things. I'm trying for example to launch ActivityB from ActivityA using app deep link. ActivityB includes <intent-filter> as described in https://dev.branch.io/marketing-channels/android-instant-apps/guide/ for example. In ActivityA I'm currently trying to open/create Branch.io link as follows (have also formed link directly, as is used in that android-instant-apps sample for example, but that's not considered a "branch link")
HashMap<String, String> metadata = new HashMap<>();
metadata.put(PARAM, param);
BranchUniversalObject branchUniversalObject = new BranchUniversalObject().addContentMetadata(metadata);
LinkProperties linkProperties = new LinkProperties();
branchUniversalObject.generateShortUrl(context, linkProperties, (url, error) -> {
if (error == null) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
context.startActivity(intent);
}
});
If I try to open the url generated (by generateShortUrl) directly on device (clicking on link for example) then ActivityB is launched and I see the param I included in initSession callback. If I try to open it using code above (navigating betweeen ActivityA and ActivityB then ActivityB is launched but don't get the params (and +clicked_branch_link is false)
Sojan from Branch here
If you are trying to deep link to a new feature from another instant app apk unfortunetely Branch is not supporting this feature now.
If you are trying to get deep link params on opening an activity B in a new feature from activity A from another feature in an installed app you can achieve it in the following way.
ActivityA.java
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("yourBranchLink"));
intent.putExtra("branch","yourBranchLink");
intent.putExtra("branch_force_new_session",true);
Hope this helps
In order to configure the Branch SDK with your Instant App supported Android App you can follow the steps below:
1. Initialize the Branch SDK
Initialize the Branch SDK in the onCreate() method of your Application class. If you plan on deep linking from your Android Instant App to your full Android app after its installed, you'll need to add the line enablePlayStoreReferrer. This adds a delay to the initialization to wait for the Google Play Referrer, which can take up to a second.
public void onCreate() {
super.onCreate();
// This is needed to deferred deep link from an Android Instant App to a full app
// It tells the Branch initialization to wait for the Google Play Referrer before proceeding.
Branch.enablePlayStoreReferrer(1000L);
// Initialize the Branch SDK
Branch.getAutoInstance(this);
}
2. Add your Branch keys and register for Install Referrer
Instant Apps can be rather confusing as there are many different manifests, but you want to find the Manifest that contains your application tags. Make sure your Application class name is defined here, and then specify the Branch keys inside the application element.
<application
android:allowBackup="true"
android:label="#string/app_name"
android:theme="#style/AppTheme"
android:supportsRtl="true"
android:name=".MyApplication">
<!-- Set to true to use Branch_Test_Key -->
<meta-data android:name="io.branch.sdk.TestMode" android:value="false" />
<meta-data android:name="io.branch.sdk.BranchKey" android:value="key_live_app_live_key" />
<meta-data android:name="io.branch.sdk.BranchKey.test" android:value="key_test_app_test_key" />
<receiver android:name="io.branch.referral.InstallListener" android:exported="true">
<intent-filter>
<action android:name="com.android.vending.INSTALL_REFERRER" />
</intent-filter>
</receiver>
</application>
3. Configure your Branch links as Android App Links
Assuming you've already configured Branch for Android App Links for your Android App the next step is to add the intent filter for App links support in your Application Tag. Make sure to replace the xxxx with your link Domain. (If you haven't configured your full native app to use Branch as Android App Links you can follow the steps mentioned here and here.)
<application
......
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" android:host="xxxx.app.link" />
<data android:scheme="https" android:host="xxxx-alternate.app.link" />
</intent-filter>
</application>
4. Retrieve Branch deep link data
Add Branch initSession in Activities which are configured to open from a link click in order to receive the deep link params. This will return the deep link data from the referring link.
protected void onStart() {
super.onStart();
Branch.getInstance().initSession(new Branch.BranchReferralInitListener() {
#Override
public void onInitFinished(JSONObject referringParams, BranchError error) {
Log.d("Branch","onInitFinished() with deep link data: " + referringParams);
}
});
}
5. Configure the deep linking from Instant App to your Full App
To convert your user who just arrived in your Instant App to your full native app, Branch SDK provides convenient methods to check for app types and full app conversion. This eliminates the dependency on Google IA support SDK ('com.google.android.instantapp'). Here are some of the methods:
Branch.isInstantApp()
This convenience method checks whether the current version of app running is Instant App or Full Android App
Branch.showInstallPrompt()
This methods shows an install prompt for the full Android app, allowing you an easy way to pass Branch referring deep data to the full app through the install process.

Using accessibility

I want to create an android app which will "scan" other applications'layouts and find out if there are buttons etc.
Some apps, like whatsapp, doesn't allow third parts to move into the app, so I though that using accessibility could be the solution.
Now the problem is that I've never used Accessibility, so can someone of you can please show me how to "scan" an app's layout to find buttons?
Thanks a lot
What you're looking for is an Accessibility Service. Configuring an accessibility service is somewhat complicated. I have set up a repository of accessibility boilerplate code that sets up an AccessibilityService that logs the node heirarchy to LogCat, and has the default project settings activity set up as it's settings activity. Here are some of the highlights, as I dislike just posting github repos as answers. Note that I use a lot of my own libraries. CLog is a logging library and AndroidAccessibilityUtils wraps node infos with some common utility functions. You can find references to the dependencies in the build.gradle file on the github repo at the bottom. Here are some code highlights.
Your manifest.xml file is going to be significantly different from an Activity, and should contain an entry like this in your Application element:
<application .... >
...
<service
android:name="com.moba11y.basicaccessibilityservice.BasicAccessibilityService"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE" >
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<meta-data
android:name="android.accessibilityservice"
android:resource="#xml/service_config" />
</service>
...
</application>
First you need to create a subclass of Accessibility Service:
public class BasicAccessibilityService extends AccessibilityService {
static {
CLog.initialize(BasicAccessibilityService.class.getSimpleName(), BuildConfig.DEBUG);
}
#Override
public void onAccessibilityEvent(AccessibilityEvent event) {
CLog.d(event.toString());
switch (event.getEventType()) {
//On Gesture events print out the entire view heirarchy!
case AccessibilityEvent.TYPE_GESTURE_DETECTION_START:
CLog.d(A11yNodeInfo.wrap(getRootInActiveWindow()).toViewHeirarchy());
default: {
//If the event has a source, let's print it out separately.
if (event.getSource() != null) {
CLog.d(A11yNodeInfo.wrap(event.getSource()).toViewHeirarchy());
}
}
}
}
#Override
public void onInterrupt() {
CLog.e("Service Interrupted: Have never actually had this happen.");
}
}
That is the bulk of the highlights. You also should have a "service_config" XML with properties, as is referenced in the changes to the Manifest XML file. More details, and a reasonable starer point can be found in the open source repo on GitHub.
https://github.com/chriscm2006/Android-Accessibility-Service-Boilerplate

Can I pass in a member variable for an Android Service to use?

I would like to reuse some service layer code in another project. I do want to be able to change one of the member variables the service uses, without having different service code. I don't see how this can be done as I don't "create" the service; the manifest and the system do that. Thus I can't pass something in a constructor.
I have a hard time finding the write words to ask this question, so perhaps a quick example is best. I have a service, such as:
public class MyService extends Service implements MyCommunicatorListener {
ICommunicator _comm = new BlueToothCommunicator(); // great for project 1, not for project 2!
public void shutUpCommunicator() // example of using communicator
{
_comm.Shutup();
}
// MyCommunicatorListener methods
#Override
public void onPageReceived(Page page) { // example of listening to communicator
Log.d(TAG,"onPageReceived");
}
} // class greatly simplified...
All works well. The service is started my virtue of the fact that it's declared in the manifest:
<service android:name="com.acme.servicelayer.MyService" android:process=":remote" >
<intent-filter>
<action android:name="com.acme.servicelayer.MyService" />
<action android:name="android.intent.action.BOOT_COMPLETED" />
<category android:name="android.intent.category.HOME" />
</intent-filter>
</service>
I'd like to keep this service code and reuse it another project. In the other project, I don't want to use:
ICommunicator _comm = new BlueToothCommunicator(); // great for project 1, not for project 2!
I want to use:
ICommunicator _comm = new WiFiCommunicator(); // great for project 2, not for project
1!
How can I do this and keep the same code base? Is there someway to pass arguments through the manifest?
Thanks,
Dave

Crash in Android Native while changing to new Activity

My application is unable to open OpenFeint dashboard methods. The implementation of the native c++ libraries uses cocos2d-x as a graphic library, but it has a handler and a wrapper to allow the use of OpenFeint functions. OpenFeint initialization and non-activity methods work correctly.
When UI dashboard functions such as openLaderBoards or openAchievements are called either from a Jni call or in the Java onCreate initialization, the application crashes.
EDIT: I have tested and it happens to any Activity change I try, even my own new classes.
EDIT2: I have a +100 bounty in a similar question, anyone who comes up with the answer gets it.
Code
Activity:
public class App extends Cocos2dxActivity{
private Cocos2dxGLSurfaceView mGLView;
OpenFeintX m_kOpenFeintX;
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
// get the packageName,it's used to set the resource path
String packageName = getApplication().getPackageName();
super.setPackageName(packageName);
InternetConnection.setM_kActivity(this);
m_kOpenFeintX = new OpenFeintX( this);
setContentView(R.layout.applayout);
mGLView = (Cocos2dxGLSurfaceView) findViewById(R.id.game_gl_surfaceview);
mGLView.setTextField((EditText)findViewById(R.id.textField));
// Testspace for new Activities, OpenFeint or self-made
//
// Intent myIntent = new Intent(this, TestActivity.class);
// startActivityForResult(myIntent, 0);
// Dashboard.open();
// Cocos2d-x scene opens after this
}
static {
System.loadLibrary("TestProject");
// Native library loaded for cocos2d-x
}
Wrapper:
public class OpenFeintX {
private static OpenFeintXHandler ms_kOpenFeintHandler;
public OpenFeintX(Activity kActivity) {
initializeOpenFeint("TestApp", "derp",
"hurr", "6546516516541",
kActivity, ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
ms_kOpenFeintHandler = new OpenFeintXHandler();
}
public static void openLeaderBoards() {
Message msg = new Message();
msg.what = OpenFeintXHandler.SHOW_LEADERBOARDS;
ms_kOpenFeintHandler.sendMessage(msg);
}
Handler openDashboard function:
private void openLeaderBoards() {
System.out.println("Opening Dashboard");
Dashboard.openLeaderboards();
}
Manifest:
<application
android:debuggable="true"
android:label="#string/app_name">
<activity
android:configChanges="orientation"
android:label="#string/app_name"
android:name=".App"
android:screenOrientation="portrait"
android:theme="#android:style/Theme.NoTitleBar.Fullscreen" >
<intent-filter >
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.openfeint.internal.ui.IntroFlow"
android:label=".IntroFlow"
android:configChanges="orientation|keyboardHidden"
android:theme="#style/OFNestedWindow" />
<activity android:name="com.openfeint.api.ui.Dashboard"
android:label=".Dashboard"
android:configChanges="orientation|keyboardHidden"
android:theme="#style/OFNestedWindow"/>
<activity android:name="com.openfeint.internal.ui.Settings"
android:label=".Settings"
android:configChanges="orientation|keyboardHidden"
android:theme="#style/OFNestedWindow"/>
<activity android:name="com.openfeint.internal.ui.NativeBrowser"
android:label=".NativeBrowser"
android:configChanges="orientation|keyboardHidden"
android:theme="#style/OFNestedWindow"/>
</application>
Stacktrace (won't indent in SO):
http://pastebin.com/jsmSbgw4
The answer was easy but complex. When the app changes to a new activity, the nativeOnPause method from cocos2d-x MessageJNI is called. This method is supposed to call a CCApplication::sharedApplication(), but one of my classes had previously called the CCApplication destructor, which cleared the shared singleton to null.
Even though the answer is easy to fix and project specific, I am going to give some advice on what made me find it. Keep in mind that all my tools are windows and cygwin.
First, make Eclipse do the ndk-builds for you on clean.
Rightclick your Project-> Properties -> C/C++ Build. Builder Settings tab, Command
C:\NVPACK\cygwin\bin\bash.exe -c "cd /cygdrive/path/to/project && ./build_script.sh"
Second, setup your debugger.
Using this tutorial. It may take some time and attempts to get there but it is worth it. My adb-server call script is
export ANDROID_NDK_ROOT=/cygdrive/c/NVPACK/android-ndk-r6b/
cd $ANDROID_NDK_ROOT
./ndk-gdb-eclipse --adb=/cygdrive/c/NVPACK/android-sdk-windows/platform-tools/adb --project=/cygdrive/c/project/path --force --verbose
Third, set your logcat in verbose.
UPDATE: This last step can now be skipped as latest versions of Logcat output the class and line for the whole stacktrace.
You will get long stacktraces like the one in my question. To check where the stacktrace errors point follow this other tutorial. My script to access the library is
C:\NVPACK\android-ndk-r6b\toolchains\x86-4.4.3\prebuilt\windows\bin\i686-android-linux-addr2line.exe -C -f -e c:\path\to\project\libMyLib.so

Categories

Resources