আমার কাছে একটি অ্যাপ্লিকেশন রয়েছে যা আমি পেইড অ্যাপ হিসাবে বাজারে আসতে চাই। আমি অন্য সংস্করণটি চাই যা 5 দিনের জন্য সময়সীমা সহ একটি "ট্রায়াল" সংস্করণ হবে?
আমি কীভাবে এটি করতে পারি?
আমার কাছে একটি অ্যাপ্লিকেশন রয়েছে যা আমি পেইড অ্যাপ হিসাবে বাজারে আসতে চাই। আমি অন্য সংস্করণটি চাই যা 5 দিনের জন্য সময়সীমা সহ একটি "ট্রায়াল" সংস্করণ হবে?
আমি কীভাবে এটি করতে পারি?
উত্তর:
বর্তমানে বেশিরভাগ বিকাশকারী নিম্নলিখিত 3 টি কৌশল ব্যবহার করে এটি সম্পাদন করে।
প্রথম পন্থাটি সহজেই পরিলক্ষিত হয়, আপনি প্রথমবার অ্যাপটি চালানোর সময় কোনও ফাইল, ডাটাবেস বা ভাগ করা পছন্দগুলিতে তারিখ / সময় বাঁচান এবং পরীক্ষার সময় শেষ হয়েছে কিনা তা পরীক্ষা করে দেখার পরে প্রতিবার আপনি অ্যাপটি চালাবেন। এটি উদ্ঘাটন করা সহজ কারণ আনইনস্টল করা এবং পুনরায় ইনস্টল করা ব্যবহারকারীর আরও একটি পরীক্ষার সময়কাল মঞ্জুর করে।
দ্বিতীয় পদ্ধতির উদ্দীপনা কঠিন, তবে এখনও পরিবেশনযোগ্য। একটি হার্ড কোডড টাইম বোমা ব্যবহার করুন। মূলত এই পদ্ধতির সাথেই আপনি পরীক্ষার শেষ কোড হয়ে যাবেন এবং অ্যাপটি ডাউনলোড এবং ব্যবহার করেন এমন সমস্ত ব্যবহারকারী একই সাথে অ্যাপটি ব্যবহার করতে সক্ষম হবেন stop আমি এই পদ্ধতির ব্যবহার করেছি কারণ এটি কার্যকর করা সহজ এবং বেশিরভাগ অংশে আমি কেবল তৃতীয় কৌশলটির ঝামেলার মধ্য দিয়ে যাচ্ছি বলে মনে করি না। ব্যবহারকারীরা ম্যানুয়ালি তাদের ফোনে তারিখটি পরিবর্তন করে এটিকে রোধ করতে পারে, তবে বেশিরভাগ ব্যবহারকারীরা এ জাতীয় কাজ করতে সমস্যাটি কাটাবেন না।
তৃতীয় কৌশলটি হ'ল একমাত্র উপায় যা শুনেছি সত্যই আপনি যা করতে চান তা সম্পাদন করতে সক্ষম হবেন। আপনাকে একটি সার্ভার সেট আপ করতে হবে এবং তারপরে যখনই আপনার অ্যাপ্লিকেশন শুরু হবে তখনই অ্যাপ্লিকেশন ফোনটিকে সার্ভারের অনন্য সনাক্তকারী প্রেরণ করবে । সার্ভারের যদি সেই ফোন আইডির জন্য এন্ট্রি না থাকে তবে এটি একটি নতুন করে তোলে এবং সময়টি নোট করে। সার্ভারের যদি ফোন আইডির জন্য একটি প্রবেশিকা থাকে তবে এটি পরীক্ষার সময়সীমা শেষ হয়ে গেছে কিনা তা দেখার জন্য এটি একটি সাধারণ চেক করে। এটির পরে পরীক্ষার মেয়াদ শেষ হওয়ার ফলাফলগুলি আপনার অ্যাপ্লিকেশনটিতে ফিরে আসে। এই পদ্ধতির সংক্ষিপ্ততর হওয়া উচিত নয়, তবে এটি একটি ওয়েব সার্ভার এবং এ জাতীয় সেটআপ করা দরকার।
অনক্রিটে এই চেকগুলি করা সর্বদা ভাল অনুশীলন। মেয়াদ শেষ হয়ে গেলে অ্যাপের পূর্ণ সংস্করণে বাজারের লিঙ্ক সহ একটি সতর্কতা ডায়ালগ পপআপ শেষ হয়েছে । কেবলমাত্র একটি "ওকে" বোতাম অন্তর্ভুক্ত করুন এবং একবার ব্যবহারকারী "ওকে" ক্লিক করে ক্রিয়াকলাপ শেষ করতে "ফিনিস ()" তে কল করুন।
আমি একটি অ্যান্ড্রয়েড ট্রায়াল এসডিকে তৈরি করেছি যা আপনি কেবল আপনার অ্যান্ড্রয়েড স্টুডিও প্রকল্পে ফেলে দিতে পারেন এবং এটি আপনার জন্য সমস্ত সার্ভার-সাইড ম্যানেজমেন্টের যত্ন নেবে (অফলাইন অনুগ্রহকাল সহ)।
এটি সহজভাবে ব্যবহার করতে
আপনার মূল মডিউলে লাইব্রেরি যুক্ত করুন 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);
আপনার অ্যাপ্লিকেশন কীটিতে কল করুন এবং ট্রায়াল এসকিউ আপনার ট্রায়ালি বিকাশকারী ড্যাশবোর্ডে পাওয়া যাবে ।
এটি একটি পুরানো প্রশ্ন তবে যাইহোক, সম্ভবত এটি কাউকে সহায়তা করবে।
আপনি যদি সর্বাধিক সরল পদ্ধতিতে যেতে চান ( তবে অ্যাপটি আনইনস্টল করা / পুনরায় ইনস্টল করা বা ব্যবহারকারী নিজেই ডিভাইসের তারিখ পরিবর্তন করে তবে ব্যর্থ হবে ), এটি এটি হতে পারে:
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
।
এই প্রশ্ন এবং স্নাক্টলনের উত্তর আমাকে আমার ব্যাচেলর থিসিস হিসাবে পদ্ধতি 3 এর ভিত্তিতে একটি সমাধানে কাজ করতে অনুপ্রাণিত করেছিল। আমি জানি বর্তমান অবস্থা উত্পাদনশীল ব্যবহারের জন্য নয় তবে আপনি এটি সম্পর্কে কী ভাবছেন তা শুনতে আমি পছন্দ করব! আপনি কি এমন সিস্টেম ব্যবহার করবেন? আপনি কি এটি ক্লাউড পরিষেবা হিসাবে দেখতে চান (কোনও সার্ভার কনফিগার করতে সমস্যা হচ্ছে না)? সুরক্ষা সমস্যা বা স্থিতিশীলতার কারণে সম্পর্কে উদ্বিগ্ন?
ব্যাচেলর প্রক্রিয়া শেষ হওয়ার সাথে সাথেই আমি সফ্টওয়্যারটিতে কাজ চালিয়ে যেতে চাই। সুতরাং এখন সময় আমি আপনার মতামত প্রয়োজন!
সোর্সকোডটি গিটহাব https://github.com/MaChristmann/momot-trial এ হোস্ট করা হয়েছে
সিস্টেম সম্পর্কে কিছু তথ্য: - সিস্টেমটির তিনটি অংশ রয়েছে, একটি অ্যান্ড্রয়েড গ্রন্থাগার, একটি নোড.জেএস সার্ভার এবং একাধিক পরীক্ষামূলক অ্যাপ্লিকেশন এবং প্রকাশক / বিকাশকারী অ্যাকাউন্ট পরিচালনা করার জন্য একটি কনফিগার রয়েছে।
এটি কেবল সময় ভিত্তিক পরীক্ষাগুলি সমর্থন করে এবং এটি ফোন আইডির পরিবর্তে আপনার (প্লে স্টোর বা অন্যান্য) অ্যাকাউন্ট ব্যবহার করে।
অ্যান্ড্রয়েড লাইব্রেরির জন্য এটি গুগল প্লে লাইসেন্সিং যাচাইকরণ লাইব্রেরির উপর ভিত্তি করে। আমি এটিকে নোড.জেএস সার্ভারের সাথে সংযোগ করতে সংশোধন করেছি এবং অতিরিক্তভাবে গ্রন্থাগারটি কোনও ব্যবহারকারী সিস্টেমের তারিখ পরিবর্তন করেছে কিনা তা সনাক্ত করার চেষ্টা করে। এটি AES এনক্রিপ্ট করা ভাগ করা পছন্দসইগুলিতে একটি পুনরুদ্ধারকৃত ট্রায়াল-লাইসেন্সকে ক্যাশে করে। আপনি কনফিগারারের সাথে ক্যাশের বৈধ সময় কনফিগার করতে পারেন। যদি কোনও ব্যবহারকারী "ডেটা সাফ করুন" গ্রন্থাগারটি একটি সার্ভার-সাইড চেক করতে বাধ্য করবে।
সার্ভারটি https ব্যবহার করছে এবং ডিজিটাল লাইসেন্স-চেক প্রতিক্রিয়া স্বাক্ষর করছে। এটিতে CRUD ট্রায়াল অ্যাপ্লিকেশন এবং ব্যবহারকারীদের (প্রকাশক এবং বিকাশকারী) জন্য একটি এপিআই রয়েছে। লাইসেন্সিং যাচাইকরণ লাইব্রেরির সাথে পরিচিত বিকাশকারীরা পরীক্ষার ফলাফলের সাথে ট্রায়াল অ্যাপটিতে তাদের আচরণ বাস্তবায়ন পরীক্ষা করতে পারেন। সুতরাং আপনি কনফিগারারে আপনি আপনার লাইসেন্স প্রতিক্রিয়াটিকে "লাইসেন্সযুক্ত", "লাইসেন্সবিহীন" বা "সার্ভার ত্রুটি" এ স্পষ্টভাবে সেট করতে পারেন।
যদি আপনি আপনার অ্যাপটিকে একটি গাধা-লাথি মারার নতুন বৈশিষ্ট্যটি আপডেট করেন তবে আপনি চাইবেন যে সকলেই আবার চেষ্টা করতে পারে। কনফিগারারে আপনি মেয়াদোত্তীর্ণ লাইসেন্স সহ ব্যবহারকারীদের জন্য ট্রায়াল করা উচিত এমন একটি সংস্করণকোড সেট করে ট্রায়াল লাইসেন্সটি পুনর্নবীকরণ করতে পারেন। উদাহরণস্বরূপ, ব্যবহারকারী আপনার অ্যাপ্লিকেশনটি ভার্সনকোড 3 এ চালাচ্ছেন এবং আপনি চান তিনি সংস্করণকোডের বৈশিষ্ট্যগুলি ব্যবহার করে দেখতে চান 4.. যদি তিনি অ্যাপটি আপডেট করেন বা পুনরায় ইনস্টল করেন তবে তিনি আবার পুরো পরীক্ষার সময়কালটি ব্যবহার করতে সক্ষম হবেন কারণ সার্ভারটি জানে যে তিনি কোন সংস্করণে শেষবার চেষ্টা করেছেন on সময়।
সবকিছু অ্যাপাচি ২.০ লাইসেন্সের আওতায় রয়েছে
এটি করার সবচেয়ে সহজ এবং সর্বোত্তম উপায় হ'ল প্রয়োগ ব্যাকআপশ্রেডপ্রিফারেন্সেস।
অ্যাপ্লিকেশনটি আনইনস্টল ও পুনরায় ইনস্টল করা থাকলেও অগ্রাধিকারগুলি সংরক্ষণ করা হয়।
কেবল পছন্দ হিসাবে ইনস্টলের তারিখটি সংরক্ষণ করুন এবং আপনি যেতে ভাল।
তত্ত্বটি এখানে: http://developer.android.com/references/android/app/backup/SharedPreferencesBackupHelper.html
উদাহরণটি এখানে: Android SharedPreferences ব্যাকআপ কাজ করছে না
পদ্ধতির 4: অ্যাপ্লিকেশন ইনস্টল সময় ব্যবহার করুন।
যেহেতু এপিআই স্তর 9 (অ্যান্ড্রয়েড 2.3.2, 2.3.1, অ্যান্ড্রয়েড 2.3, জিঙ্গারব্রেড) আছে সেখানে ফার্স্টইনস্টলটাইম এবং সর্বশেষে আপডেটটাইম রয়েছে PackageInfo
।
আরও পড়তে: অ্যান্ড্রয়েড থেকে অ্যাপ ইনস্টল করার সময়টি কীভাবে পাবেন
এখন অ্যান্ড্রয়েড ফ্রি ট্রায়াল সাবস্ক্রিপশনটির সাম্প্রতিক সংস্করণে যুক্ত করা হয়েছে, আপনি বিনামূল্যে পরীক্ষার সময়কালের জন্য অ্যাপের মধ্যে সাবস্ক্রিপশন কেনার পরেই আপনার সমস্ত অ্যাপের বৈশিষ্ট্যগুলি আনলক করতে পারেন। এটি ব্যবহারকারীকে একটি পরীক্ষার সময়কালে আপনার অ্যাপ্লিকেশনটি ব্যবহার করতে দেয়, যদি পরীক্ষার সময়কালের পরেও অ্যাপটি আনইনস্টল করা হয় তবে সাবস্ক্রিপশনের অর্থ আপনার কাছে স্থানান্তরিত হবে। আমি চেষ্টা করিনি, তবে কেবল একটি ধারণা ভাগ করছি।
আমার মতে, এটি করার সর্বোত্তম উপায় হ'ল ফায়ারবেস রিয়েলটাইম ডেটাবেস ব্যবহার করা:
1) আপনার অ্যাপ্লিকেশন ফায়ারবেস সমর্থন যোগ করুন
2) 'বেনামে প্রমাণীকরণ' নির্বাচন করুন যাতে ব্যবহারকারীর সাইন আপ করতে বা আপনি কী করছেন তা জানতে না পারে। এটি বর্তমানে প্রমাণীকৃত ব্যবহারকারীর অ্যাকাউন্টে লিঙ্ক দেওয়ার গ্যারান্টিযুক্ত এবং এটি ডিভাইসগুলিতে কাজ করবে।
3) রিয়েলটাইম ডেটাবেস এপিআই ব্যবহার করুন 'ইনস্টলডডেট' এর জন্য একটি মান সেট করতে। আরম্ভের সময়, কেবল এই মানটি পুনরুদ্ধার করুন এবং এটি ব্যবহার করুন।
আমি একই কাজ করেছি এবং এটি দুর্দান্ত কাজ করে। আমি আনইনস্টল / পুনরায় ইনস্টল জুড়ে এটি পরীক্ষা করতে সক্ষম হয়েছি এবং রিয়েলটাইম ডাটাবেসের মান একই থাকে। আপনার পরীক্ষার সময়টি একাধিক ব্যবহারকারীর ডিভাইস জুড়ে কাজ করে। এমনকি আপনি আপনার ইনস্টল_ডেটও সংস্করণ করতে পারেন যাতে অ্যাপ্লিকেশনটি প্রতিটি নতুন বড় রিলিজের জন্য পরীক্ষার তারিখটিকে 'পুনরায় সেট' করে।
আপডেট : আরও কিছুটা পরীক্ষার পরে, বেনামে ফায়ারবেস মনে হচ্ছে যে আপনি যদি বিভিন্ন ডিভাইস পেয়ে থাকেন এবং পুনরায় ইনস্টলের মধ্যে কোনও গ্যারান্টিযুক্ত না থাকে তবে: / ফায়ারবেস ব্যবহার করা তবে তাদের গুগলে বেঁধে দেওয়া একমাত্র গ্যারান্টিযুক্ত উপায় অ্যাকাউন্ট। এটি কাজ করা উচিত, তবে ব্যবহারকারীর প্রথমে লগইন / সাইনআপ করা দরকার এমন একটি অতিরিক্ত পদক্ষেপের প্রয়োজন।
আমি এখন পর্যন্ত ব্যাক-আপ পছন্দগুলি এবং ইনস্টল করার পরে পছন্দগুলিতে সংরক্ষণ করা তারিখের বিরুদ্ধে কেবল চেক করার সামান্য কম মার্জিত পদ্ধতির সাথে শেষ করেছি। এটি ডেটা-কেন্দ্রিক অ্যাপ্লিকেশনগুলির জন্য কাজ করে যেখানে কোনও ব্যক্তির পক্ষে অ্যাপটি পুনরায় ইনস্টল করা এবং পূর্বে যোগ করা সমস্ত ডেটা পুনরায় প্রবেশ করা অর্থহীন, তবে কোনও সাধারণ গেমের জন্য কাজ করবে না।
এটি এবং অন্যান্য থ্রেডের সমস্ত বিকল্প দেখার পরে, এটি আমার অনুসন্ধান
ভাগ করা পছন্দসই, ডেটাবেস অ্যান্ড্রয়েড সেটিংসে সাফ করা যায়, কোনও অ্যাপ পুনরায় ইনস্টলের পরে হারিয়ে যায়। অ্যান্ড্রয়েডের ব্যাকআপ পদ্ধতিতে ব্যাক আপ নেওয়া যেতে পারে এবং একটি পুনরায় ইনস্টল করার পরে পুনরুদ্ধার করা হবে। ব্যাকআপ সর্বদা উপলব্ধ নাও হতে পারে, যদিও বেশিরভাগ ডিভাইসে থাকা উচিত
বাহ্যিক স্টোরেজ (কোনও ফাইলে লেখার জন্য) সেটিংস থেকে পরিষ্কার হওয়া বা আমরা যদি অ্যাপ্লিকেশনটির ব্যক্তিগত ডিরেক্টরিতে না লিখি তবে একটি পুনরায় ইনস্টল দ্বারা প্রভাবিত হয় না । তবে: আপনাকে নতুন অ্যান্ড্রয়েড সংস্করণগুলিতে রানটাইমের সময় ব্যবহারকারীদের কাছে তাদের অনুমতি চাইতে হবে , সুতরাং আপনার যদি এখনও সেই অনুমতি প্রয়োজন হয় তবে এটি সম্ভবত সম্ভব as ব্যাক আপ করা যায়।
প্যাকেজআইএনফো.ফর্স্টইনস্টলটাইম পুনরায় ইনস্টল করার পরে পুনরায় ইনস্টল করা হয়েছে তবে আপডেটগুলিতে স্থিতিশীল
কিছু অ্যাকাউন্টে সাইন ইন করুন এটি ফায়ারবেসের মাধ্যমে তাদের গুগল অ্যাকাউন্ট বা আপনার নিজের সার্ভারে থাকা কোনও বিষয় নয়: ট্রায়ালটি অ্যাকাউন্টে আবদ্ধ। একটি নতুন অ্যাকাউন্ট তৈরি করা ট্রায়ালটিকে পুনরায় সেট করবে।
ফায়ারবেস বেনামে সাইন ইন আপনি কোনও ব্যবহারকারীকে বেনামে সাইন ইন করতে পারেন এবং ফায়ারবেসে তাদের জন্য ডেটা সঞ্চয় করতে পারেন। তবে স্পষ্টতই অ্যাপটি পুনরায় ইনস্টল করুন এবং অন্যান্য অনথিভুক্ত ইভেন্টগুলি ব্যবহারকারীকে তাদের পরীক্ষার সময়টি পুনরায় সেট করে নতুন বেনামে আইডি দিতে পারে । (গুগল নিজেরাই এ বিষয়ে খুব বেশি ডকুমেন্টেশন সরবরাহ করে না)
ANDROID_ID উপলভ্য নাও হতে পারে এবং নির্দিষ্ট পরিস্থিতিতে যেমন কারখানার পুনরায় সেট করা যেতে পারে change ডিভাইসগুলি সনাক্ত করতে এটি ব্যবহার করা ভাল ধারণা সম্পর্কে মতামতগুলি পৃথক বলে মনে হচ্ছে।
বিজ্ঞাপন আইডি খেলুন ব্যবহারকারীর দ্বারা পুনরায় সেট করা যেতে পারে। বিজ্ঞাপন ট্র্যাকিং নির্বাচন না করে ব্যবহারকারী দ্বারা অক্ষম করা যেতে পারে।
ইনস্ট্যান্সআইডি একটি পুনরায় ইনস্টল পুনরায় সেট করুন । সুরক্ষা ইভেন্টের ক্ষেত্রে পুনরায় সেট করুন। আপনার অ্যাপ্লিকেশন দ্বারা রিসেট করা যেতে পারে।
কোন পদ্ধতির (সংমিশ্রণ) আপনার জন্য কাজ করে তা আপনার অ্যাপের উপর নির্ভর করে এবং আপনি মনে করেন যে গড়পড়তা জন আরও একটি পরীক্ষার সময় অর্জন করতে কতটা প্রচেষ্টা করবে। আমি কেবল অজ্ঞাতনামা ফায়ারবেস এবং বিজ্ঞাপন আইডি তাদের অস্থিতিশীলতার কারণে ব্যবহারের বিষয়ে স্টিয়ারিং সাফ করার পরামর্শ দেব । একাধিক-ফ্যাক্টর পদ্ধতির মতো দেখে মনে হচ্ছে এটি সেরা ফলাফল দেবে। আপনার কাছে কোন উপাদানগুলি উপলব্ধ তা আপনার অ্যাপ এবং এর অনুমতিগুলির উপর নির্ভর করে।
আমার নিজের অ্যাপ্লিকেশানের জন্য আমি ভাগ করে নেওয়া পছন্দগুলি + প্রথম ইনস্টলটাইম + পছন্দগুলির ব্যাকআপটি ন্যূনতম অনুপ্রবেশযোগ্য তবে কার্যকর যথেষ্ট পদ্ধতি হিসাবেও পেয়েছি। আপনাকে নিশ্চিত করতে হবে যে আপনি ভাগ করে নেওয়া পছন্দগুলিতে পরীক্ষার শুরু সময়টি পরীক্ষা করে এবং সংরক্ষণ করার পরে কেবল ব্যাকআপের জন্য অনুরোধ করেছেন request ভাগ করা প্রিফেসের মানগুলিতে অবশ্যই প্রথম ইনস্টলটাইম-এর চেয়ে বেশি প্রাধান্য থাকতে হবে। তারপরে ব্যবহারকারীকে অ্যাপটি পুনরায় ইনস্টল করতে হবে, একবার এটি চালাতে হবে এবং তারপরে ট্রায়ালটি পুনরায় সেট করতে অ্যাপটির ডেটা সাফ করতে হবে, এটি বেশ কাজ। ব্যাকআপ পরিবহন ব্যতীত ডিভাইসগুলিতে ব্যবহারকারী কেবল পুনরায় ইনস্টল করে বিচারটি পুনরায় সেট করতে পারবেন।
আমি সেই পদ্ধতির এক্সটেনসিবল লাইব্রেরি হিসাবে উপলব্ধ করেছি ।
সংজ্ঞা দ্বারা, সব বাজারে প্রদত্ত অ্যান্ড্রয়েড অ্যাপ্লিকেশন কেনার 24 ঘন্টা পরে মূল্যায়ন করা যায়।
একটি 'আনইনস্টল এবং রিফান্ড' বোতাম রয়েছে যা 24 ঘন্টা পরে 'আনইনস্টল' এ পরিবর্তিত হয়।
আমি এই বোতামটি তাত্পর্যপূর্ণভাবে উপস্থাপন করবো!
একই সমস্যাটি অনুসন্ধান করার সময় আমি এই প্রশ্নটি জুড়ে এসেছি, আমি মনে করি আমরা ফ্রি ডেট এপিআই যেমন http://www.timeapi.org/utc/now ব্যবহার করতে পারি ট্রেইল অ্যাপ্লিকেশনটির মেয়াদ শেষ হওয়ার জন্য বা অন্য কোনও তারিখ এপি । আপনি যদি ডেমো সরবরাহ করতে চান এবং অর্থ প্রদানের বিষয়ে উদ্বিগ্ন হন এবং স্থায়ী মেয়াদ ডেমো প্রয়োজন হয় তবে এই উপায়টি কার্যকর। :)
নীচের কোডটি সন্ধান করুন
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 টি অ্যাপ তৈরি করেছি,
আমি পরিশোধিত অ্যাপ হিসাবে স্টোর খেলতে পরীক্ষার ক্রিয়াকলাপ ছাড়াই একটিটিকে আপলোড করেছি,
এবং যা বিনামূল্যে অ্যাপ হিসাবে ট্রায়াল কার্যকলাপ সহ একটি।
প্রথম লঞ্চের ফ্রি অ্যাপ্লিকেশনটিতে ট্রায়াল এবং স্টোর ক্রয়ের বিকল্প রয়েছে, যদি ব্যবহারকারী পছন্দ করে স্টোর ক্রয় করে এটি ব্যবহারকারীকে ক্রয়ের জন্য দোকানে পুনঃনির্দেশ করে তবে ব্যবহারকারী যদি পরীক্ষার জন্য ক্লিক করে এটি পরীক্ষামূলক ক্রিয়াকলাপে নিয়ে যায়
এনবি: আমি বিকল্পটি 3 এস @ এসএনসিটিএলএন এর মতো ব্যবহার করেছি তবে পরিবর্তনের জন্য
প্রথমত , আমি ডিভাইসের সময়ের উপর নির্ভর করি না, আমার পিএইচপি ফাইল থেকে আমার সময় এসেছে যা ট্রায়াল রেজিস্ট্রেশন ডিবিতে করে,
দ্বিতীয়ত , আমি প্রতিটি ডিভাইস স্বতন্ত্রভাবে সনাক্ত করতে ডিভাইস সিরিয়াল নম্বর ব্যবহার করেছি,
শেষ অবধি , অ্যাপটি সার্ভার সংযোগটি তার নিজস্ব সময় নয় বরং সময় মানটির উপর নির্ভর করে তাই ডিভাইস সিরিয়াল নম্বরটি পরিবর্তিত হলেই কেবল সিস্টেমটি অবরুদ্ধ করা যায়, যা ব্যবহারকারীর পক্ষে বেশ চাপের কারণ।
সুতরাং আমার কোডটি এখানে রয়েছে (ট্রায়াল ক্রিয়াকলাপের জন্য):
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);
}
}
আমার পিএইচপি ফাইলটি দেখতে এটির মতো লাগে (এটি একটি রেস্ট-স্লিম প্রযুক্তি):
/**
* 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;
}
}
তারপরে মূল ক্রিয়াকলাপে আমি অংশীদারি পছন্দটি ব্যবহার করি (ট্রায়াল ক্রিয়ায় তৈরি ইনস্টলড) বাকি দিনগুলি নিরীক্ষণ করতে এবং যদি দিনগুলি শেষ হয় তবে আমি মূল ক্রিয়াকলাপ UI কে একটি বার্তা দিয়ে আটকায় যা তাদের কেনার দোকানে নিয়ে যায়।
পাশ শুধুমাত্র নিচে আমি এখানে দেখতে যে একটি যদি বিশৃঙ্খল ব্যবহারকারী Zender মত অ্যাপ্লিকেশান, ফাইল ভাগ সাথে ভাগ করুন প্রদত্ত অ্যাপের ক্রয় এবং সিদ্ধান্ত নেয় বা এমনকি বিনামূল্যে জন্য ডাউনলোড করতে মানুষের জন্য সার্ভারে সরাসরি APK ফাইল হোস্ট। তবে আমি নিশ্চিত যে খুব শীঘ্রই আমি এই উত্তরটি সমাধানের সাথে বা সমাধানের একটি লিঙ্ক দিয়ে সম্পাদনা করব।
আশা করি এটি একটি আত্মাকে বাঁচায় ... কোনও দিন
শুভ কোডিং ...
@snctln অপশন 3 সহজেই পিএইচপি এবং মাইএসকিএল ইনস্টল করে একটি ওয়েব সার্ভারে পিএইচপি ফাইল যুক্ত করে তাদের অনেকেরই ইনস্টল করা যায়।
অ্যান্ড্রয়েড দিক থেকে একটি সনাক্তকারী (ডিভাইস আইডি, গুগল অ্যাকাউন্ট ও আপনি যা চান) ইউটিউবে এইচটিপিআরএল সংযোগ ব্যবহার করে আর্গুমেন্ট হিসাবে পাস করা হয়েছে এবং পিএইচপি যদি টেবিলের মধ্যে উপস্থিত থাকে বা এটি একটি নতুন সারি সন্নিবেশ করে তবে প্রথম ইনস্টলের তারিখ ফেরত দেয় এবং এটি বর্তমান তারিখটি ফেরত দেয়।
এটা আমার জন্য ভালই কাজ করে।
আমার সময় থাকলে আমি কিছু কোড পোস্ট করব!
শুভকামনা!