Creación de una aplicación de prueba de Android que caduca después de un período de tiempo fijo

Tengo una aplicación que quiero llegar al mercado como una aplicación de pago. Me gustaría tener otra versión que sería una versión "de prueba" con un límite de tiempo de decir, 5 días?

¿Cómo puedo hacer esto?

    13 Solutions collect form web for “Creación de una aplicación de prueba de Android que caduca después de un período de tiempo fijo”

    Actualmente la mayoría de los desarrolladores logran esto usando una de las siguientes 3 técnicas.

    La primera aproximación es fácilmente eludida, la primera vez que ejecutas la aplicación, guarda la fecha y la hora en un archivo, base de datos o preferencias compartidas y cada vez que ejecutas la aplicación después de esa comprobación para ver si el período de prueba ha terminado. Esto es fácil de evitar, ya que la desinstalación y reinstalación le permitirá al usuario tener otro período de prueba.

    El segundo enfoque es más difícil de eludir, pero sigue siendo evitable. Utilice una bomba de tiempo codificada. Básicamente, con este enfoque, el código duro será una fecha de finalización del ensayo y todos los usuarios que descarguen y usen la aplicación dejarán de poder utilizar la aplicación al mismo tiempo. He utilizado este enfoque porque es fácil de implementar y en su mayor parte simplemente no tenía ganas de pasar por el problema de la tercera técnica. Los usuarios pueden evitar esto cambiando manualmente la fecha en su teléfono, pero la mayoría de los usuarios no pasarán por el problema de hacer tal cosa.

    La tercera técnica es la única manera que he oído acerca de realmente ser capaz de lograr lo que quieres hacer. Usted tendrá que configurar un servidor, y luego cada vez que su aplicación se inicia su aplicación envía el identificador único de teléfonos al servidor. Si el servidor no tiene una entrada para ese identificador de teléfono, entonces crea una nueva y anota la hora. Si el servidor tiene una entrada para el identificador del teléfono entonces hace una comprobación simple para ver si el período de prueba ha expirado. A continuación, comunica los resultados de la verificación de vencimiento del ensayo a su aplicación. Este enfoque no debe ser evitable, pero requiere la creación de un servidor web y tal.

    Siempre es una buena práctica hacer estas comprobaciones en el onCreate. Si la expiración ha terminado popup un AlertDialog con un enlace de mercado a la versión completa de la aplicación. Sólo incluya un botón "Aceptar" y una vez que el usuario haga clic en "Aceptar", haga una llamada a "finalizar ()" para finalizar la actividad.

    Esta es una vieja pregunta, pero de todos modos, tal vez esto ayudará a alguien.

    En caso de que quiera ir con el enfoque más simplista (que fallará si la aplicación es desinstalada / reinstalada o el usuario cambia la fecha del dispositivo manualmente), así es como podría ser:

    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 !!! } } ... } 

    He desarrollado un SDK de prueba para Android, el cual puede simplemente incluir en su proyecto de Android Studio y se encargará de toda la administración del servidor para usted (incluidos los períodos de gracia sin conexión).

    Para usarlo, simplemente

    Agregue la biblioteca al build.gradle su módulo principal

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

    Inicialice la biblioteca en el método onCreate() su actividad principal

     @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); } 

    Agregar un controlador de devolución de llamada:

     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)); } }; 

    Para iniciar una prueba, llame a mTrialy.startTrial("YOUR_TRIAL_SKU", mTrialyCallback); La clave de la aplicación y el SKU de prueba se pueden encontrar en el tablero de desarrolladores de Trialy .

    Hola chicos esta pregunta y la respuesta de snctln me inspiró a trabajar en una solución basada en el método 3 como mi tesis de licenciatura. Sé que el estado actual no es para uso productivo, pero me encantaría escuchar lo que piensa acerca de él! ¿Utilizarías tal sistema? ¿Le gustaría verlo como un servicio en la nube (no tener problemas con la configuración de un servidor)? Preocupado por cuestiones de seguridad o razones de estabilidad? Una vez que termine el procedimiento de licenciatura quiero seguir trabajando en el software. Así que ahora es el momento en que necesito sus comentarios!

    Sourcecode está alojado en GitHub https://github.com/MaChristmann/mobile-trial

    Alguna información sobre el sistema: – El sistema tiene tres partes, una biblioteca de Android, un servidor node.js y un configurador para administrar múltiples aplicaciones de prueba y cuentas de editor / desarrollador.

    • Sólo admite ensayos basados ​​en tiempo y utiliza su cuenta (play store u otra) en lugar de una identificación de teléfono.

    • Para la biblioteca de Android se basa en la biblioteca de verificación de licencias de Google Play. Lo modificó para conectarse al servidor node.js y además la biblioteca intenta reconocer si un usuario cambió la fecha del sistema. También almacena en caché una licencia de prueba recuperada en las Preferencias compartidas cifradas por AES. Puede configurar el tiempo válido de la caché con el configurador. Si un usuario "borrar datos" la biblioteca forzará una comprobación del servidor.

    • El servidor está usando https y también firma digital la respuesta de la comprobación de licencia. También tiene una API para aplicaciones de prueba CRUD y usuarios (editor y desarrollador). Similares a la Verificación de Licencias Los desarrolladores de la biblioteca pueden probar su implementación de comportamiento en la aplicación de prueba con el resultado de la prueba. Así que en el configurador puede establecer explícitamente su respuesta de licencia a "licenciado", "sin licencia" o "error de servidor".

    • Si actualizas tu aplicación con una nueva función de asno-kicking, es posible que quieras que todo el mundo pueda probar de nuevo. En el configurador puede renovar la licencia de prueba para usuarios con licencias caducadas estableciendo un código de versión que debería activar esto. Por ejemplo, el usuario está ejecutando su aplicación en el código de versión 3 y desea que pruebe las características de la versión de código 4. Si actualiza la aplicación o reinstalar que es capaz de utilizar el período de prueba completa de nuevo porque el servidor sabe en qué versión ha intentado última hora.

    • Todo está bajo la licencia de Apache 2.0

    La manera más fácil y mejor de hacer esto es implementar BackupSharedPreferences.

    Las preferencias se conservan, incluso si la aplicación se desinstala y vuelve a instalar.

    Simplemente guarde la fecha de instalación como una preferencia y estará bien.

    Aquí está la teoría: http://developer.android.com/reference/android/app/backup/SharedPreferencesBackupHelper.html

    Aquí está el ejemplo: Android SharedPreferences Copia de seguridad no funciona

    Enfoque 4: use el tiempo de instalación de la aplicación.

    Desde API nivel 9 (Android 2.3.2, 2.3.1, Android 2.3, GINGERBREAD) hay firstInstallTime y lastUpdateTime en PackageInfo .

    Para obtener más información: Cómo obtener el tiempo de instalación de la aplicación desde android

    Ahora, en la versión reciente de la suscripción de prueba gratuita de Android se ha agregado, puede desbloquear todas las características de su aplicación sólo después de comprar la suscripción dentro de la aplicación para un período de prueba gratuita. Esto le permitirá al usuario usar su aplicación durante un período de prueba, si la aplicación sigue desinstalada después del período de prueba, el dinero de la suscripción se transferirá a usted. No lo he intentado, sino solo compartir una idea.

    Aquí está la documentación

    En mi opinión, la mejor manera de hacerlo es simplemente usar la base de datos en tiempo real Firebase:

    1) Añada soporte de Firebase a su aplicación

    2) Seleccione 'Autenticación anónima' para que el usuario no tenga que inscribirse ni siquiera saber lo que está haciendo. Esto garantiza que se vincule a la cuenta de usuario actualmente autenticada y que funcionará en todos los dispositivos.

    3) Utilice la API Realtime Database para establecer un valor para 'installed_date'. En el momento del lanzamiento, simplemente recuperar este valor y utilizar esto.

    He hecho lo mismo y funciona muy bien. Fui capaz de probar esto a través de desinstalación / reinstalaciones y el valor en la base de datos en tiempo real sigue siendo el mismo. De esta forma, su período de prueba funciona en varios dispositivos de usuario. Usted puede incluso la versión de su install_date para que la aplicación "restablece" la fecha de prueba para cada nueva versión principal.

    ACTUALIZACIÓN : Después de probar un poco más, parece que Firebase anónimo parece asignar un ID diferente en caso de que tenga diferentes dispositivos y no está garantizado entre re-instala: / La única manera garantizada es usar Firebase pero atarlo a su google cuenta. Esto debería funcionar, pero requeriría un paso extra donde el usuario primero necesita iniciar sesión / registrarse.

    He terminado hasta ahora con un acercamiento levemente menos elegante de comprobar simplemente contra las preferencias respaldadas y una fecha almacenada en preferencias sobre instale. Esto funciona para aplicaciones centradas en datos donde es inútil que una persona reinstale la aplicación y vuelva a introducir todos los datos agregados anteriormente, pero no funcionaría para un juego simple.

    Por definición, todas las aplicaciones de Android pagadas en el mercado se pueden evaluar durante 24 horas después de la compra.

    Hay un botón de "Desinstalación y reembolso" que cambia a "Desinstalar" después de 24 horas.

    ¡Yo diría que este botón es demasiado prominente!

    Me encuentro con esta pregunta mientras se busca el mismo problema, creo que podemos utilizar api de fecha libre como http://www.timeapi.org/utc/now o alguna otra api fecha para comprobar la caducidad de la aplicación de seguimiento. Esta manera es eficiente si usted desea entregar la demostración y preocupado por el pago y requiere la demostración de la tenencia del arreglo. 🙂

    Encuentra el código abajo

     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 } } 

    Su solución de trabajo …..

    Después de mirar todas las opciones en este y otros hilos, estas son mis conclusiones

    Preferencias compartidas, base de datos Se puede borrar en la configuración de Android, se pierde después de reinstalar una aplicación. Se puede hacer una copia de seguridad con el mecanismo de copia de seguridad de Android y se restaurará después de reinstalar. La copia de seguridad no siempre está disponible, aunque debería estar en la mayoría de los dispositivos

    Almacenamiento externo (escribiendo en un archivo) No afectado por un borrado de la configuración o una reinstalación si no escribimos en el directorio privado de la aplicación . Pero: requiere que usted pida al usuario su permiso en tiempo de ejecución en nuevas versiones de android, por lo que esto es probablemente sólo factible si necesita ese permiso de todos modos. También se puede hacer una copia de seguridad.

    PackageInfo.firstInstallTime Se restablece después de una reinstalación, pero es estable a través de las actualizaciones

    Iniciar sesión en alguna cuenta No importa si es su cuenta de Google a través de Firebase o una en su propio servidor: el proceso está vinculado a la cuenta. La creación de una nueva cuenta restablecerá la prueba.

    Conexión anónima de Firebase Puede iniciar sesión en un usuario anónimamente y almacenar datos para ellos en Firebase. Pero aparentemente una reinstalación de la aplicación y tal vez otros eventos indocumentados pueden dar al usuario un nuevo ID anónimo , restableciendo su tiempo de prueba. (Google ellos mismos no proporcionan mucha documentación sobre esto)

    ANDROID_ID Puede no estar disponible y puede cambiar en ciertas circunstancias , p. Ej. Restablecimiento de fábrica. Las opiniones sobre si es una buena idea usar esto para identificar dispositivos parecen diferir.

    Play Advertising ID Puede ser restablecido por el usuario. Puede ser inhabilitado por el usuario al inhabilitar el seguimiento de anuncios.

    InstanceID Restablecer en una reinstalación . Restablecer en caso de un evento de seguridad. Se puede restablecer mediante la aplicación.

    ¿Qué métodos (combinación de) funcionan para usted depende de su aplicación y de cuánto esfuerzo cree que el promedio de John pondrá en ganar otro período de prueba. Recomendaría dirigir claramente de usar solamente Firebase anónimo y la identificación de la publicidad debido a su inestabilidad. Un enfoque multi-factor parece que dará los mejores resultados. Los factores que están disponibles para usted dependen de su aplicación y sus permisos.

    Para mi propia aplicación encontré preferencias compartidas + firstInstallTime + copia de seguridad de las preferencias para ser el método menos intrusivo, pero también lo suficientemente eficaz. Debe asegurarse de que sólo solicita una copia de seguridad después de comprobar y almacenar la hora de inicio de prueba en las preferencias compartidas. Los valores de los Prefs compartidos deben tener precedencia sobre el primerInstallTime. Entonces el usuario tiene que reinstalar la aplicación, ejecutarla una vez y luego borrar los datos de la aplicación para restablecer la prueba, que es bastante trabajo. En los dispositivos sin un transporte de copia de seguridad, el usuario puede restablecer la prueba simplemente reinstalando.

    He hecho que el enfoque disponible como una biblioteca extensible .

    Aquí es cómo me fui sobre la mía, he creado 2 aplicaciones con una actividad de prueba el otro sin,

    He subido el uno sin actividad de prueba para jugar tienda como aplicación de pago,

    Y el que tiene la actividad de prueba como aplicación gratuita.

    La aplicación gratuita en el primer lanzamiento tiene opciones para la compra de prueba y de tienda, si el usuario selecciona la compra de la tienda, redirecciona a la tienda para que el usuario la compre, pero si el usuario hace clic en la prueba,

    Nota: Utilicé la opción 3 como @snctln pero con modificaciones

    Primero , no dependía de la hora del dispositivo, tengo mi tiempo desde el archivo php que hace el registro de prueba a la base de datos,

    En segundo lugar , he utilizado el número de serie del dispositivo para identificar de forma única cada dispositivo,

    Por último , la aplicación depende del valor de tiempo devuelto desde la conexión del servidor no su propio tiempo por lo que el sistema sólo puede ser eludido si el número de serie del dispositivo se cambia, lo que es bastante estresante para un usuario.

    Así que aquí va mi código (para la actividad de prueba):

     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); } } 

    Mi archivo php se parece a esto (es una tecnología 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('Ym-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; } } 

    Entonces en la actividad principal utilizo la preferencia compartida (installDate creada en la actividad de prueba) para monitorear el número de días restantes y si los días terminan, bloqueo la IU de la actividad principal con un mensaje que los lleva a la tienda a comprar.

    El único inconveniente que veo aquí es que si un usuario Rogue compra la aplicación de pago y decide compartir con aplicaciones como Zender, compartir archivos o incluso alojar el archivo apk directamente en un servidor para que la gente descargue de forma gratuita. Pero estoy seguro de que pronto editaré esta respuesta con una solución a eso o un enlace a la solución.

    Espero que esto salve un alma … algún día

    Codificación feliz

    @snctln opción 3 se puede hacer fácilmente la adición de un archivo php a un servidor web con php y mysql instalado como muchos de ellos tienen.

    Desde el lado de Android un identificador (el ID de dispositivo, la cuenta de google o lo que quieras) se pasa como argumento en la URL usando HttpURLConnection y el php devuelve la fecha de la primera instalación si existe en la tabla o inserta una nueva fila y Devuelve la fecha actual.

    Funciona bien para mí.

    Si tengo tiempo, publicaré algún código!

    Buena suerte !

    FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.