Determining if user installed an app (AIR / AS3) - android

Ok, so I'm working on a mobile game using AIR and AS3. The idea here is that I want to provide an incentive for installing another game.
In other words:
user taps button in game A, brings them to app page for game B. User installs game B, goes back to game A, get's reward in game A.
Is there a way to detect that game B has been installed? The most likely way seems to be just detecting if game B is installed when game A resumes, but I can't seem to find a way of doing that using AS3/AIR.
I will likely also need to do this for both Android and iOS, which I assume will require different methodology.

There are a couple ways to answer this:
Custom URL Scheme:
https://developer.apple.com/library/ios/documentation/iphone/conceptual/iphoneosprogrammingguide/AdvancedAppTricks/AdvancedAppTricks.html
( the Communicating with Other Apps section)
This requires that the app you are attempting to up-sell does in fact implement a custom URL scheme, but it is a simple thing to implement. Once you have the URL Scheme, you can make a method that checks: canOpenURL:
Returns whether an app can open a given URL resource.
(BOOL)canOpenURL:(NSURL *)url
(in UIApplication)
If you want more significant data exchange (shared login, or some such) you could use the Keychain facility:
https://developer.apple.com/library/mac/documentation/security/conceptual/keychainServConcepts/iPhoneTasks/iPhoneTasks.html

You can do this with java by quering the PackageManager class. In Java, that would look something like this:
isAppInstalled("your.package.name.here");
private boolean isAppInstalled(String packageName) {
PackageManager pm = getPackageManager();
boolean installed = false;
try {
pm.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES);
installed = true;
} catch (PackageManager.NameNotFoundException e) {
installed = false;
}
return installed;
}
Since you are using AS3, you should have to build a native extension to access the PackageManager. Luckly, internet is great and someone else has done this already. you can check out this extension. Its 5 bucks, but totaly worth it. I used it to do a very similar logic.
Also, if app B is your own, you can add a new file in a SD card directory and check the existence of this file with app A.
Good luck :D

Related

Is a Trial-like behavior possible in android apps

I'm new to Android developement (I know very basic stuffs), and there is a chance that soon I'll be tasked with porting a WP7 app to Android (fortunately, I can use MonoDroid...).
Now that app has a trial functionality (see here), which for WP7 means that I can check whether the user bought it (so I can enable additional features inside the app) or downloaded the free edition. I do not want the trial to expire, I want a "free edition" of my app to be limited to certain features.
Is there anything similiar for Android? (And can it be done on MonoDroid?)
I've looked at Google Licensing Service, but I don't see how that helps me.
I would go for two apps solution. One "real" application, which contains all the functionality. Second "key" application which only check licensing.
First application will check if the key application is installed. If the check is positive then display full content, enable all features. If the key application is missing the application behaves like free version.
It is also very important to check if the private key that signed both applications is the same. Without this check someone might create their own key application and unlock your functionality. To do so consider this snippet, which I took from this blog: http://www.yoki.org/2010/07/31/creating-a-freepaid-app-pair-for-the-android-market/
protected boolean isProInstalled(Context context) {
// the packagename of the 'key' app
String proPackage = "org.yoki.android.pkgname";
// get the package manager
final PackageManager pm = context.getPackageManager();
// get a list of installed packages
List<PackageInfo> list =
pm.getInstalledPackages(PackageManager.GET_DISABLED_COMPONENTS);
// let's iterate through the list
Iterator<PackageInfo> i = list.iterator();
while(i.hasNext()) {
PackageInfo p = i.next();
// check if proPackage is in the list AND whether that package is signed
// with the same signature as THIS package
if((p.packageName.equals(proPackage)) &&
(pm.checkSignatures(context.getPackageName(), p.packageName) == PackageManager.SIGNATURE_MATCH))
return true;
}
return false;
}
This approach gives you few advantages in flexibility:
separate paid areas. You can assign sets of features to different key applications. eg. app key1 unlocks additional game levels a1,a2,a3 and app key2 unlocks levels b1,b2
time licensing - instead of only checking the existence of key application. You can query it to check if the licence is still valid. That way you can achieve time licences.
Probably the best way for you would be to use in-app purchases

"no activity found to handle intent" Not all phones have Android Market installed

Not all phones have Android Market installed, and therefore using intent to open market app fails.
What's the best way to handle this?
Hide this feature if user doesn't have Android Market installed (how would I detect this?).
Handle the possible error, how (and possibly suggest that the user downloads the Android Market)?
The problem with the answer above is that if you just pass a URL the user will be prompted how to handle the Intent.
A more graceful way to do it IMO, expanding upon the 1st answer above, is to test whether the market app is installed, and then if not, pass a URL (which actually you would then want to test to see if something can handle that intent, but if you happen to have a device without both the play store and a browser then I would question why the user would have my app installed in the first place (another story I suppose)....
Perhaps there is a better way, but here's what works for me:
private String getMarketURI(String marketURL) {
String returnURL = "";
PackageManager packageManager = getApplication().getPackageManager();
Uri marketUri = Uri.parse("market://" + marketURL);
Intent marketIntent = new Intent(Intent.ACTION_VIEW).setData(marketUri);
if (marketIntent.resolveActivity(packageManager) != null) {
returnURL = "market://" + marketURL;
} else {
returnURL = "https://play.google.com/store/apps/" + marketURL;
}
return returnURL;
}
And then use it like so:
marketIntent.setData(Uri.parse(getMarketURI("details?id=com.yourapps.packagename")));
If your app is being provided by Android Market, then it does have Android Market installed. :)
Okay that is snide, but there is an important truth -- Google goes to a lot of effort to enforce compatibility guarantees on devices for them to be allowed to ship with Android Market, so that is how you can know that whatever you are running on will behave as it should.
If you are delivering your app from something besides Android Market, you need to get information from whoever is delivering the app about what compatibility guarantees they have.
If they don't have compatibility guarantees (or you are just putting a raw .apk up on a web site or such), then you have a complete crap shoot. The device you are running on could have had its software modified in pretty much any way, and have any kind of differences in behavior you can imagine.
That said, if you want to determine whether there is an activity on the current device to handle a particular Intent, you can use this: PackageManager.resolveActivity
Use the web address as the intent target and then if there is no android market it will open in a browser.

Supporting Amazon and Android market (Google Play) links inside application

So one of my applications was rejected from the Amazon app store today. The reason was because inside my app, I linked to the paid version of the app on the Android market. Disappointing, but whatever, I guess everyone wants their cut...
So now I'm left having to modify the application to change the Android market link to an Amazon app store link. Not really a big deal except for now if I do that I'm left with a discrepancy when I want to upload a newer version back to the Android market. After all, it would be rather dumb to link someone to the Amazon app store if they purchase the app from the Android market.
Now we all know that it is a pain supporting/managing multiple versions of the same app. Consequently my question becomes how can I link to both at the same time? Is there a way to tell where an app was downloaded from so I can code both links into the app and thus point the user automatically to one or the other? Secondly, is it against the Amazon TOS to give the user a choice (say I pop up a dialog instead and ask the user where to download from)?
Thanks all.
Edit: Direct from Amazon customer service "Re: Link to both markets" (I wish the approval process was as fast as these guys):
For the time being, we need any
linking to point back to the Amazon
Appstore only for market links.
Linking to your website is allowed,
just not other markets.
When pointing to other apps from
within your app, including up-sells,
completion of purchase must be from
the Amazon Appstore.
Good news! Apparently the latest version of the Amazon store finally sets PackageManager.getInstallerPackageName() to "com.amazon.venezia" to contrast with Google Play's "com.android.vending". This will be the easiest way to determine if your app is sideloaded, installed from Amazon, or installed from Google.
Here's what you can do:
Complete the preparation and signing of your application.
Install it on your test device
Use PackageManager.getPackageInfo
How to do this:
public static boolean isMarket(Context context){
boolean isMarketSig = false;
int currentSig = 1;
try {
Signature[] sigs = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES).signatures;
for (Signature sig : sigs)
{
currentSig = sig.hashCode();
Log.i("MyApp", "Signature hashcode : " + sig.hashCode());
// This Log is for first time testing so you can find out what the int value of your signature is.
}
} catch (Exception e){
e.printStackTrace();
}
//-1545485543 was the int I got from the log line above, so I compare the current signature hashCode value with that value to determine if it's market or not.
if (currentSig==-1545485543){
isMarketSig = true;
} else {
isMarketSig = false;
}
return isMarketSig;
}
public static void openStore(Context context){
if (isMarket(context)){
Intent goToMarket = new Intent(Intent.ACTION_VIEW,Uri.parse("market://d" +
"etails?id=com.jakar.myapp"));
goToMarket.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(goToMarket);
} else {
Intent goToAppstore = new Intent(Intent.ACTION_VIEW,Uri.parse("http://www.amazon.com/gp/mas/dl/andro" +
"id?p=com.jakar.myapp"));
goToAppstore.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(goToAppstore);
}
}
Basically, the hashCode() that you get from the app installed on your testing device will be the same one from the market. The hash code from the app store will be different because according to https://developer.amazon.com/help/faq.html, the app store signs the application with a signature specific to your developer account, so that will return a different value that what you actually signed it with.
Note: It works to open the market successfully, but I haven't yet deployed this method on the App Store, so I haven't completely tested it. I am confident it will work, but can make no guarantees, so if you use what I've suggested and it fails, please don't hold me accountable.
You can do the following things:
Check if the device based on its Manufacturer.
For ex: https://developer.amazon.com/sdk/fire/specifications.html
For writing reviews and opening the Amazon App Store use the following intent
amzn://apps/android?p=package_name
where p=Link to the detail page for a specific package name.
Ref: Amazon developer link.
https://developer.amazon.com/sdk/in-app-purchasing/sample-code/deeplink.html
As you said, you could use a boolean and then be forced to build your apps twice, so I bevelive it's not the best way.
The best way is to check if android market is installed and act accordingly: here.
Another even more complex way is to query the name of the installer of your app, using PackageManager.getInstallerPackageName. This needs extra work since the app can be installed by parallel markets even if you are on android device with android market installed, and also you must check if it's installed as debug/development (installer package name is null in this case).
What a hard way to do this. If you just want to open the market url, just check if the intent with the url for android market has any activity that knows how to handle it. If not, open up the amazon appstore with another intent.
/**
* Returns intent that opens app in Google Play or Amazon Appstore
* #param context
* #param packageName
* #return null if no market available, otherwise intent
*/
public static Intent showApp(Activity activity, String packageName)
{
Intent i = new Intent(Intent.ACTION_VIEW);
String url = "market://details?id=" + packageName;
i.setData(Uri.parse(url));
if (isIntentAvailable(activity, i))
{
return i;
}
i.setData(Uri.parse("http://www.amazon.com/gp/mas/dl/android?p=" + packageName));
if (isIntentAvailable(activity, i))
{
return i;
}
return null;
}
public static boolean isIntentAvailable(Context context, Intent intent) {
final PackageManager packageManager = context.getPackageManager();
List<ResolveInfo> list =
packageManager.queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY);
return list.size() > 0;
}
Another way is to build using ant. This way you can generate dynamically a java class with constants set to a value that represent the app market, and output different builds with ease. This however takes some learning but when you have it running, it's very easy.
I was also struggling with this, but decided that the instant success I'm seeing with my free app on Amazon warrants the time to create a second set of .apks when I make a new build. I went with the Amazon boolean flag for now, and create one common version, then a version incremented by one for the Amazon market. No other markets demand internal linking, AFAIK.
I eventually intend to code up a market chooser with a slick way to automatically figure out what to do, but there are a lot of variables - not just the market address of one app, but how different markets recognize the company name to find all apps. Some market apps hijack the main Android market protocol (e.g. Appslib and SlideMe if I remember correctly) but don't store the company name the same way. Then you need to decide where upsell links go - to the same market or to a common one (I only submitted my free app to most of the markets I use).
I am waiting for my paid app to be approved, but I am pretty sure it will be worth it to have it available on the Amazon market given how many downloads I've gotten for my free app.
Just refactor most of your project into a Project Library, and then create multiple projects (e.g., for each app store) that have only unique icon and string resources, their own package ID declared in the manifest, and then a main activity that extends the main activity that you have defined in your library.
That way, all the unique URLs can be provided by overriding, in each particular app project's activity, the virtual or abstract methods defined in your library's main activity. The library code that that displays these URLs can obtain them via a polymorphic call to each of those methods.
That way, each such specialized project will be very small, and maintenance will be mainly to your library, as described here:
Multiple Apps with a shared code base

Write an application that can use plugins

I am trying to find a way to have plugins in an application.
My goal is to have a Core application, and offer plugins that can be downloadable on the market. (It can be anything, weather, radio player, etc...)
The plugins would not interact with each other, so the core application is more like a directory of multiple applications with kind of a SDK that the plugins use.
There is the Tic Tac Toe example in the Android doc, but the requires the main app to declare the external lib. My wish is that the core app detects the new installed plugins and shows them.
I found this other question but there is no answer.
Is there a way to do that?
Edit: There are also applications that can be unlocked by buying another app on the market. How do they work? I could not find anything interesting yet. You know what you find when you google "android unlock" :)
This is a little cleaner, so you don't have to use a try catch block.
Also, this avoids someone creating an app with the same name and manually installing it on their phone.
public boolean checkIfAllowed()
{
PackageManager pm = getPackageManager();
int match = pm.checkSignatures("your.first.package", "your.second.package");
if (match == PackageManager.SIGNATURE_MATCH)
{
Log.d("ALLOWED?", "signatures match");
return true;
}
else
{
Log.d("ALLOWED?", "signatures don't match");
return false;
}
}
You can use PackageManager to look for another application. If you know the package names of all of the 'plugins' then you can just check for each of them this way.
PackageManager pm = getPackageManager();
try {
ApplicationInfo appInfo = pm.getApplicationInfo("com.package.name.your.looking.for", 0);
//if we get to here then the app was found, do whatever you need to do.
} catch (NameNotFoundException e) {
//app was not found
}
if you want to decouple the main app from the plugins (using PackageManager.getApplicationInfo(package, int) MainApp has to know which package to search for) you can adopt this scheme:
at run time MainApp send a broadcast intent that every plugin has to listen to (by contract).
In response, every plugin send a direct intent to a component of MainApp that register information about available plugins and how to talk to them.
In this way, you don't have to update MainApp each time a new plugin is created.

Auto-Update for (private) Android apps

I'm developing a non-public Android app, i.e. the app won't be available in the global Android Market. The app will be installed on a limited number of clients, e.g. by using an apk file.
How can I enable an auto-update functionality in this app?
I see different potential options (I do not know if those are technically hard or even impossible to implement or if there are any existing functionalities that can be reused):
On each launch the app tests if a new version exists (by requesting a server), if so downloads the new apk and replaces itself with the new version.
Use (or develop?) a separated app or service that undertakes the update-check and replacement-process.
Use (or develop?) a private market app which has an auto-update option. This option is similar to the second one, but more generic: The market app would be connected to a repository, i.e. it would handle an arbitrary number of (private) apps.
I would prefer option one since the auto-update functionality is included in the app which needs less development efforts.
janjonas, in the company I work we had a similar problem with Windows Mobile 6.x, and we use pretty much the same solution pointed by EboMike:
The main app check if it's updated, against a WebService. It receives the current version & the URL from where download the new version, if necessary. The main app then start the Updater app, passing the URL, and quit.
The Updater do the download of the new program, via HTTP, showing to the user the % downloaded. The user can cancel the download anytime, in a controlled way, and the Updater can registry this cancellation.
Since the new app is downloaded, the Updater run the new app, and quit.
I think option one is the least amount of work for you, and actually the cleanest one too since it will go through the proper channel of using Android's built-in package installer which includes user notification and the option for the user to abort the installation if desired.
You already have it all outlined - check for a new version on a server (would be nice to give the user the option to turn that off), and if there is a new version, you could either just link to the URL with the APK (which will, IIRC, use the browser's download manager to download it), or you could download it with your app and then point the intent to your local file. Using the HTTP link is technically less work and cleaner - the more you let the operating system do, the better - unless there's a reason not to.
Enabling "Install non-market app" is still needed for any application outside the Google Play. If it not enabled, the installation process is going to ask for it and redirect the user to the Application Settings, and after that, the user can install the app.
Depending on your needs, you can delegate to a third part lib.
Some of the permissions we'll use to get this done are the following:
<uses-permission android:name="android.permission.ACCESS_SUPERUSER" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Let me explain a bit... The last, WRITE_EXTERNAL_STORAGE, is self-explanatory. With ACCESS_SUPERUSER we'll tell the system that we intend to use root privileges. READ_EXTERNAL_STORAGE will be needed in the future in order for your app to read files on SD card.
Assuming that you have downloaded the file and that all those devices can be rooted (limited number of clients, not on Play, etc.), you could do this:
String filePath = Environment.getExternalStorageDirectory().toString() + "/your_app_directory/your_app_filename.apk";
Process installProcess = null;
int installResult = -1337;
try {
installProcess = Runtime.getRuntime().exec("su -c pm install -r " + filePath);
} catch (IOException e) {
// Handle IOException the way you like.
}
if (installProcess != null) {
try {
installResult = installProcess.waitFor();
} catch(InterruptedException e) {
// Handle InterruptedException the way you like.
}
if (installResult == 0) {
// Success!
} else {
// Failure. :-/
}
} else {
// Failure 2. :-(
}
Here might be a very lame method but for some companies, if you believe its applicable, this might be very easy to implement.
Create an password screen (passwordActivity) that asks a password to access the application.
Once the password is entered, raise a flag (set a boolean value from false to true using sharedpreferences)
Place the .apk file on Google Store.
Change the password once everyone installs the app, and release a new update on Google Play Store.
Since the software is going to cache the flag value, the password screen won`t show up even the password is change. It will only show up for new installations so might need to repeat the process.
Note: This method might better fit if there is not hundreds of users using the application. And don`t forget this method is also not secure. To sum up, if you are looking a way to keep the application private and have no security concerns, this is what I recommend.
Update app
Make sure that you already have your new apk download on location
void installNewVersion(String location) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File(location + "app-debug.apk")),
"application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}

Categories

Resources