有料アプリとして売りたいアプリがあります。たとえば、5日間という時間制限のある「試用版」である他のバージョンが欲しいのですが?
これを行うにはどうすればよいですか?
有料アプリとして売りたいアプリがあります。たとえば、5日間という時間制限のある「試用版」である他のバージョンが欲しいのですが?
これを行うにはどうすればよいですか?
回答:
現在、ほとんどの開発者は、次の3つの手法のいずれかを使用してこれを実現しています。
最初のアプローチは簡単に回避できます。アプリを初めて実行するときは、日付、時刻をファイル、データベース、または共有設定に保存し、そのチェック後に試用期間が終了したかどうかを確認するたびにアプリを実行します。アンインストールと再インストールにより、ユーザーは別の試用期間を持つことができるため、これは簡単に回避できます。
2番目のアプローチは回避するのが困難ですが、依然として回避できます。ハードコードされた時限爆弾を使用します。基本的にこのアプローチでは、試用期間の終了日をハードコーディングし、アプリをダウンロードして使用するすべてのユーザーが同時にアプリを使用できなくなります。実装が簡単で、ほとんどの場合、3番目の手法の問題を経験したくなかったので、このアプローチを使用しました。ユーザーは自分の電話で日付を手動で変更することでこれを回避できますが、ほとんどのユーザーはそのようなことをする手間をかけません。
3番目の手法は、あなたがやりたいことを本当に実現できると私が聞いた唯一の方法です。サーバーを設定する必要があります。その後、アプリケーションが起動するたびに、アプリは電話の一意の識別子をサーバーに送信します。サーバーにその電話IDのエントリがない場合、サーバーは新しい電話IDを作成し、時刻を記録します。サーバーに電話IDのエントリがある場合、試用期間が終了したかどうかを確認するための簡単なチェックを行います。次に、試用期限チェックの結果をアプリケーションに伝えます。このアプローチは回避できないはずですが、ウェブサーバーなどを設定する必要があります。
onCreateでこれらのチェックを行うことは常に良い習慣です。有効期限が終了した場合、アプリのフルバージョンへのマーケットリンクを含むAlertDialogがポップアップします。「OK」ボタンのみを含め、ユーザーが「OK」をクリックしたら「finish()」を呼び出してアクティビティを終了します。
私は、Android Studioプロジェクトに簡単にドロップできるAndroid Trial SDKを開発しました。これにより、すべてのサーバー側の管理(オフラインの猶予期間を含む)が処理されます。
それを使用するには、単に
ライブラリをメインモジュールに追加します 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 !!!
}
}
...
}
getTime
ませんgetTimeInMillis
。
この質問とsnctlnの答え、学士論文として方法3に基づく解決策に取り組むように私に刺激を与えました。現在の状況は生産的な使用のためのものではないことは知っていますが、あなたがそれについてどう思うか聞いてみたいです!そのようなシステムを使用しますか?クラウドサービスとして表示しますか(サーバーの構成に問題はありません)?セキュリティの問題や安定性の理由が心配ですか?
学士号の手順を終えたらすぐに、ソフトウェアの作業を続けたいと思います。だから今こそ私はあなたのフィードバックを必要としています!
ソースコードはGitHub https://github.com/MaChristmann/mobile-trialでホストされています
システムに関するいくつかの情報:-システムには、Androidライブラリ、node.jsサーバー、および複数のトライアルアプリとパブリッシャー/開発者アカウントを管理するコンフィギュレーターの3つの部分があります。
時間ベースの試用版のみをサポートし、電話IDではなく(プレイストアまたはその他の)アカウントを使用します。
Androidライブラリの場合は、Google Playライセンス検証ライブラリに基づいています。node.jsサーバーに接続するように変更し、さらにライブラリは、ユーザーがシステム日付を変更したかどうかを認識しようとします。また、取得した試用ライセンスをAES暗号化共有設定にキャッシュします。キャッシュの有効期間は、コンフィギュレーターで構成できます。ユーザーが「データをクリア」すると、ライブラリはサーバー側のチェックを強制します。
サーバーはhttpsを使用しており、ライセンスチェック応答にもデジタル署名しています。また、CRUDトライアルアプリとユーザー(パブリッシャーとデベロッパー)のためのAPIも備えています。ライセンス検証ライブラリと同様に、開発者はテスト結果を使用して、トライアルアプリでの動作の実装をテストできます。したがって、コンフィギュレータで、ライセンス応答を「ライセンス済み」、「ライセンスなし」、または「サーバーエラー」に明示的に設定できます。
お尻を蹴るような新機能でアプリを更新する場合は、誰もがもう一度試すことができるようにしたいと思うかもしれません。コンフィギュレータで、これをトリガーするバージョンコードを設定することにより、期限切れのライセンスを持つユーザーのトライアルライセンスを更新できます。たとえば、ユーザーがバージョンコード3でアプリを実行していて、バージョンコード4の機能を試してもらいたいとします。アプリを更新するか再インストールすると、サーバーが最後に試したバージョンをサーバーが認識しているため、再び試用期間全体を使用できます。時間。
すべてはApache 2.0ライセンスの下にあります
これを行う最も簡単で最適な方法は、BackupSharedPreferencesを実装することです。
アプリがアンインストールされて再インストールされても、設定は保持されます。
インストール日付を設定として保存するだけで、問題ありません。
これが理論です:http : //developer.android.com/reference/android/app/backup/SharedPreferencesBackupHelper.html
アプローチ4:アプリケーションのインストール時間を使用します。
APIレベル9(Android 2.3.2、2.3.1、Android 2.3、GINGERBREAD)以降、にはfirstInstallTimeとlastUpdateTimeがありPackageInfo
ます。
続きを読む: Androidからアプリのインストール時間を取得する方法
Androidの無料トライアルサブスクリプションの最新バージョンが追加されたため、アプリ内でサブスクリプションを無料トライアル期間購入した後でのみ、アプリのすべての機能のロックを解除できます。これにより、ユーザーは試用期間中アプリを使用できます。試用期間後にアプリがまだアンインストールされている場合、サブスクリプションのお金があなたに送金されます。私は試していませんが、アイデアを共有しています。
私の意見では、これを行う最善の方法は、Firebase Realtime Databaseを使用することです。
1)アプリにFirebaseサポートを追加する
2)[匿名認証]を選択して、ユーザーがサインアップしたり、何をしているのかを知ったりする必要がないようにします。これは、現在認証されているユーザーアカウントにリンクすることが保証されているため、デバイス間で機能します。
3)Realtime Database APIを使用して、「installed_date」の値を設定します。起動時に、この値を取得して使用します。
私は同じことをしました、そしてそれは素晴らしい働きをします。アンインストール/再インストールでこれをテストすることができ、リアルタイムデータベースの値は同じままです。このように、試用期間は複数のユーザーデバイスで機能します。アプリが新しいメジャーリリースごとに試用日を「リセット」するように、install_dateをバージョン管理することもできます。
更新:もう少しテストした後、デバイスが異なると匿名のFirebaseが別のIDを割り当てているようで、再インストールの間に保証されません:/保証されている唯一の方法は、Firebaseを使用することですが、Googleに関連付けますアカウント。これは機能するはずですが、ユーザーが最初にログイン/サインアップする必要がある追加の手順が必要になります。
これまでのところ、バックアップされた設定とインストール時に設定に保存された日付を単にチェックするという、少し洗練されていないアプローチに終わりました。これは、ユーザーがアプリを再インストールして以前に追加したすべてのデータを再入力しても意味がないデータ中心のアプリでは機能しますが、単純なゲームでは機能しません。
このスレッドと他のスレッドのすべてのオプションを見た後、これらは私の発見です
共有設定、データベース Android設定でクリアでき、アプリの再インストール後に失われます。Androidのバックアップメカニズムでバックアップでき、再インストール後に復元されます。バックアップは常に利用できるとは限りませんが、ほとんどのデバイスで可能です
外部ストレージ(ファイルへの書き込み)アプリケーションのプライベートディレクトリに書き込み を行わない場合、設定のクリアや再インストールの影響を受けません。しかし:新しいandroidバージョンでは、実行時にユーザーに許可を求める必要があるため、これはおそらく、とにかくその許可が必要な場合にのみ可能です。バックアップすることもできます。
PackageInfo.firstInstallTime は再インストール後にリセットされますが、更新全体で安定しています
一部のアカウントにサインインする Firebaseを介したGoogleアカウントであるか、自分のサーバーにあるGoogleアカウントであるかは関係ありません。試用版はアカウントにバインドされています。新しいアカウントを作成すると、トライアルがリセットされます。
Firebase匿名サインイン ユーザーを匿名でサインインして、Firebaseにユーザーのデータを保存できます。ただし、アプリの再インストールや他の文書化されていないイベントにより、ユーザーに新しい匿名IDが与えられ、試用期間がリセットされる可能性があります。(Google自身はこれに関する多くのドキュメントを提供していません)
ANDROID_ID 使用できない場合があり、出荷時設定へのリセットなど、特定の状況では変更される可能性があります。これを使用してデバイスを識別するのが良いかどうかについての意見は異なるようです。
Play広告ID ユーザーによってリセットされる場合があります。ユーザーが広告の追跡を無効にすることで無効にされる場合があります。
再インストール時にInstanceIDがリセットされます。セキュリティイベントの場合にリセットします。アプリでリセットできます。
どの(組み合わせの)方法が効果的かは、アプリと、平均的なジョンが次の試用期間を獲得するために費やすと考える努力に依存します。不安定であるため、匿名のFirebaseと広告ID のみを使用しないようにすることをお勧めします。多要素アプローチは、最良の結果が得られるようです。どの要素を利用できるかは、アプリとその権限によって異なります。
私自身のアプリの場合、共有設定+ firstInstallTime +設定のバックアップは、最も煩わしくないが十分に効果的な方法であることがわかりました。共有設定で試用開始時刻を確認して保存した後でのみ、バックアップを要求することを確認する必要があります。共有設定の値は、firstInstallTimeよりも優先される必要があります。次に、ユーザーはアプリを再インストールして1回実行し、アプリのデータを消去して試用をリセットする必要があります。これはかなりの作業です。ただし、バックアップトランスポートのないデバイスでは、再インストールするだけでトライアルをリセットできます。
私はそのアプローチを拡張可能なライブラリとして利用できるようにしました。
同じ問題を検索しているときにこの質問に遭遇しました。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つのアプリを作成しました。
試用版のないものをアップロードして、有料アプリとしてストアをプレイしました。
そして、無料アプリとして試用活動をしているもの。
初回起動時の無料アプリには、トライアルとストア購入のオプションがあります。ユーザーがストア購入を選択すると、ユーザーはストアにリダイレクトされて購入しますが、ユーザーがトライアルをクリックするとトライアルアクティビティに移動します
注:@snctlnのようにオプション3を使用しましたが、変更を加えました
最初に、デバイスの時間に依存していませんでした。DBへのトライアル登録を行う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スリムテクノロジー)。
/**
* 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がテーブルに存在する場合、または新しい行を挿入して、最初のインストールの日付を返します。現在の日付を返します。
それは私にとってはうまくいきます。
時間があれば、コードを投稿します。
幸運を !