У меня есть приложение, которое я хочу выпустить на рынок как платное приложение. Я хотел бы иметь другую версию, которая была бы "пробной" версией с временным ограничением, например, 5 дней?
Как я могу это сделать?
У меня есть приложение, которое я хочу выпустить на рынок как платное приложение. Я хотел бы иметь другую версию, которая была бы "пробной" версией с временным ограничением, например, 5 дней?
Как я могу это сделать?
В настоящее время большинство разработчиков выполняют это, используя одну из следующих трех методик.
Первый подход легко обойти, при первом запуске приложения сохранить дату/время в файле, базе данных или общих настройках и каждый раз, когда вы запускаете приложение после этой проверки, чтобы проверить, закончился ли пробный период. Это легко обойти, поскольку удаление и переустановка позволят пользователю провести еще один пробный период.
Второй подход сложнее обойти, но все же обойти. Используйте жестко закодированную бомбу замедленного действия. В основном при таком подходе вам будет сложный код даты окончания пробной версии, и все пользователи, загружающие и использующие приложение, перестанут быть в состоянии использовать приложение одновременно. Я использовал этот подход, потому что его легко реализовать, и по большей части мне просто не хотелось преодолевать проблемы третьего метода. Пользователи могут обойти это, вручную изменив дату на своем телефоне, но большинство пользователей не смогут решить эту проблему.
Третий метод - единственный способ, которым я слышал, чтобы действительно быть в состоянии выполнить то, что вы хотите сделать. Вам нужно будет настроить сервер, а затем, когда ваше приложение будет запущено, ваше приложение отправит на сервер уникальный номер телефона. Если на сервере нет записи для этого идентификатора телефона, он создает новый и отмечает время. Если у сервера есть запись для идентификатора телефона, тогда он выполняет простую проверку, чтобы проверить, истек ли пробный период. Затем он передает результаты проверки истечения срока действия пробной версии в ваше приложение. Этот подход не должен быть обходным, но требует установки веб-сервера и т.д.
Это всегда хорошая практика, чтобы делать эти проверки в 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 не работает
Подход 4: используйте время установки приложения.
Так как уровень API 9 (Android 2.3.2, 2.3.1, Android 2.3, GINGERBREAD), firstInstallTime и lastUpdateTime в PackageInfo
.
Подробнее: Как получить время установки приложения от android
Теперь в последней версии бесплатной пробной подписки на Android вы можете разблокировать все свои функции приложения только после покупки подписки в приложении для бесплатного пробного периода. Это позволит пользователю использовать ваше приложение в течение пробного периода, если приложение по-прежнему удаляется после пробного периода, тогда деньги подписки будут переданы вам. Я не пробовал, а просто делился идеей.
На мой взгляд, лучший способ сделать это - просто использовать базу данных Firebase Realtime:
1) Добавьте поддержку Firebase в ваше приложение.
2) Выберите "Анонимная аутентификация", чтобы пользователь не должен был регистрироваться или даже знать, что вы делаете. Это, как гарантируется, будет привязано к учетной записи пользователя, прошедшей аутентификацию, и поэтому будет работать на всех устройствах.
3) Используйте API базы данных Realtime, чтобы установить значение для 'installed_date'. Во время запуска просто извлеките это значение и используйте его.
Я сделал то же самое, и он отлично работает. Я смог проверить это при удалении/переустановке, а значение в базе данных в реальном времени остается неизменным. Таким образом, пробный период работает на нескольких пользовательских устройствах. Вы даже можете установить версию install_date, чтобы приложение "сбрасывало" дату пробной версии для каждой новой основной версии.
ОБНОВЛЕНИЕ. После тестирования немного больше, кажется, что анонимная Firebase, похоже, выделяет другой идентификатор, если у вас есть разные устройства и не гарантируется между переустановками:/Единственный гарантированный способ - использовать Firebase, но привязать его к своей учетной записи google. Это должно работать, но для этого потребуется дополнительный шаг, когда пользователю сначала нужно войти в систему/зарегистрироваться.
Я до сих пор оказался немного менее изящным подходом просто проверять резервные настройки и дату, сохраненную в настройках при установке. Это работает для приложений, ориентированных на данные, где бессмысленно переустанавливать приложение и повторно вводить все ранее добавленные данные, но не будет работать для простой игры.
По определению все платные Android-приложения на рынке можно оценить в течение 24 часов после покупки.
Там появится кнопка "Удалить и вернуть", которая изменится на "Удалить" через 24 часа.
Я бы сказал, что эта кнопка слишком заметна!
Я сталкиваюсь с этим вопросом во время поиска той же проблемы, я думаю, что мы можем использовать бесплатную дату 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, потерянных после повторного установки приложения. Может быть скопирован с помощью механизма резервного копирования android и будет восстановлен после переустановки. Резервное копирование может не всегда быть доступным, хотя должно быть на большинстве устройств
Внешнее хранилище (запись в файл) Не зависит от четкости настроек или переустановки, если мы не записываем в приватный каталог приложения. Но: требует, чтобы вы спросили пользователя о своем разрешении во время выполнения в новых версиях для Android, так что это, вероятно, возможно только в том случае, если вам нужно это разрешение в любом случае. Также можно выполнить резервное копирование.
PackageInfo.firstInstallTime Является reset после переустановки, но стабильной через обновления
Войдите в учетную запись Неважно, если это их аккаунт Google через Firebase или один на вашем собственном сервере: пробная версия связана с учетной записью. Создание новой учетной записи будет reset пробной версией.
анонимный знак Firebase Вы можете войти в систему анонимно и сохранить данные для них в Firebase. Но по-видимому, переустановка приложения и, возможно, другие недокументированные события могут дать пользователю новый анонимный идентификатор, сбросив пробное время. (Сами Google не предоставляют много документации)
ANDROID_ID Может быть недоступен и может меняться при определенных обстоятельствах, например factory reset. Мнения о том, стоит ли использовать это для идентификации устройств, по-видимому, отличаются.
Воспроизвести рекламный идентификатор Может быть reset пользователем. Может быть отключен пользователем путем отказа от отслеживания объявлений.
InstanceID Reset при переустановке. Reset в случае события безопасности. Может быть reset вашим приложением.
Какая (комбинация) методов работает для вас, зависит от вашего приложения и от того, сколько усилий вы думаете, что средний Джон внесет в очередной испытательный период. Я бы порекомендовал рулевое управление без использования только анонимной Firebase и рекламного идентификатора из-за их нестабильности. Кажется, что многофакторный подход даст наилучшие результаты. Какие факторы доступны для вас, зависит от вашего приложения и его прав.
Для моего собственного приложения я нашел общие предпочтения + firstInstallTime + резервное копирование предпочтений как наименее интрузивный, но также и достаточно эффективный метод. Вы должны убедиться, что вы запрашиваете резервную копию только после проверки и сохранения времени запуска в общих настройках. Значения в общих префайлах должны иметь приоритет перед firstInstallTime. Затем пользователь должен переустановить приложение, запустить его один раз, а затем очистить данные приложения до reset пробной версии, что довольно много. На устройствах без резервного транспорта пользователь может reset попробовать просто переустановить, хотя.
Я сделал такой подход доступным как расширяемая библиотека.
Вот как я пошел по морю, Я создал 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<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 непосредственно на сервере для людей скачать бесплатно. Но я уверен, что скоро отредактирую этот ответ с помощью решения или ссылки на решение.
Надеюсь, это спасет душу... когда-нибудь
Счастливое кодирование...
@snctln вариант 3 можно легко сделать, добавив php файл на веб-сервер с установленными php и mysql, как и многие из них.
На стороне Android идентификатор (идентификатор устройства, учетная запись google o все, что вам нужно) передается в качестве аргумента в URL-адресе с использованием HttpURLConnection, а php возвращает дату первой установки, если она существует в таблице, или вставляет новая строка и возвращает текущую дату.
Это отлично работает для меня.
Если у меня есть время, я отправлю код!
Удачи!