Question

J'ai une application que je veux frapper le marché comme une application payante. Je voudrais avoir une autre version qui serait une version « d'essai » avec une limite de temps de dire, 5 jours?

Comment puis-je prendre?

Était-ce utile?

La solution

À l'heure actuelle la plupart des développeurs y parvenir grâce à l'un des 3 techniques suivantes.

La première approche est facilement contournée, la première fois que vous exécutez l'application enregistrer la date / heure dans un fichier, base de données ou préférences partagées et chaque fois que vous exécutez l'application après vérification pour voir si la période d'essai est terminée. Cela est facile à contourner car la désinstallation et la réinstallation permettra à l'utilisateur d'avoir une autre période d'essai.

La seconde approche est plus difficile à contourner, mais encore circumventable. Utilisez une bombe à retardement codée en dur. Fondamentalement, avec cette approche, vous serez coder en dur une date de fin pour le procès, et tous les utilisateurs qui téléchargent et utilisent l'application cessera d'être en mesure d'utiliser l'application en même temps. Je l'ai utilisé cette approche car il est facile à mettre en œuvre et pour la plupart, je ne me sentais pas comme passer par la peine de la troisième technique. Les utilisateurs peuvent contourner ce problème en modifiant manuellement la date sur leur téléphone, mais la plupart des utilisateurs ne passeront pas par la peine de faire une telle chose.

La troisième technique est la seule façon que je l'ai entendu parler de 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 le téléphones identifiant unique au serveur. Si le serveur ne dispose pas d'entrée pour cet identifiant de téléphone, puis il fait une nouvelle et note le temps. Si le serveur dispose d'une entrée pour l'ID de téléphone, puis il fait une simple vérification pour voir si la période d'essai a expiré. Il communique ensuite les résultats de l'expiration d'essai chèque à votre demande. Cette approche ne doit pas être circumventable, mais ne nécessite la mise en place d'un serveur web et tel.

Il est toujours bon de faire ces contrôles dans le onCreate. Si l'expiration a terminé un pop-up AlertDialog avec un au maximum version de l'application. Seulement inclure une touche « OK », et une fois que l'utilisateur clique sur « OK » faire un appel à « la fin () » pour mettre fin à l'activité.

Autres conseils

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

Pour l'utiliser, simplement

Ajouter la bibliothèque au build.gradle de votre module principal

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

Initialiser la bibliothèque dans la méthode de 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);
}

Ajouter 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 commencer un procès, appel mTrialy.startTrial("YOUR_TRIAL_SKU", mTrialyCallback); La clé de l'application et essai SKU peuvent être trouvés dans votre Trialy tableau de bord développeur.

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

Dans le cas où vous voulez aller avec le l'application approche la plus simpliste (qui échouera si est désinstallés / réinstallées ou utilisateur modifie la date de l'appareil manuellement), voici comment il 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 !!!
        }
    }

    ...
}

Hé les gars cette question et la réponse de snctln m'a inspiré pour travailler sur une solution basée sur la méthode 3 que ma thèse de baccalauréat. Je sais que l'état actuel n'est pas pour une utilisation productive mais j'aimerais entendre ce que vous en pensez! Souhaitez-vous utiliser un tel système? Voulez-vous voir en tant que service de nuage (ne pas avoir des problèmes avec la configuration d'un serveur)? Préoccupés par les problèmes de sécurité ou pour des raisons de stabilité? Un peu comme je l'ai terminé la procédure de baccalauréat, je veux continuer à travailler sur le logiciel. Alors maintenant, son moment 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 comporte trois parties, une bibliothèque Android, un serveur Node.js et un configurateur pour gérer plusieurs applications d'essai et les comptes éditeur / développeur de

  • Il ne supporte que les essais basés sur le temps et il utilise votre (magasin de jeu ou autre) compte plutôt qu'un numéro de téléphone.

  • Pour bibliothèque Android il est basé sur la bibliothèque de vérification des licences Google Play. Je l'ai modifié pour se 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 un procès-licence récupérée en AES cryptée Préférences partagées. Vous pouvez configurer le temps valide du cache avec le configurateur. Si un utilisateur « données claires » la bibliothèque forceront un contrôle côté serveur.

  • Le serveur utilise https et la signature numérique également la réponse licence chèque. Il a également une API pour les applications d'essai CRUD et les utilisateurs (éditeur et développeur). Similiar pour obtenir un permis Verfication développeurs Library peuvent tester leur mise en œuvre de comportement dans l'application d'essai avec un résultat de test. Alors vous dans le configurateur, vous pouvez explicitement définir votre réponse de licence à « sous licence », « non autorisé » ou « erreur de serveur ».

  • Si vous mettez à jour votre application avec un âne-kicking nouvelle fonctionnalité vous voudrez peut-être que tout le monde peut essayer à nouveau. Dans le configurateur, vous pouvez renouveler la licence d'essai pour les utilisateurs ayant des licences expirées en établissant un codeVersion qui devrait déclencher cela. Par exemple utilisateur exécute votre application sur codeVersion 3 und que vous voulez lui pour essayer caractéristiques codeVersion 4. S'il met à jour l'application ou le réinstaller, il est capable d'utiliser la période d'essai complète à nouveau parce que le serveur sait sur quelle version il a essayé la dernière temps.

  • Tout est sous la licence Apache 2.0

Le plus simple et meilleur façon de faire est la mise en œuvre BackupSharedPreferences.

Les préférences sont conservées, même si l'application est désinstallée et réinstallée.

Il suffit d'enregistrer la date d'installation comme une préférence et vous êtes bon pour aller.

Voici la théorie: http://developer.android.com/reference/android/app/ sauvegarde / SharedPreferencesBackupHelper.html

Voici l'exemple: SharedPreferences Android sauvegarde ne fonctionne pas

Approche 4:. Utiliser l'application installer le temps

Depuis niveau de l'API 9 (Android 2.3.2, 2.3.1, Android 2.3, Gingerbread) il y a firstInstallTime et Comment obtenir l'application installer le temps d'Android

Maintenant dans la version récente de l'abonnement d'essai gratuit Android a été ajouté, vous pouvez déverrouiller toutes les fonctionnalités de votre application seulement après l'achat de l'abonnement dans l'application pour une période d'essai gratuite. Cela permettra à l'utilisateur d'utiliser votre application pour une période d'essai, si l'application est toujours désinstallée après la période d'essai, puis l'argent de l'abonnement sera transféré à vous. Je ne l'ai pas essayé, mais le partage juste une idée.

est ici la documentation

A mon avis, la meilleure façon de le faire est d'utiliser simplement la base de données en temps réel Firebase:

1) Ajouter un support Firebase à votre application

2) Sélectionnez « authentification anonyme » afin que l'utilisateur n'a pas à enregistrer ou même de savoir ce que vous faites. Ceci est garanti pour créer un lien vers le compte utilisateur authentifié et ainsi fonctionnera 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érer simplement cette valeur et utiliser.

Je l'ai fait la même chose et il fonctionne très bien. J'ai pu tester ce dans désinstaller / réinstalle et la valeur de la base de données en temps réel reste le même. De cette façon, votre période d'essai fonctionne sur plusieurs appareils des utilisateurs. Vous pouvez même la version de votre install_date afin que l'application "remet à zéro la date d'essai pour chaque nouvelle version majeure.

UPDATE : Après avoir testé un peu plus, il semble anonyme Firebase semble attribuer un ID différent dans le cas où vous avez des appareils différents et n'est pas garantie entre réinstalle: / Le seul moyen garanti est d'utiliser Firebase mais l'attacher à leur compte Google. Cela devrait fonctionner, mais nécessiterait une étape supplémentaire dans laquelle l'utilisateur doit d'abord ouvrir une session / inscription.

J'ai jusqu'ici fini avec une approche légèrement moins élégante de vérifier simplement contre 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 Retapez toutes les données précédemment ajoutées, mais ne fonctionnerait pas pour un jeu simple.

Après avoir examiné toutes les options dans ce domaine et d'autres sujets, ce sont mes conclusions

Préférences partagées, base de données Peut être effacé dans les paramètres Android, perdu après une réinstallation de l'application. Peut être sauvegardé avec le mécanisme de sauvegarde android et sera restauré après une réinstallation. Sauvegarde ne sont pas toujours disponibles, mais devrait être sur la plupart des périphériques

Stockage externe (écrire dans un fichier) Non affecté par un ressort clairement des paramètres ou une réinstallation si nous don « t écrire dans le répertoire privé de l'application . Mais: vous oblige à demander à l'utilisateur la permission à l'exécution dans les nouvelles versions Android, il est sans doute possible que si vous avez besoin que l'autorisation de toute façon. Peut également être sauvegardé.

PackageInfo.firstInstallTime Est remis à zéro après une réinstallation mais stable dans les mises à jour

Se connecter à un certain compte Peu importe si elle est leur compte Google via Firebase ou un dans votre propre serveur: le procès est lié au compte. Faire un nouveau compte réinitialisera le procès.

Firebase signe anonyme Vous pouvez vous connecter à un utilisateur anonyme et stocker des données pour eux dans Firebase. Mais apparemment une réinstallation de l'application et peut-être d'autres événements en situation irrégulière peut donner à l'utilisateur un nouvel ID anonyme , la réinitialisation de leur temps d'essai. (Google eux-mêmes ne fournissent pas beaucoup de documentation à ce sujet)

ANDROID_ID ne pas être disponibles et peuvent changer dans certaines circonstances , par exemple réinitialisation d'usine. Les opinions sur que ce soit une bonne idée de l'utiliser pour identifier les périphériques semblent différer.

Lire Publicité ID Peut être remis à zéro par l'utilisateur. peut être désactivé par l'utilisateur en se retirant du suivi des annonces.

InstanceID réarmés Réinstaller. Réinitialiser en cas d'un événement de sécurité. Peut être remis à zéro par votre application.

Les méthodes qui (combinaison de) travailler pour vous dépend de votre application et combien d'effort vous pensez que la moyenne John sera mis en gagner une autre période d'essai. Je recommande de direction claire de l'utilisation de uniquement anonyme Firebase et publicité ID en raison de leur instabilité. Une approche multifactorielle semble que cela donnera les meilleurs résultats. Quels sont les facteurs à votre disposition dépend de vous et de l'application de ses autorisations.

Pour ma propre application, j'ai trouvé les préférences partagées + firstInstallTime + sauvegarde des préférences pour être la méthode la moins intrusive, mais aussi assez efficace. Vous devez vous assurer que vous demandez seulement une sauvegarde après vérification et le stockage de l'heure de début d'essai dans les préférences partagées. Les valeurs de la Préf commune doivent avoir la priorité sur la firstInstallTime. Ensuite, l'utilisateur doit réinstaller l'application, exécutez une fois, puis effacer les données de l'application de rétablir le procès, ce qui est tout à fait beaucoup de travail. Sur les appareils sans le transport de sauvegarde, l'utilisateur peut réinitialiser le procès simplement réinstallant, cependant.

Je l'ai fait cette approche disponible une bibliothèque extensible

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

Il y a un bouton « Désinstaller et remboursement », qui passe à « Désinstaller » au bout de 24 heures.

Je dirais ce bouton est trop important!

Je viens sur cette question tout en recherchant le même problème, je pense que nous pouvons utiliser api date libre comme http : //www.timeapi.org/utc/now ou une autre date api pour vérifier l'expiration de l'application de sentiers. cette façon est efficace si vous souhaitez offrir la démo et inquiet sur le paiement et besoin de fixer démo permanence. :)

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 je suis allé la mienne, J'ai créé 2 applications avec une activité d'essai l'autre sans,

i uploadé une sans activité d'essai pour jouer au magasin comme application payante,

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

L'application gratuite sur le premier lancement a des options d'achat d'essai et de stocker, si l'utilisateur magasin sélectionnez l'acheter réoriente au magasin pour l'utilisateur à acheter mais si l'utilisateur clique sur procès, il les prendre à l'activité d'essai

NB: je l'option 3 comme @snctln mais avec des modifications

premier , je ne dépends du temps de l'appareil, j'ai obtenu mon temps à partir du fichier php qui fait l'enregistrement du procès au db,

deuxièmement , i, à utiliser son numéro de série pour identifier de manière unique chaque dispositif,

enfin , l'application dépend de la valeur temporelle de retour de la connexion du serveur pas son propre temps pour que le système ne peut être contournée si l'appareil le numéro de série est modifié, ce qui est assez stressant pour un utilisateur .

va ici 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 (sa technologie REST-mince):

/**
     * 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 i utilise la préférence partagée (InstallDate créé l'activité d'essai) pour surveiller le nombre de jours restants et si les jours sont révolus i bloquer l'interface utilisateur principale d'activité avec un message qui les emmè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, partage de fichiers ou hôte, même le fichier apk directement sur un serveur pour les personnes à télécharger gratuitement. Mais je suis sûr que je vais bientôt modifier cette réponse avec une solution à ce ou un lien vers la solution.

Espérons que cela sauve une âme ... un jour

Le codage heureux ...

@snctln 3 peut être facilement fait l'ajout d'un fichier php à un serveur web avec php et mysql installé comme un grand nombre d'entre eux ont.

Du côté Android un identifiant (l'ID de l'appareil, compte google o tout ce que vous voulez) est passé comme argument dans l'URL en utilisant HttpURLConnection et le php retourne la date de la première installation si elle existe dans le tableau ou il insère une nouvelle ligne et retourner la date actuelle.

Il fonctionne très bien pour moi.

Si j'ai le temps je vais poster un code!

Bonne chance!

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top