web-dev-qa-db-fra.com

Création d'une Android qui expire après une période de temps fixe

J'ai une application que je souhaite commercialiser en tant qu'application payante. J'aimerais avoir une autre version qui serait une version "d'essai" avec un délai de disons 5 jours?

Comment puis-je m'y prendre?

102
Tom

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 cette vérification pour voir 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 deuxième approche est plus difficile à contourner, mais reste contournable. Utilisez une bombe à retardement codée en dur. Fondamentalement, avec cette approche, vous coderez en dur une date de fin pour la période d'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 traverser les ennuis de 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 le seul moyen dont j'ai entendu parler pour vraiment pouvoir accomplir ce que vous voulez faire. Vous devrez configurer un serveur, puis chaque fois que votre application sera lancée, votre application enverra le 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 a une entrée pour l'ID de téléphone, il vérifie simplement si la période d'essai a expiré. Il communique ensuite les résultats de la vérification d'expiration de la version d'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, ouvrez 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 a cliqué sur "OK", appelez "finish ()" pour terminer l'activité.

183
snctln

J'ai développé un Android Trial SDK que vous pouvez simplement déposer dans votre Android projet Studio et il se chargera de toute la gestion côté serveur pour vous (y compris périodes de grâce hors ligne).

Pour l'utiliser, simplement

Ajoutez la bibliothèque au build.gradle De votre module principal

dependencies {
  compile 'io.trialy.library:trialy:1.0.2'
}

Initialisez la bibliothèque dans la méthode onCreate() 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 mTrialy.startTrial("YOUR_TRIAL_SKU", mTrialyCallback); Votre clé d'application et votre SKU d'essai se trouvent dans votre Tableau de bord du développeur Trialy .

18
Nick

C'est une vieille question mais de toute façon, cela aidera peut-être quelqu'un.

Dans le cas où 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 !!!
        }
    }

    ...
}
15
Caner

Hé les gars, cette question et la réponse de snctln m'ont inspiré à travailler sur une solution basée sur la méthode 3 comme ma thèse de baccalauréat. Je sais que le statut actuel n'est pas à usage productif mais j'aimerais entendre ce que vous en pensez! Utiliseriez-vous un tel système? Souhaitez-vous le voir comme un service cloud (sans problème de 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!

Le code source est hébergé sur GitHub https://github.com/MaChristmann/mobile-trial

Quelques informations sur le système: - Le système se compose de trois parties, une Android, un serveur node.js et un configurateur pour gérer plusieurs applications d'essai et des comptes d'éditeur/développeur.

  • Il prend uniquement en charge les essais basés sur le temps et utilise votre compte (Play Store ou autre) plutôt qu'un identifiant de téléphone.

  • Pour la bibliothèque Android, elle est basée sur la bibliothèque de vérification des licences Google Play. Je l'ai modifiée pour se connecter au serveur node.js et, en outre, 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'essai récupérée dans les préférences partagées cryptées AES. Vous pouvez configurer l'heure valide 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 et également la signature numérique de la réponse de vérification de licence. Il dispose également d'une API pour les applications d'essai et les utilisateurs CRUD (éditeur et développeur). Similaire à Licensing Verfication Library, les développeurs 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 votre réponse de licence sur "sous licence", "non sous licence" ou "erreur de serveur".

  • Si vous mettez à jour votre application avec une nouvelle fonctionnalité, 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 dont les licences ont expiré en définissant un code de version qui devrait déclencher cela. Par exemple, l'utilisateur exécute votre application sur versioncode 3 et vous voulez qu'il essaie les fonctionnalités de versioncode 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 la licence Apache 2.0

10
Martin Christmann

La manière la plus simple et la meilleure de le faire est l'implémentation de 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 en tant que 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

6
pstorli

Approche 4: utilisez le temps d'installation de l'application.

Depuis le niveau 9 de l'API (Android 2.3.2, 2.3.1, Android 2.3, Gingerbread), il existe firstInstallTime et lastUpdateTime in PackageInfo.

Pour en savoir plus: Comment obtenir le temps d'installation de l'application depuis Android

5

À mon avis, la meilleure façon de procéder consiste à simplement utiliser la base de données Firebase Realtime:

1) Ajoutez le support Firebase à votre application

2) Sélectionnez 'Authentification anonyme' afin que l'utilisateur n'ait pas à s'inscrire ou même à savoir ce que vous faites. Il est garanti que le lien vers le compte d'utilisateur actuellement authentifié fonctionnera sur tous les appareils.

3) Utilisez l'API de base de données en temps réel pour définir une valeur pour "date_installée". Au moment du lancement, récupérez simplement cette valeur et utilisez-la.

J'ai fait la même chose et ça marche très bien. J'ai pu tester cela à travers 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 date d'installation pour 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 attribue un ID différent au cas où vous avez différents appareils et n'est pas garanti entre installe:/Le seul moyen garanti est d'utiliser Firebase mais de le lier à leur compte Google. 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 un peu moins élégante de vérifier simplement 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 ressaisir toutes les données précédemment ajoutées, mais ne fonctionnerait pas pour un jeu simple.

3
strangetimes

Maintenant, dans la version récente de Android abonnement d'essai gratuit a été ajouté, vous pouvez déverrouiller toutes les fonctionnalités de votre application uniquement 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 je partage juste une idée.

Voici la documentation

3
Vins

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. Can ê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 être sur la plupart des appareils

Stockage externe (écriture dans un fichier) Pas affecté par un effacement des paramètres ou une réinstallation si nous n'écrivez pas dans l'application répertoire privé . Mais: vous oblige à demander à l'utilisateur sa permission au moment de l'exécution dans les versions plus récentes Android versions, donc cela n'est probablement possible que si vous avez besoin de cette autorisation de toute façon. Peut également être sauvegardé.

PackageInfo.firstInstallTime Est réinitialisé après une réinstallation mais stable sur toutes les mises à jour

Connectez-vous à un compte Peu importe que ce soit leur compte Google via Firebase ou celui de votre propre serveur: l'essai est lié au compte. La création d'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 temps 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 Réinitialiser lors d'une réinstallation . Réinitialiser en cas d'événement de sécurité. Peut être réinitialisé par votre application.

Les méthodes (combinées) qui fonctionnent pour vous dépendent de votre application et de l'effort que vous pensez que le Jean moyen mettra à gagner une autre période d'essai. Je recommanderais d'éviter d'utiliser uniquement Firebase anonyme et l'identifiant publicitaire en raison de leur instabilité. Une approche multifactorielle semble donner les meilleurs résultats. Les facteurs disponibles dépendent de votre application et de ses autorisations.

Pour ma propre application, j'ai trouvé que les préférences partagées + firstInstallTime + la 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 enregistré 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 le premierInstallTime. 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. Sur les appareils sans transport de sauvegarde, l'utilisateur peut réinitialiser la version d'essai en réinstallant simplement.

J'ai rendu cette approche disponible sous la forme ne bibliothèque extensible .

3
Fabian Streitel

Par définition, tous payés Android sur le marché peuvent être évaluées pendant 24 heures après l'achat.

Il y a un bouton "Désinstaller et rembourser" qui devient "Désinstaller" après 24 heures.

Je dirais que ce bouton est bien trop proéminent!

1
AlexJReid

Je tombe sur 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 trail. cette méthode est efficace si vous souhaitez livrer la démo et que vous vous inquiétez du paiement et que vous avez besoin d'une démo de tenure 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 .....

1
RQube

Voici comment j'ai abordé la mienne, j'ai créé 2 applications l'une avec une activité d'essai, l'autre sans,

j'ai téléchargé celle sans activité d'essai sur le Play Store en tant qu'application payante,

et celui avec l'activité d'essai comme application gratuite.

L'application gratuite au premier lancement propose des options d'essai et d'achat en magasin, si l'utilisateur sélectionne l'achat en magasin, il redirige vers le magasin pour que l'utilisateur l'achète, mais si l'utilisateur clique sur essai, il les amène à l'activité d'essai

NB: j'ai utilisé l'option 3 comme @snctln mais avec des modifications

premier, 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 sur 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, donc 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.

alors voici 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 emmène au magasin pour acheter.

Le seul inconvénient que je vois ici est que si un tilisateur voyo achète l'application payante et décide de partager avec des applications comme Zender, partager des fichiers ou même héberger le fichier apk directement sur un serveur pour les personnes à télécharger pour libre. Mais je suis sûr que je vais bientôt modifier cette réponse avec une solution à cela ou un lien vers la solution.

J'espère que cela sauve une âme ... un jour

Bon codage ...

0
The Dead Guy

@ snctln l'option 3 peut être facilement ajoutée en ajoutant un fichier php à un serveur web avec php et mysql installés comme beaucoup d'entre eux l'ont.

Du côté Android côté un identifiant (l'ID de l'appareil, le compte google o tout ce que vous voulez) est passé comme argument dans l'URL en utilisant HttpURLConnection et le php renvoie la date de la première installation si elle existe dans le tableau 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 !

0
Lluis Felisart