Belirli bir süre sonra sona eren bir Android deneme uygulaması oluşturma


Yanıtlar:


186

Şu anda çoğu geliştirici, aşağıdaki 3 teknikten birini kullanarak bunu başarmaktadır.

İlk yaklaşım kolayca atlatılır, uygulamayı ilk çalıştırdığınızda tarihi / saati bir dosyaya, veritabanına veya paylaşılan tercihlere kaydedin ve bu kontrolden sonra uygulamayı her çalıştırdığınızda deneme süresinin bitip bitmediğini kontrol edin. Bunu aşmak kolaydır, çünkü kaldırma ve yeniden yükleme kullanıcının başka bir deneme süresine sahip olmasına izin verir.

İkinci yaklaşımın üstesinden gelmek daha zordur, ancak yine de aşılabilir. Sabit kodlu bir saatli bomba kullanın. Temel olarak bu yaklaşımla, deneme için bir bitiş tarihini sabit kodlayacaksınız ve uygulamayı indirip kullanan tüm kullanıcılar aynı anda uygulamayı kullanamayacak. Bu yaklaşımı, uygulaması kolay olduğu için kullandım ve çoğunlukla üçüncü tekniğin sıkıntısından geçmek istemedim. Kullanıcılar, telefonlarındaki tarihi manuel olarak değiştirerek bunu aşabilirler, ancak çoğu kullanıcı böyle bir şey yapmak için zahmete girmez.

Üçüncü teknik, yapmak istediğinizi gerçekten başarmak için duyduğum tek yoldur. Bir sunucu kurmanız gerekecek ve ardından uygulamanız her başlatıldığında, uygulamanız telefonlara benzersiz tanımlayıcıyı sunucuya gönderir . Sunucunun bu telefon kimliği için bir girişi yoksa, yeni bir tane oluşturur ve saati not eder. Sunucunun telefon kimliği için bir girişi varsa, deneme süresinin dolup dolmadığını görmek için basit bir kontrol yapar. Ardından, deneme süresinin dolduğu kontrolün sonuçlarını uygulamanıza geri iletir. Bu yaklaşım atlatılamaz, ancak bir web sunucusu kurmayı gerektirir.

Bu kontrolleri onCreate'te yapmak her zaman iyi bir uygulamadır. Son kullanma süresi sona erdiyse , uygulamanın tam sürümüne bir pazar bağlantısı içeren bir AlertDialog açılır . Yalnızca bir "Tamam" düğmesi ekleyin ve kullanıcı "Tamam" ı tıkladığında etkinliği sonlandırmak için "bitir ()" çağrısı yapın.


2
Harika cevap. Dediğin gibi, ikinci seçeneğin muhtemelen en iyisi olduğunu düşünüyorum. Hem küçük hem de büyük marka geliştiricileri daha da fazla android uygulaması üretmeye teşvik edebileceğinden, Google'ın kendisinin bir tür lisans sistemi sunmaması utanç verici.
Tom

8
Ayrıca, başlatma sırasında kontrol etmezdim. Amacınız uygulamayı satmak, kullanıcıyı cezalandırmak değil (bu sadece bir bonus;) Çalışırken her 2 dakikada bir kontrol etmeye ayarladıysanız, kullanıcının bir şeyler yapmaya başlamasına izin verin ve ardından ödeme yapması gerektiğini anlayın. Ödeme yapmayı ve işe geri dönmeyi gerçekten kolaylaştırırsanız (Android'de yapıp yapamayacağınızdan emin değilim), onCreate sırasında kontrol etmekten daha fazla satacağınızı düşünüyorum.
Whaledawg

4
@Whaledawg: Kendi sunucunuzu çalıştırmanız gerekiyor çünkü sunucu telefon kimliğini ve ilk çalıştırma zamanını daha sonra karşılaştırılan bir veritabanında depoluyor.Ayrıca, kontrolü yaptığınızda tamamen geliştiricinin tercihi, zoru kullandım harika sonuçlar veren bir oyunda kodlu saatli bomba. Tüm uygulama yüklenir, ancak kullanıcı yalnızca görünen iletişim kutusu ile etkileşime girebilir, bu iletişim kutusunda kullanıcıyı doğrudan oyunun satın alma sayfasına götüren bir düğme vardır. Kullanıcılar, bu oyun Android Market'in açılmasından bu yana en iyi 10 ücretli uygulama arasında yer aldığı için AFAIK'i umursamıyor gibi görünüyor.
snctln

11
Ek sunucu kurulumu nedeniyle 3. seçeneği kullanmak istemeyenler için Parse.com'a bir göz atın - bu bir senkronizasyondur.
Joel Skrepnek

3
Sabit kod deneme bitiş tarihi ile ne kastedilmektedir? Bu, gelecekte farklı kodlanmış tarihlerle deneme uygulamasının yeni sürümlerini sonsuza kadar yayınlamaya devam edeceğiniz anlamına mı geliyor?
Jasper

21

Android Studio projenize kolayca bırakabileceğiniz bir Android Deneme SDK'sı geliştirdim ve sizin için tüm sunucu tarafı yönetimini (çevrimdışı yetkisiz kullanım süreleri dahil) halledecektir.

Kullanmak için basitçe

Kitaplığı ana modülünüze ekleyin build.gradle

dependencies {
  compile 'io.trialy.library:trialy:1.0.2'
}

Ana etkinliğinizin onCreate()yönteminde kitaplığı başlatın

@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);
}

Geri arama işleyicisi ekleyin:

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));
    }

};

Bir deneme başlatmak için, mTrialy.startTrial("YOUR_TRIAL_SKU", mTrialyCallback); uygulama anahtarınızı arayın ve deneme SKU'sunu Trialy geliştirici panonuzda bulabilirsiniz .


Verilerin etkinleştirilmesi mi gerekiyor?
Sivaram Boina

deneme güvenilir değil
Amir Dora

1
@AmirDe Merhaba Amir, sizin için neyin işe yaramadığını bana bildirir misiniz? Yardımcı olmaktan mutluluk duyarım, support@trialy.io Trialy 1000'den fazla kullanıcı için harika çalışıyor
Nick

@Nick cihazımın neden android lolipop çalıştırdığını bilmiyor, panodan günü ayarladığımda, gün birkaç dakika sonra negatif değer gösteriyor, kontrol panelinde hala birçok günüm olmasına rağmen deneme süresinin dolduğunu söylüyor. Ayrıca nugat cihazında da test ettim, naugat üzerinde iyi çalışıyor gibi görünüyor. belki bazı eski Android sürüm uyumluluk sorunları vardır
Amir Dora

1
Bu hizmeti 2016'dan beri kullanıyorum, her seferinde iyi çalışıyor. Bunu resmi projelerimde de kullandım. Bu cevap kabul edilmelidir.
Tarık Mahmood

17

Bu eski bir soru ama her neyse, belki bu birine yardımcı olur.

En basit yaklaşımla gitmek istemeniz durumunda ( uygulama kaldırılırsa / yeniden yüklenirse veya kullanıcı cihazın tarihini manuel olarak değiştirirse başarısız olur ), şu şekilde olabilir:

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

    ...
}

Aslında, SharedPreferences ve Android 2.2 Froyo veya üstünü kullanıyorsanız, Google Veri Senkronizasyonu için Veri Yedekleme API'larını uyguladığınız sürece, kullanıcı cihazlarda bu durumdan kaçamaz veya yalnızca Ayarlar> Uygulamalar'a giderek verileri temizleme. Ayrıca, Date'deki yöntem getTimedeğildir getTimeInMillis.
Tom

Kabul etmiyorum, kullanıcı Cihazın tarihini manuel olarak değiştirirse bu da başarısız olur ve ayrıca Kullanıcı verileri manuel olarak temizlerse ne olur?
Mohammed Azharuddin Shaikh

@Caner bunu paylaşılan referans ile kontrol etmek iyidir, ancak kullanıcı, uygulama yöneticisini ayarlamadan hafızayı temizleyecek ve yeniden kullanacaktır, o zaman ne yapacak?
CoronaPintu

@CoronaPintu, böylece bu yaklaşım firebase ile de denenecek, bu mükemmel bir kombinasyon oluşturacak ve deneme süresi uygulama bile kaldırılacaktır.
Noor Hossain

yaklaşımı "garip zamanlar" cevabı ile birleştirin ve ekleyin, bu, yaklaşımı mükemmel hale getirecektir.
Noor Hossain

10

Bu soru ve snctln'nin cevabı, bana lisans tezim olarak yöntem 3'e dayalı bir çözüm üzerinde çalışmaya ilham verdi. Şu anki durumun verimli kullanım için olmadığını biliyorum, ancak bunun hakkında ne düşündüğünüzü duymak isterim! Böyle bir sistem kullanır mısın? Bunu bir bulut hizmeti olarak görmek ister misiniz (bir sunucuyu yapılandırmada sorun yaşamıyorsanız)? Güvenlik sorunları veya kararlılık nedenleri hakkında endişeli misiniz?

Lisans prosedürünü bitirir bitirmez yazılım üzerinde çalışmaya devam etmek istiyorum. Bu yüzden şimdi geri bildiriminize ihtiyacım olan zaman!

Kaynak kodu GitHub https://github.com/MaChristmann/mobile-trial üzerinde barındırılmaktadır

Sistemle ilgili bazı bilgiler: - Sistemin üç bölümü, bir Android kitaplığı, bir node.js sunucusu ve birden çok deneme uygulamasını ve yayıncı / geliştirici hesaplarını yönetmek için bir yapılandırıcı vardır.

  • Yalnızca zamana dayalı denemeleri destekler ve telefon kimliği yerine (oyun mağazası veya diğer) hesabınızı kullanır.

  • Android kitaplığı için Google Play lisans doğrulama kitaplığına dayanır. Bunu node.js sunucusuna bağlanmak için değiştirdim ve ayrıca kütüphane bir kullanıcının sistem tarihini değiştirip değiştirmediğini tanımaya çalışıyor. Ayrıca, alınan bir deneme lisansını AES şifreli Paylaşılan Tercihler'de önbelleğe alır. Önbelleğin geçerli zamanını yapılandırıcı ile yapılandırabilirsiniz. Bir kullanıcı "verileri temizlerse", kitaplık sunucu tarafı denetimi zorlar.

  • Sunucu https kullanıyor ve ayrıca lisans kontrol yanıtını dijital olarak imzalıyor. Ayrıca CRUD deneme uygulamaları ve kullanıcıları (yayıncı ve geliştirici) için bir API'ye sahiptir. Lisanslama Doğrulama Kitaplığı'na benzer geliştiriciler, deneme uygulamasında davranış uygulamalarını test sonucuyla test edebilir. Böylece yapılandırıcıda, lisans yanıtınızı açıkça "lisanslı", "lisanssız" veya "sunucu hatası" olarak ayarlayabilirsiniz.

  • Uygulamanızı can sıkıcı yeni bir özellikle güncellerseniz, herkesin tekrar denemesini isteyebilirsiniz. Konfigüratörde, lisansı sona ermiş kullanıcılar için deneme lisansını, bunu tetiklemesi gereken bir sürüm kodu belirleyerek yenileyebilirsiniz. Örneğin, kullanıcı uygulamanızı sürüm kodu 3 üzerinde çalıştırıyor ve siz onun sürüm kodu 4'ün özelliklerini denemesini istiyorsunuz. Uygulamayı güncellerse veya yeniden yüklerse, sunucu en son hangi sürümü denediğini bildiği için tam deneme süresini tekrar kullanabilir. zaman.

  • Her şey Apache 2.0 lisansı altında


2
Günümü kurtardın, sıkı çalıştığın için teşekkürler. Sadece bir kez elde edilen şifreli yapılandırmayı kullanma ve uygulama içinde açık anahtarı tutma konusunda aynı çözümü yazmayı düşündüm. Öyleyse büyük sorun sadece nasıl bir lisans verdiğinizi görebiliyorum? Birden fazla hibeden kaçınmak için yine de bir telefonla birlikte bazı benzersiz kimlik taşımaya ihtiyacınız olabilir.
user2305886


5

Yaklaşım 4: Uygulama yükleme zamanını kullanın.

API seviyesi 9'dan beri (Android 2.3.2, 2.3.1, Android 2.3, GINGERBREAD) ilkInstallTime ve lastUpdateTime içinde PackageInfo.

Daha fazlasını okumak için: Android'den uygulama yükleme süresi nasıl alınır?


Snctln'nin cevabından gelen 1. yöntem, kolayca atlatılmadan, bununla güvenilir bir şekilde kullanılabilir mi, yoksa aynı veya benzer bir sorunu var mı?
jwinn

Bu yöntemi test ettim. İyi tarafı, veriler temizlenmiş olsa bile çalışmasıdır. Kötü yanı, bir kaldırma / yeniden yükleme ile atlanabilmesidir.
Jean-Philippe Jodoin

onaylayabilir: Pixel cihazımda, uygulamanın kaldırılması ve yeniden yüklenmesi ilk yükleme süresini sıfırlar. bu, kullanıcıların denemeyi sıfırlamasını oldukça önemsiz hale getiriyor
Fabian Streitel

3

Artık android ücretsiz deneme aboneliğinin son sürümünde, tüm uygulamanızın özelliklerinin kilidini yalnızca uygulama içinden ücretsiz deneme süresi için aboneliği satın aldıktan sonra açabilirsiniz. Bu, kullanıcının uygulamanızı bir deneme süresi boyunca kullanmasına izin verecektir, uygulama deneme süresinden sonra hala kaldırılmışsa, abonelik parası size aktarılacaktır. Denemedim, sadece bir fikir paylaşıyorum.

İşte belgeler


2
Keşke bu tekli alımlarda işe yaradı. Yalnızca yıllık veya aylık olabilen abonelikler için çalışır.
jwinn

3

Kanımca, bunu yapmanın en iyi yolu Firebase Realtime Database'i kullanmaktır:

1) Uygulamanıza Firebase desteği ekleyin

2) Kullanıcının kaydolmasına veya ne yaptığınızı bilmesine gerek kalmaması için 'Anonim kimlik doğrulama'yı seçin. Bunun şu anda kimliği doğrulanmış kullanıcı hesabına bağlanması garanti edilir ve bu nedenle cihazlar arasında çalışacaktır.

3) 'installed_date' için bir değer ayarlamak üzere Realtime Database API'sini kullanın. Başlatma zamanında, bu değeri alın ve bunu kullanın.

Ben de aynısını yaptım ve harika çalışıyor. Bunu kaldırma / yeniden yüklemelerde test edebildim ve gerçek zamanlı veritabanındaki değer aynı kaldı. Bu şekilde deneme süreniz birden fazla kullanıcı cihazında çalışır. Hatta yükleme_tarihinizin sürümünü, uygulamanın her yeni ana sürüm için Deneme tarihini 'sıfırlaması' için bile değiştirebilirsiniz.

GÜNCELLEME : Biraz daha test ettikten sonra, farklı cihazlara sahip olmanız ve yeniden yüklemeler arasında garanti edilmemesi durumunda anonim Firebase'in farklı bir kimlik tahsis ettiği görülüyor: / Garantili tek yol Firebase'i kullanmak, ancak Google'a bağlamaktır. hesabı. Bu işe yaramalı, ancak kullanıcının önce oturum açması / kaydolması gerektiğinde ekstra bir adım gerektirecektir.

Şimdiye kadar, yedeklenmiş tercihlere karşı kontrol etme konusunda biraz daha az zarif bir yaklaşımla ve kurulumdan sonra tercihlerde saklanan bir tarihle sonuçlandım. Bu, bir kişinin uygulamayı yeniden yüklemesinin ve önceden eklenen tüm verileri yeniden girmesinin anlamsız olduğu, ancak basit bir oyunda işe yaramayacağı veri merkezli uygulamalar için işe yarar.


Android uygulamam için de aynı gereksiniminiz var ve kendi veritabanım / web sunucum var. Kullanıcıların oturum açmasına gerek yok, Bu yüzden Cihaz Kimliğini install_date ile kaydetmeyi planlıyordum, bu çalışır mı?
user636525

@strangetimes, çözümünüzün en iyi şekilde çalıştığını düşünüyorum, daha fazla bilgi verebilir misiniz? teşekkürler
DayDayHappy

Bu iş parçacığı stackoverflow.com/q/41733137/1396068 , uygulamayı yeniden yükledikten sonra yeni bir deneme süresi almanızı önerir
Fabian Streitel

3

Bu ve diğer konulardaki tüm seçeneklere baktıktan sonra, bunlar benim bulgularım

Paylaşılan tercihler, veritabanı Android ayarlarında silinebilir, bir uygulama yeniden yüklendikten sonra kaybolur. Android'in yedekleme mekanizması ile yedeklenebilir ve bir yeniden yüklemeden sonra geri yüklenir. Yedekleme her zaman mevcut olmayabilir, ancak çoğu cihazda olmalıdır

Harici depolama (bir dosyaya yazma) Uygulamanın özel dizinine yazmazsak, ayarların temizlemesinden veya yeniden yüklemeden etkilenmez . Ancak: daha yeni android sürümlerinde çalışma zamanında kullanıcıdan izin istemenizi gerektirir , bu nedenle bu muhtemelen yalnızca yine de bu izne ihtiyacınız varsa uygulanabilir. Ayrıca yedeklenebilir.

PackageInfo.firstInstallTime Bir yeniden yüklemeden sonra sıfırlanır, ancak güncellemeler boyunca kararlıdır

Bir hesapta oturum açın Firebase aracılığıyla veya kendi sunucunuzdaki bir Google hesabı farketmez: deneme hesaba bağlıdır. Yeni bir hesap oluşturmak denemeyi sıfırlayacaktır.

Firebase anonim oturum açma Bir kullanıcı anonim olarak oturum açabilir ve Firebase'de onun için veri depolayabilirsiniz. Ancak görünüşe göre uygulamanın yeniden yüklenmesi ve belki de diğer belgelenmemiş olaylar kullanıcıya deneme sürelerini sıfırlayarak yeni bir anonim kimlik verebilir . (Google'ın kendisi bu konuda çok fazla belge sağlamaz)

ANDROID_ID mevcut olmayabilir ve bazı durumlarda değişebilir , örneğin fabrika ayarlarına sıfırlama. Cihazları tanımlamak için bunu kullanmanın iyi bir fikir olup olmadığına dair görüşler farklı görünmektedir.

Play Reklam Kimliği Kullanıcı tarafından sıfırlanabilir. Reklam takibini devre dışı bırakarak kullanıcı tarafından devre dışı bırakılabilir.

Bir yeniden kurulumda Örnek Kimliği Sıfırlama . Bir güvenlik olayı durumunda sıfırlayın. Uygulamanız tarafından sıfırlanabilir.

Hangi yöntemlerin (kombinasyonunun) işinize yarayacağı, uygulamanıza ve ortalama John'un başka bir deneme süresi kazanmak için ne kadar çaba göstereceğini düşündüğünüze bağlıdır. Kararsızlıkları nedeniyle yalnızca anonim Firebase ve Reklam Kimliği kullanmaktan kaçınmanızı öneririm . Çok faktörlü bir yaklaşım, en iyi sonuçları verecek gibi görünüyor. Sizin için hangi faktörlerin mevcut olduğu, uygulamanıza ve izinlerine bağlıdır.

Kendi uygulamam için paylaşılan tercihler + firstInstallTime + tercihlerin yedeklemesinin en az müdahaleci ama aynı zamanda yeterince etkili yöntem olduğunu buldum. Yalnızca deneme başlangıç ​​zamanını paylaşılan tercihlerde kontrol edip kaydettikten sonra bir yedekleme talep ettiğinizden emin olmalısınız. Paylaşılan Tercihlerdeki değerler, firstInstallTime'a göre önceliğe sahip olmalıdır. Daha sonra kullanıcının uygulamayı yeniden yüklemesi, bir kez çalıştırması ve ardından denemeyi sıfırlamak için uygulamanın verilerini temizlemesi gerekir ki bu oldukça fazla bir iştir. Yedek aktarımı olmayan cihazlarda kullanıcı, denemeyi basitçe yeniden yükleyerek sıfırlayabilir.

Bu yaklaşımı genişletilebilir bir kitaplık olarak kullanıma sundum .


1

Tanım olarak, piyasadaki tüm ücretli Android uygulamaları satın alındıktan sonra 24 saat değerlendirilebilir.

24 saat sonra "Kaldır" olarak değişen bir "Kaldır ve Geri Ödeme" düğmesi vardır.

Bu düğmenin çok belirgin olduğunu iddia ediyorum!


17
Geri ödeme süresinin artık sadece 15 dakika olduğunu unutmayın.
Intrications

1

Aynı sorunu ararken bu soruyla karşılaşıyorum , iz uygulamasının süresinin dolup dolmadığını kontrol etmek için http://www.timeapi.org/utc/now veya başka bir tarih api gibi ücretsiz tarih api kullanabiliriz diye düşünüyorum . Bu yol, demoyu sunmak istiyorsanız ve ödeme konusunda endişeleriniz varsa ve hizmet süresi demosunu düzeltmek istiyorsanız etkilidir. :)

aşağıdaki kodu bulun

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

}
}

çalışma çözümü .....


Elbette kullanabilirsiniz, ancak bu durumda yalnızca ana faaliyetiniz sona erme durumunu kontrol edebilir.
RQube

0

İşte benimkini nasıl yaptım, biri deneme etkinliği olan diğeri olmadan 2 uygulama oluşturdum.

Mağazayı ücretli uygulama olarak oynamak için deneme etkinliği olmayan birini yükledim,

ve ücretsiz uygulama olarak deneme etkinliği olan.

İlk başlatıldığında ücretsiz uygulama, deneme ve mağaza satın alma seçeneklerine sahiptir, kullanıcı mağazadan satın almayı seçerse, kullanıcının satın alması için mağazaya yönlendirir, ancak kullanıcı denemeyi tıklarsa deneme etkinliğine götürür.

NB: @snctln gibi 3. seçeneği kullandım ama değişikliklerle

ilk olarak , cihaz saatine bağlı değildim, zamanımı db'ye deneme kaydını yapan php dosyasından aldım,

ikinci olarak , her bir cihazı benzersiz şekilde tanımlamak için cihazın seri numarasını kullandım,

son olarak , uygulama kendi zamanına değil sunucu bağlantısından dönen zaman değerine bağlıdır, bu nedenle sistem yalnızca cihaz seri numarası değiştirilirse engellenebilir, bu da bir kullanıcı için oldukça streslidir.

işte kodum burada (Deneme etkinliği için):

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 dosyam şöyle görünüyor (bir REST-ince teknolojisi):

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

    }

daha sonra ana etkinlikte, kalan gün sayısını izlemek için paylaşılan tercihi (deneme etkinliğinde oluşturulan yükleme tarihi) kullanıyorum ve günler bittiyse ana etkinlik kullanıcı arayüzünü, satın almaları için mağazaya götüren bir mesajla engelliyorum.

Burada gördüğüm tek olumsuz taraf, bir Rogue kullanıcısı ücretli uygulamayı satın alırsa ve Zender gibi uygulamalarla paylaşmaya karar verirse, dosya paylaşımı veya hatta apk dosyasını insanların ücretsiz indirmesi için doğrudan bir sunucuda barındırmasıdır. Ama eminim yakında bu yanıtı buna bir çözümle veya çözüme bağlantıyla düzenleyeceğim.

Umarım bu bir ruhu kurtarır ... bir gün

Mutlu Kodlama ...


0

@snctln seçenek 3, çoğunun sahip olduğu php ve mysql yüklü bir web sunucusuna bir php dosyası ekleyerek kolayca yapılabilir.

Android tarafından HttpURLConnection kullanılarak URL'de bağımsız değişken olarak bir tanımlayıcı (cihaz kimliği, google hesabı veya ne isterseniz) iletilir ve php tabloda varsa ilk kurulum tarihini döndürür veya yeni bir satır ekler ve geçerli tarihi döndürür.

Benim için iyi çalışıyor.

Zamanım olursa bir kod göndereceğim!

İyi şanslar !


bekleyin, ancak uygulamayı yeniden yükledikten sonra benzersiz kimliğinizi kaybediyor musunuz? !!
Maksim Kniazev

Bu kimlik donanımı, telefonun kendisini tanımlar, kullanıcı onu görmez ve değiştiremez. Uygulamayı yeniden yüklerse, php web hizmeti aynı telefon olduğunu algılar. Öte yandan kullanıcı telefonunu değiştirirse yeni bir dönemin keyfini çıkaracaktır.
Lluis Felisart
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.