Создание пробного приложения Android, срок действия которого истекает через фиксированный период времени.
У меня есть приложение, которое я хочу выпустить на рынок в качестве платного приложения. Я хотел бы иметь другую версию, которая будет "пробной" версией с ограничением по времени, скажем, 5 дней?
Как я могу сделать это?
13 ответов
В настоящее время большинство разработчиков выполняют это, используя один из следующих 3 методов.
Первый подход легко обойти, при первом запуске приложения сохраните дату / время в файле, базе данных или общих настройках и каждый раз, когда вы запускаете приложение после этой проверки, чтобы увидеть, закончился ли пробный период. Это легко обойти, потому что удаление и переустановка позволит пользователю иметь еще один испытательный срок.
Второй подход сложнее обойти, но все же обходим. Используйте жестко закодированную бомбу замедленного действия. В основном, при таком подходе вы будете жестко указывать дату окончания пробного периода, и все пользователи, которые загружают и используют приложение, перестанут иметь возможность использовать приложение одновременно. Я использовал этот подход, потому что его легко реализовать, и по большей части мне просто не хотелось переживать проблему третьего метода. Пользователи могут обойти это, вручную изменив дату на своем телефоне, но большинство пользователей не будут испытывать затруднений, чтобы сделать такую вещь.
Третий метод - это единственный способ, который я слышал о том, чтобы действительно быть в состоянии выполнить то, что вы хотите сделать. Вам нужно будет настроить сервер, а затем, когда ваше приложение будет запущено, ваше приложение отправляет на сервер уникальный идентификатор телефона. Если на сервере нет записи для этого идентификатора телефона, он создает новую и записывает время. Если на сервере действительно есть запись для идентификатора телефона, он выполняет простую проверку, чтобы проверить, не истек ли пробный период. Затем он сообщает результаты проверки истечения пробного периода обратно в вашу заявку. Этот подход не должен быть обходным, но требует настройки веб-сервера и тому подобного.
Это всегда хорошая практика делать эти проверки в onCreate. Если срок действия истек, выведите всплывающее окно AlertDialog с рыночной ссылкой на полную версию приложения. Включите только кнопку "ОК", и, как только пользователь нажмет "ОК", вызовите "finish()", чтобы завершить действие.
Я разработал Android Trial SDK, который вы можете просто вставить в свой проект Android Studio, и он позаботится обо всем управлении на стороне сервера для вас (включая автономные льготные периоды).
Чтобы использовать это, просто
Добавьте библиотеку в ваш основной модуль build.gradle
dependencies {
compile 'io.trialy.library:trialy:1.0.2'
}
Инициализируйте библиотеку в вашей основной деятельности onCreate()
метод
@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);
}
Добавьте обработчик обратного вызова:
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));
}
};
Чтобы начать пробную версию, позвоните mTrialy.startTrial("YOUR_TRIAL_SKU", mTrialyCallback);
Ключ вашего приложения и пробную версию SKU можно найти на панели инструментов разработчика Trialy.
Это старый вопрос, но в любом случае, может, это кому-нибудь поможет.
Если вы хотите использовать самый упрощенный подход(который не удастся, если приложение будет удалено / переустановлено или пользователь вручную изменит дату устройства), это может быть так:
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 !!!
}
}
...
}
Привет, ребята, этот вопрос и ответ snctln вдохновили меня на работу над решением, основанным на методе 3, в качестве дипломной работы бакалавра. Я знаю, что текущий статус не для продуктивного использования, но я хотел бы услышать, что вы думаете об этом! Будете ли вы использовать такую систему? Хотели бы вы видеть его как облачный сервис (без проблем с настройкой сервера)? Обеспокоены вопросами безопасности или соображениями стабильности? Как только я закончу бакалавриат, я хочу продолжить работу над программным обеспечением. Так что теперь время, когда мне нужны ваши отзывы!
Исходный код размещен на GitHub https://github.com/MaChristmann/mobile-trial
Немного информации о системе: - Система состоит из трех частей: библиотека Android, сервер node.js и конфигуратор для управления несколькими пробными приложениями и учетными записями издателя / разработчика.
Он поддерживает только временные испытания и использует вашу учетную запись (игровой магазин или другую), а не идентификатор телефона.
Для библиотеки Android она основана на библиотеке проверки лицензии Google Play. Я изменил его, чтобы подключиться к серверу node.js, и дополнительно библиотека пытается распознать, изменил ли пользователь системную дату. Он также кэширует восстановленную пробную лицензию в зашифрованных общих настройках AES. Вы можете настроить действительное время кеша с помощью конфигуратора. Если пользователь "очистит данные", библиотека принудительно выполнит проверку на стороне сервера.
Сервер использует https, а также цифровую подпись ответа проверки лицензии. Он также имеет API для пробных приложений и пользователей CRUD (издатель и разработчик). Аналогично проверке подлинности лицензирования разработчики библиотеки могут проверить свою реализацию поведения в пробном приложении с результатами теста. Таким образом, вы в конфигураторе можете явно указать свой лицензионный ответ как "лицензированный", "не лицензированный" или "ошибка сервера".
Если вы обновите свое приложение с помощью новой функции, вы можете захотеть, чтобы каждый мог попробовать ее снова. В конфигураторе вы можете продлить пробную лицензию для пользователей с истекшими лицензиями, установив код версии, который должен инициировать это. Например, пользователь запускает ваше приложение с кодом версии 3, и вы хотите, чтобы он попробовал функции кода версии 4. Если он обновляет приложение или переустанавливает его, он может снова использовать полный пробный период, поскольку сервер знает, в какой версии он пробовал его в последний раз. время.
Все под лицензией Apache 2.0
Самый простой и лучший способ сделать это - реализовать BackupSharedPreferences.
Настройки сохраняются, даже если приложение удалено и переустановлено.
Просто сохраните дату установки в качестве предпочтения, и вы готовы к работе.
Вот теория: http://developer.android.com/reference/android/app/backup/SharedPreferencesBackupHelper.html
Вот пример: резервное копирование Android SharedPreferences не работает
Подход 4: используйте время установки приложения.
Поскольку уровень API 9 (Android 2.3.2, 2.3.1, Android 2.3, GINGERBREAD), есть firstInstallTime и lastUpdateTime в PackageInfo
,
Читать больше: Как получить время установки приложения с Android
Посмотрев на все варианты в этой и других темах, это мои выводы
Общие настройки, базы данных Можно очистить в настройках Android, потерянные после переустановки приложения. Можно создать резервную копию с помощью механизма резервного копирования Android и будет восстановлен после переустановки. Резервная копия не всегда доступна, хотя должна быть на большинстве устройств
Внешнее хранилище (запись в файл) Не зависит от очистки или переустановки, если мы не записываем в личный каталог приложения. Но: требует, чтобы вы запросили у пользователя его разрешение во время выполнения в новых версиях Android, так что это, вероятно, возможно только в том случае, если вам все равно нужно это разрешение. Можно также сделать резервную копию.
PackageInfo.firstInstallTime Сбрасывается после переустановки, но стабильно при обновлении
Войдите в какой-либо аккаунт. Не имеет значения, является ли это их аккаунтом Google через Firebase или аккаунтом на вашем собственном сервере: пробная версия привязана к аккаунту. Создание новой учетной записи приведет к сбросу пробной версии.
Анонимный вход в Firebase Вы можете анонимно войти в систему и сохранить данные для него в Firebase. Но, очевидно, переустановка приложения и, возможно, другие недокументированные события могут дать пользователю новый анонимный идентификатор, сбрасывая его пробное время. (Сами Google не предоставляют много документации по этому вопросу)
ANDROID_ID Может быть недоступен и может меняться при определенных обстоятельствах, например, при сбросе настроек. Мнения о том, стоит ли использовать это для идентификации устройств, похоже, отличаются.
Play Advertising ID Может быть сброшен пользователем. Может быть отключен пользователем, отключив отслеживание рекламы.
InstanceID Сброс при переустановке. Сброс в случае события безопасности. Может быть сброшено вашим приложением.
Какой (комбинация) методов работает для вас, зависит от вашего приложения и от того, сколько усилий, как вы думаете, средний Джон приложит для получения еще одного испытательного периода. Я бы рекомендовал избегать использования только анонимных Firebase и Advertising ID из-за их нестабильности. Многофакторный подход, похоже, даст лучшие результаты. Какие факторы вам доступны, зависит от вашего приложения и его разрешений.
Для моего собственного приложения я нашел общие предпочтения + firstInstallTime + резервное копирование настроек как наименее навязчивый, но и достаточно эффективный метод. Вы должны убедиться, что запрашиваете резервную копию только после проверки и сохранения времени запуска пробной версии в общих настройках. Значения в общих настройках должны иметь приоритет над firstInstallTime. Затем пользователь должен переустановить приложение, запустить его один раз, а затем очистить данные приложения, чтобы сбросить пробную версию, что довольно много работы. На устройствах без резервного копирования пользователь может сбросить пробную версию, просто переустановив.
Я сделал этот подход доступным как расширяемая библиотека.
На мой взгляд, лучший способ сделать это - просто использовать базу данных Firebase Realtime:
1) Добавьте поддержку Firebase в ваше приложение
2) Выберите "Анонимная аутентификация", чтобы пользователю не приходилось регистрироваться или даже знать, что вы делаете. Это гарантирует связь с аутентифицированной в настоящее время учетной записью пользователя и будет работать на всех устройствах.
3) Используйте API базы данных в реальном времени, чтобы установить значение для "instal_date". Во время запуска просто получите это значение и используйте его.
Я сделал то же самое, и это прекрасно работает. Я смог проверить это при удалении / повторной установке, и значение в базе данных в реальном времени остается прежним. Таким образом, пробный период работает на нескольких пользовательских устройствах. Вы даже можете установить версию install_date, чтобы приложение "сбрасывало" дату пробной версии для каждого нового основного выпуска.
ОБНОВЛЕНИЕ: После тестирования немного больше, кажется, что анонимный Firebase выделяет другой идентификатор в случае, если у вас есть разные устройства и между переустановками не гарантируется:/ Единственный гарантированный способ - использовать Firebase, но привязать его к своему Google учетная запись. Это должно работать, но потребует дополнительного шага, когда пользователь сначала должен войти / зарегистрироваться.
До сих пор у меня получился немного менее элегантный подход, состоящий в простой проверке резервных копий настроек и даты, сохраненной в настройках после установки. Это работает для приложений, ориентированных на данные, когда человеку бессмысленно переустанавливать приложение и повторно вводить все ранее добавленные данные, но это не сработает для простой игры.
Теперь в последней версии бесплатной пробной подписки на Android вы можете разблокировать все функции своего приложения только после покупки подписки в приложении в течение бесплатного пробного периода. Это позволит пользователю использовать ваше приложение в течение пробного периода. Если приложение все еще будет удалено после испытательного периода, вам будут переведены деньги за подписку. Я не пробовал, а просто поделился идеей.
Я сталкиваюсь с этим вопросом при поиске той же самой проблемы, я думаю, что мы можем использовать бесплатный API даты, например http://www.timeapi.org/utc/now или какой-либо другой API даты, чтобы проверить, не истекло ли приложение trail. Этот способ эффективен, если вы хотите доставить демо-версию и беспокоитесь об оплате, и вам требуется демонстрация с фиксированным сроком пребывания.:)
найдите код ниже
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
}
}
его рабочее решение.....
По определению, все платные Android-приложения на рынке могут быть оценены в течение 24 часов после покупки.
Есть кнопка "Удалить и вернуть", которая изменится на "Удалить" через 24 часа.
Я бы сказал, что эта кнопка слишком заметна!
Вот как я работал, я создал 2 приложения, одно с пробной активностью, другое без,
я загрузил один без пробной активности, чтобы играть в магазине как платное приложение,
и тот, с пробной активностью в качестве бесплатного приложения.
Бесплатное приложение при первом запуске имеет опции для пробной покупки и покупки в магазине, если пользователь выбирает покупку в магазине, оно перенаправляет в магазин для покупки пользователем, но если пользователь нажимает пробную версию, оно переводит их в пробную активность.
NB: я использовал вариант 3 как @snctln, но с изменениями
во-первых, я не зависел от времени устройства, я получил свое время из файла php, который выполняет пробную регистрацию в БД,
во-вторых, я использовал серийный номер устройства для уникальной идентификации каждого устройства,
и, наконец, приложение зависит от значения времени, возвращаемого из соединения с сервером, а не от его собственного времени, поэтому систему можно обойти только при изменении серийного номера устройства, что является довольно напряженным для пользователя.
так вот мой код (для пробной деятельности):
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);
}
}
Мой php-файл выглядит следующим образом (это технология 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;
}
}
затем в основном действии я использую общее предпочтение (installDate, созданный в пробном действии), чтобы контролировать количество оставшихся дней, и если дни закончились, я блокирую основной интерфейс действия с сообщением, которое доставляет их в магазин для покупки.
Единственный недостаток, который я вижу здесь, это то, что если пользователь Rogue покупает платное приложение и решает поделиться им с такими приложениями, как Zender, предоставить общий доступ к файлам или даже разместить файл apk непосредственно на сервере, чтобы люди могли его бесплатно скачать. Но я уверен, что скоро я отредактирую этот ответ с решением или ссылкой на решение.
Надеюсь, это спасет душу... когда-нибудь
Удачного кодирования...
Вариант 3@snctln можно легко сделать, добавив php-файл на веб-сервер с установленными php и mysql, как и многие из них.
Со стороны Android идентификатор (идентификатор устройства, учетная запись Google или все, что вы хотите) передается в качестве аргумента в URL-адресе с помощью HttpURLConnection, а php возвращает дату первой установки, если она существует в таблице, или вставляет новую строку и возвращает текущую дату.
Он отлично работает для меня.
Если у меня будет время, я выложу код!
Удачи!