J'ai une application que je souhaite commercialiser en tant qu'application payante. Je voudrais avoir une autre version qui serait une version "d'essai" avec une limite de temps de disons, 5 jours?
Comment puis-je faire cela?
J'ai une application que je souhaite commercialiser en tant qu'application payante. Je voudrais avoir une autre version qui serait une version "d'essai" avec une limite de temps de disons, 5 jours?
Comment puis-je faire cela?
Réponses:
Actuellement, la plupart des développeurs accomplissent cela en utilisant l'une des 3 techniques suivantes.
La première approche est facilement contournée, la première fois que vous exécutez l'application, enregistrez la date / l'heure dans un fichier, une base de données ou des préférences partagées et chaque fois que vous exécutez l'application après cela, vérifiez si la période d'essai est terminée. Ceci est facile à contourner car la désinstallation et la réinstallation permettront à l'utilisateur d'avoir une autre période d'essai.
La seconde approche est plus difficile à contourner, mais toujours contournable. Utilisez une bombe à retardement codée en dur. Fondamentalement, avec cette approche, vous coderez en dur une date de fin pour l'essai, et tous les utilisateurs qui téléchargent et utilisent l'application cesseront de pouvoir utiliser l'application en même temps. J'ai utilisé cette approche parce qu'elle est facile à mettre en œuvre et, pour la plupart, je n'avais tout simplement pas envie de passer par la troisième technique. Les utilisateurs peuvent contourner cela en modifiant manuellement la date sur leur téléphone, mais la plupart des utilisateurs ne se donneront pas la peine de faire une telle chose.
La troisième technique est la seule façon dont j'ai entendu parler pour vraiment être en mesure d'accomplir ce que vous voulez faire. Vous devrez configurer un serveur, puis chaque fois que votre application démarre, votre application envoie l' identifiant unique du téléphone au serveur. Si le serveur n'a pas d'entrée pour cet identifiant de téléphone, il en crée une nouvelle et note l'heure. Si le serveur dispose d'une entrée pour l'identifiant du téléphone, il effectue une simple vérification pour voir si la période d'essai a expiré. Il communique ensuite les résultats du contrôle d'expiration de l'essai à votre application. Cette approche ne doit pas être contournable, mais nécessite la mise en place d'un serveur Web et autres.
Il est toujours recommandé d'effectuer ces vérifications dans onCreate. Si l'expiration est terminée, affichez un AlertDialog avec un lien de marché vers la version complète de l'application. N'incluez qu'un bouton "OK", et une fois que l'utilisateur clique sur "OK", appelez "Terminer ()" pour mettre fin à l'activité.
J'ai développé un SDK d'essai Android que vous pouvez simplement insérer dans votre projet Android Studio et il se chargera de toute la gestion côté serveur pour vous (y compris les périodes de grâce hors ligne).
Pour l'utiliser, simplement
Ajoutez la bibliothèque à votre module principal build.gradle
dependencies {
compile 'io.trialy.library:trialy:1.0.2'
}
Initialisez la bibliothèque dans la onCreate()
méthode de votre activité principale
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Initialize the library and check the current trial status on every launch
Trialy mTrialy = new Trialy(mContext, "YOUR_TRIALY_APP_KEY");
mTrialy.checkTrial(TRIALY_SKU, mTrialyCallback);
}
Ajoutez un gestionnaire de rappel:
private TrialyCallback mTrialyCallback = new TrialyCallback() {
@Override
public void onResult(int status, long timeRemaining, String sku) {
switch (status){
case STATUS_TRIAL_JUST_STARTED:
//The trial has just started - enable the premium features for the user
break;
case STATUS_TRIAL_RUNNING:
//The trial is currently running - enable the premium features for the user
break;
case STATUS_TRIAL_JUST_ENDED:
//The trial has just ended - block access to the premium features
break;
case STATUS_TRIAL_NOT_YET_STARTED:
//The user hasn't requested a trial yet - no need to do anything
break;
case STATUS_TRIAL_OVER:
//The trial is over
break;
}
Log.i("TRIALY", "Trialy response: " + Trialy.getStatusMessage(status));
}
};
Pour démarrer un essai, appelez la mTrialy.startTrial("YOUR_TRIAL_SKU", mTrialyCallback);
clé de votre application et la référence SKU d'essai se trouve dans votre tableau de bord du développeur Trialy .
C'est une vieille question mais de toute façon, peut-être que cela aidera quelqu'un.
Si vous souhaitez utiliser l' approche la plus simpliste (qui échouera si l'application est désinstallée / réinstallée ou si l'utilisateur change la date de l'appareil manuellement), voici comment cela pourrait être:
private final SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
private final long ONE_DAY = 24 * 60 * 60 * 1000;
@Override
protected void onCreate(Bundle state){
SharedPreferences preferences = getPreferences(MODE_PRIVATE);
String installDate = preferences.getString("InstallDate", null);
if(installDate == null) {
// First run, so save the current date
SharedPreferences.Editor editor = preferences.edit();
Date now = new Date();
String dateString = formatter.format(now);
editor.putString("InstallDate", dateString);
// Commit the edits!
editor.commit();
}
else {
// This is not the 1st run, check install date
Date before = (Date)formatter.parse(installDate);
Date now = new Date();
long diff = now.getTime() - before.getTime();
long days = diff / ONE_DAY;
if(days > 30) { // More than 30 days?
// Expired !!!
}
}
...
}
getTime
ne l' est pas getTimeInMillis
.
Cette question et la réponse de snctln m'ont inspiré à travailler sur une solution basée sur la méthode 3 comme mémoire de licence. Je sais que l'état actuel n'est pas destiné à une utilisation productive, mais j'aimerais savoir ce que vous en pensez! Utiliseriez-vous un tel système? Souhaitez-vous le voir comme un service cloud (vous n'avez pas de problèmes avec la configuration d'un serveur)? Préoccupé par des problèmes de sécurité ou des raisons de stabilité?
Dès que j'ai terminé la procédure de licence, je veux continuer à travailler sur le logiciel. Alors maintenant, c'est le moment où j'ai besoin de vos commentaires!
Sourcecode est hébergé sur GitHub https://github.com/MaChristmann/mobile-trial
Quelques informations sur le système: - Le système comprend trois parties, une bibliothèque Android, un serveur node.js et un configurateur pour gérer plusieurs applications d'essai et comptes d'éditeurs / développeurs.
Il ne prend en charge que les essais chronologiques et utilise votre compte (Play Store ou autre) plutôt qu'un identifiant de téléphone.
Pour la bibliothèque Android, il est basé sur la bibliothèque de vérification des licences Google Play. Je l'ai modifié pour me connecter au serveur node.js et en plus la bibliothèque essaie de reconnaître si un utilisateur a changé la date du système. Il met également en cache une licence d'évaluation récupérée dans les préférences partagées chiffrées AES. Vous pouvez configurer l'heure de validité du cache avec le configurateur. Si un utilisateur "efface les données", la bibliothèque forcera une vérification côté serveur.
Le serveur utilise https ainsi que la signature numérique de la réponse de vérification de licence. Il dispose également d'une API pour les applications d'essai CRUD et les utilisateurs (éditeur et développeur). Les développeurs de bibliothèques de vérification de licence peuvent tester leur implémentation de comportement dans l'application d'essai avec le résultat du test. Ainsi, dans le configurateur, vous pouvez définir explicitement la réponse de votre licence sur "sous licence", "sans licence" ou "erreur de serveur".
Si vous mettez à jour votre application avec une nouvelle fonctionnalité époustouflante, vous voudrez peut-être que tout le monde puisse l'essayer à nouveau. Dans le configurateur, vous pouvez renouveler la licence d'essai pour les utilisateurs avec des licences expirées en définissant un code de version qui devrait déclencher cela. Par exemple, l'utilisateur exécute votre application sur le code de version 3 et vous voulez qu'il essaie les fonctionnalités du code de version 4. S'il met à jour l'application ou la réinstalle, il peut à nouveau utiliser la période d'essai complète car le serveur sait sur quelle version il l'a essayée en dernier. temps.
Tout est sous licence Apache 2.0
Le moyen le plus simple et le meilleur pour ce faire est d'implémenter BackupSharedPreferences.
Les préférences sont conservées, même si l'application est désinstallée et réinstallée.
Enregistrez simplement la date d'installation comme préférence et vous êtes prêt à partir.
Voici la théorie: http://developer.android.com/reference/android/app/backup/SharedPreferencesBackupHelper.html
Voici l'exemple: La sauvegarde Android SharedPreferences ne fonctionne pas
Approche 4: utilisez le temps d'installation de l'application.
Depuis le niveau d'API 9 (Android 2.3.2, 2.3.1, Android 2.3, GINGERBREAD), il y a firstInstallTime et lastUpdateTime dans PackageInfo
.
Pour en savoir plus: Comment obtenir le temps d'installation de l'application depuis Android
Maintenant, dans la version récente de l'abonnement d'essai gratuit Android a été ajoutée, vous ne pouvez déverrouiller toutes les fonctionnalités de votre application qu'après avoir acheté l'abonnement dans l'application pour une période d'essai gratuite. Cela permettra à l'utilisateur d'utiliser votre application pendant une période d'essai, si l'application est toujours désinstallée après la période d'essai, l'argent de l'abonnement vous sera transféré. Je n'ai pas essayé, mais juste partager une idée.
À mon avis, le meilleur moyen de le faire est d'utiliser simplement la base de données Firebase Realtime:
1) Ajoutez la prise en charge de Firebase à votre application
2) Sélectionnez «Authentification anonyme» pour que l'utilisateur n'ait pas à s'inscrire ou même à savoir ce que vous faites. Ceci est garanti pour être lié au compte d'utilisateur actuellement authentifié et fonctionnera donc sur tous les appareils.
3) Utilisez l'API de base de données en temps réel pour définir une valeur pour «Installed_date». Au moment du lancement, récupérez simplement cette valeur et utilisez-la.
J'ai fait la même chose et cela fonctionne très bien. J'ai pu tester cela lors de la désinstallation / réinstallation et la valeur dans la base de données en temps réel reste la même. De cette façon, votre période d'essai fonctionne sur plusieurs appareils utilisateur. Vous pouvez même mettre à jour votre install_date afin que l'application «réinitialise» la date d'essai pour chaque nouvelle version majeure.
MISE À JOUR : Après avoir testé un peu plus, il semble que Firebase anonyme semble allouer un identifiant différent au cas où vous auriez différents appareils et n'est pas garanti entre les réinstallations: / Le seul moyen garanti est d'utiliser Firebase mais de le lier à leur google Compte. Cela devrait fonctionner, mais nécessiterait une étape supplémentaire où l'utilisateur doit d'abord se connecter / s'inscrire.
Jusqu'à présent, je me suis retrouvé avec une approche légèrement moins élégante consistant simplement à vérifier les préférences sauvegardées et une date stockée dans les préférences lors de l'installation. Cela fonctionne pour les applications centrées sur les données où il est inutile pour une personne de réinstaller l'application et de saisir à nouveau toutes les données précédemment ajoutées, mais ne fonctionnerait pas pour un jeu simple.
Après avoir examiné toutes les options de ce fil et d'autres, voici mes conclusions
Préférences partagées, base de données Peut être effacé dans les paramètres Android, perdu après la réinstallation d'une application. Peut être sauvegardé avec le mécanisme de sauvegarde d'Android et sera restauré après une réinstallation. La sauvegarde peut ne pas toujours être disponible, mais devrait l'être sur la plupart des appareils
Stockage externe (écriture dans un fichier) Non affecté par une suppression des paramètres ou une réinstallation si nous n'écrivons pas dans le répertoire privé de l'application . Mais: vous oblige à demander à l'utilisateur son autorisation au moment de l' exécution dans les nouvelles versions d'Android, donc cela n'est probablement possible que si vous avez de toute façon besoin de cette autorisation. Peut également être sauvegardé.
PackageInfo.firstInstallTime Est réinitialisé après une réinstallation mais stable entre les mises à jour
Connectez-vous à un compte Peu importe qu'il s'agisse de son compte Google via Firebase ou de celui de votre propre serveur: la version d'évaluation est liée au compte. Créer un nouveau compte réinitialisera l'essai.
Connexion anonyme à Firebase Vous pouvez connecter un utilisateur de manière anonyme et stocker des données pour lui dans Firebase. Mais apparemment, une réinstallation de l'application et peut-être d'autres événements non documentés peuvent donner à l'utilisateur un nouvel identifiant anonyme , réinitialisant leur période d'essai. (Google lui-même ne fournit pas beaucoup de documentation à ce sujet)
ANDROID_ID Peut ne pas être disponible et peut changer dans certaines circonstances , par exemple réinitialisation d'usine. Les opinions sur l'opportunité de l'utiliser pour identifier les appareils semblent différer.
Play Advertising ID Peut être réinitialisé par l'utilisateur. Peut être désactivé par l'utilisateur en désactivant le suivi des annonces.
InstanceID Reset lors d'une réinstallation . Réinitialiser en cas d'événement de sécurité. Peut être réinitialisé par votre application.
La (combinaison de) méthodes qui fonctionne pour vous dépend de votre application et de l'effort que vous pensez que le Jean moyen mettra pour gagner une autre période d'essai. Je recommanderais d'éviter d'utiliser uniquement Firebase et Advertising ID anonymes en raison de leur instabilité. Une approche multifactorielle semble donner les meilleurs résultats. Les facteurs dont vous disposez dépendent de votre application et de ses autorisations.
Pour ma propre application, j'ai trouvé que les préférences partagées + firstInstallTime + sauvegarde des préférences étaient la méthode la moins intrusive mais aussi assez efficace. Vous devez vous assurer de ne demander une sauvegarde qu'après avoir vérifié et stocké l'heure de début de l'essai dans les préférences partagées. Les valeurs des Prefs partagés doivent avoir la priorité sur firstInstallTime. Ensuite, l'utilisateur doit réinstaller l'application, l'exécuter une fois, puis effacer les données de l'application pour réinitialiser la version d'essai, ce qui représente beaucoup de travail. Cependant, sur les appareils sans transport de sauvegarde, l'utilisateur peut réinitialiser la version d'évaluation en réinstallant simplement.
J'ai rendu cette approche disponible sous forme de bibliothèque extensible .
Par définition, toutes les applications Android payantes du marché peuvent être évaluées pendant 24 heures après l'achat.
Il existe un bouton «Désinstaller et rembourser» qui devient «Désinstaller» après 24 heures.
Je dirais que ce bouton est trop important!
Je rencontre cette question en recherchant le même problème, je pense que nous pouvons utiliser des API de date gratuites comme http://www.timeapi.org/utc/now ou une autre API de date pour vérifier l'expiration de l'application de trail. cette méthode est efficace si vous souhaitez fournir la démo et que vous vous inquiétez du paiement et que vous avez besoin d'une démo fixe. :)
trouver le code ci-dessous
public class ValidationActivity extends BaseMainActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void onResume() {
processCurrentTime();
super.onResume();
}
private void processCurrentTime() {
if (!isDataConnectionAvailable(ValidationActivity.this)) {
showerrorDialog("No Network coverage!");
} else {
String urlString = "http://api.timezonedb.com/?zone=Europe/London&key=OY8PYBIG2IM9";
new CallAPI().execute(urlString);
}
}
private void showerrorDialog(String data) {
Dialog d = new Dialog(ValidationActivity.this);
d.setTitle("LS14");
TextView tv = new TextView(ValidationActivity.this);
tv.setText(data);
tv.setPadding(20, 30, 20, 50);
d.setContentView(tv);
d.setOnDismissListener(new OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
finish();
}
});
d.show();
}
private void checkExpiry(int isError, long timestampinMillies) {
long base_date = 1392878740000l;// feb_19 13:8 in GMT;
// long expiryInMillies=1000*60*60*24*5;
long expiryInMillies = 1000 * 60 * 10;
if (isError == 1) {
showerrorDialog("Server error, please try again after few seconds");
} else {
System.out.println("fetched time " + timestampinMillies);
System.out.println("system time -" + (base_date + expiryInMillies));
if (timestampinMillies > (base_date + expiryInMillies)) {
showerrorDialog("Demo version expired please contact vendor support");
System.out.println("expired");
}
}
}
private class CallAPI extends AsyncTask<String, String, String> {
@Override
protected void onPreExecute() {
// TODO Auto-generated method stub
super.onPreExecute();
}
@Override
protected String doInBackground(String... params) {
String urlString = params[0]; // URL to call
String resultToDisplay = "";
InputStream in = null;
// HTTP Get
try {
URL url = new URL(urlString);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
in = new BufferedInputStream(urlConnection.getInputStream());
resultToDisplay = convertStreamToString(in);
} catch (Exception e) {
System.out.println(e.getMessage());
return e.getMessage();
}
return resultToDisplay;
}
protected void onPostExecute(String result) {
int isError = 1;
long timestamp = 0;
if (result == null || result.length() == 0 || result.indexOf("<timestamp>") == -1 || result.indexOf("</timestamp>") == -1) {
System.out.println("Error $$$$$$$$$");
} else {
String strTime = result.substring(result.indexOf("<timestamp>") + 11, result.indexOf("</timestamp>"));
System.out.println(strTime);
try {
timestamp = Long.parseLong(strTime) * 1000;
isError = 0;
} catch (NumberFormatException ne) {
}
}
checkExpiry(isError, timestamp);
}
} // end CallAPI
public static boolean isDataConnectionAvailable(Context context) {
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = connectivityManager.getActiveNetworkInfo();
if (info == null)
return false;
return connectivityManager.getActiveNetworkInfo().isConnected();
}
public String convertStreamToString(InputStream is) throws IOException {
if (is != null) {
Writer writer = new StringWriter();
char[] buffer = new char[1024];
try {
Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
int n;
while ((n = reader.read(buffer)) != -1) {
writer.write(buffer, 0, n);
}
} finally {
is.close();
}
return writer.toString();
} else {
return "";
}
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
}
}
sa solution de travail .....
Voici comment j'ai procédé au mien, j'ai créé 2 applications l'une avec une activité d'essai l'autre sans,
j'ai téléchargé celui sans activité d'essai pour jouer au magasin en tant qu'application payante,
et celui avec une activité d'essai en tant qu'application gratuite.
L'application gratuite lors du premier lancement propose des options d'essai et d'achat en magasin.Si l'utilisateur sélectionne l'achat en magasin, elle est redirigée vers le magasin pour que l'utilisateur l'achète, mais si l'utilisateur clique sur l'essai, cela l'amène à l'activité d'essai.
NB: j'ai utilisé l'option 3 comme @snctln mais avec des modifications
d'abord , je ne dépendais pas de l'heure de l'appareil, j'ai obtenu mon temps du fichier php qui fait l'enregistrement d'essai à la base de données,
deuxièmement , j'ai utilisé le numéro de série de l'appareil pour identifier de manière unique chaque appareil,
Enfin , l'application dépend de la valeur de temps renvoyée par la connexion au serveur et non de son propre temps, de sorte que le système ne peut être contourné que si le numéro de série de l'appareil est modifié, ce qui est assez stressant pour un utilisateur.
voici donc mon code (pour l'activité d'essai):
package com.example.mypackage.my_app.Start_Activity.activity;
import android.Manifest;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.telephony.TelephonyManager;
import android.view.KeyEvent;
import android.widget.TextView;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonObjectRequest;
import com.android.volley.toolbox.Volley;
import com.example.onlinewisdom.cbn_app.R;
import com.example.mypackage.my_app.Start_Activity.app.Config;
import com.example.mypackage.my_app.Start_Activity.data.TrialData;
import com.example.mypackage.my_app.Start_Activity.helper.connection.Connection;
import com.google.gson.Gson;
import org.json.JSONObject;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import cn.pedant.SweetAlert.SweetAlertDialog;
public class Trial extends AppCompatActivity {
Connection check;
SweetAlertDialog pDialog;
TextView tvPleaseWait;
private static final int MY_PERMISSIONS_REQUEST_READ_PHONE_STATE = 0;
String BASE_URL = Config.BASE_URL;
String BASE_URL2 = BASE_URL+ "/register_trial/"; //http://ur link to ur API
//KEY
public static final String KEY_IMEI = "IMEINumber";
private final SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
private final long ONE_DAY = 24 * 60 * 60 * 1000;
SharedPreferences preferences;
String installDate;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_trial);
preferences = getPreferences(MODE_PRIVATE);
installDate = preferences.getString("InstallDate", null);
pDialog = new SweetAlertDialog(this, SweetAlertDialog.PROGRESS_TYPE);
pDialog.getProgressHelper().setBarColor(Color.parseColor("#008753"));
pDialog.setTitleText("Loading...");
pDialog.setCancelable(false);
tvPleaseWait = (TextView) findViewById(R.id.tvPleaseWait);
tvPleaseWait.setText("");
if(installDate == null) {
//register app for trial
animateLoader(true);
CheckConnection();
} else {
//go to main activity and verify there if trial period is over
Intent i = new Intent(Trial.this, MainActivity.class);
startActivity(i);
// close this activity
finish();
}
}
public void CheckConnection() {
check = new Connection(this);
if (check.isConnected()) {
//trigger 'loadIMEI'
loadIMEI();
} else {
errorAlert("Check Connection", "Network is not detected");
tvPleaseWait.setText("Network is not detected");
animateLoader(false);
}
}
public boolean onKeyDown(int keyCode, KeyEvent event) {
//Changes 'back' button action
if (keyCode == KeyEvent.KEYCODE_BACK) {
finish();
}
return true;
}
public void animateLoader(boolean visibility) {
if (visibility)
pDialog.show();
else
pDialog.hide();
}
public void errorAlert(String title, String msg) {
new SweetAlertDialog(this, SweetAlertDialog.ERROR_TYPE)
.setTitleText(title)
.setContentText(msg)
.show();
}
/**
* Called when the 'loadIMEI' function is triggered.
*/
public void loadIMEI() {
// Check if the READ_PHONE_STATE permission is already available.
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE)
!= PackageManager.PERMISSION_GRANTED) {
// READ_PHONE_STATE permission has not been granted.
requestReadPhoneStatePermission();
} else {
// READ_PHONE_STATE permission is already been granted.
doPermissionGrantedStuffs();
}
}
/**
* Requests the READ_PHONE_STATE permission.
* If the permission has been denied previously, a dialog will prompt the user to grant the
* permission, otherwise it is requested directly.
*/
private void requestReadPhoneStatePermission() {
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.READ_PHONE_STATE)) {
// Provide an additional rationale to the user if the permission was not granted
// and the user would benefit from additional context for the use of the permission.
// For example if the user has previously denied the permission.
new AlertDialog.Builder(Trial.this)
.setTitle("Permission Request")
.setMessage(getString(R.string.permission_read_phone_state_rationale))
.setCancelable(false)
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
//re-request
ActivityCompat.requestPermissions(Trial.this,
new String[]{Manifest.permission.READ_PHONE_STATE},
MY_PERMISSIONS_REQUEST_READ_PHONE_STATE);
}
})
.setIcon(R.drawable.warning_sigh)
.show();
} else {
// READ_PHONE_STATE permission has not been granted yet. Request it directly.
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_PHONE_STATE},
MY_PERMISSIONS_REQUEST_READ_PHONE_STATE);
}
}
/**
* Callback received when a permissions request has been completed.
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == MY_PERMISSIONS_REQUEST_READ_PHONE_STATE) {
// Received permission result for READ_PHONE_STATE permission.est.");
// Check if the only required permission has been granted
if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// READ_PHONE_STATE permission has been granted, proceed with displaying IMEI Number
//alertAlert(getString(R.string.permision_available_read_phone_state));
doPermissionGrantedStuffs();
} else {
alertAlert(getString(R.string.permissions_not_granted_read_phone_state));
}
}
}
private void alertAlert(String msg) {
new AlertDialog.Builder(Trial.this)
.setTitle("Permission Request")
.setMessage(msg)
.setCancelable(false)
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// do somthing here
}
})
.setIcon(R.drawable.warning_sigh)
.show();
}
private void successAlert(String msg) {
new SweetAlertDialog(this, SweetAlertDialog.SUCCESS_TYPE)
.setTitleText("Success")
.setContentText(msg)
.setConfirmText("Ok")
.setConfirmClickListener(new SweetAlertDialog.OnSweetClickListener() {
@Override
public void onClick(SweetAlertDialog sDialog) {
sDialog.dismissWithAnimation();
// Prepare intent which is to be triggered
//Intent i = new Intent(Trial.this, MainActivity.class);
//startActivity(i);
}
})
.show();
}
public void doPermissionGrantedStuffs() {
//Have an object of TelephonyManager
TelephonyManager tm =(TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
//Get IMEI Number of Phone //////////////// for this example i only need the IMEI
String IMEINumber = tm.getDeviceId();
/************************************************
* **********************************************
* This is just an icing on the cake
* the following are other children of TELEPHONY_SERVICE
*
//Get Subscriber ID
String subscriberID=tm.getDeviceId();
//Get SIM Serial Number
String SIMSerialNumber=tm.getSimSerialNumber();
//Get Network Country ISO Code
String networkCountryISO=tm.getNetworkCountryIso();
//Get SIM Country ISO Code
String SIMCountryISO=tm.getSimCountryIso();
//Get the device software version
String softwareVersion=tm.getDeviceSoftwareVersion()
//Get the Voice mail number
String voiceMailNumber=tm.getVoiceMailNumber();
//Get the Phone Type CDMA/GSM/NONE
int phoneType=tm.getPhoneType();
switch (phoneType)
{
case (TelephonyManager.PHONE_TYPE_CDMA):
// your code
break;
case (TelephonyManager.PHONE_TYPE_GSM)
// your code
break;
case (TelephonyManager.PHONE_TYPE_NONE):
// your code
break;
}
//Find whether the Phone is in Roaming, returns true if in roaming
boolean isRoaming=tm.isNetworkRoaming();
if(isRoaming)
phoneDetails+="\nIs In Roaming : "+"YES";
else
phoneDetails+="\nIs In Roaming : "+"NO";
//Get the SIM state
int SIMState=tm.getSimState();
switch(SIMState)
{
case TelephonyManager.SIM_STATE_ABSENT :
// your code
break;
case TelephonyManager.SIM_STATE_NETWORK_LOCKED :
// your code
break;
case TelephonyManager.SIM_STATE_PIN_REQUIRED :
// your code
break;
case TelephonyManager.SIM_STATE_PUK_REQUIRED :
// your code
break;
case TelephonyManager.SIM_STATE_READY :
// your code
break;
case TelephonyManager.SIM_STATE_UNKNOWN :
// your code
break;
}
*/
// Now read the desired content to a textview.
//tvPleaseWait.setText(IMEINumber);
UserTrialRegistrationTask(IMEINumber);
}
/**
* Represents an asynchronous login task used to authenticate
* the user.
*/
private void UserTrialRegistrationTask(final String IMEINumber) {
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.GET, BASE_URL2+IMEINumber, null,
new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
Gson gson = new Gson();
TrialData result = gson.fromJson(String.valueOf(response), TrialData.class);
animateLoader(false);
if ("true".equals(result.getError())) {
errorAlert("Error", result.getResult());
tvPleaseWait.setText("Unknown Error");
} else if ("false".equals(result.getError())) {
//already created install/trial_start date using the server
// so just getting the date called back
Date before = null;
try {
before = (Date)formatter.parse(result.getResult());
} catch (ParseException e) {
e.printStackTrace();
}
Date now = new Date();
assert before != null;
long diff = now.getTime() - before.getTime();
long days = diff / ONE_DAY;
// save the date received
SharedPreferences.Editor editor = preferences.edit();
editor.putString("InstallDate", String.valueOf(days));
// Commit the edits!
editor.apply();
//go to main activity and verify there if trial period is over
Intent i = new Intent(Trial.this, MainActivity.class);
startActivity(i);
// close this activity
finish();
//successAlert(String.valueOf(days));
//if(days > 5) { // More than 5 days?
// Expired !!!
//}
}
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
animateLoader(false);
//errorAlert(error.toString());
errorAlert("Check Connection", "Could not establish a network connection.");
tvPleaseWait.setText("Network is not detected");
}
})
{
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<String, String>();
params.put(KEY_IMEI, IMEINumber);
return params;
}
};
RequestQueue requestQueue = Volley.newRequestQueue(this);
requestQueue.add(jsonObjectRequest);
}
}
Mon fichier php ressemble à ceci (c'est une technologie REST-slim):
/**
* registerTrial
*/
public function registerTrial($IMEINumber) {
//check if $IMEINumber already exist
// Instantiate DBH
$DBH = new PDO_Wrapper();
$DBH->query("SELECT date_reg FROM trials WHERE device_id = :IMEINumber");
$DBH->bind(':IMEINumber', $IMEINumber);
// DETERMINE HOW MANY ROWS OF RESULTS WE GOT
$totalRows_registered = $DBH->rowCount();
// DETERMINE HOW MANY ROWS OF RESULTS WE GOT
$results = $DBH->resultset();
if (!$IMEINumber) {
return 'Device serial number could not be determined.';
} else if ($totalRows_registered > 0) {
$results = $results[0];
$results = $results['date_reg'];
return $results;
} else {
// Instantiate variables
$trial_unique_id = es_generate_guid(60);
$time_reg = date('H:i:s');
$date_reg = date('Y-m-d');
$DBH->beginTransaction();
// opening db connection
//NOW Insert INTO DB
$DBH->query("INSERT INTO trials (time_reg, date_reg, date_time, device_id, trial_unique_id) VALUES (:time_reg, :date_reg, NOW(), :device_id, :trial_unique_id)");
$arrayValue = array(':time_reg' => $time_reg, ':date_reg' => $date_reg, ':device_id' => $IMEINumber, ':trial_unique_id' => $trial_unique_id);
$DBH->bindArray($arrayValue);
$subscribe = $DBH->execute();
$DBH->endTransaction();
return $date_reg;
}
}
puis sur l'activité principale, j'utilise la préférence partagée (installDate créée dans l'activité d'essai) pour surveiller le nombre de jours restants et si les jours sont terminés, je bloque l'interface utilisateur de l'activité principale avec un message qui les amène au magasin pour acheter.
Le seul inconvénient que je vois ici est que si un utilisateur Rogue achète l'application payante et décide de partager avec des applications comme Zender, le partage de fichiers ou même l'héberger le fichier apk directement sur un serveur pour que les gens le téléchargent gratuitement. Mais je suis sûr que je modifierai bientôt cette réponse avec une solution à cela ou un lien vers la solution.
J'espère que cela sauve une âme ... un jour
Codage heureux ...
L' option 3 @snctln peut être facilement effectuée en ajoutant un fichier php à un serveur Web avec php et mysql installés comme beaucoup d'entre eux.
Du côté Android, un identifiant (l'identifiant de l'appareil, le compte google ou ce que vous voulez) est passé en argument dans l'URL en utilisant HttpURLConnection et le php renvoie la date de la première installation s'il existe dans la table ou il insère une nouvelle ligne et il renvoie la date actuelle.
Ça fonctionne bien pour moi.
Si j'ai le temps, je posterai du code!
Bonne chance !