Создание пробного приложения Android, срок действия которого истекает через определенный период времени
У меня есть приложение, которое я хочу выпустить на рынок в качестве платного приложения. Я хотел бы иметь другую версию, которая будет «пробной» версией с временным интервалом, скажем, 5 дней?
Как я могу это сделать?
- android.content.res.Resources $ NotFoundException: Идентификатор ресурса строки # 0x0
- Listview itemclick не работает
- Использование виджета NumberPicker со строками
- Как обрезать изображение в Android?
- Как работают координаты экрана Android?
- Понимание fragmentа setRetainInstance (boolean)
- В Android, как сделать пространство между детьми LinearLayout?
- Обновление библиотеки поддержки Android до 23.2.0 вызывает ошибку: XmlPullParserException Бинарная строка XML-файла # 17 требует viewportWidth> 0
- Выбрана фотография с помощью нового приложения Google Фото
- Внутренний class может получить доступ, но не обновлять значения - AsyncTask
- Соединение Android NDK
- Как изменить FontSize в Android WebView?
- Виджет не обновляется при перезапуске
В настоящее время большинство разработчиков выполняют это, используя один из следующих 3 методов.
Первый подход легко обойти, при первом запуске приложения сохранить дату / время в файле, базе данных или общих настройках и каждый раз, когда вы запускаете приложение после этой проверки, чтобы проверить, закончился ли пробный период. Это легко обойти, поскольку удаление и переустановка позволят пользователю провести еще один пробный период.
Второй подход сложнее обойти, но все же обходить. Используйте жестко закодированную бомбу замедленного действия. В основном при таком подходе вам будет сложный код даты окончания пробной версии, и все пользователи, загружающие и использующие приложение, перестанут быть в состоянии использовать приложение одновременно. Я использовал этот подход, потому что его легко реализовать, и по большей части мне просто не хотелось преодолевать проблемы третьего метода. Пользователи могут обойти это, вручную изменив дату на своем телефоне, но большинство пользователей не смогут решить эту проблему.
Третий метод – единственный способ, которым я слышал, чтобы действительно быть в состоянии выполнить то, что вы хотите сделать. Вам нужно будет настроить сервер, а затем, когда ваше приложение будет запущено, ваше приложение отправит уникальный идентификатор телефона на сервер. Если на сервере нет записи для этого идентификатора телефона, он создает новый и отмечает время. Если у сервера есть запись для идентификатора телефона, тогда он выполняет простую проверку, чтобы проверить, истек ли пробный период. Затем он передает результаты проверки истечения срока действия пробной версии в ваше приложение. Этот подход не должен быть обходным, но требует установки веб-сервера и т. Д.
Всегда хорошая практика делать эти проверки в onCreate. Если истечение закончило всплывающее окно AlertDialog с рыночной ссылкой на полную версию приложения. Включите только кнопку «ОК», и как только пользователь нажмет «ОК», позвоните в «finish ()», чтобы завершить действие.
Это старый вопрос, но в любом случае, возможно, это поможет кому-то.
В случае, если вы захотите перейти к самому упрощенному подходу (который не удастся, если приложение будет удалено / переустановлено или пользователь изменит дату устройства вручную), так оно и может быть:
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 !!! } } ... }
Я разработал 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 .
Эй, ребята, этот вопрос, и ответ snctln вдохновил меня на решение проблемы, основанной на методе 3, как мой диплом бакалавра. Я знаю, что текущий статус не для продуктивного использования, но я хотел бы услышать, что вы об этом думаете! Вы бы использовали такую систему? Вы хотите увидеть его как облачную службу (не имея проблем с настройкой сервера)? Обеспокоены проблемами безопасности или соображениями стабильности? Как только я закончил процедуру бакалавра, я хочу продолжить работу над программным обеспечением. Итак, теперь мне нужно ваше мнение!
Исходный код размещен на GitHub https://github.com/MaChristmann/mobile-trial
Некоторая информация о системе: – Система состоит из трех частей: библиотеки Android, сервера node.js и конфигуратора для управления несколькими пробными приложениями и учетными записями издателей / разработчиков.
-
Он поддерживает только временные испытания и использует ваш (игровой магазин или другую) учетную запись, а не идентификатор телефона.
-
Для библиотеки Android она основана на библиотеке проверки лицензий Google Play. Я изменил его, чтобы подключиться к серверу node.js, и, кроме того, библиотека пытается узнать, изменил ли пользователь системную дату. Он также кэширует полученную пробную лицензию в AES зашифрованных общих настройках. Вы можете настроить допустимое время кеша с помощью конфигуратора. Если пользователь «очищает данные», библиотека будет принудительно проверять сервер.
-
Сервер использует https, а также цифровую подпись для проверки лицензии. Он также имеет API для пробных приложений и пользователей CRUD (издатель и разработчик). Совместимость с лицензиатом Verfication. Разработчики библиотеки могут протестировать реализацию своего поведения в пробном приложении с результатом теста. Таким образом, вы в конфигураторе можете явно указать свой лицензионный ответ на «лицензию», «не лицензию» или «ошибку сервера».
-
Если вы обновите приложение с помощью новой функции, вы можете захотеть, чтобы все могли попробовать ее снова. В конфигураторе вы можете обновить пробную лицензию для пользователей с истекшими лицензиями, установив код версии, который должен инициировать это. Например, пользователь запускает ваше приложение на версии 3, и вы хотите, чтобы он попробовал функции кода версии 4. Если он обновляет приложение или переустанавливает его, он снова может использовать полный пробный период, потому что сервер знает, в какой версии он пробовал последний время.
-
Все находится под лицензией Apache 2.0
Самый простой и лучший способ сделать это – реализовать BackupSharedPreferences.
Настройки сохраняются, даже если приложение удалено и переустановлено.
Просто сохраните дату установки в качестве предпочтения, и вам хорошо идти.
Вот теория: http://developer.android.com/reference/android/app/backup/SharedPreferencesBackupHelper.html
Вот пример: Android SharedPreferences Backup не работает
Подход 4: используйте время установки приложения.
Начиная с уровня API 9 (Android 2.3.2, 2.3.1, Android 2.3, GINGERBREAD) в PackageInfo
есть firstInstallTime и lastUpdateTime .
Чтобы узнать больше: Как получить время установки приложения из Android
Теперь в недавней версии бесплатной ознакомительной подписки на Android вы можете разблокировать все функции вашего приложения только после покупки подписки в приложении для бесплатного пробного периода. Это позволит пользователю использовать ваше приложение в течение пробного периода, если приложение по-прежнему удаляется после пробного периода, тогда деньги подписки будут переданы вам. Я не пробовал, а просто делился идеей.
Вот документация
На мой взгляд, лучший способ сделать это – просто использовать базу данных Firebase Realtime:
1) Добавьте поддержку Firebase в свое приложение
2) Выберите «Анонимная аутентификация», чтобы пользователю не приходилось регистрироваться или даже знать, что вы делаете. Это, как гарантируется, будет связано с текущей аутентифицированной учетной записью пользователя, и поэтому будет работать на всех устройствах.
3) Используйте API базы данных Realtime, чтобы установить значение для ‘installed_date’. Во время запуска просто извлеките это значение и используйте его.
Я сделал то же самое, и он отлично работает. Я смог проверить это при удалении / переустановке, а значение в базе данных в реальном времени остается неизменным. Таким образом, пробный период работает на нескольких пользовательских устройствах. Вы даже можете установить версию install_date, чтобы приложение «сбрасывало» дату пробной версии для каждой новой основной версии.
ОБНОВЛЕНИЕ : после тестирования немного больше, кажется, что анонимная Firebase, похоже, выделяет другой идентификатор, если у вас есть разные устройства и не гарантируется между переустановками: / Единственный гарантированный способ – использовать Firebase, но привязать его к их Google Счет. Это должно работать, но для этого потребуется дополнительный шаг, когда пользователю сначала нужно войти в систему / зарегистрироваться.
Я до сих пор оказывался немного менее изящным, просто проверяя на резервные настройки и дату, сохраненную в настройках при установке. Это работает для приложений, ориентированных на данные, где человеку бессмысленно переустанавливать приложение и повторно вводить все ранее добавленные данные, но не будет работать для простой игры.
Рассмотрев все варианты этого и других streamов, это мои выводы
Общие настройки, firebase database Может быть очищена в настройках Android, потеряна после повторной установки приложения. Может быть скопирован с помощью механизма резервного копирования android и будет восстановлен после переустановки. Резервное копирование может не всегда быть доступным, хотя должно быть на большинстве устройств
Внешнее хранилище (запись в файл) Не влияет на ясность из настроек или переустановка, если мы не пишем в частный каталог приложения . Но: требуется, чтобы вы попросили пользователя получить их разрешение во время выполнения в новых версиях Android, поэтому это, вероятно, возможно только в том случае, если вам все равно нужно это разрешение. Также можно выполнить резервное копирование.
PackageInfo.firstInstallTime Сбрасывается после переустановки, но стабильна для обновлений
Войдите в какую-то учетную запись. Неважно, если это их учетная запись Google через Firebase или одну на вашем собственном сервере: пробная версия связана с учетной записью. Создание новой учетной записи приведет к сбрасыванию пробной версии.
Анонимный анонимный анонимный анонимный вход. Вы можете анонимно входить в систему и хранить данные для них в Firebase. Но, по- видимому, переустановка приложения и, возможно, другие недокументированные события могут дать пользователю новый анонимный идентификатор , сбросив пробное время. (Сам Google не предоставляет много документации)
ANDROID_ID Может быть недоступен и может измениться при определенных обстоятельствах , например, сброс настроек. Мнения о том, стоит ли использовать это для идентификации устройств, по-видимому, отличаются.
Play Advertising ID Может быть сброшен пользователем. Может быть отключен пользователем путем отказа от отслеживания объявлений.
InstanceID Сброс при повторной установке . Сброс в случае события безопасности. Может быть сброшено вашим приложением.
Какие (комбинации) методов работают для вас, зависит от вашего приложения и от того, сколько усилий вы думаете, что средний Джон внесет в очередной испытательный период. Я бы порекомендовал рулевое управление использовать только анонимную Firebase и рекламный идентификатор из-за их нестабильности. Кажется, что многофакторный подход даст наилучшие результаты. Какие факторы доступны вам, зависит от вашего приложения и его разрешений.
Для моего собственного приложения я нашел общие предпочтения + firstInstallTime + резервное копирование предпочтений как наименее интрузивный, но также и достаточно эффективный метод. Вы должны убедиться, что вы запрашиваете резервную копию только после проверки и сохранения времени запуска в общих настройках. Значения в общих префайлах должны иметь приоритет перед firstInstallTime. Затем пользователь должен переустановить приложение, запустить его один раз, а затем очистить данные приложения, чтобы сбросить пробную версию, что довольно много. Однако на устройствах без резервного копирования пользователь может сбросить пробную версию, просто переустановив ее.
Я сделал этот подход доступным как расширяемая библиотека .
По определению все платные Android-приложения на рынке можно оценить в течение 24 часов после покупки.
Появилась кнопка «Удалить и вернуть», которая после 24 часов меняется на «Удалить».
Я бы сказал, что эта кнопка слишком заметна!
Я сталкиваюсь с этим вопросом во время поиска той же проблемы, я думаю, что мы можем использовать бесплатную дату api, например http://www.timeapi.org/utc/now или другую дату api, чтобы проверить истечение срока действия трейла. этот способ эффективен, если вы хотите доставить демоверсию и беспокоиться о платеже и нуждаетесь в демо-версии. 🙂
найти код ниже
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 { @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("") == -1 || result.indexOf(" ") == -1) { System.out.println("Error $$$$$$$$$"); } else { String strTime = result.substring(result.indexOf("") + 11, result.indexOf(" ")); 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 } }
его рабочее решение …..
Вот как я пошел по моему, я создал 2 приложения с пробной деятельностью,
я загрузил один без пробной активности, чтобы играть в качестве платного приложения,
и тот, у кого есть пробная деятельность, как бесплатное приложение.
В бесплатном приложении при первом запуске есть опции для пробной покупки и покупки магазина, если пользователь выбирает покупку магазина, он перенаправляет в магазин для покупки пользователем, но если пользователь нажимает на пробную версию, он переходит к пробной деятельности
NB: Я использовал опцию 3 как @snctln, но с изменениями
во-первых , я не зависел от времени устройства, я получил свое время от php-файла, который делает пробную регистрацию в db,
во-вторых , я использовал серийный номер устройства, чтобы однозначно идентифицировать каждое устройство,
наконец , приложение зависит от значения времени, возвращаемого от соединения с сервером, а не его собственного времени, поэтому система может быть обойдена только в том случае, если серийный номер устройства изменен, что является довольно напряженным для пользователя.
поэтому здесь идет мой код (для пробной деятельности):
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() { @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 getParams() { Map params = new HashMap(); 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('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; } }
то в основном действии я использую общие предпочтения (installDate, созданные в пробной деятельности), чтобы отслеживать количество оставшихся дней, а если дни заканчиваются, я блокирую основной интерфейс активности с сообщением, которое отправляет их в магазин для покупки.
Единственная нижняя сторона, которую я вижу здесь, заключается в том, что если пользователь Rogue покупает платное приложение и решает поделиться с такими приложениями, как Zender, общий доступ к файлам или даже разместить файл apk непосредственно на сервере, чтобы люди могли скачать бесплатно. Но я уверен, что скоро отредактирую этот ответ с помощью решения или ссылки на решение.
Надеюсь, это спасет душу … однажды
Счастливое кодирование …
Опция @snctln 3 может быть легко выполнена, добавив php-файл на веб-сервер с установленными php и mysql, как и многие из них.
На стороне Android идентификатор (идентификатор устройства, учетная запись google o независимо от того, что вы хотите) передается в качестве аргумента в URL-адресе с использованием HttpURLConnection, а php возвращает дату первой установки, если она существует в таблице, или вставляет новую строку и он возвращает текущую дату.
Он отлично работает для меня.
Если у меня будет время, я отправлю код!
Удачи !