Android Tutorial Trial Tutorial

Hey everyone,

Here I will explain how you can make an evaluation/trial version of your app using an online database.
With this, the user will be able to open up the app x times. When 0 is achieved, it will force close the app.

First you'd need some basic knowledge of HTML, PHP, SQL and ofcourse Basic4Android.
I will not explain on how to setup your directory files, SQL Database, etc.

Here's how it works:

Before the app runs (in globals) I send my IMEI with a POST Method to a php webserver. I do this with one code with a library I have written, instead of using HTTPUtils etc.
The database exists of three columns:
id, IMEI, starts.

The PHP file looks in the database if the IMEI exists, if it exists, it looks for the amount in the third column and substracts 1. If it doesn't exists, a IMEI.txt file is generated. The file contains how much the app can be opened. 30 here.
If 0 is succeeded, the text Expired appears.

In my app, a timer runs during 6 seconds. This is done to process everything server side. When the timer ticks, I perform a http GET to the IMEI.txt (its not IMEI.txt but e.g. 59644885484.txt). If the response string is an amount, then the app runs, otherwise if it's "Expired", then the app is closed.

Some code:

B4X:
Sub Globals
        ProgressDialogShow("Trial Activated" & CRLF & "Connecting with webserver for authentication")
   Dim p As PhoneId
   Dim cantopen As Boolean
   cantopen = False
   Dim camera1 As AdvancedCamera
   Dim strActivationCheck As String
   strActivationCheck = "http://www.example.com/" & p.GetDeviceId & ".txt"

'Small password protection, PHP File on webserver, IMEI, Trials
' change the url to the url of your site.
'You should download the latest version of my Advanced Camera Library to use 'AppTrial' because it is a lot easie for me this way instead of working with HTTPutlils.
   Camera1.AppTrial("Password","http://www.example.com/trialCheck.php",p.GetDeviceId,30)

   tmrEvaluation.Initialize("tmrEvaluation",6000)
   tmrEvaluation.Enabled = True

End Sub

Sub tmrEvaluation_tick
   ProgressDialogShow("Checking Activation.")
   tmrEvaluation.Enabled = False
   Dim request1 As HttpRequest
   request1.InitializeGet(strActivationCheck)
   request1.Timeout=50000
   If HttpClient1.Execute(request1, 1) = False Then
   Return
   End If
End Sub

Sub http_ResponseSuccess (Response As HttpResponse, TaskId As Int)
   ProgressDialogHide()
   If TaskId = 1 Then
   Dim strActivationResponse As String
   strActivationResponse = response.GetString("UTF8")
      If strActivationResponse = "Expired" Then
      cantopen = True
         Msgbox("The evaluation version of StreamNation Studio has expired." & CRLF & "You can buy the full version or take the chance of winning the full version by rating the app and giving it a small review!","Evaluation")
         ExitApplication
      Else
         Msgbox("You can run the app " & strActivationResponse & " more times.","Evaluation")
      End If
   End If

End Sub

Sub Http_ResponseError (Response As HttpResponse, Reason As String, StatusCode As Int, TaskId As Int)
    If TaskId = 1 Then
   Msgbox(Reason,"An error occured." & CRLF & "Check your internet connection or try again later.")
   ExitApplication
   End If

End Sub

Attached is the PHP file. Upload it and link to that PHP File.
It is commented. Open it and edit where asked.

Here is a maintenance table of how many times StreamNation Studio is downloaded, opened, used,...:

http://www.rootsoftllc.com/streamNation/trialStats.php

XverhelstX
 

Attachments

  • ServerPHP.zip
    739 bytes · Views: 832
Last edited:

rbsoft

Active Member
Licensed User
Longtime User
It sure is. Thanks a lot for it.

Rolf
 

JonPM

Well-Known Member
Licensed User
Longtime User
Exactly what I need, except I am having difficulty setting up my SQL db (i think). App just gets stuck on "Checking Activation".
 

JonPM

Well-Known Member
Licensed User
Longtime User
Ok well I think I have my SQL db set up correctly, but my app hangs on "Checking Activation", what types of things might cause this?
 

JonPM

Well-Known Member
Licensed User
Longtime User
It seems that the IMEI.txt file isn't being created for some reason, and the app just hangs on this line:
B4X:
    If HttpClient1.Execute(request1, 1) = False Then
    Return
    End If

Any ideas?

Also, what exactly is the point of creating a txt file? why not just use the database?
 

timo

Active Member
Licensed User
Longtime User
Maybe because of the $_FILES php superglobal which manages the uploaded file informations with the POST method?

No, it's wrong.

As I can understand, the php works updating data (#trials) on the DB and puts (translate) the result in the Imei.txt file on the serever, while a timer in b4a code looks for it's content as response (expired or xmore times). What Camera lib really does I don't know.

The code stops at HttpClient1 because (I think) it is not declared.
 

Penko

Active Member
Licensed User
Longtime User
Good tutorial :)

Unfortunately, we can't predict what would happen if the user disables their internet connection regularly(I am such an example). My battery gets drained by almost everything in this phone and it's a habit to disable unnecessary features.

I'd suggest that an application stores the number of trials locally and handles operations accordingly. At a suitable moment(when the application detects internet connection, it can sync with the server).

The file should be written to the internal memory for the sake of security!
 

JonPM

Well-Known Member
Licensed User
Longtime User
i have some trial apps out that use a similar method, and when there is no internet it tells the user and then closes the app.

Sent from my DROIDX
 

JonPM

Well-Known Member
Licensed User
Longtime User
Also, it would be easier for the user to "modify" a local file and perhaps have unlimited trials...

Sent from my DROIDX
 

timo

Active Member
Licensed User
Longtime User
I think I've finally understood why you need to write a file on the web with the php results. What you would become as direct response (TaskId 1) from the query with that php code would be a string that could only show the right remainig openings, but that couldn't be converted to any kind of variable by the b4a code (I mean Php echo/print where json_encode() is unsupported by a specific server).

Here is a way I've translated it which only needs Http and Phone libs.
The code is still 'crude' and needs some finitions, but it works well enough.
I've also tried to make secure some sensible informations, even with the GET method. I think that GET is not secure only on PC, but on phones it's not so dangerous.

(one mySql table with 3 fields: ID,imei,startsleft)

trials.php:
B4X:
<?php

$hst  = $_GET['hst'];
$db = $_GET['db'];
$usr = $_GET['usr'];
$pwd = $_GET['pwd'];
$imi= $_GET['imei'];

$conn_DB=mysql_connect($hst,$usr,$pwd) or die(mysql_error());
mysql_select_db($db) or die(mysql_error());

$query="SELECT * FROM lpptrial WHERE imei LIKE $imi";
$result=mysql_query($query,$conn_DB) or die(mysql_error());
$numrows=mysql_num_rows($result);

If ($numrows == 0) {
$restano=3;
$queryINS="INSERT INTO lpptrial (imei,startsleft) VALUES ($imi,$restano)";
mysql_query($queryINS);
               }
else if ($numrows > 0) {
$restano=mysql_result($result,0,'startsleft'); 
      if($restano>0)       {
         $restano=$restano -1;
$queryUPDT="UPDATE lpptrial SET startsleft = $restano WHERE imei=$imi";
mysql_query($queryUPDT);
                     }
}
                  
//NO echo/print $restano (TaskId 1) 
                  
//write/update file(TaskId 2):
$filename= $imi.'.txt';
$file=fopen($filename,'w+');
fwrite($file,$restano);
fclose($file);
            
?>
b4a:
B4X:
Sub Process_Globals
Dim httpC As HttpClient
'put here to obfuscate user,password,php.name and hosting [->Release(obfuscated)]
Dim indirizzo As String : indirizzo= "http://www.yourSite.com/trials.php?hst=youHost&db=youDb&usr=yourUser&pwd=yourPass&imei="
Dim parcheggio As String: parcheggio="http://www.yourSite.com/"
End Sub

Sub Globals

Dim p As PhoneId
Dim deviceID As String
deviceID = p.GetDeviceId
'Dim btnGo As Button

End Sub

Sub Activity_Create(FirstTime As Boolean)
If FirstTime Then
   httpC.Initialize("httpC")
   checkTrialsRemaining
   recuperaFile
End If

End Sub

Sub Activity_Resume
End Sub

Sub Activity_Pause (UserClosed As Boolean)
Activity.Finish
End Sub

Sub checkTrialsRemaining
Dim req As HttpRequest
req.InitializeGet(indirizzo&deviceID)
httpC.Execute(req,1)
ProgressDialogShow("Validation ...")
End Sub


Sub httpC_ResponseSuccess (Response As HttpResponse, TaskId As Int)

Dim result As String
result = Response.GetString("UTF8")
ProgressDialogHide

If TaskId=2 Then

   If result=0 Then
   Msgbox("Trial expired","  Trial")
   Activity.Finish
   Else
   Msgbox(result&"/3 times","  Trial")
   End If

End If

End Sub

Sub httpC_ResponseError (Response As HttpResponse, Reason As String, StatusCode As Int, TaskId As Int)
'no Reason shown in order not to give the user informations about hosting and php names:
Msgbox("Connection error. Try again later.","")'"Error ("&StatusCode&")")
ProgressDialogHide
Activity.Finish
End Sub

Sub recuperaFile

Dim req2 As HttpRequest
req2.InitializeGet(parcheggio&deviceID&".txt")
httpC.Execute(req2,2)

End Sub
 
Last edited:

Penko

Active Member
Licensed User
Longtime User
Timo, tanto complimenti da me :)

You've written a nice piece of code. Can I suggest modifications?

In your server side you are using "LIKE imei". Why are you doing it? IMEI shouldn't change anytime. My suggestion is:

WHERE IMEI='..." and use LIMIT 1 - this is from performance point of view. I don't know about your application uses count but I saw that Tomas has a good amount of user installations. At a certain point, this LIMIT 1 will give you a performance benefit. Also with the UPDATE, you should LIMIT 1 to the end.

In PHP you are allowed to use ++ and -- for incrementing/decrementing operations. I am talking about this: $restano=$restano -1;
However, you won't benefit much from performance point of view.

Regarding the other colleague who said that local files can be modified. I've heard that Internal Memory is not easily accessible. Is that correct? You may also use SQLite, encrypt it, etc... Don't expect the regular user to try to hack your app, they are unable to do it.
 
Last edited:

timo

Active Member
Licensed User
Longtime User
Thank you Penko. Php is new to me, I just began reading a little bit few days ago. I also have to modify something in the other code to make all more stable in the count down. All this it's not yet related to a real app; I just was intrigated and experimenting. Regarding the internal storage of a similar way to protect a trial version, it could be easily bypassed by everyone if you go to phone settings and chose 'delete data'. You don't need to be an expert user to do it; and so you counter starts from beginning again. More: you can uninstall the trial, reinstall again and restart from zero because nobody is verifying the Imei of the phone.The only sure way is by an http communication. And if an expert of the forum would have the time to arrange my code it would be a usefull thing for everyone. Ciao!
 

Penko

Active Member
Licensed User
Longtime User
I can arrange anything related to the Server Side but I am still not confident enough to touch the B4A part.

Regarding the Internal Memory - I see what you mean, I haven't thought it that way. If there is possibility that we ship the .txt file with our app, it will be in the Internal Memory before the users starts the app for the first time. At least I understand it that way. Then, you can check whether the file is deleted and "punish" the user for hacking attempts.

I am not sure what Clear Memory actually does. Also, not sure if I can install the txt file the way I explained above.

Now, back to the main topic, HTTP communication is the most secure way but it forces the user to enable Wifi/3G. Now, if you application is in no way connected to using the Internet, this could be redundant and annoying for the user. It's a vast topic we can talk on with hours.

Consider what I've written and let me know what you think, especially about the Internal Memory Issue.

Once more, for any PHP-related questions, don't hesitate to ask me :)
 

timo

Active Member
Licensed User
Longtime User
I...
Regarding the Internal Memory - I see what you mean, I haven't thought it that way. If there is possibility that we ship the .txt file with our app, it will be in the Internal Memory before the users starts the app for the first time.
No, there isn't any possibility. All stay in the .apk distribution file. From there(DirAsset)you can install/create whatever you want (internal/external). But once the app is uninstalled, everything disappear. And so on.
...it forces the user to enable Wifi/3G. Now, if you application is in no way connected to using the Internet, this could be redundant and annoying for the user...
...
I agree. On the other hand, Android Market, with his Licence protection act exactly the same way. It depends on what you (user) want. If a user is really interested in an App, nowadays he knows that a connection is often required. All permissions are clearely declared before downloading.

I personally don't like that way to protect an App, but if even the Market self cannot find a better solution it means that there aren't similar 'offline' (a part from the old internal solution that is disliked and soon disappearing).

Anyway, my intention was only to have control on Trials Apps.

I proposed another solution to stop, at the same time, all demo circulating
here: http://www.b4x.com/forum/basic4android-updates-questions/15204-stop-all-trial-versions-app.html
 

timo

Active Member
Licensed User
Longtime User
OK, my 'crusade' is finally at the end. This version doesn't need to write any file and works well. (sorry Penko, I didn't use --/++ and Limit 1 (no more than 1 record per imei is here possible)

(MySql: 1 Table, 3 Fields: ID/imei/startsleft)
(Libs: Http/Json/Phone)

trials.php
B4X:
<?php
$hst  = "xxx";
$db = "xxx";
$usr = "xxx";
$pwd = "xxx";
$imi= $_GET['imei'];

$conn_DB=mysql_connect($hst,$usr,$pwd) or die(mysql_error());
mysql_select_db($db) or die(mysql_error());

$query="SELECT * FROM lpptrial WHERE imei = '$imi' ";
$result=mysql_query($query,$conn_DB) or die(mysql_error());
$numrows=mysql_num_rows($result);

If ($numrows == 0) {
$restano=3;
$queryINS="INSERT INTO lpptrial (imei,startsleft) VALUES ('$imi',$restano)";
mysql_query($queryINS);
               }
else if ($numrows > 0) {
//never > 1 anyway
$restano=mysql_result($result,0,'startsleft'); 
      if($restano>0)       {
         $restano=$restano -1;
$queryUPDT="UPDATE lpptrial SET startsleft = $restano WHERE imei='$imi'";
mysql_query($queryUPDT);
                     }
                  }
                  
$queryOUT="SELECT * FROM lpptrial WHERE imei = '$imi' ";
$output=mysql_query($queryOUT);   

$rows = array();
    while($r = mysql_fetch_assoc($output)) {
        $rows[] = $r;
    }
    print json_encode($rows);
?>
b4a
B4X:
Sub Process_Globals
Dim httpC As HttpClient
'put here to obfuscate user,password,php.name and hosting [->Release(obfuscated)]
Dim indirizzo As String : indirizzo= "http://www.yourSite.com/trials.php?imei="
'Dim parcheggio As String: parcheggio="http://www.yourSite.com/" '(no more needed here)
End Sub

Sub Globals
Dim p As PhoneId
Dim deviceID As String
deviceID = p.GetDeviceId

End Sub

Sub Activity_Create(FirstTime As Boolean)
If FirstTime Then
   httpC.Initialize("httpC")
End If
checkTrialsRemaining
   
End Sub

Sub Activity_Resume
End Sub

Sub Activity_Pause (UserClosed As Boolean)
Activity.Finish
End Sub

Sub checkTrialsRemaining
Dim req As HttpRequest
req.InitializeGet(indirizzo&deviceID)
httpC.Execute(req,1)
ProgressDialogShow("Validation ...")

End Sub


Sub httpC_ResponseSuccess (Response As HttpResponse, TaskId As Int)

Dim result As String
Dim JSON As JSONParser
Dim restOpen As Int

result = Response.GetString("UTF8")

'Optional: Remove '[' and ']' from the response received from my server:-> array[{jsonObject}]
Dim tmp As String
Dim lungh As Int
tmp=result
lungh= tmp.Length
tmp= tmp.SubString2(1,lungh-1) 'cut first+last char
result=tmp
'-----------------------------------------------------

JSON.Initialize(result)
Dim m As Map
m=JSON.NextObject

restOpen=m.Get("startsleft")
ProgressDialogHide

   If restOpen=0 Then
      Msgbox("Trial expired","  Trial")
                Response.Release
      Activity.Finish
   Else 
      If restOpen > 1 Then
         Msgbox((restOpen - 1)&" out of 3 openings remaining","  Trial")
                 Else
         Msgbox("Last opening of 3","  Trial")
                 End If
   End If
Response.Release
End Sub

Sub httpC_ResponseError (Response As HttpResponse, Reason As String, StatusCode As Int, TaskId As Int)
'no Reason shown in order not to give the user informations about hosting and php names:
ProgressDialogHide
Msgbox("Connection error. Try later again.","  Trial")

If Response <> Null Then 
    Log("Resp.not Null")
   Response.Release
End If
   'If no web connection is available, Response is naturally Null and you don't have to release a Null object
ExitApplication
End Sub
 
Last edited:

timo

Active Member
Licensed User
Longtime User
et voila!
 

Attachments

  • img.jpg
    img.jpg
    54.3 KB · Views: 453

Penko

Active Member
Licensed User
Longtime User
Timo, it is very interesting how the PHP side accomplishes the SQL queries successfully as you have to use WHERE imei='$imei' becuse $imei is a string value. Please correct it as it may fail you at some point. You are using LIKE %. The INSERT query also lacks inverted-commas (').LIMIT is also a benefit.
 
Last edited:
Top