Tengo una aplicación que quiero lanzar 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, digamos, 5 días.
¿Cómo puedo hacer esto?
Tengo una aplicación que quiero lanzar 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, digamos, 5 días.
¿Cómo puedo hacer esto?
Respuestas:
Actualmente, la mayoría de los desarrolladores logran esto utilizando una de las siguientes 3 técnicas.
El primer enfoque se evita fácilmente, la primera vez que ejecuta la aplicación, guarde la fecha / hora en un archivo, base de datos o preferencias compartidas y cada vez que ejecute la aplicación después de eso, verifique si el período de prueba ha finalizado. Esto es fácil de eludir porque desinstalar y reinstalar le permitirá al usuario tener otro período de prueba.
El segundo enfoque es más difícil de eludir, pero aún así eludible. Utilice una bomba de tiempo codificada de forma rígida. Básicamente, con este enfoque, se codificará una fecha de finalización para la prueba, y todos los usuarios que descarguen y usen la aplicación dejarán de poder usar 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 eludir esto cambiando manualmente la fecha en su teléfono, pero la mayoría de los usuarios no se tomarán la molestia de hacer tal cosa.
La tercera técnica es la única forma que he escuchado de poder realmente lograr lo que quieres hacer. Tendrá que configurar un servidor, y luego, cada vez que se inicia su aplicación, su aplicación envía el identificador único del teléfono al servidor. Si el servidor no tiene una entrada para esa identificación de teléfono, entonces crea una nueva y anota la hora. Si el servidor tiene una entrada para la identificación del teléfono, entonces hace una simple verificación para ver si el período de prueba ha expirado. Luego, comunica los resultados de la verificación de vencimiento de la prueba a su aplicación. Este enfoque no debe ser evitable, pero requiere la configuración de un servidor web y demás.
Siempre es una buena práctica realizar estas comprobaciones en onCreate. Si el vencimiento ha finalizado, aparecerá un AlertDialog con un enlace de mercado a la versión completa de la aplicación. Solo incluya un botón "Aceptar" y, una vez que el usuario haga clic en "Aceptar", haga una llamada a "finalizar ()" para finalizar la actividad.
Desarrollé un SDK de prueba de Android que simplemente puede colocar en su proyecto de Android Studio y se encargará de toda la administración del lado del servidor por usted (incluidos los períodos de gracia fuera de línea).
Para usarlo, simplemente
Agregue la biblioteca a su módulo principal build.gradle
dependencies {
compile 'io.trialy.library:trialy:1.0.2'
}
Inicialice la biblioteca en el onCreate()
método de 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);
}
Agregue 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 la mTrialy.startTrial("YOUR_TRIAL_SKU", mTrialyCallback);
clave de su aplicación y encontrará el SKU de prueba en el panel de control del desarrollador de Trialy .
Esta es una vieja pregunta, pero de todos modos, tal vez esto ayude a alguien.
En caso de que desee optar por el enfoque más simplista (que fallará si la aplicación se desinstala / reinstala 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 !!!
}
}
...
}
getTime
no lo es getTimeInMillis
.
Esta pregunta y la respuesta de snctln me inspiraron 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 saber lo que piensas al respecto! ¿Usarías un sistema así? ¿Le gustaría verlo como un servicio en la nube (sin problemas para configurar un servidor)? ¿Preocupado por problemas de seguridad o razones de estabilidad?
Tan pronto como termine el trámite de licenciatura quiero seguir trabajando en el software. ¡Así que ahora es el momento de que necesito tus comentarios!
El código fuente 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.
Solo admite pruebas basadas en el tiempo y usa 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 modifiqué para conectarme 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 Preferencias compartidas cifradas con AES. Puede configurar la hora válida de la caché con el configurador. Si un usuario "borra datos", la biblioteca forzará una verificación del lado del servidor.
El servidor utiliza https y también firma digitalmente la respuesta de verificación de licencia. También tiene una API para aplicaciones y usuarios de prueba de CRUD (editor y desarrollador). De manera similar a la biblioteca de verificación de licencias, los desarrolladores pueden probar la implementación de su comportamiento en la aplicación de prueba con el resultado de la prueba. Por lo tanto, en el configurador puede establecer explícitamente la respuesta de su licencia en "con licencia", "sin licencia" o "error del servidor".
Si actualizas tu aplicación con una nueva característica espectacular, es posible que desees que todos puedan volver a intentarlo. En el configurador, puede renovar la licencia de prueba para usuarios con licencias vencidas configurando un código de versión que debería activarlo. Por ejemplo, el usuario está ejecutando su aplicación en el código de versión 3 y desea que pruebe las funciones del código de versión 4. Si actualiza la aplicación o la reinstala, puede usar el período de prueba completo nuevamente porque el servidor sabe en qué versión la probó por última vez. hora.
Todo está bajo la licencia Apache 2.0
El mas facil y mejor forma de hacerlo es implementar BackupSharedPreferences.
Las preferencias se conservan, incluso si la aplicación se desinstala y se vuelve a instalar.
Simplemente guarde la fecha de instalación como preferencia y estará listo.
Aquí está la teoría: http://developer.android.com/reference/android/app/backup/SharedPreferencesBackupHelper.html
Aquí está el ejemplo: La copia de seguridad de preferencias compartidas de Android no funciona
Método 4: utilice el tiempo de instalación de la aplicación.
Desde el nivel de API 9 (Android 2.3.2, 2.3.1, Android 2.3, GINGERBREAD) hay firstInstallTime y lastUpdateTime en PackageInfo
.
Para leer más: 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 que se ha agregado, puede desbloquear todas las funciones de su aplicación solo después de comprar la suscripción dentro de la aplicación para un período de prueba gratuito. Esto permitirá al usuario usar su aplicación durante un período de prueba, si la aplicación aún se desinstala después del período de prueba, se le transferirá el dinero de la suscripción. No lo he intentado, solo comparto una idea.
En mi opinión, la mejor manera de hacer esto es simplemente usar Firebase Realtime Database:
1) Agregue soporte de Firebase a su aplicación
2) Seleccione 'Autenticación anónima' para que el usuario no tenga que registrarse o incluso saber lo que está haciendo. Se garantiza que esto se vinculará a la cuenta de usuario actualmente autenticado y, por lo tanto, funcionará en todos los dispositivos.
3) Utilice la API de Realtime Database para establecer un valor para 'installed_date'. En el momento del lanzamiento, simplemente recupere este valor y utilícelo.
Yo he hecho lo mismo y funciona muy bien. Pude probar esto a través de desinstalaciones / reinstalaciones y el valor en la base de datos en tiempo real sigue siendo el mismo. De esta manera, su período de prueba funciona en varios dispositivos de usuario. Incluso puede versionar su install_date para que la aplicación 'restablezca' la fecha de prueba para cada nueva versión principal.
ACTUALIZAR : Después de probar un poco más, parece que Firebase anónimo parece asignar una ID diferente en caso de que tenga diferentes dispositivos y no está garantizado entre reinstalaciones: / La única forma garantizada es usar Firebase pero vincularlo a su google cuenta. Esto debería funcionar, pero requeriría un paso adicional donde el usuario primero necesita iniciar sesión / registrarse.
Hasta ahora, he terminado con un enfoque un poco menos elegante de simplemente verificar las preferencias respaldadas y una fecha almacenada en las preferencias al instalar. Esto funciona para aplicaciones centradas en datos donde no tiene sentido que una persona vuelva a instalar la aplicación y vuelva a ingresar todos los datos agregados anteriormente, pero no funcionaría para un juego simple.
Después de ver todas las opciones en este y otros hilos, estos son mis hallazgos.
Preferencias compartidas, base de datos Puede borrarse en la configuración de Android, perderse 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 una reinstalación. Es posible que la copia de seguridad no siempre esté disponible, aunque debería estar en la mayoría de los dispositivos
Almacenamiento externo (escribir en un archivo) No se ve 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 le pidas permiso al usuario en tiempo de ejecución en las versiones más nuevas de Android, por lo que probablemente solo sea factible si necesitas 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 en todas 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: la versión de prueba está vinculada a la cuenta. Crear una nueva cuenta restablecerá la prueba.
Inicio de sesión anónimo en Firebase Puede iniciar sesión con un usuario de forma anónima y almacenar sus datos en Firebase. Pero aparentemente una reinstalación de la aplicación y tal vez otros eventos indocumentados pueden darle al usuario una nueva identificación anónima , restableciendo su tiempo de prueba. (Google mismo no proporciona mucha documentación sobre esto)
ANDROID_ID Puede no estar disponible y puede cambiar bajo ciertas circunstancias , por ejemplo, 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. El usuario puede inhabilitarlo si se excluye del seguimiento de anuncios.
InstanceID Reset en una reinstalación . Reiniciar en caso de un evento de seguridad. Puede ser restablecido por su aplicación.
Qué (combinación de) métodos funcionan para usted depende de su aplicación y de cuánto esfuerzo cree que el John promedio pondrá para obtener otro período de prueba. Recomendaría evitar el uso solamente el anonimato Firebase y el ID de publicidad debido a su inestabilidad. Un enfoque multifactorial 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é que las preferencias compartidas + firstInstallTime + copia de seguridad de las preferencias es el método menos intrusivo pero también lo suficientemente efectivo. Debe asegurarse de solicitar una copia de seguridad solo después de verificar y almacenar la hora de inicio de la prueba en las preferencias compartidas. Los valores de las preferencias compartidas deben tener prioridad sobre firstInstallTime. Luego, el usuario tiene que reinstalar la aplicación, ejecutarla una vez y luego borrar los datos de la aplicación para restablecer la versión de prueba, que es bastante trabajo. Sin embargo, en dispositivos sin un transporte de respaldo, el usuario puede restablecer la versión de prueba simplemente reinstalando.
He hecho que ese enfoque esté disponible como una biblioteca extensible .
Por definición, todas las aplicaciones de pago de Android en el mercado se pueden evaluar durante 24 horas después de la compra.
Hay un botón 'Desinstalar y reembolsar' 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 busco el mismo problema, creo que podemos utilizar la API de fecha gratuita como http://www.timeapi.org/utc/now o alguna otra API de fecha para verificar la caducidad de la aplicación de seguimiento. de esta manera es eficiente si desea entregar la demostración y está preocupado por el pago y requiere una demostración de tenencia fija. :)
encuentra el código a continuación
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 .....
Así es como hice el mío, creé 2 aplicaciones, una con actividad de prueba y la otra sin,
Cargué el que no tenía actividad de prueba en Play Store como aplicación paga,
y el que tiene actividad de prueba como aplicación gratuita.
La aplicación gratuita en el primer lanzamiento tiene opciones de prueba y compra en la tienda, si el usuario selecciona la compra en la tienda, se redirige a la tienda para que el usuario compre, pero si el usuario hace clic en prueba, lo lleva a la actividad de prueba.
NB: utilicé la opción 3 como @snctln pero con modificaciones
Primero , no dependí de la hora del dispositivo, obtuve mi tiempo del archivo php que hace el registro de prueba en la base de datos,
en segundo lugar , utilicé 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 por la conexión del servidor, no de su propio tiempo, por lo que el sistema solo se puede eludir si se cambia el número de serie del dispositivo, lo cual es bastante estresante para el 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 ve así (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('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;
}
}
luego, en la actividad principal, uso la preferencia compartida (installDate creada en la actividad de prueba) para monitorear la cantidad de días restantes y, si los días terminaron, bloqueo la interfaz de usuario de la actividad principal con un mensaje que los lleva a la tienda para comprar.
El único inconveniente que veo aquí es que si un usuario de Rogue compra la aplicación paga y decide compartirla con aplicaciones como Zender, compartir archivos o incluso alojar el archivo apk directamente en un servidor para que la gente lo descargue gratis. 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 de puede hacer fácilmente agregando un archivo php a un servidor web con php y mysql instalados como muchos de ellos tienen.
Desde el lado de Android, se pasa un identificador (la ID del dispositivo, la cuenta de Google o lo que quieras) 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 !