GithubHelp home page GithubHelp logo

javiersantos / piracychecker Goto Github PK

View Code? Open in Web Editor NEW
1.5K 55.0 176.0 9.06 MB

An Android library that prevents your app from being pirated / cracked using Google Play Licensing (LVL), APK signature protection and more. API 14+ required.

License: Apache License 2.0

Java 67.82% Kotlin 31.33% AIDL 0.85%
apk signature android-library attacker lvl apk-signature-protection verify gradle

piracychecker's Introduction

PiracyChecker

Android Library

An Android library that prevents your app from being pirated / cracked using Google Play Licensing (LVL), APK signature protection and more.

Owner & Author: Javier Santos
Co-Author: Jahir Fiquitiva

Disclaimer

This library applies some techniques to help protect your app's users and attempt to thwart reverse engineers and attackers. BUT, this isn't guaranteed to stop your app from getting pirated. There is no such thing as 100% security, and a determined and skilled attacker with enough time, could remove these checks from the code. The real objective here is to raise the bar out of reach of opportunist and automatic attackers.

Some of the techniques included in this library can be found here.

How to include

Add the repository to your project build.gradle:

allprojects {
    repositories {
        maven {
            url "https://jitpack.io"
        }
    }
}

And add the library to your module build.gradle:

AndroidX

dependencies {
    implementation 'com.github.javiersantos:PiracyChecker:1.2.8'
}

Pre AndroidX (no longer supported)

dependencies {
    implementation 'com.github.javiersantos:PiracyChecker:1.2.4'
}

Recommendations

  • Always enable ProGuard in your production release. Always, without exceptions.
  • PiracyChecker should be included in your onCreate method in order to check for a valid license as soon as possible.
  • It's recommended to show a new Activity instead of a Dialog when the license is not valid. This way you make sure that the main activity of the app is finished. See "Display results in a Dialog or a new Activity".
  • Don't forget to enable ProGuard ;)

Usage

Verify Google Play Licensing (LVL)

Google Play offers a licensing service that lets you enforce licensing policies for applications that you publish on Google Play. With Google Play Licensing, your application can query Google Play to obtain the licensing status for the current user.

Any application that you publish through Google Play can use the Google Play Licensing service. No special account or registration is needed.

For more information check out the Google Developers page.

piracyChecker {
	enableGooglePlayLicensing("BASE_64_LICENSE_KEY")
	...
}.start()
Java Sample
new PiracyChecker(this)
	.enableGooglePlayLicensing("BASE_64_LICENSE_KEY")
	...
	.start();

In order to retrieve your BASE64 license key your app must be uploaded to the Google Play Developer Console. Then access to your app -> Services and APIs.

When using Google Play Licensing your should call .destroy() in the onDestroy() method of your Activity to avoid multiple instances of the service running. Have a look to the Wiki for a sample Activity with destroy().

Verify your app's signing certificates (signatures)

In a nutshell, developers must sign applications with their private key/certificate (contained in a .keystore file) before the app can be installed on user devices. The signing certificate must stay consistent throughout the life of the app, and typically have an expiry date of 25 years in the future.

The app signatures will be broken if the .apk is altered in any way — unsigned apps cannot typically be installed. We can imagine an attacker removing license-checking code to enable full app features without paying, for instance. A more dangerous example would be altering the .apk to include malware in a legitimate app to harvest sensitive user data. In order for the altered .apk to be installed, the attacker must resign it.

piracyChecker {
	enableSigningCertificates("478yYkKAQF+KST8y4ATKvHkYibo=") // The original APK signature for the PRODUCTION version
	...
}.start()
Java Sample
new PiracyChecker(this)
	.enableSigningCertificates("478yYkKAQF+KST8y4ATKvHkYibo=") // The original APK signature for the PRODUCTION version
	...
	.start();

Don't use this method when using Google Play App Signing since Google removes the original signature and add another one, so this method will fail.

BE CAREFUL!! Your app signature can be retrieved using a PiracyCheckerUtils method. Make sure that you have signed your APK using your PRODUCTION keystore (not using the DEBUG one) and installed the version that you plan to distribute. Then copy the signature returned by this method on the console and paste in enableSigningCertificate("YOUR_APK_SIGNATURE")

// This method will print your app signatures in the console
apkSignatures.forEach { Log.e("SIGNATURE", it) }
Java Sample
// This method will print your app signatures in the console
for (String signature : LibraryUtilsKt.getApkSignatures(this)) {
    Log.e("SIGNATURE", signature);
}

Verify the installer

If you only plan to distribute the app on a particular store this technique will block from installing the app using any another store.

Supported stores: Google Play, Amazon App Store and Samsung Galaxy Apps.

piracyChecker {
	enableInstallerId(InstallerID.GOOGLE_PLAY, InstallerID.AMAZON_APP_STORE, InstallerID.GALAXY_APPS)
	...
}.start()
Java Sample
new PiracyChecker(this)
	.enableInstallerId(InstallerID.GOOGLE_PLAY, InstallerID.AMAZON_APP_STORE, InstallerID.GALAXY_APPS)
	...
	.start();

BE CAREFUL!! This is a really restrictive technique since it will block your app from being installed using another market or directly installing the .apk on the device. It isn't recommended for most cases.

Verify the use of pirate apps

If you want to check if user has pirate apps installed, you can use this code.

It will check for: Lucky Patcher, Uret Patcher, Freedom, CreeHack and HappyMod.

piracyChecker {
	enableUnauthorizedAppsCheck()
	...
}.start()
Java Sample
new PiracyChecker(this)
	.enableUnauthorizedAppsCheck()
	...
	.start();

Add custom apps to check

Since version 1.2.2 you can add additional apps to be checked using this code:

val app = PirateApp("Lucky Patcher", "the.package.name")
piracyChecker {
	addAppToCheck(app)
	...
}.start()
Java Sample
PirateApp app = new PirateApp("Lucky Patcher", "the.package.name");
new PiracyChecker(this)
	.addAppToCheck(app)
	...
	.start();

You can block the app even when this pirate apps has been uninstalled. This prevents the app from being patched and then uninstall the pirate app in order to continue using your app. The library will save a SharedPreference value to know when a pirate app has been detected.

There are two ways to do this:

Define the SharedPreferences and the name of the preference where you want to save the result.

piracyChecker {
	enableUnauthorizedAppsCheck()
	blockIfUnauthorizedAppUninstalled(preferences, "app_unauthorized") // Change "app_unauthorized" with your own value
	...
}.start()
Java Sample
new PiracyChecker(this)
	.enableUnauthorizedAppsCheck()
	.blockIfUnauthorizedAppUninstalled(preferences, "app_unauthorized") // Change "app_unauthorized" with your own value
	...
	.start();

Define the SharedPreferences name and the name of the preference where you want to save the result.

piracyChecker {
	enableUnauthorizedAppsCheck()
	blockIfUnauthorizedAppUninstalled("license_preferences", "app_unauthorized") // Change "license_preferences" and "app_unauthorized" with your own value
	...
}.start()
Java Sample
new PiracyChecker(this)
	.enableUnauthorizedAppsCheck()
	.blockIfUnauthorizedAppUninstalled("license_preferences", "app_unauthorized") // Change "license_preferences" and "app_unauthorized" with your own value
	...
	.start();

Verify the use of third-party store apps

If you want to check if user has third-party store apps installed, you can use this code.

It will check for: Aptoide, BlackMart, Mobogenie, 1Mobile, GetApk, GetJar, SlideMe and ACMarket.

piracyChecker {
	enableStoresCheck()
	...
}.start()
Java Sample
new PiracyChecker(this)
	.enableStoresCheck()
	...
	.start();

Enable deep pirate and third-party store apps check

If you want to check if these kind of apps left some files that could make your app work as pirated still, you can enable its check as follows:

piracyChecker {
	enableFoldersCheck()
	...
}.start()
Java Sample
new PiracyChecker(this)
	.enableFoldersCheck()
	...
	.start();

If you also want to check for .apk files in certain system folders, you can enable it like so:

piracyChecker {
	enableAPKCheck()
	...
}.start()
Java Sample
new PiracyChecker(this)
	.enableAPKCheck()
	...
	.start();

BE CAREFUL! This means, that some times, the app will be recognized as pirated even after those pirate and third-party store apps were uninstalled. Set it to false if you don't like this behaviour/approach.

Verify if app is a debug build

Allowing apps to be debugged when installed on an Android device is something that, as developers, we only enable during the development process. Therefore, if debugging occurs on a live build of your app, it's likely that someone other than you is trying to analyze the app.

piracyChecker {
	enableDebugCheck()
	...
}.start()
Java Sample
new PiracyChecker(this)
	.enableDebugCheck()
	...
	.start();

Verify if app is being run in an emulator

If your app is running on an emulator outside the development process, it gives an indication that someone other than you is trying to analyze the app.

Warning!: Using deep check can cause crashes in some specific devices.

val deepCheck = false
piracyChecker {
	.enableEmulatorCheck(deepCheck)
	...
}.start()
Java Sample
boolean deep = false;
new PiracyChecker(this)
	.enableEmulatorCheck(deep)
	...
	.start();

Note: the deep boolean with make the library do extra checks to detect if device is an emulator or not. It could lead to some weird crashes, so be wise when using it.

Save the result of the license check in SharedPreferences

Saving the result of the license check is useful for checking the license status without calling .start() multiple times.

There are two ways to do this:

Define the SharedPreferences and the name of the preference where you want to save the result.

piracyChecker {
	saveResultToSharedPreferences(preferences, "valid_license") // Change "valid_license" with your own value
	...
}.start()
Java Sample
new PiracyChecker(this)
	.saveResultToSharedPreferences(preferences, "valid_license") // Change "valid_license" with your own value
	...
	.start();

Define the SharedPreferences name and the name of the preference where you want to save the result.

piracyChecker {
	saveResultToSharedPreferences("license_preferences", "valid_license") // Change "license_preferences" and "valid_license" with your own value
	...
}.start()
Java Sample
new PiracyChecker(this)
	.saveResultToSharedPreferences("license_preferences", "valid_license") // Change "license_preferences" and "valid_license" with your own value
	...
	.start();

Customizations

Display results in a Dialog or a new Activity

It's recommended to show a new Activity instead of a Dialog when the license is not valid. This way you make sure that the main activity of the app is finished.

By default a non-cancelable Dialog will be displayed.

piracyChecker {
	display(Display.ACTIVITY)
	...
}.start()
Java Sample
new PiracyChecker(this)
	.display(Display.ACTIVITY)
	...
	.start();

By default, the displayed Activity will use the library colors. To apply a custom primary and primary dark color, and to define if the activity should show normal or light status bar, use:

withActivityColors(R.color.colorPrimary, R.color.colorPrimaryDark, withLightStatusBar)

You can also define a custom layout xml for this activity content, using:

.withActivityLayout(R.layout.my_custom_layout)

Using custom callbacks

Adding a callback to the builder allows you to customize what will happen when the license has been checked and manage the license check errors if the user is not allowed to use the app. Keep in mind that when using this method you must be aware of blocking the app from unauthorized users.

By default, the library will display a non-cancelable dialog if the user is not allowed to use the app, otherwise nothing will happen.

Use the builder and add following:

callback {
    allow {
        // Do something when the user is allowed to use the app
    }
    doNotAllow { piracyCheckerError, pirateApp ->
        // You can either do something specific when the user is not allowed to use the app
        // Or manage the error, using the 'error' parameter, yourself (Check errors at {@link PiracyCheckerError}).
        
        // Additionally, if you enabled the check of pirate apps and/or third-party stores, the 'app' param
        // is the app that has been detected on device. App can be null, and when null, it means no pirate app or store was found,
        // or you disabled the check for those apps.
        // This allows you to let users know the possible reasons why license is been invalid.
    }
    onError { error ->
        // This method is not required to be implemented/overriden but...
        // You can either do something specific when an error occurs while checking the license,
        // Or manage the error, using the 'error' parameter, yourself (Check errors at {@link PiracyCheckerError}).
    }
}
Java Sample
.callback(new PiracyCheckerCallback() {
	@Override
	public void allow() {
		// Do something when the user is allowed to use the app
	}
	
	@Override
	public void doNotAllow(@NonNull PiracyCheckerError error, @Nullable PirateApp app) {
		// You can either do something specific when the user is not allowed to use the app
		// Or manage the error, using the 'error' parameter, yourself (Check errors at {@link PiracyCheckerError}).
		
		// Additionally, if you enabled the check of pirate apps and/or third-party stores, the 'app' param
		// is the app that has been detected on device. App can be null, and when null, it means no pirate app or store was found,
		// or you disabled the check for those apps.
		// This allows you to let users know the possible reasons why license is been invalid.
	}

	@Override
	public void onError(@NonNull PiracyCheckerError error) {
		// This method is not required to be implemented/overridden but...
		// You can either do something specific when an error occurs while checking the license,
		// Or manage the error, using the 'error' parameter, yourself (Check errors at {@link PiracyCheckerError}).
    }
})

FAQs

Can I protect my app using more than one validation method?

Sure. You can use as many validation methods in the builder as you want. For example:

piracyChecker {
	enableGooglePlayLicensing("BASE_64_LICENSE_KEY")
	enableSigningCertificates("YOUR_APK_SIGNATURE")
	enableUnauthorizedAppsCheck()
	saveResultToSharedPreferences("my_app_preferences", "valid_license")
	...
}.start()
Java Sample
new PiracyChecker(this)
	.enableGooglePlayLicensing("BASE_64_LICENSE_KEY")
	.enableSigningCertificates("YOUR_APK_SIGNATURE")
	.enableUnauthorizedAppsCheck()
	.saveResultToSharedPreferences("my_app_preferences", "valid_license")
	...
	.start();

License

Copyright 2018 Javier Santos

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

piracychecker's People

Contributors

avipars avatar grv99 avatar jahirfiquitiva avatar jasi2169 avatar javiersantos avatar jonathanprecise avatar lahdekorpi avatar nicholaschum avatar nyancrimew avatar pylersm avatar risalfajar avatar ubulem avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

piracychecker's Issues

not working for me

  • [x ] I have verified there are no duplicate active or recent bugs, questions, or requests.
  • [x ] I have verified that I am using the latest version of PiracyChecker.
  • [ x] I have given my issue a non-generic title.
  • [x ] I have read over the documentation (before asking questions on how to do something).
Details
  • PiracyChecker version: 1.2.4
  • Device OS version: 7.1.1
  • Device Manufacturer: MIUI
  • Device Name: REDMI NOTE 4

new PiracyChecker(this)
.enableGooglePlayLicensing("i_added_here_my_BASE_64_LICENSE_KEY")
.enableSigningCertificate("i_added_here_my_signin_key")
.display(Display.ACTIVITY)
.start();

i used signin key which shows in log
and base key from store

when i try app
it show only error that app is not sign try from valid source.
i uploaded as beta tester for app still same error. please help

is while debug app sign in key is different from app in beta tester and release apk?

Detecting lucky patcher even if uninstalled

Details
  • PiracyChecker version: 1.x.x
  • Device OS version: 6.0.1
  • Device Manufacturer: Asus
  • Device Name: ZenFone 2
Reproduction Steps
  1. Implement check for enableUnauthorizedApps without saving result in shared preferences
  2. Launch app with lucky patcher installed --> Detected
  3. Uninstall lucky patcher and launch app --> Detected

I haven't enabled result saving in shared preferences and that unauthorized apps is the only method I am calling after initializing PiracyChecker. I want to let the user use the app after uninstalling lucky patcher.

More info:
I have a rooted device with Xposed installed.
I have got a lot of feedbacks from my users saying they don't have lucky patcher but the app says they have it.
After uninstalling lucky patcher I tried to reinstall my app but it still says lucky patcher is installed.

Expected Result

Let the user use the app after uninstalling lucky patcher.

Actual Result

Detecting lucky patcher even if not installed.

External app test

Could you add a way to check some licensing on external apps? For example looking if an app is installed with the play store, by giving up a package name.

Attribution required?

This library is licensed under Apache v2. Under normal circumstances I would add this to the about section of my app and give attribution.
In this case it seems counterproductive to give a broad hint to the cracker? What is your advice in this case?

Unnecessary dependency on Android Support Library v7

Caused by: java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity. at android.support.v7.app.AppCompatDelegateImplV7.createSubDecor(AppCompatDelegateImplV7.java:343) at android.support.v7.app.AppCompatDelegateImplV7.ensureSubDecor(AppCompatDelegateImplV7.java:312) at android.support.v7.app.AppCompatDelegateImplV7.setContentView(AppCompatDelegateImplV7.java:277) at android.support.v7.app.AppCompatDialog.setContentView(AppCompatDialog.java:80) at android.support.v7.app.AlertController.installContent(AlertController.java:214) at android.support.v7.app.AlertDialog.onCreate(AlertDialog.java:256) at android.app.Dialog.dispatchOnCreate(Dialog.java:578) at android.app.Dialog.show(Dialog.java:314) at com.github.javiersantos.piracychecker.PiracyChecker$1.dontAllow(PiracyChecker.java:78) at com.github.javiersantos.piracychecker.PiracyChecker.verify(PiracyChecker.java:86) at com.github.javiersantos.piracychecker.PiracyChecker.start(PiracyChecker.java:72)

Getting false positives on the Google Play license check

Hi,
I just found out about this library a few days ago and it looked very promising, I really appreciate your effort to make other developers lives easier.

I tried implementing this library in a beta version of my app which I later released to the Play Store for beta testers, and it seems like I got several false positives from the library.

I made it so I get Firebase reports for when PiracyCheckerCallback returns dontAllow.
I also had Firebase log the installer app package name when sending the report.
After less than 30 minutes of the app being available to beta testers on Google Play I got already 2
reports for the following devices:

Manufacturer: Samsung
Model: SM-J111M
Board: Sc9830i
Android API: 22
Android OS: 5.1.1
Brand: Samsung
RAM: 919.67MB
Orientation: Portrait

Installed from: com.android.vending
Reason: This user is not using a licensed application from Google Play.

Manufacturer: Htc
Model: Nexus 9
Board: Flounder
Android API: 21
Android OS: 5.0.2
Brand: Google
RAM: 1.79GB
Orientation: Portrait

Probably an emulator for automatic crash reports (pre launch reports)
Reason: This user is not using a licensed application from Google Play.

The second one is a Google emulator so it might be a different story, but I'm almost certain that the first report is genuine, and is from a valid user.

This is the code I used:

String[] lines = new String(arr).split(System.getProperty("line.separator")); //Getting the first line of the file that contains the Google Play Licensing and Signing certificate codes 
            new PiracyChecker(context)
                    .enableGooglePlayLicensing(lines[0])
                    .enableSigningCertificate(lines[1])
                    .enableDebugCheck()
                    //.enableUnauthorizedAppsCheck()
                    .callback(new PiracyCheckerCallback() {
                        @Override
                        public void allow() {

                        }

                        @Override
                        public void dontAllow(@NonNull PiracyCheckerError piracyCheckerError, @Nullable PirateApp pirateApp) {
                            if (prefs.enabled)
                                prefs.setBool(Prefs.KEYS.ENABLED.toString(), Constants.disabled);
                            FirebaseCrash.log(piracyCheckerError.toString());
                            FirebaseCrash.log(context.getPackageManager().getInstallerPackageName(context.getPackageName()));
                            nullObject.toString(); // make the app crash
                        }
                    })
                    .display(Display.ACTIVITY)
                    .start();

I saw in the README that you recommend not running the PiracyCheck in multiple instances

When using Google Play Licensing your should call .destroy() in the onDestroy() method of your Activity to avoid multiple instances of the service running. Have a look to the Wiki for a sample Activity with .destroy().

But in the current version of the app it might still happen, because I also check for license verification error from a service that may run simultaneously with the activity. Could that really be the source of the issue?

As I mentioned above, this seems to work for the most part, with the exception of this one user, which I doubt had the time to update and crack the app in less than 30 minutes, especially with the app being installed from Google Play, as the Firebase report says.

Looking forward to your response, I will try to analyse it further and any other information I find

Edit:
I updated the app again, now I should get the license key used in the verification process through firebase, I just got another report:

Manufacturer: Meizu
Model: PRO 5
Board: Pro5
Android API: 22
Android OS: 5.1
Brand: Meizu
RAM: 3.63GB
Orientation: Portrait
App used to install com.android.vending
Error: This user is not using a licensed application from Google Play.

I compared the license key I received from the report with my license key from the Play Store and they match

Edit2:
OK, maybe I figured it out, previously I wasn't implementing the onError method, after implementing it I got many reports about it being triggered, is it possible that by default when an error occurs with the license verification, the dontAllow method is being called?

Any who this is the error message that I get:

OnErrorNot market managed error.

Will update as I get more information

Edit3:
Never mind, seems like it didn't solve the issue, for some reason Google pre launch report Nexus 9 manages to reproduce this issue every time

Manufacturer: Htc
Model: Nexus 9
Board: Flounder
Android API: 21
Android OS: 5.0.2
Brand: Google
RAM: 1.79GB
Orientation: Portrait

Edit3:
Ended up commenting out the .enableGooglePlayLicensing(lines[0]) line, for now I will rely on the app certificate verification

LuckyPatcher easily bypasses simple LVL implementation

Hi Javier. Thanks for your efforts. As much as we try to protect our apps other 3rd party tools keep finding new ways to fuck us up.

For example LuckyPatcher detects this string https://github.com/javiersantos/PiracyChecker/blob/master/library/src/main/java/com/google/android/vending/licensing/LicenseValidator.java#L105
and simply returns true.

My suggestion is simply change the methods name, move strings around, just make it a tad harder for automated programs to understand what's going on. Seems simple, but it'll bypass some of the automated scripts. And yes, it'll work even if the app is properly obfuscated etc.

AndroidTest UnauthorizedAppTest: Fails due to PiracyCheckerError enum return value not PIRATE_APP_INSTALLED but BLOCK_PIRATE_APP.

  • [ x] I have verified there are no duplicate active or recent bugs, questions, or requests.
  • [ x] I have verified that I am using the latest version of PiracyChecker.
  • [ x] I have given my issue a non-generic title.
  • [ x] I have read over the documentation (before asking questions on how to do something).
Details
  • PiracyChecker version: 1.2.3
  • Device OS version: emulator
  • Device Manufacturer: N/A
  • Device Name: Nexus 5
Reproduction Steps

As says UnauthorizedAppTest:
/**

    1. Specific test cases for unauthorized apps. Requires to install an unauthorized app before running this tests.
      */
  1. install apk for unauthorized app (i.e. Lucky patch 7.4.2)
  2. Run test
  3. Test fails with message "PiracyChecker FAILED : PiracyCheckError is not At least one pirate app has been detected and the app must be reinstalled when all unauthorized apps are uninstalled.."

Possible Solution :

  @Test
  public void verifyUnauthorizedApps_DONTALLOW() throws Throwable {
    final CountDownLatch signal = new CountDownLatch(1);
    uiThreadTestRule.runOnUiThread(new Runnable() {
      @Override
      public void run() {
        new PiracyChecker(InstrumentationRegistry.getTargetContext())
            .enableUnauthorizedAppsCheck(true)
            .blockIfUnauthorizedAppUninstalled("piracychecker_preferences", "app_unauthorized")
            .callback(new PiracyCheckerCallback() {
              @Override
              public void allow() {
                assertTrue("PiracyChecker FAILED: There is an unauthorized app installed.", false);
                signal.countDown();
              }

              @Override
              public void dontAllow(@NonNull PiracyCheckerError error, @Nullable PirateApp app) {
                if (error == PiracyCheckerError.PIRATE_APP_INSTALLED
                    || error == PiracyCheckerError.BLOCK_PIRATE_APP) {
...

so add || error == PiracyCheckerError.BLOCK_PIRATE_APP.

NB : same probably holds true for verifyUnauthorizedCustomApp_DONTALLOW test method, but that test doesn't fail.

New Package Name for Lucky Patcher

Please add latest package name for Lucky Patcher

OLD: com.android.vending.billing.InAppBillingService.LUCK

LATEST: com.android.vending.billing.InAppBillingService.CLON

pirateApp.getPack() returns array

I'm trying to show a dialog to the user when he attempts to make an IAP and on that dialog I'm showing an uninstall button to start uninstaller activity.
The problem is that the pirateApp.getPack() returns an Array instead of package string.
So currently I'm using
Arrays.toString(pirateApp.getPack()).replace(",", "").replace(" ", "").replace("[", "").replace("]", "") to get the package string.
Can you please return clean string?

google play console report

  • PiracyChecker version: 1.2.2 & '1.2.3'
  • Device OS version: 8.0

Google report Policy Issue: Your app was rejected for violating our Device and Network Abuse policy and sections 4.8 and 4.9 of the Developer Distribution Agreement.

I'm not able to release my app with piracychecker, developer console reject any update if I not remove this Piracychecker, please fix this issue

Crash in showing dialog

Details
  • PiracyChecker version: 1.2.4
  • Device OS version: pie
  • Device Manufacturer: Xiaomi
  • Device Name: Mi 5s
    (tested on an other huawei device running android 8 I think)
Builder
        checker = new PiracyChecker(this)
                .saveResultToSharedPreferences(preferences.getSharedPrefs(), "valid_license")
                .enableUnauthorizedAppsCheck(true)
                .enableGooglePlayLicensing("PRIVATE");

        checker.start();
Reproduction Steps
  1. install LuckyPatcher
  2. Open the app running PrivacyChecker
    java.lang.IllegalArgumentException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkParameterIsNotNull, parameter savedInstanceState
        at com.github.javiersantos.piracychecker.PiracyCheckerDialog.onCreateDialog(Unknown Source:2)
        at android.app.DialogFragment.onGetLayoutInflater(DialogFragment.java:411)
        at android.app.Fragment.performGetLayoutInflater(Fragment.java:1339)
        at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1298)
        at android.app.FragmentManagerImpl.addAddedFragments(FragmentManager.java:2426)
        at android.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2205)
        at android.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2161)
        at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:2062)
        at android.app.FragmentManagerImpl$1.run(FragmentManager.java:738)
        at android.os.Handler.handleCallback(Handler.java:873)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6669)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

Not working on Xiaomi and Moto Devices

Not working on Moto and Xiaomi devices!

Details #1
  • PiracyChecker version: 1.2.3
  • Device OS version: 7.0.0
  • Device Manufacturer: Moto
  • Device Name: G4 Plus
Details #2
  • PiracyChecker version: 1.2.3
  • Device OS version: 7.1.1
  • Device Manufacturer: Xiaomi
  • Device Name: Note 5 Pro
Builder

private void verify() {
checker = new PiracyChecker(this)
.enableGooglePlayLicensing("MY_KEY")
checker.start();
}

######Result
Working on my Nexus 5 (7.1.1)

Actual Result

Not working on these 2 devices I've tested so far

Paid App varification

@javiersantos I want to clarify something
I have an Already paid App in Playstore. then I want to check user already paid in play store or not. can I achieve it using PiracyChecker?

README omission?

Should the README mention the need for the CHECK_LICENSE permission in the Manifest?

Inadequate Documentation

Is there a sample App and a detailed behavior of what will happen when the conditions aren't met. The current documentation is inadequate.

What will be shown/happen per case bases?

Some gif or a youtube tutorial would go a long way.

Thanks!

Library stops working on latest Play Store version 8.7.09

  • I have verified there are no duplicate active or recent bugs, questions, or requests.
  • I have verified that I am using the latest version of PiracyChecker.
  • I have given my issue a non-generic title.
  • I have read over the documentation (before asking questions on how to do something).
Details
  • PiracyChecker version: 1.1
  • Device OS version: 7.0 - 8.0
  • Device Manufacturer: All
  • Device Name: Pixel 2 XL, OP5, S8, S7, S7E, S6
Builder
            piracyChecker = PiracyChecker(this)
            piracyChecker!!.enableGooglePlayLicensing(BASE_64_LICENSE_KEY)
            piracyChecker!!.enableSigningCertificate(APK_SIGNATURE_PRODUCTION)
            piracyChecker!!.enableInstallerId(InstallerID.GOOGLE_PLAY)
            piracyChecker!!.callback(object : PiracyCheckerCallback() {
                override fun allow() {
                    quitSelf()
                }

                override fun dontAllow(error: PiracyCheckerError, pirateApp: PirateApp?) {
                    finish()
                }
            })
            piracyChecker!!.start()
Reproduction Steps
  1. Clear data of your app including the PiracyChecker lib
  2. Relaunch the app to get it to reauthorize
  3. It will fail
Expected Result

The piracy checker lib should green light.

Actual Result

The library will throw some generic response:

01-13 23:37:48.602 24417-24417/? I/LibraryChecker: Binding to licensing service.
01-13 23:37:48.641 24417-24417/? I/LibraryChecker: Calling checkLicense on service for projekt.sungstratum
01-13 23:37:48.641 24417-24417/? I/LibraryChecker: Start monitoring timeout.
01-13 23:37:48.908 24417-24433/? I/LibraryChecker: Received response.
01-13 23:37:48.908 24417-24433/? I/LibraryChecker: Clearing timeout.

and occassion

01-13 16:00:13.947 I/LibraryChecker(17981): Using cached license response

But since it is freshly wiped, there is no cached license response, so it will be negated and will tell me I "pirated" my own app.

License check not working on Play Store 10.7

  • [v] I have verified there are no duplicate active or recent bugs, questions, or requests.
  • [v] I have verified that I am using the latest version of PiracyChecker.
  • [v] I have given my issue a non-generic title.
  • [v] I have read over the documentation (before asking questions on how to do something).
Details
  • PiracyChecker version: any
  • Device OS version: any
  • Device Manufacturer: any
  • Device Name: any
Builder

https://github.com/substratum/template/blob/kt-n/app/src/main/kotlin/substratum/theme/template/SubstratumLauncher.kt
[line 64 to 100]

Reproduction Steps
  1. Update play store to 10.7
  2. Try to open app
Expected Result

License check doing its work letting the user in

Actual Result

License check won't check the license successfully, and the user will be blocked out.
Don't know if happens on any kind of apps, I am a substratum theme developer.

Resources NotFoundException

  • I have verified there are no duplicate active or recent bugs, questions, or requests.
  • I have verified that I am using the latest version of PiracyChecker.
  • I have read over the documentation (before asking questions on how to do something).
  • I have given my issue a non-generic title.
Details
  • PiracyChecker version: 1.1
  • Device OS version: all
  • Device Manufacturer: all
  • Device Name: all
Reproduction Steps

I have Proguard and shrinkressource activate.

Intent intent = new Intent(MainMenu.this, LicenseActivity.class)
                .putExtra("content", msg)
                .putExtra("colorPrimary", ContextCompat.getColor(this,R.color.green_500))
                .putExtra("colorPrimaryDark", ContextCompat.getColor(this,R.color.green_700));
        MainMenu.this.startActivity(intent);
        MainMenu.this.finish();
Expected Result

Work

Actual Result

Caused by: android.content.res.Resources$NotFoundException: Resource ID #0xff4caf50
at android.content.res.ResourcesImpl.getValue(ResourcesImpl.java:190)
at android.content.res.Resources.getColor(Resources.java:922)
at android.content.Context.getColor(Context.java:508)
at android.support.v4.content.b.c(ContextCompat.java:411)
at com.github.javiersantos.piracychecker.activities.LicenseActivity.setActivityStyle(LicenseActivity.java:48)
at com.github.javiersantos.piracychecker.activities.LicenseActivity.onCreate(LicenseActivity.java:29)

Context leaks on activity recreation

  • I have verified there are no duplicate active or recent bugs, questions, or requests.
  • I have verified that I am using the latest version of PiracyChecker.
  • I have given my issue a non-generic title.
  • I have read over the documentation (before asking questions on how to do something).
Details
  • PiracyChecker version: 1.1
  • Device OS version: 7.1.2
  • Device Manufacturer: Huawei
  • Device Name: Nexus 6P
Reproduction Steps
  1. Open the app
  2. Rotate the activity
Expected Result

Activity's instance shouldn't be retained & duplicated.

Actual Result

Activity's context leak.

Maybe put it inside a WeakReference, that way it'll be garbage collected if the activity isn't valid anymore. You can use LeakCanary to double check on actvity leaks.

Question: Are there disadvantages/pitfalls when replacing internal base64 class with android.util.Base64?

  • I have verified there are no duplicate active or recent bugs, questions, or requests.
  • I have verified that I am using the latest version of PiracyChecker.
  • I have given my issue a non-generic title.
  • I have read over the documentation (before asking questions on how to do something).
Details
  • PiracyChecker version: 1.2.3

I know it's always hard to "kill your darlings", especially when I see that probably a lot of work went into the Base64.class. But because of {@link CharBase64} not working, I found on Robert Harder's site link http://iharder.sourceforge.net/base64/ that it is built-in since java 8, and android has android.util.Base64 class.

I'm no expert in this matter, thus my question :
are there disadvantages/pitfalls when replacing this with the standard library?

Edit : searching for Base64 in the entire project, I found that the android library version Base64.encodeToString is already used in LibraryUtils class.
So is there any reason, why not to use Base64.encode for byte-arrays in the classes AESObfuscator, LibraryChecker and LibraryValidator (see below) ?

I tried it on the library app, and the main PiracyCheckerTest ran to success, so it seems to be working fine.
(still have to test on Android play store with a real app later on)

That leads me to a secondary question : How to unzip or use apk in files SampleUnauthorizedApp.zip for the second test, UnauthorizedAppTest because it is locked with a password, and second part of this test fails ?

Changes I made :

  1. AESObfuscator class
    method obfuscate
    change to : import android.util.Base64
    change Base64.encode(...) ->Base64.encode(... , Base64.DEFAULT)
    (or maybe better : Base64.URL_SAFE ?)
    method unobfuscate (NB: maybe change to unObfuscate for better camelCase ?)
    change Base64.decode(...) ->Base64.decode(... , Base64.DEFAULT)
    change catch (Base64DecoderException... to catch (IllegalArgumentException...

  2. same goes for decodes in LibraryChecker and LibraryValidator classes

Create a null debug version

First off, Thanks for PricaryChecker. Once I add PiracyChecker, I can't debug my app without wrapping the code with a if BuildConfig.DEBUG. But that gives hook for disabling PiracyChecker (some of crackers are looking things if'd off with BuildConfig.DEBUG). It would easy enough to make release and debug version, I think it just mean moving one class in src/release/java and copy of it that's stripped in src/debug/java. Then the gradle lines are changed to:
releaseCompile 'com.github.javiersantos:PiracyCheckerRelease:1.1'
debugCompile 'com.github.javiersantos:PiracyCheckerDebug:1.1'

No hook for anything to grab onto. The release build will always have it, the deubg will always be disabled.

I'm happy to make a PR for this if there's interest.

Add more packages to "licensed" installers

In my app, I used a similar method to check the app that installed my app, the allowed packages were:

"com.google.android.feedback"
"com.android.vending"

Any of these 2 options mean app was installed from Play Store

"com.amazon.venezia"

This the package for Amazon App Store.

I would do a Pull Request, but I wouldn't like to do it just for these minor things.

Hope they can be added soon, so I can start implementing this awesome library.

Thank you Javier. Keep up the good work.

License issue when using LVL and APK signature

For some reason when using this library my app is reporting that the license isn't valid. I've implemented the play store check, the app is then downloaded from the play store and is returning that the app is invalid. Same with my signing key and if I use the license check. I'm not sure what I'm doing wrong.

License always fails

  • I have verified there are no duplicate active or recent bugs, questions, or requests.
  • I have verified that I am using the latest version of PiracyChecker.
  • I have given my issue a non-generic title.
  • I have read over the documentation (before asking questions on how to do something).
Details
  • PiracyChecker version: 1.2
  • Device OS version: 8.0.0
  • Device Manufacturer: Xiamo
  • Device Name: Mi A1
Builder
new PiracyChecker(this)
   .enableGooglePlayLicensing("XXXX")
                //.enableInstallerId(InstallerID.GOOGLE_PLAY)
                .enableSigningCertificate("XXXXX")// The original APK signature for the PRODUCTION version
                .enableUnauthorizedAppsCheck()
                .saveResultToSharedPreferences(mpref, "valid_license")
                .enableDebugCheck()
                .display(Display.ACTIVITY);
   .start();
Reproduction Steps
Expected Result

License success

Actual Result

License fail

SALT

More of a question than an issue... is it a vulnerability that the same SALT is used by all users of this library? If not, what exactly is the point of the SALT, if it doesn't matter that everyone is using the same? Would it make sense to be able to supply a custom SALT when setting up the checker?

allow() and dontAllow() shouldn't run be called on a background thread

As the title explicitly says, those callbacks shouldn't be posted on a background thread. You can either give it the option to be posted to a bg thread or simply post it on the main thread. It's slightly annoying when doing UI operations on a custom allow()/dontAllow() and then getting exceptions warning about "Only the original thread that created a view hierarchy can touch its views." and then having to wrap everything inside a main looper. At least give them the @workerthread annotation so that we know what kind of thread they're being posted on.

error: failed linking references.

I have a problem after adding dependency and sync

Android resource linking failed
Output: /Users/marwan/Documents/Android-Projects/photogo/app/build/intermediates/incremental/mergeDebugResources/merged.dir/values-v28/values-v28.xml:7: error: resource android:attr/dialogCornerRadius not found.
/Users/marwan/Documents/Android-Projects/photogo/app/build/intermediates/incremental/mergeDebugResources/merged.dir/values-v28/values-v28.xml:11: error: resource android:attr/dialogCornerRadius not found.
/Users/marwan/Documents/Android-Projects/photogo/app/build/intermediates/incremental/mergeDebugResources/merged.dir/values/values.xml:1590: error: resource android:attr/fontVariationSettings not found.
/Users/marwan/Documents/Android-Projects/photogo/app/build/intermediates/incremental/mergeDebugResources/merged.dir/values/values.xml:1591: error: resource android:attr/ttcIndex not found.
error: failed linking references.

Command: /Users/marwan/.gradle/caches/transforms-1/files-1.1/aapt2-3.2.0-4818971-osx.jar/76c130dc24c69748b151c0483251f08a/aapt2-3.2.0-4818971-osx/aapt2 link -I
/Users/marwan/Library/Android/sdk/platforms/android-27/android.jar
--manifest
/Users/marwan/Documents/Android-Projects/photogo/app/build/intermediates/merged_manifests/debug/processDebugManifest/merged/AndroidManifest.xml
-o
/Users/marwan/Documents/Android-Projects/photogo/app/build/intermediates/processed_res/debug/processDebugResources/out/resources-debug.ap_
-R
@/Users/marwan/Documents/Android-Projects/photogo/app/build/intermediates/incremental/processDebugResources/resources-list-for-resources-debug.ap_.txt
--auto-add-overlay
--java
/Users/marwan/Documents/Android-Projects/photogo/app/build/generated/not_namespaced_r_class_sources/debug/processDebugResources/r
--proguard-main-dex
/Users/marwan/Documents/Android-Projects/photogo/app/build/intermediates/legacy_multidex_aapt_derived_proguard_rules/debug/processDebugResources/manifest_keep.txt
--custom-package
net.kayisoft.photogo
-0
apk
--output-text-symbols
/Users/marwan/Documents/Android-Projects/photogo/app/build/intermediates/symbols/debug/R.txt
--no-version-vectors
Daemon: AAPT2 aapt2-3.2.0-4818971-osx Daemon #0

How could I solve it?

License verification fails when using Google Play Licensing

  • I have verified there are no duplicate active or recent bugs, questions, or requests.
  • I have verified that I am using the latest version of PiracyChecker.
  • I have given my issue a non-generic title.
  • I have read over the documentation (before asking questions on how to do something).
Details
  • PiracyChecker version: 1.2.4
  • Device OS version: Any
  • Device Manufacturer: Any
  • Device Name: Any
Builder
        piracyChecker {
            enableSigningCertificate("/**/")
            enableGooglePlayLicensing("/**/")

            callback {
                doNotAllow { error, _ ->
                    sendResult(false, "PiracyChecker doNotAllow: $error")
                }

                onError {
                    sendResult(false, "PiracyChecker onError: $it")
                }

                allow {
                    sendResult(true, "PiracyChecker valid")
                }
            }
        }
Reproduction Steps
  1. Add LVL check with proper licensing key
  2. Test on any device
  3. PiracyChecker returns error in title
Expected Result

Users who've bought the APK should receive the "PiracyChecker valid" status.

Actual Result

Many are reporting that the verification fails.

This doesn't seem to be related to version 1.2.4, as users on the Play Store are complaining as well, with an APK using an older version of PiracyChecker.

Full class: https://pastebin.com/mLamh0xD

Using the library, makes my app crash on its very first use.

  • I have verified there are no duplicate active or recent bugs, questions, or requests.
  • I have verified that I am using the latest version of PiracyChecker.
  • I have given my issue a non-generic title.
  • I have read over the documentation (before asking questions on how to do something).
Details
  • PiracyChecker version: 1.0.2
  • Device OS version: 7.1.1
  • Device Manufacturer: All
  • Device Name: All
Information

So, when I enable the license check in my app, it causes a weird crash but it only happens the very first time I use the app.

All the logs say is:

channel 'cf4caaf pl.patrykgoworowski.cornieicons/jahirfiquitiva.iconshowcase.activities.ShowcaseActivity (server)' ~ Channel is unrecoverably broken and will be disposed!

and

RemoteException caught trying to send a callback msg for NetworkRequest [ LISTEN id=12, [ Capabilities: INTERNET&NOT_RESTRICTED&TRUSTED] ]

Can you please share some help about it? Thanks in advance.

How can I fully verify my application with call back functions?

Actually I am a cordova developer and I have tried many cordova plugins but no use, so I have decided to implement this license check process in java code, I tried many pugins but nothing was run successfully. But finally I found your plugin, really it is awsome, it was run successfully without any errors.
And I have two questions

  1. I have .jks file and how can I get the signing certification from that?
  2. enableGooglePlayLicensing does not call the call back functions.

How?

new PiracyChecker(this).callback(new PiracyCheckerCallback() {
            @Override
            public void allow() {
                new AlertDialog.Builder(MainActivity.this)
                        .setTitle("License Check")
                        .setMessage("License is Valid")
                        .show();
            }

            @Override
            public void dontAllow(PiracyCheckerError error) {
                new AlertDialog.Builder(MainActivity.this)
                        .setTitle("License Check")
                        .setMessage("Not Valid License")
                        .show();
            }
        })
                .enableGooglePlayLicensing("my 64 BIT KEY")
                .start();

But this code does not trigger any one of the call back method.

Please tried to reply soon as possible.
Thanks Advance.

Gradle dependency

Hi,
I'm trying to add this library through Gradle.build file:

dependencies {
compile 'com.github.javiersantos:PiracyChecker:0.0.2'
}

Syncing Gradle will result in dependency not found error

Crash when installed from Google Play

Details
  • PiracyChecker version: 1.2.3
  • Device OS version: 8.0.0
  • Device Manufacturer: Samsung
  • Device Name: Note8 (greatlte)

My app with PiracyChecker installed works fine when:

  • Installed by running from Android Studio
  • Installed from signed apk
  • Installed from derived Google Play apk (signed by Google)

It doesn't work though when installed directly from Google Play (only first launch)

How I use PiracyChecker:

auth = new PiracyChecker(this)
.display(Display.ACTIVITY)
.enableInstallerId(InstallerID.GOOGLE_PLAY)
.enableDebugCheck(true)
.enableEmulatorCheck(true)
.enableGooglePlayLicensing(key);
auth.start();

Error on Google Play Developer Console:

java.lang.RuntimeException:
at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:2957)
at android.app.ActivityThread.handleLaunchActivity (ActivityThread.java:3032)
at android.app.ActivityThread.-wrap11 (Unknown Source)
at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1696)
at android.os.Handler.dispatchMessage (Handler.java:105)
at android.os.Looper.loop (Looper.java:164)
at android.app.ActivityThread.main (ActivityThread.java:6940)
at java.lang.reflect.Method.invoke (Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run (Zygote.java:327)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1374)
Caused by: java.lang.IllegalArgumentException:
at com.github.javiersantos.licensing.LibraryChecker.generatePublicKey (LibraryChecker.java:126)
at com.github.javiersantos.licensing.LibraryChecker. (LibraryChecker.java:98)
at com.github.javiersantos.piracychecker.PiracyChecker.verify (PiracyChecker.java:323)
at com.github.javiersantos.piracychecker.PiracyChecker.start (PiracyChecker.java:300)
at com.layoutxml.support.MainActivity.onCreate (MainActivity.java:45)
at android.app.Activity.performCreate (Activity.java:7174)
at android.app.Instrumentation.callActivityOnCreate (Instrumentation.java:1220)
at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:2910)
Caused by: com.github.javiersantos.licensing.util.Base64DecoderException:
at com.github.javiersantos.licensing.util.Base64.decode (Base64.java:619)
at com.github.javiersantos.licensing.util.Base64.decode (Base64.java:526)
at com.github.javiersantos.licensing.util.Base64.decode (Base64.java:465)
at com.github.javiersantos.licensing.LibraryChecker.generatePublicKey (LibraryChecker.java:117)

Things to consider:

  • App is Beta version on Google Play
  • That it works when installed directly from apk
  • That it crashes only on first launch (whether installed fresh, or updated)
  • Key supplied to PiracyChecker is the same as used in in app purchases and works there

My app is really very lite - no dependencies that are not there by default and PiracyChecker, only shows some images and text, allows in app purchases, doesn't use internet connection etc.

Additional question:
If the hacker was to decompile the app and remove a couple of lines that launch PiracyChecker, wouldn't it allow signing with different signature, installing from apk etc?

Crash in showing the dialog after onSaveInstanceState

Details
  • PiracyChecker version: 1.2.3
  • Device OS version: 7.0
  • Device Manufacturer: LG LG-H870, HUAWEI RNE-L21, Samsung Galaxy S7, so far
Builder
                    mPiracyChecker = new PiracyChecker(this)
                        .saveResultToSharedPreferences(prefs, CHECK_RESULT)
                        .enableGooglePlayLicensing(key)
                        .enableSigningCertificate(AppKeyUtil.INSTANCE.getSigningSig())
                        .enableEmulatorCheck(false);
                    if (!BuildConfig.DEBUG) {
                        try {
                            mPiracyChecker.start();
                        } catch (final VerifyError e) {
                            Timber.e(e);
                        }
                    }
Reproduction Steps

I don't know the reproduction steps, I'm seeing the crash in crashlytics.

Expected Result

Too not crash

Actual Result

a Crash:

Fatal Exception: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
       at android.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1434)
       at android.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1452)
       at android.app.BackStackRecord.commitInternal(BackStackRecord.java:707)
       at android.app.BackStackRecord.commit(BackStackRecord.java:671)
       at android.app.DialogFragment.show(DialogFragment.java:231)
       at com.github.javiersantos.piracychecker.PiracyCheckerDialog.show(PiracyCheckerDialog.java:26)
       at com.github.javiersantos.piracychecker.PiracyChecker$1.dontAllow(PiracyChecker.java:279)
       at com.github.javiersantos.piracychecker.PiracyChecker.doExtraVerification(PiracyChecker.java:401)
       at com.github.javiersantos.piracychecker.PiracyChecker.access$000(PiracyChecker.java:34)
       at com.github.javiersantos.piracychecker.PiracyChecker$2.dontAllow(PiracyChecker.java:334)
       at com.github.javiersantos.licensing.LibraryChecker.handleServiceConnectionError(LibraryChecker.java:280)
       at com.github.javiersantos.licensing.LibraryChecker.access$100(LibraryChecker.java:60)
       at com.github.javiersantos.licensing.LibraryChecker$ResultListener$1.run(LibraryChecker.java:329)
       at android.os.Handler.handleCallback(Handler.java:751)
       at android.os.Handler.dispatchMessage(Handler.java:95)
       at android.os.Looper.loop(Looper.java:154)
       at android.os.HandlerThread.run(HandlerThread.java:61)

New Logo/Icon proposal

Good day sir. I am a graphic designer and i am interested in designing a logo for your good project. I will be doing it as a gift for free. I just need your permission first before I begin my design. Hoping for your positive feedback. Thanks

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.