有料アプリとして市場に出したいアプリケーションがあります。たとえば、5日間の制限時間がある「試用」バージョンになる他のバージョンが欲しいのですが。
どうすればこれを行うことができますか?
現在、ほとんどの開発者は、次の3つの手法のいずれかを使用してこれを実現しています。
最初のアプローチは簡単に回避できます。最初にアプリを実行すると、日付/時刻がファイル、データベース、または共有設定に保存され、試用期間が終了したかどうかを確認するためにアプリを実行するたびに保存されます。これを回避するのは簡単です。アンインストールと再インストールにより、ユーザーは別の試用期間を設けることができます。
2番目のアプローチは回避が困難ですが、回避可能です。ハードコードされた時限爆弾を使用します。基本的にこのアプローチでは、試用の終了日をハードコーディングし、アプリをダウンロードして使用するすべてのユーザーが同時にアプリを使用できなくなるようにします。このアプローチを使用したのは、実装が簡単であり、ほとんどの場合、3番目の手法の問題を経験したくなかったからです。ユーザーは電話の日付を手動で変更することでこれを回避できますが、ほとんどのユーザーはそのようなことをするのに苦労することはありません。
3番目の手法は、あなたがやりたいことを本当に達成できると聞いた唯一の方法です。サーバーをセットアップする必要があります。その後、アプリケーションが開始されるたびに、アプリは phones unique identifier をサーバーに送信します。サーバーにその電話IDのエントリがない場合、サーバーは新しいものを作成し、時間を記録します。サーバーに電話IDのエントリがある場合、試用期間が終了したかどうかを確認する簡単なチェックを行います。その後、試用期限切れチェックの結果をアプリケーションに送り返します。このアプローチは回避可能ではありませんが、ウェブサーバーなどの設定が必要です。
OnCreateでこれらのチェックを行うことは常に良い習慣です。有効期限がポップアップを終了した場合、アプリのフルバージョンへの market link を含むAlertDialogが表示されます。 「OK」ボタンのみを含め、ユーザーが「OK」をクリックすると、アクティビティを終了するために「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開発者ダッシュボード にあります。
これは古い質問ですが、とにかく、これは誰かを助けるかもしれません。
most simplistic approach(which はifアプリがアンインストール/再インストールされるか、ユーザーがデバイスの日付を手動で変更します)、これは次のようになります:
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
システムに関する情報:-システムには3つの部分があります。Androidライブラリ、node.jsサーバー、複数の試用版アプリと発行者/開発者アカウントを管理するためのコンフィギュレーターです。
時間ベースの試用のみをサポートし、電話IDではなく(Playストアまたはその他の)アカウントを使用します。
Androidライブラリの場合、Google Playライセンス検証ライブラリに基づいています。node.jsサーバーに接続するように修正し、さらにライブラリはユーザーがシステムの日付を変更したかどうかを認識しようとします。また、取得したトライアルライセンスをAES暗号化共有設定にキャッシュします。キャッシュの有効時間はコンフィギュレーターで設定できます。ユーザーが「データをクリア」すると、ライブラリはサーバー側のチェックを強制します。
サーバーはhttpsを使用しており、ライセンスチェック応答にデジタル署名もしています。また、CRUDトライアルアプリおよびユーザー(パブリッシャーおよびデベロッパー)用のAPIもあります。ライセンス検証ライブラリと同様に、開発者はテスト結果を使用してトライアルアプリで動作実装をテストできます。そのため、コンフィギュレーターで、ライセンス応答を「ライセンス済み」、「ライセンスなし」、または「サーバーエラー」に明示的に設定できます。
お尻を蹴る新機能でアプリを更新する場合は、誰もがもう一度試してみることをお勧めします。コンフィギュレーターで、これをトリガーするバージョンコードを設定することにより、期限切れのライセンスを持つユーザーの試用ライセンスを更新できます。たとえば、ユーザーはバージョンコード3でアプリを実行しており、バージョンコード4の機能を試してもらいたいと考えています。時間。
すべてがApache 2.0ライセンスの下にあります
これを行う最も簡単でbest方法は、BackupSharedPreferencesを実装することです。
アプリをアンインストールして再インストールしても、設定は保持されます。
好みとしてインストール日を保存するだけで、準備完了です。
理論は次のとおりです。 http://developer.Android.com/reference/Android/app/backup/SharedPreferencesBackupHelper.html
以下に例を示します: Android SharedPreferences Backup Not Working
アプローチ4:アプリケーションのインストール時間を使用します。
APIレベル9(Android 2.3.2、2.3.1、Android 2.3、Gingerbread)以降、PackageInfo
には firstInstallTime および lastUpdateTime があります。
さらに読むには: Androidからアプリのインストール時間を取得する方法
私の意見では、これを行う最善の方法は、Firebase Realtime Databaseを単に使用することです。
1)アプリにFirebaseサポートを追加する
2)「匿名認証」を選択して、ユーザーがサインアップする必要がないようにします。これにより、現在認証されているユーザーアカウントへのリンクが保証されるため、デバイス間で機能します。
3)Realtime Database APIを使用して、「installed_date」の値を設定します。起動時に、単にこの値を取得して使用します。
私も同じことをしましたが、うまくいきます。アンインストール/再インストールでこれをテストできましたが、リアルタイムデータベースの値は同じままです。このようにして、試用期間は複数のユーザーデバイスで機能します。 install_dateをバージョン管理して、アプリが新しいメジャーリリースごとに試用日を「リセット」することもできます。
[〜#〜] update [〜#〜]:もう少しテストした後、匿名のFirebaseは異なるデバイスを持っている場合に別のIDを割り当てているようであり、インストール:/唯一の保証された方法は、Firebaseを使用するが、Googleアカウントに結び付けることです。これは機能するはずですが、ユーザーが最初にログイン/サインアップする必要がある追加の手順が必要になります。
これまでのところ、バックアップされた設定とインストール時に設定に保存されている日付とを単純に照合するというわずかにエレガントなアプローチになりました。これは、ユーザーがアプリを再インストールし、以前に追加したすべてのデータを再入力するのは無意味なデータ中心のアプリでは機能しますが、単純なゲームでは機能しません。
現在、Android無料トライアルサブスクリプションが追加されました。無料トライアル期間中にアプリ内でサブスクリプションを購入した後にのみ、アプリのすべての機能をロック解除できます。これにより、ユーザーは試用期間中のアプリの場合、試用期間後もアプリがまだアンインストールされている場合、サブスクリプションのお金が送金されます。
このスレッドと他のスレッドのすべてのオプションを見た後、これらは私の発見です
共有設定、データベースAndroid設定でクリアできます。アプリの再インストール後に失われます。 Can Androidのバックアップメカニズムでバックアップされます および再インストール後に復元されます。バックアップは常に使用できるとは限りませんが、ほとんどのデバイスにあるはずです
外部ストレージ(ファイルへの書き込み)設定のクリアまたは再インストールの影響を受けない アプリケーションの書き込みを行わないプライベートディレクトリ 。しかし: 実行時にユーザーに許可を求める必要があります in newer Android versions、これはおそらく許可が必要な場合にのみ実行可能です。バックアップされました。
PackageInfo.firstInstallTime再インストール後にリセットされますが、更新後も安定しています
一部のアカウントにサインインするFirebase経由のGoogleアカウントか、自分のサーバーにあるアカウントかは関係ありません。試用版はアカウントにバインドされています。新しいアカウントを作成すると、試用版がリセットされます。
Firebase匿名サインインユーザーに匿名でサインインし、Firebaseにデータを保存できます。しかし 明らかにアプリの再インストールと、おそらくドキュメント化されていない他のイベントがユーザーに新しい匿名IDを与える可能性があります 、試用時間をリセットします。 (Google自体はこれに関するドキュメントをあまり提供していません)
Android_ID利用できない場合があり、特定の状況下で変更される場合があります 、例:工場出荷時設定へのリセット。これを使用してデバイスを識別するのが良い考えかどうかについての意見は異なるようです。
広告IDの再生ユーザーによってリセットされる場合があります。 広告トラッキングをオプトアウトすることにより、ユーザーが無効にすることができます。
InstanceID再インストール時にリセット 。 セキュリティイベントの場合にリセットします。アプリでリセットできます。
どの方法(の組み合わせ)が適切かは、アプリと、平均的なジョンが別の試用期間を獲得するためにどれだけの労力をかけるかによって異なります。不安定なため、匿名のFirebaseとAdvertising IDをonlyを使用することは避けてください。多要素アプローチは、最良の結果が得られるようです。どの要素を使用できるかは、アプリとその権限によって異なります。
私自身のアプリでは、共有設定+ firstInstallTime +設定のバックアップが最も邪魔にならないが、十分に効果的な方法であることがわかりました。共有設定で試用開始時刻を確認して保存した後にのみバックアップを要求するようにしてください。共有設定の値は、firstInstallTimeよりも優先される必要があります。その後、ユーザーはアプリを再インストールし、一度実行してからアプリのデータを消去して試用版をリセットする必要がありますが、これは非常に多くの作業です。ただし、バックアップトランスポートのないデバイスでは、ユーザーは単に再インストールするだけで試用版をリセットできます。
拡張可能なライブラリ としてそのアプローチを利用可能にしました。
定義により、all有料Android市販のアプリは、購入後24時間評価できます。
24時間後に[アンインストール]に変わる[アンインストールと払い戻し]ボタンがあります。
私は、このボタンがあまりにも目立つと主張します!
私は同じ問題を探しているときにこの質問に遭遇しました、私は http://www.timeapi.org/utc/now または有効期限をチェックする他の日付APIのような無料の日付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<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
}
}
その実用的なソリューション.....
ここに私がどうやって行ったのか、私は2つのアプリを作成しましたが、1つは試用活動でもう1つはなし
試用アクティビティのないものを有料アプリとしてPlayストアにアップロードし、
そして、無料アプリとして試用活動があるもの。
初回起動時の無料アプリには、ユーザーがストア購入を選択した場合、ユーザーが購入するストアにリダイレクトされますが、ユーザーがトライアルをクリックすると、トライアルアクティビティに進みます。
NB:@snctlnのようなオプション3を使用しましたが、変更を加えました
最初の、私はデバイスの時間に依存していませんでした、dbへのトライアル登録を行うphpファイルから時間を得ました、
2番目、各デバイスを一意に識別するためにデバイスのシリアル番号を使用し、
lastly、アプリはサーバー接続から返される時間値に依存するため、デバイスのシリアル番号が変更された場合にのみシステムを回避できます。これはユーザーにとって非常にストレスの多いものです。
だからここに私のコードに行きます(試用アクティビティのために):
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スリムテクノロジー):
/**
* 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)を使用して残りの日数を監視し、日数が過ぎた場合、メインアクティビティUIをブロックして、購入するストアに移動するメッセージを表示します。
私がここで見る唯一の欠点は、不正なユーザーが有料アプリを購入し、Zender、ファイル共有などのアプリと共有するか、ダウンロードする人々のためにサーバー上でapkファイルを直接ホストすることを決定した場合です無料です。しかし、私はすぐにこの答えを解決するか、解決策へのリンクで編集します。
これが魂を救うことを願っています...いつか
ハッピーコーディング...
@ snctlnオプション3は、phpおよびmysqlがインストールされているWebサーバーに多くのファイルを追加して、phpファイルを簡単に追加できます。
Android側から)識別子(デバイスID、必要なGoogleアカウント)がHttpURLConnectionを使用してURLの引数として渡され、phpは最初のインストールの日付を返します(存在する場合)テーブルまたは新しい行を挿入し、現在の日付を返します。
それは私のためにうまく機能します。
時間があれば、コードを投稿します!
がんばろう !