एक Android परीक्षण एप्लिकेशन बनाना जो एक निश्चित समयावधि के बाद समाप्त हो जाता है


103

मेरे पास एक एप्लिकेशन है जिसे मैं पेड ऐप के रूप में बाजार में हिट करना चाहता हूं। मैं अन्य संस्करण जो 5 दिनों की कहने की समय सीमा के साथ एक "परीक्षण" संस्करण होगा, चाहेंगे?

मैं ऐसा कैसे कर सकता हूं?


Google को वास्तव में Play Services में इसका समर्थन करना चाहिए!
पाउडर 366

@ powder366 वास्तव में Google इसका समर्थन करता है, डेवलपर
।android.com

जवाबों:


186

वर्तमान में अधिकांश डेवलपर्स निम्नलिखित 3 तकनीकों में से एक का उपयोग करके इसे पूरा करते हैं।

पहले दृष्टिकोण को आसानी से दरकिनार किया जाता है, पहली बार जब आप ऐप चलाते हैं तो फ़ाइल, डेटाबेस, या साझा वरीयताओं को दिनांक / समय बचाते हैं और हर बार जब आप ऐप चलाते हैं, तो यह देखने के लिए जांचें कि क्या परीक्षण अवधि समाप्त हो गई है। इसे दरकिनार करना आसान है क्योंकि अनइंस्टॉल और रीइंस्टॉल करने से उपयोगकर्ता को एक और परीक्षण अवधि मिल सकेगी।

दूसरा दृष्टिकोण परिधि के लिए कठिन है, लेकिन फिर भी परिवृत्त है। हार्ड कोडेड टाइम बम का इस्तेमाल करें। मूल रूप से इस दृष्टिकोण के साथ आप परीक्षण के लिए हार्ड कोड की अंतिम तिथि होगी, और ऐप डाउनलोड करने और उपयोग करने वाले सभी उपयोगकर्ता उसी समय ऐप का उपयोग करना बंद कर देंगे। मैंने इस दृष्टिकोण का उपयोग किया है क्योंकि इसे लागू करना आसान है और अधिकांश भाग के लिए मुझे बस तीसरी तकनीक की परेशानी से गुजरने का मन नहीं था। उपयोगकर्ता अपने फोन पर तारीख को मैन्युअल रूप से बदलकर इसे दरकिनार कर सकते हैं, लेकिन अधिकांश उपयोगकर्ता ऐसा काम करने के लिए परेशानी से नहीं गुजरेंगे।

तीसरी तकनीक एकमात्र तरीका है जिसके बारे में मैंने सुना है कि आप जो करना चाहते हैं उसे वास्तव में पूरा करने में सक्षम हैं। आपको एक सर्वर सेट करना होगा, और फिर जब भी आपका आवेदन शुरू किया जाता है, आपका ऐप सर्वर को विशिष्ट पहचानकर्ता फोन भेजता है। यदि सर्वर के पास उस फ़ोन आईडी के लिए कोई प्रविष्टि नहीं है, तो यह एक नया बनाता है और समय को नोट करता है। यदि सर्वर में फोन आईडी के लिए एक प्रविष्टि है, तो यह देखने के लिए एक सरल जांच करता है कि क्या परीक्षण अवधि समाप्त हो गई है। यह तब आपके आवेदन पर वापस परीक्षण समाप्ति के परिणामों का संचार करता है। यह दृष्टिकोण नहीं होना चाहिए, लेकिन इसके लिए एक वेबसर्वर और इस तरह की स्थापना की आवश्यकता होती है।

ऑनक्रिएट में इन चेक को करना हमेशा अच्छा अभ्यास होता है। यदि समाप्ति की समाप्ति ऐप के पूर्ण संस्करण के लिए एक मार्केट लिंक के साथ एक अलर्टडायलॉग पॉपअप हुई है । केवल एक "ओके" बटन शामिल करें, और एक बार "ओके" पर क्लिक करने पर उपयोगकर्ता गतिविधि को समाप्त करने के लिए "खत्म ()" करने के लिए कॉल करता है।


2
शानदार जवाब। जैसा कि आप कहते हैं कि मुझे लगता है कि दूसरा विकल्प संभवतः सबसे अच्छा है। इसकी शर्म की बात यह है कि यह स्वयं भी किसी प्रकार की लाइसेंस प्रणाली की पेशकश नहीं करता है क्योंकि यह छोटे और बड़े ब्रांड डेवलपर्स दोनों को और भी अधिक एंड्रॉइड एप्लिकेशन बनाने के लिए प्रोत्साहित कर सकता है।
टॉम

8
इसके अलावा, मैं स्टार्टअप के दौरान जांच नहीं करूंगा। आपका लक्ष्य ऐप को बेचना है, न कि उपयोगकर्ता को दंडित करना (यह सिर्फ एक बोनस है;) यदि आपने इसे चलाने के दौरान हर 2 मिनट की जांच करने के लिए सेट किया है, तो आप उपयोगकर्ता को कुछ करना शुरू करते हैं और फिर एहसास करते हैं कि उन्हें भुगतान करना चाहिए। यदि आप भुगतान करना वास्तव में आसान बनाते हैं और काम पर वापस आते हैं (मुझे यकीन नहीं है कि अगर आप एंड्रॉइड में कर सकते हैं तो) मुझे लगता है कि आप onCreate के दौरान अधिक बिक्री करेंगे।
व्हेलडॉग

4
@Whaledawg: आपको अपना स्वयं का सर्वर चलाने की आवश्यकता है क्योंकि सर्वर किसी डेटाबेस में पहली बार चलने वाले फ़ोन आईडी और समय को स्टोर कर रहा है, जिसकी तुलना बाद में की जाएगी। जब आप चेक डेवलपर की विशुद्ध रूप से पसंद करते हैं, तो मैंने हार्ड का उपयोग किया महान परिणामों के साथ एक खेल में कोडित समय बम। पूरा ऐप लोड हो जाएगा, लेकिन उपयोगकर्ता केवल उस संवाद के साथ बातचीत कर सकता है जो देखा जाता है, उस संवाद पर एक बटन होता है जो उपयोगकर्ता को गेम के लिए सीधे खरीद पृष्ठ पर ले जाता है। एंड्रॉइड मार्केट के खुलने के बाद से उपयोगकर्ता AFAIK को ध्यान में नहीं रखते हैं क्योंकि यह गेम शीर्ष 10 भुगतान किए गए ऐप्स में है।
Snctln

11
अतिरिक्त सर्वर सेटअप के कारण विकल्प 3 का उपयोग करने के लिए अनिच्छुक, Parse.com पर एक नज़र डालें - यह एक सिंक है।
जोएल स्क्रेपनेक

3
परीक्षण की हार्डकोड समाप्ति तिथि का क्या मतलब है? क्या इसका मतलब है कि आप भविष्य में अलग-अलग हार्डकोडेड तारीखों के साथ ट्रायल ऐप के नए संस्करण हमेशा के लिए जारी रखेंगे?
जैस्पर

21

मैंने एक एंड्रॉइड ट्रायल एसडीके विकसित किया है जिसे आप बस अपने एंड्रॉइड स्टूडियो प्रोजेक्ट में छोड़ सकते हैं और यह आपके लिए सभी सर्वर-साइड प्रबंधन (ऑफ़लाइन अनुग्रह अवधि सहित) का ध्यान रखेगा।

इसका उपयोग करने के लिए, बस

लाइब्रेरी को अपने मुख्य मॉड्यूल में जोड़ें 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 को आपके ट्राइबल डेवलपर डैशबोर्ड में पाया जा सकता है ।


इसे सक्षम करने के लिए डेटा की आवश्यकता है?
शिवराम बोनाया

ट्रायल विश्वसनीय नहीं है
अमीर डोरा

1
@AmirDe हाय आमिर, क्या आप मुझे बता सकते हैं कि आपके लिए क्या काम नहीं कर रहा है? मुझे मदद करने के लिए खुशी है, support@trialy.io Trialy 1000+ उपयोगकर्ताओं के लिए बढ़िया काम कर रहा है
Nick

@ कोई कारण नहीं जानता कि मेरा डिवाइस एंड्रॉइड लॉलीपॉप चला रहा है, जब मैं डैशबोर्ड से दिन सेट करता हूं, तो दिन कुछ मिनटों के बाद नकारात्मक मूल्य में दिखाता है, यह कहता है कि परीक्षण समाप्त हो गया है, भले ही मेरे पास अभी भी कई दिन डैशबोर्ड में हैं। मैंने नौगाट डिवाइस पर भी परीक्षण किया, लगता है कि यह नौगट पर ठीक काम कर रहा है। हो सकता है कि इसमें कुछ पुराने एंड्रॉइड वर्जन कम्पैटिबिलिटी इश्यू हों
अमीर डोरा

1
मैं 2016 से इस सेवा का उपयोग कर रहा हूं, यह हर बार ठीक काम करती है। मैंने अपनी आधिकारिक परियोजनाओं में भी इसका इस्तेमाल किया। यह उत्तर स्वीकार किया जाना चाहिए।
तारिक महमूद

17

यह एक पुराना सवाल है, लेकिन वैसे भी, शायद यह किसी की मदद करेगा।

यदि आप सबसे सरल दृष्टिकोण के साथ जाना चाहते हैं (जो कि यदि एप्लिकेशन अनइंस्टॉल / पुनः इंस्टॉल या उपयोगकर्ता की डिवाइस की तारीख को मैन्युअल रूप से बदल देता है) तो यह विफल हो जाएगा , यह इस प्रकार है:

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 !!!
        }
    }

    ...
}

वास्तव में यदि आप SharedPreferences और Android 2.2 Froyo या इसके बाद के संस्करण का उपयोग कर रहे हैं, जब तक कि आप Google डेटा सिंक्रनाइज़ेशन के लिए डेटा बैकअप API को कार्यान्वित करते हैं, तब उपयोगकर्ता को डिवाइसों पर या स्थापना रद्द करके, सेटिंग्स और एप्लिकेशन पर जाकर इससे बचना नहीं चाहिए समाशोधन डेटा। इसके अलावा, दिनांक पर विधि getTimeनहीं है getTimeInMillis
टॉम

असहमत, यह भी विफल हो जाएगा यदि उपयोगकर्ता डिवाइस की तारीख को मैन्युअल रूप से बदलता है और यह भी कि अगर उपयोगकर्ता मैन्युअल रूप से डेटा को साफ करता है तो क्या होगा?
मोहम्मद अजहरुद्दीन शेख

@ यह साझाकरण के साथ अच्छा करने के लिए अच्छा है, लेकिन उपयोगकर्ता सेटिंग से स्पष्ट स्मृति wiil-> आवेदन प्रबंधक और पुन: उपयोग तो क्या करेंगे?
कोरोनापिन्टू

@CoronaPintu, ताकि यह दृष्टिकोण भी फायरबेस के साथ प्रयास कर रहा हो, इससे सही संयोजन हो जाएगा, और परीक्षण अवधि भी समाप्त हो जाएगी, यहां तक ​​कि ऐप की स्थापना रद्द हो जाएगी।
नूर हुसैन

"अजनबी" जवाब के साथ दृष्टिकोण को जोड़ो और जोड़ो, इससे दृष्टिकोण सही होगा।
नूर हुसैन

10

इस सवाल और स्नक्लेन के जवाब ने मुझे मेरी स्नातक थीसिस के रूप में विधि 3 पर आधारित समाधान पर काम करने के लिए प्रेरित किया। मुझे पता है कि वर्तमान स्थिति उत्पादक उपयोग के लिए नहीं है, लेकिन मुझे यह सुनना अच्छा लगेगा कि आप इसके बारे में क्या सोचते हैं! क्या आप ऐसी प्रणाली का उपयोग करेंगे? क्या आप इसे क्लाउड सेवा के रूप में देखना चाहेंगे (सर्वर को कॉन्फ़िगर करने में परेशानी नहीं होगी)? सुरक्षा मुद्दों या स्थिरता के कारणों के बारे में चिंतित हैं?

जैसे ही मैंने स्नातक प्रक्रिया पूरी की, मैं सॉफ्टवेयर पर काम करना जारी रखना चाहता हूं। तो अब इसका समय मुझे आपकी प्रतिक्रिया की आवश्यकता है!

सोर्सकोड को GitHub https://github.com/MaChristmann/mobile-trial पर होस्ट किया गया है

सिस्टम के बारे में कुछ जानकारी: - सिस्टम में तीन भाग होते हैं, एक एंड्रॉइड लाइब्रेरी, एक नोड.जेएस सर्वर और कई ट्रायल ऐप्स और प्रकाशक / डेवलपर खातों के प्रबंधन के लिए एक कॉन्फ़िगरेशन।

  • यह केवल समय-आधारित परीक्षणों का समर्थन करता है और यह फोन आईडी के बजाय आपके (प्ले स्टोर या अन्य) खाते का उपयोग करता है।

  • Android लाइब्रेरी के लिए यह Google Play लाइसेंसिंग सत्यापन लाइब्रेरी पर आधारित है। मैंने इसे नोड.जेएस सर्वर से कनेक्ट करने के लिए संशोधित किया है और इसके अलावा पुस्तकालय यह पहचानने की कोशिश करता है कि क्या उपयोगकर्ता ने सिस्टम की तारीख बदल दी है। यह एईएस एन्क्रिप्टेड साझा वरीयताएँ में एक पुनः प्राप्त परीक्षण-लाइसेंस को भी कैश करता है। आप कॉन्फ़िगरकर्ता के साथ कैश के मान्य समय को कॉन्फ़िगर कर सकते हैं। यदि कोई उपयोगकर्ता "स्पष्ट डेटा" लायब्रेरी सर्वर-साइड जाँच के लिए बाध्य करेगा।

  • सर्वर, https का उपयोग कर रहा है और लाइसेंस-चेक प्रतिक्रिया पर डिजिटल हस्ताक्षर भी कर रहा है। इसमें CRUD ट्रायल ऐप्स और उपयोगकर्ताओं (प्रकाशक और डेवलपर) के लिए API भी है। लाइसेंसिंग वेरिफिकेशन लाइब्रेरी डेवलपर्स के पास परीक्षण के परिणाम के साथ परीक्षण अनुप्रयोग में उनके व्यवहार के कार्यान्वयन का परीक्षण कर सकते हैं। तो आप विन्यासकर्ता में आप स्पष्ट रूप से "लाइसेंस", "लाइसेंस नहीं" या "सर्वर त्रुटि" के लिए अपनी लाइसेंस प्रतिक्रिया सेट कर सकते हैं।

  • यदि आप अपने ऐप को एक गधा-किकिंग नई सुविधा के साथ अपडेट करते हैं तो आप चाहते हैं कि हर कोई इसे फिर से आज़मा सकता है। विन्यासकर्ता में आप उन उपयोगकर्ताओं के लिए परीक्षण लाइसेंस को नवीनीकृत कर सकते हैं जिनकी समय सीमा समाप्त हो गई है, जो एक संस्करणकोड स्थापित करके इसे ट्रिगर करना चाहिए। उदाहरण के लिए, उपयोगकर्ता आपके एप्लिकेशन को वर्जनकोड 3 पर चला रहा है, तो आप चाहते हैं कि वह वर्जनकोड 4 के फीचर्स को आजमाए। यदि वह एप को अपडेट करता है या उसे फिर से इंस्टॉल करता है, तो वह पूर्ण परीक्षण अवधि का फिर से उपयोग करने में सक्षम होता है, क्योंकि सर्वर को पता होता है कि उसने किस संस्करण को अंतिम रूप से आजमाया है। समय।

  • सब कुछ अपाचे 2.0 लाइसेंस के तहत है


2
आपने सिर्फ मेरा दिन बचाया, कड़ी मेहनत के लिए धन्यवाद। मैंने बस एक बार प्राप्त एन्क्रिप्टेड कॉन्फ़िगरेशन का उपयोग करने और आवेदन के अंदर सार्वजनिक कुंजी रखने का एक ही समाधान लिखने के लिए सोचा था। इतनी बड़ी समस्या मैं केवल यह देख सकता हूं कि आप लाइसेंस कैसे प्रदान करते हैं? एकाधिक अनुदानों से बचने के लिए आपको फोन के साथ कुछ विशिष्ट आईडी ले जाने की आवश्यकता हो सकती है।
user2305886

6

ऐसा करने का सबसे आसान और सबसे अच्छा तरीका है, बैकअपसेर्डपायरिफिकेशन लागू करना।

वरीयताओं को संरक्षित किया जाता है, भले ही ऐप को अनइंस्टॉल और पुनः इंस्टॉल किया गया हो।

बस इंस्टॉल डेट को प्राथमिकता के रूप में सहेजें और आप जाने के लिए अच्छे हैं।

यहाँ सिद्धांत है: http://developer.android.com/reference/android/app/backup/SaringPreferencesBackupHelper.html

यहाँ उदाहरण है: Android SharedPreferences बैकअप काम नहीं कर रहा है


3
उपयोगकर्ता सिस्टम सेटिंग्स में बैकअप को अक्षम कर सकता है।
पैट्रिक

5

दृष्टिकोण 4: एप्लिकेशन इंस्टॉल समय का उपयोग करें।

चूंकि API स्तर 9 (Android 2.3.2, 2.3.1, Android 2.3, GINGERBREAD ) में पहले InstallTime और lastUpdateTime हैं PackageInfo

अधिक पढ़ने के लिए: Android से एप्लिकेशन इंस्टॉल समय कैसे प्राप्त करें


क्या Snctln के उत्तर से विधि 1 का उपयोग इस मज़बूती के साथ किया जा सकता है, बिना आसानी से चक्कर लगाए, या क्या इसमें समान या समान समस्या है?

मैंने इस विधि का परीक्षण किया है। अच्छा पक्ष यह है कि यह तब भी काम करता है जब डेटा क्लियर हो जाता है। इसका बुरा पक्ष यह है कि इसे अनइंस्टॉल / रीइंस्टॉल करके दरकिनार किया जा सकता है।
जीन-फिलिप जोडइन

पुष्टि कर सकते हैं: मेरे पिक्सेल पर, ऐप को अनइंस्टॉल और रीइंस्टॉल करना पहले इंस्टॉल समय को रीसेट करता है। उपयोगकर्ताओं को परीक्षण को रीसेट करने के लिए यह बहुत तुच्छ बनाता है
फेबियन स्ट्रीटेल 14

3

अब एंड्रॉइड फ्री ट्रायल सब्सक्रिप्शन के हाल के संस्करण में जोड़ दिया गया है, आप निशुल्क परीक्षण अवधि के लिए ऐप के भीतर सदस्यता खरीदने के बाद ही अपने सभी ऐप की विशेषताओं को अनलॉक कर सकते हैं। यह उपयोगकर्ता को परीक्षण अवधि के लिए आपके ऐप का उपयोग करने देगा, अगर परीक्षण अवधि के बाद भी ऐप को अनइंस्टॉल किया जाता है तो सदस्यता धन आपको स्थानांतरित कर दिया जाएगा। मैंने कोशिश नहीं की है, लेकिन सिर्फ एक विचार साझा कर रहा हूं।

यहाँ प्रलेखन है


2
मेरी इच्छा है कि यह एकल खरीद के लिए काम करे। केवल सदस्यता के लिए काम करता है, जो वार्षिक या मासिक हो सकता है।

3

मेरी राय में, ऐसा करने का सबसे अच्छा तरीका केवल फायरबेस रियलटाइम डेटाबेस का उपयोग करना है:

1) अपने ऐप में फायरबेस सपोर्ट जोड़ें

2) 'बेनामी प्रमाणीकरण' का चयन करें ताकि उपयोगकर्ता को साइनअप न करना पड़े या यह भी पता चले कि आप क्या कर रहे हैं। यह वर्तमान में प्रमाणित उपयोगकर्ता खाते से लिंक करने की गारंटी है और इसलिए यह सभी डिवाइसों पर काम करेगा।

3) 'install_date' का मान सेट करने के लिए रीयलटाइम डेटाबेस API का उपयोग करें। लॉन्च के समय, बस इस मान को पुनः प्राप्त करें और इसका उपयोग करें।

मैंने वही किया है और यह बहुत अच्छा काम करता है। मैं इसे अनइंस्टॉल / री-इंस्टॉल्स में परीक्षण करने में सक्षम था और रियलटाइम डेटाबेस में मूल्य समान रहता है। इस तरह आपकी परीक्षण अवधि कई उपयोगकर्ता उपकरणों पर काम करती है। आप अपने install_date को भी संस्करणित कर सकते हैं ताकि ऐप प्रत्येक नई प्रमुख रिलीज़ के लिए परीक्षण दिनांक को 'रीसेट' कर सके।

अद्यतन : थोड़ा और अधिक परीक्षण करने के बाद, ऐसा लगता है कि अनाम फायरबेस आपको अलग-अलग उपकरणों के मामले में एक अलग आईडी आवंटित करता है और फिर से इंस्टॉल के बीच गारंटी नहीं दी जाती है: / एकमात्र गारंटी तरीका फायरबेस का उपयोग करना है लेकिन इसे अपने Google पर टाई करें लेखा। यह काम करना चाहिए, लेकिन इसके लिए एक अतिरिक्त कदम की आवश्यकता होगी जहां उपयोगकर्ता को पहले लॉगिन / साइनअप करना होगा।

इस प्रकार मैंने अब तक बैक-अप प्राथमिकताओं के खिलाफ थोड़ी कम सुरुचिपूर्ण दृष्टिकोण और स्थापित होने पर वरीयताओं में संग्रहीत तारीख के साथ समाप्त कर दिया है। यह डेटा-केंद्रित एप्लिकेशन के लिए काम करता है, जहां किसी व्यक्ति के लिए ऐप को फिर से इंस्टॉल करना और पहले जोड़े गए सभी डेटा को फिर से दर्ज करना बेकार है, लेकिन एक साधारण गेम के लिए काम नहीं करेगा।


मुझे अपने एंड्रॉइड ऐप के लिए आपकी समान आवश्यकता है और मेरे पास अपना खुद का डेटाबेस / वेबसर्वर है। उपयोगकर्ताओं को लॉगिन की आवश्यकता नहीं है, इसलिए मैं इंस्टॉल किए गए_डेट के साथ डिवाइस आईडी को बचाने की योजना बना रहा हूं, क्या यह काम करेगा?
user636525

@strangetimes, मुझे लगता है कि आपका समाधान सबसे अच्छा काम करता है, क्या आप अधिक जानकारी प्रदान कर सकते हैं? धन्यवाद
DayDayHappy

यह धागा stackoverflow.com/q/41733137/1396068 पता चलता है कि एप्लिकेशन पुन: इंस्टॉल करने के बाद आप एक नया यूजर आईडी, Ie एक नया परीक्षण अवधि पाने
फैबियन Streitel

3

इस और अन्य धागों में सभी विकल्पों को देखने के बाद, ये मेरे निष्कर्ष हैं

साझा प्राथमिकताएँ, डेटाबेस को एंड्रॉइड सेटिंग्स में साफ़ किया जा सकता है, ऐप पुनः इंस्टॉल करने के बाद खो जाता है। Android के बैकअप तंत्र के साथ बैकअप लिया जा सकता है और पुनर्स्थापना के बाद पुनर्स्थापित किया जाएगा। बैकअप हमेशा उपलब्ध नहीं हो सकता है, हालांकि अधिकांश उपकरणों पर होना चाहिए

बाहरी संग्रहण (फ़ाइल में लिखना) सेटिंग्स से स्पष्ट या प्रभावित नहीं होता है, अगर हम एप्लिकेशन की निजी निर्देशिका में नहीं लिखते हैं । लेकिन: आपको नए एंड्रॉइड संस्करणों में रनटाइम पर उपयोगकर्ता से उनकी अनुमति मांगने की आवश्यकता होती है , इसलिए यह केवल संभव है यदि आपको उस अनुमति की आवश्यकता है। का बैकअप भी लिया जा सकता है।

PackageInfo.firstInstallTime एक पुन: स्थापित है, लेकिन अपडेट के दौरान स्थिर है

कुछ खाते में साइन इन करें अगर आपके Google खाते में Firebase या आपके स्वयं के सर्वर में एक के माध्यम से कोई फर्क नहीं पड़ता: परीक्षण खाते के लिए बाध्य है। एक नया खाता बनाने से परीक्षण रीसेट हो जाएगा।

आप में Firebase अनाम साइन इन कर सकते हैं गुमनाम रूप से एक उपयोगकर्ता में साइन इन करें और Firebase में उनके लिए डेटा संग्रहीत करें। लेकिन जाहिरा तौर पर ऐप को फिर से इंस्टॉल किया जा सकता है और हो सकता है कि अन्य अवांछित घटनाएं उपयोगकर्ता को अपना ट्रायल टाइम रीसेट करते हुए एक नई अनाम आईडी दे सकें। (Google स्वयं इस पर बहुत अधिक दस्तावेज़ प्रदान नहीं करता है)

ANDROID_ID उपलब्ध नहीं हो सकता है और कुछ परिस्थितियों में बदल सकता है , जैसे फ़ैक्टरी रीसेट। उपकरणों की पहचान करने के लिए इसका उपयोग करने के लिए एक अच्छा विचार है या नहीं, इस पर राय भिन्न है।

विज्ञापन आईडी चलायें उपयोगकर्ता द्वारा रीसेट किया जा सकता है। विज्ञापन ट्रैकिंग से बाहर निकलकर उपयोगकर्ता द्वारा अक्षम किया जा सकता है।

एक पुनर्स्थापना पर InstanceID रीसेट करेंएक सुरक्षा घटना के मामले में रीसेट करें। अपने ऐप द्वारा रीसेट किया जा सकता है।

आपके लिए किस (संयोजन) तरीके काम करते हैं, यह आपके ऐप पर निर्भर करता है और आपको लगता है कि औसत परीक्षण जॉन कितने समय में एक और परीक्षण अवधि हासिल करने में लगाएगा। मैं उनकी अस्थिरता के कारण केवल अनाम फायरबेस और विज्ञापन आईडी का उपयोग करने की स्पष्टता की सिफारिश करूंगा । एक बहु-कारक दृष्टिकोण ऐसा लगता है कि यह सर्वोत्तम परिणाम देगा। आपके लिए कौन से कारक उपलब्ध हैं, यह आपके ऐप और उसकी अनुमतियों पर निर्भर करता है।

अपने स्वयं के ऐप के लिए मुझे साझा प्राथमिकताएँ मिलीं + प्राथमिकताओं का FirstInstallTime + बैकअप कम से कम दखल देने के लिए लेकिन यह भी पर्याप्त प्रभावी तरीका है। आपको यह सुनिश्चित करना होगा कि साझा प्राथमिकताओं में परीक्षण प्रारंभ समय की जाँच और भंडारण के बाद आप केवल एक बैकअप का अनुरोध करें। साझा किए गए Prefs में मानों को पहलेInstallTime पर पूर्ववर्ती होना चाहिए। फिर उपयोगकर्ता को ऐप को फिर से स्थापित करना होगा, इसे एक बार चलाना होगा और फिर परीक्षण को रीसेट करने के लिए ऐप के डेटा को साफ़ करना होगा, जो काफी काम का है। एक बैकअप परिवहन के बिना उपकरणों पर उपयोगकर्ता केवल पुनः स्थापित करके परीक्षण को रीसेट कर सकता है, हालांकि।

मैंने उस दृष्टिकोण को एक विस्तृत पुस्तकालय के रूप में उपलब्ध कराया है ।


1

परिभाषा के अनुसार, बाजार में सभी भुगतान किए गए एंड्रॉइड ऐप को खरीद के बाद 24 घंटे के लिए मूल्यांकन किया जा सकता है।

एक 'अनइंस्टॉल और रिफंड' बटन है जो 24 घंटे के बाद 'अनइंस्टॉल' में बदल जाता है।

मेरा तर्क है कि यह बटन बहुत प्रमुख है!


17
ध्यान दें कि रिफंड की अवधि अब केवल 15 मिनट है।
Intrications

1

मैं इसी समस्या को खोजते हुए इस सवाल पर आता हूं, मुझे लगता है कि हम 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

}
}

इसका कार्य समाधान .....


सुनिश्चित करें कि आप उपयोग कर सकते हैं, लेकिन उस स्थिति में केवल आपकी मुख्य गतिविधि ही समाप्ति की जांच कर पाएगी।
RQube

0

यहां बताया गया है कि मैं अपने बारे में कैसे गया, मैंने 2 ऐप बनाए, जिनमें से एक ट्रायल गतिविधि के बिना दूसरा था,

मैंने बिना किसी भुगतान गतिविधि के पेड ऐप के रूप में स्टोर चलाने के लिए अपलोड किया है,

और नि: शुल्क अनुप्रयोग के रूप में परीक्षण गतिविधि के साथ एक।

पहले लॉन्च पर नि: शुल्क ऐप में परीक्षण और स्टोर खरीद के लिए विकल्प हैं, यदि उपयोगकर्ता चयन करता है स्टोर खरीद इसे उपयोगकर्ता को खरीदने के लिए स्टोर पर पुनर्निर्देशित करता है लेकिन यदि उपयोगकर्ता परीक्षण पर क्लिक करता है तो यह उन्हें परीक्षण गतिविधि में ले जाता है।

NB: मैंने @snctln जैसे 3 विकल्पों का उपयोग किया लेकिन संशोधनों के साथ

सबसे पहले , मैं डिवाइस समय पर निर्भर नहीं था, मुझे अपना समय 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- स्लिम तकनीक):

/**
     * 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;
        }

    }

फिर मुख्य गतिविधि पर मैं शेष दिनों की संख्या पर नजर रखने के लिए साझा प्राथमिकता (ट्रायल गतिविधि में बनाया गया इंस्टालेशन) का उपयोग करता हूं और यदि दिन खत्म हो जाते हैं तो मैं मुख्य गतिविधि यूआई को एक संदेश के साथ ब्लॉक करता हूं जो उन्हें खरीदने के लिए स्टोर पर ले जाता है।

केवल नीचे की ओर मैं यहां देख रहा हूं कि यदि कोई दुष्ट उपयोगकर्ता भुगतान किए गए ऐप को खरीदता है और Zender, फ़ाइल शेयर या यहां तक ​​कि एपीके फ़ाइल को सीधे लोगों के लिए मुफ्त में डाउनलोड करने के लिए सर्वर के साथ साझा करने का निर्णय लेता है। लेकिन मुझे यकीन है कि मैं जल्द ही इस जवाब को उस समाधान के साथ या समाधान के लिंक के साथ संपादित करूंगा।

आशा है कि यह एक आत्मा को बचाता है ... किसी दिन

हैप्पी कोडिंग ...


0

@snctln विकल्प 3 को आसानी से वेब सर्वर के लिए php फ़ाइल जोड़कर php और mysql स्थापित किया जा सकता है, जैसा कि उनमें से कई के पास है।

एंड्रॉइड की ओर से एक पहचानकर्ता (डिवाइस आईडी, Google खाता ओ जो भी आप चाहते हैं) को HttpURLConnection का उपयोग करके URL में तर्क के रूप में पारित किया गया है और अगर यह तालिका में मौजूद है या यह एक नई पंक्ति सम्मिलित करता है और पहली स्थापना की तारीख देता है यह वर्तमान तिथि लौटाता है।

यह मेरे लिए ठीक काम करता है।

अगर मेरे पास समय है तो मैं कुछ कोड पोस्ट करूँगा!

शुभ लाभ !


रुको, लेकिन क्या आप एप्लिकेशन पुनः इंस्टॉल करने के बाद अपनी अनूठी आईडी को ढीला कर देते हैं !!
मक्सिम नियाज़ेव

यह आईडी हार्डवेयर, फोन की पहचान करता है, उपयोगकर्ता इसे नहीं देखता है और इसे बदल नहीं सकता है। यदि वह ऐप को पुनः स्थापित करता है तो php वेब सेवा यह पता लगाएगी कि यह वही फोन है। दूसरी ओर, यदि उपयोगकर्ता फोन बदलता है तो वह एक नई अवधि का आनंद लेगा।
लुलिस फेलिसर्ट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.