I have the following code to track when an app is launched for the first time on a device,
however it doesn't match the data I am getting from Google Analytics in the New Users category. Can anyone see anything in the code that could be unreliable? For example, today I see 3 installs from this code, but I have 5 new users who could only download this app from Google Play.
String INSTALL_SOURCE = "Google Play";
TelephonyManager tm;
tm = (TelephonyManager) getBaseContext().getSystemService(Context.TELEPHONY_SERVICE);
String INSTALL_COUNTRY = tm.getSimCountryIso();
prefs = getSharedPreferences("user_stats", MODE_PRIVATE);
boolean firstTime = prefs.getBoolean("isFirstTime", true);
if (firstTime) {
rentracker.trackEvent("Install Source", INSTALL_SOURCE, INSTALL_COUNTRY, 1);
Editor editor = prefs.edit();
editor.putBoolean("isFirstTime", false);
editor.commit();
}
Log.d(TAG, "Is this the first time?: " + firstTime);
String android_id = Secure.getString(this.getContentResolver(),
Secure.ANDROID_ID);
rentracker.trackEvent("App Startup - " + INSTALL_SOURCE, INSTALL_COUNTRY, "ID: " + android_id,1);
Can anyone see anything in the code that could be unreliable?
As one commenter suggested, just because this code exists does not mean that it actually has run, as the user may not have launched your activity yet. If you think that this code will somehow automatically run without the user's involvement, that is probably not the case on Android 3.1 and higher.
Presumably, rentracker is supposed to be communicating over the Internet, but not everyone has continuous Internet access. Hence, it may be that your code has run, but your back end has not found out about that yet, because the user was not online at the time.
You are assuming that the Play Store Developer Console reports downloads accurately and in a timely fashion. The Play Store Developer Console has not been the most reliable piece of software in human history, and so it is entirely possible that your comparison data is flawed.
Related
I recently started learning Android to port my iOS app to Android.
User registration in my app is optional, hence the user can decide to get started right away without any delay. However I still need a form of identification against fraud and if the user has passed the first week of free trial. Otherwise the user can just keep deleting and reinstalling the app to use it for free, forever.
On iOS I have solved the problem through the keychain. Any values stored in there remain there even after the app has been uninstalled.
import KeychainAccess
let keychain = Keychain(service: Constants.keychainServiceID).synchronizable(false).accessibility(.alwaysThisDeviceOnly).accessibility(.alwaysThisDeviceOnly)
let deviceId = UUID().uuidString.lowercased()
keychain["DEVICE_ID"] = deviceId
I don't seem to find anything like that on Android.
A unique Device ID doesn't seem to be available either based on this answer
Hence is there a way to achieve this or do I have to make user registration mandatory?
UPDATE:
In iOS when a keyChain value is set, the user can't ever update or delete it. Even after the app is uninstalled. During unit testing I can delete the keychain entry like this:
let keychain = Keychain(service: Constants.keychainServiceID).synchronizable(false).accessibility(.alwaysThisDeviceOnly)
do {
try keychain.remove("DEVICE_ID")
} catch let error {
print("error: \(error)")
}
Can I do that in Android?
More easily you can do like this, this is a wrapper on shared preference.
https://github.com/kishandonga/EasyPrefs
public static void setUniqueId(){
if(!Prefs.read().content("is_unique_id_set", false)){
String id = UUID.randomUUID().toString();
Prefs.write().content("unique_id", id)
.content("is_unique_id_set", true)
.commit();
}
}
public static String getUniqueId(){
return Prefs.read().content("unique_id", "");
}
Cases when unique id changed
App uninstalled and then reinstalled
Clear cache or reset
I use the following code to forward sms, but a system prompt message displayed, how can set system preference programmatically let prompt message don't display?
private static void ForwardSMS(MSms myMSms, MRule rule) {
SmsManager smsMgr = SmsManager.getDefault();
for (String toAddress : rule.receiverNumberList) {
smsMgr.sendTextMessage(toAddress, null, myMSms.body, null, null);
SharedPreferences prefs = PreferenceManager
.getDefaultSharedPreferences(PublicPar.myContext);
if (prefs.getBoolean("SaveSentBox", true)) {
SaveToSentBox(toAddress, myMSms.body);
}
}
}
This kind of prompt is for the users' own protection, and the Android API does not allow developers to disable it. So in short: There is no way to do this legitimately.
See, The image you have pasted here is asking the user about giving the permission to the app for sending SMS which may cause him money. Wouldn't you like to be warned in such cases where you can loose your charge because of the App.? So the Android doesn't provide any authority to the developers by which they can harm users credits or charges without even telling them.
But yes, as you can see there are options provided to "Remember the Choice". That means if users is aware of this fact once, and allows the app by clicking "Always Allow", next time there will be no popup to the user before sending the SMS.
So no matter what the app is, the Android API will always be loyal to the user and tell him about the loss me may suffer because of any App.
I want to make an application which has to be very secured. So f I install an apk in a phone it should only work in that phone. When apk is shared it should not work.Can any one help me in implementing this.
One idea from my side is using an algorithm to generate password using device mac address and so the password won't work for two different devices to log in.Is there any way to get the MAC address( or something unique to device) in android from java? .Expecting alternate solutions!!
If you are distributing the app through Google play store, you can make use of the Google Play Application licensing.
You may read Identifying App Installations on Android Developer's Blog for a discussion about how to uniquely identify a device.
I implemented with IMEI number. So my apk is designed based on IMEI number.So it will check whether IMEI given matches with the device IMEI then only it will launch the new Activity, else it will exit.
TextView tx = (TextView) findViewById(R.id.tx);
String ts = Context.TELEPHONY_SERVICE;
TelephonyManager mTelephonyMgr = (TelephonyManager) getSystemService(ts);
String imei = mTelephonyMgr.getDeviceId();
if (imei.equals("<what we given inthe source>")) {
// Launch the activity
} else {
// show an alert dialog and exit
}
I have done an small App in Android, which I want to set as trial version for 2 days, after 2 days it should ask for a key. If the user uninstalls the App after expire date, he shouldn't be able to use the App without a key...
An idea could be release two versions of the app. One free version with some limitations or Ad's in it and another paid version of the app.
You need to setup a database which will be online assign a specific id to each device. If you are going to save the date of install in the SharedPreference or any local memory, the user can just remove it from his phone by simple clearing all the application data. He wouldn't have to uninstall it even.
So, every time a new user installs your application, you need to store the date of install corresponding to each device in your online database. Everytime the application is started, you need to ask the server for the date of install corresponding to that device and let the user proceed iff it is in the trial period, otherwise ask him for the key there.
I don't know, how safe/secure it would be, but you could use SharedPreferences to store this data. Just a really small example:
On startup:
SharedPreferences prefs = getSharedPreferences("AppName", Context.MODE_PRIVATE);
boolean firstRun = prefs.getBoolean("first_run", true);
long currentTime = System.currentTimeMillis();
if(firstRun)
{
SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean("first_run", false);
editor.putLong("first_run_time", currentTime);
editor.commit();
}
else
{
long firstRunTime = prefs.getLong("first_run_time", 0);
long twoDays = 2 * 24 * 60 * 60 * 60 * 1000;
if(currentTime - firstRunTime > twoDays)
{
//Expired
}
else
{
//Not yet expired
}
}
Please note, that if the user uninstalls the app, and then reinstalls it, or simply deletes the applications stored data, the "timer" will reset!
A safer method would be to store this data in an online database, but thats more complicated to solve, and it would require internet connection all the time to check if the app is expired.
Just use Android Licencing. It's pretty straight forward and awesome.
Anything more secure than that would be so much trouble. I don't think it's worth the effort for most apps.
http://developer.android.com/google/play/licensing/index.html
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Supporting Amazon and Android market links inside application
I was wondering if and how you could differentiate between an Amazon App Store installed app and one installed from the Market.
For example, say I have my app called "Example App", and I want to develop for Amazon and the Market. In the app I have links to rate Example App. I also have a link to buy Example App Pro. This poses a problem because Amazon will not release my app if it links to a different App store.
This requires me to make 2 APK files, which is a pain. It only takes about 30 seconds extra to export both, but it creates extra clutter and testing time.
So has anyone found a way to make a single APK that can be uploaded to both Amazon and Android Market without making any changes between the two so that at run time I can check whether it's the Amazon or the Market that installed it and change the links accordingly?
Edit: At the time of this post, I wasn't aware of it, but there does exist getInstallerPackageName() but I'm not sure how reliable that is. I'm also not sure what it returns for Amazon / Market, etc. It might be worth looking at, but if it doesn't work, then the below method works for Google vs Amazon.
You will have to sign the application as normal, run on your test device, get the value of sig.hashCode() from your logs, then replace -1545485543 with whatever value you got for sig.hashCode() then export and sign again (WITH THE SAME KEY AS BEFORE) and then upload to Amazon and Market both - from the same APK.
Do it:
public static boolean isMarket(Context context){
boolean isMarketSig = false;
int currentSig = 1; // I just set this to 1 to avoid any exceptions later on.
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.
And I put the isMarket and openStore methods in a different class called OtherClass so that I only have to code it once. Then from any activity where I need to open the proper link, I just call OtherClass.openStore(context);
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.
This was a big help in coming up with an answer so I could test which signature was being used.