AndroidUtilCode | :fire: Android developers should collect the following utils(updating) | Android library
kandi X-RAY | AndroidUtilCode Summary
Support
Quality
Security
License
Reuse
- Get the country code from the map .
- Stack bitmap .
- Convert a Uri to a File .
- Update char sequence .
- Handle touch event .
- Initialize the title bar .
- Returns the RomInfo object
- Visit a method .
- Operations operations .
- Returns the foreground process name .
AndroidUtilCode Key Features
AndroidUtilCode Examples and Code Snippets
Trending Discussions on Android
Trending Discussions on Android
QUESTION
I'm unable to store captured image in (getExternalFilesDir(Environment.DIRECTORY_PICTURES)) Android 11 device.
I have added in manifest and all file access also. But it's not working.
if (Build.VERSION.SDK_INT >= 30) {
if (!Environment.isExternalStorageManager()) {
try {
val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
intent.addCategory("android.intent.category.DEFAULT")
intent.data = Uri.parse(String.format("package:%s", applicationContext.packageName))
startActivityForResult(intent, 2296)
} catch (e: Exception) {
val intent = Intent()
intent.action = Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION
startActivityForResult(intent, 2296)
}
}
}
This code is working below Android 11 device. But on Android 11 file is not creating File(context.getExternalFilesDir(Environment.DIRECTORY_PICTURES) .toString() + "/" + FolderName )
ANSWER
Answered 2021-Jul-31 at 10:25use this code for save captured image
String mPath = Environment.getExternalStorageDirectory() + "/Print";
Bitmap tmp = BitmapFactory.decodeFile(mPath);
File imageFile = new File(mPath);
FileOutputStream outStream;
try
{
outStream = new FileOutputStream(imageFile);
try
{
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, outStream);
outStream.flush();
outStream.close();
} catch (IOException e)
{
e.printStackTrace();
}
} catch (Exception e)
{
e.printStackTrace();
}
QUESTION
can someone let me know the right usage of populating image resource in Image view using Data Binding
ANSWER
Answered 2021-Dec-31 at 10:21You can use it in such way:
QUESTION
(Solution has been found, please avoid reading on.)
I am creating a pixel art editor for Android, and as for all pixel art editors, a paint bucket (fill tool) is a must need.
To do this, I did some research on flood fill algorithms online.
I stumbled across the following video which explained how to implement an iterative flood fill algorithm in your code. The code used in the video was JavaScript, but I was easily able to convert the code from the video to Kotlin:
https://www.youtube.com/watch?v=5Bochyn8MMI&t=72s&ab_channel=crayoncode
Here is an excerpt of the JavaScript code from the video:
Converted code:
Tools.FILL_TOOL -> {
val seedColor = instance.rectangles[rectTapped]?.color ?: Color.WHITE
val queue = LinkedList()
queue.offer(MathExtensions.convertIndexToXYPosition(rectangleData.indexOf(rectTapped), instance.spanCount.toInt()))
val selectedColor = getSelectedColor()
while (queue.isNotEmpty() && seedColor != selectedColor) { // While the queue is not empty the code below will run
val current = queue.poll()
val color = instance.rectangles.toList()[convertXYDataToIndex(instance, current)].second?.color ?: Color.WHITE
if (color != seedColor) {
continue
}
instance.extraCanvas.apply {
instance.rectangles[rectangleData[convertXYDataToIndex(instance, current)]] = defaultRectPaint // Colors in pixel with defaultRectPaint
drawRect(rectangleData[convertXYDataToIndex(instance, current)], defaultRectPaint)
for (index in expandToNeighborsWithMap(instance, current)) {
val candidate = MathExtensions.convertIndexToXYPosition(index, instance.spanCount.toInt())
queue.offer(candidate)
}
}
}
}
Now, I want to address two major issues I'm having with the code of mine:
Performance
Flooding glitch (fixed by suggestion from person in the comments)
Performance
A flood fill needs to be very fast and shouldn't take less than a second, the problem is, say I have a canvas of size 50 x 50, and I decide to fill in the whole canvas, it can take up to 8 seconds or more.
Here is some data I've compiled for the time it's taken to fill in a whole canvas given the spanCount
value:
The conclusion from the data is that the flood fill algorithm is unusually slow.
To find out why, I decided to test out which parts of the code are taking the most time to compile. I came to the conclusion that the expandToNeighbors
function is taking the most time out of all the other tasks:
Here is an excerpt of the expandToNeighbors
function:
fun expandToNeighbors(instance: MyCanvasView, from: XYPosition): List {
var asIndex1 = from.x
var asIndex2 = from.x
var asIndex3 = from.y
var asIndex4 = from.y
if (from.x > 1) {
asIndex1 = xyPositionData!!.indexOf(XYPosition(from.x - 1, from.y))
}
if (from.x < instance.spanCount) {
asIndex2 = xyPositionData!!.indexOf(XYPosition(from.x + 1, from.y))
}
if (from.y > 1) {
asIndex3 = xyPositionData!!.indexOf(XYPosition(from.x, from.y - 1))
}
if (from.y < instance.spanCount) {
asIndex4 = xyPositionData!!.indexOf(XYPosition(from.x, from.y + 1))
}
return listOf(asIndex1, asIndex2, asIndex3, asIndex4)
}
To understand the use of the expandToNeighbors
function, I would recommend watching the video that I linked above.
(The if statements are there to make sure you won't get an IndexOutOfBoundsException
if you try and expand from the edge of the canvas.)
This function will return the index of the north, south, west, and east pixels from the xyPositionData
list which contains XYPosition
objects.
(The black pixel is the from
parameter.)
The xyPositionData
list is initialized once in the convertXYDataToIndex
function, here:
var xyPositionData: List? = null
var rectangleData: List? = null
fun convertXYDataToIndex(instance: MyCanvasView, from: XYPosition): Int {
if (rectangleData == null) {
rectangleData = instance.rectangles.keys.toList()
}
if (xyPositionData == null) {
xyPositionData = MathExtensions.convertListOfSizeNToListOfXYPosition(
rectangleData!!.size,
instance.spanCount.toInt()
)
}
return xyPositionData!!.indexOf(from)
}
So, the code works fine (kind of) but the expandToNeighbors
function is very slow, and it is the main reason why the flood fill algorithm is taking a long time.
My colleague suggested that indexOf
may be slowing everything down, and that I should probably switch to a Map-based implementation with a key being XYPosition
and a value being Int
representing the index, so I replaced it with the following:
fun expandToNeighborsWithMap(instance: MyCanvasView, from: XYPosition): List {
var asIndex1 = from.x
var asIndex2 = from.x
var asIndex3 = from.y
var asIndex4 = from.y
if (from.x > 1) {
asIndex1 = rectangleDataMap!![XYPosition(from.x - 1, from.y)]!!
}
if (from.x < instance.spanCount) {
asIndex2 = rectangleDataMap!![XYPosition(from.x + 1, from.y)]!!
}
if (from.y > 1) {
asIndex3 = rectangleDataMap!![XYPosition(from.x, from.y - 1)]!!
}
if (from.y < instance.spanCount) {
asIndex4 = rectangleDataMap!![XYPosition(from.x, from.y + 1)]!!
}
return listOf(asIndex1, asIndex2, asIndex3, asIndex4)
}
It functions the same way, only this time it uses a Map which is initialized here:
var xyPositionData: List? = null
var rectangleData: List? = null
var rectangleDataMap: Map? = null
fun convertXYDataToIndex(instance: MyCanvasView, from: XYPosition): Int {
if (rectangleData == null) {
rectangleData = instance.rectangles.keys.toList()
}
if (xyPositionData == null) {
xyPositionData = MathExtensions.convertListOfSizeNToListOfXYPosition(
rectangleData!!.size,
instance.spanCount.toInt()
)
}
if (rectangleDataMap == null) {
rectangleDataMap = MathExtensions.convertListToMap(
rectangleData!!.size,
instance.spanCount.toInt()
)
}
return xyPositionData!!.indexOf(from)
}
Converting the code to use a map increased the speed by around 20%, although the algorithm is still slow.
After spending a couple of days trying to make the algorithm work faster, I'm out of ideas and I'm unsure why the expandToNeighbors
function is taking a long time. Any help would be appreciated to fix this issue.
Apologies if I didn't do a good enough job of explaining the exact issue, but I have tried my best. Implementation-wise it is quite messy unfortunately because of the whole list index to XYPosition
conversions, but at least it works - the only problem is the performance.
So I have two one major problem, if anyone can try and find a solution for it, it would be great because I have tried to myself without much luck.
I've actually pushed the fill tool to GitHub as a KIOL (Known Issue or Limitation), so the user can use the fill tool if they want, but they need to be aware of the limitations/issues. This is so anyone who wants to help me fix this can have a look at my code and reproduce the bugs.
Link to repository:
https://github.com/realtomjoney/PyxlMoose
Edit after bounty
I understand that this question is extremely difficult to answer and will require a lot of thinking. I've tried myself to fix these issues but haven't had much success, so I'm offering 50 reputation for anyone who can assist.
I would recommend you clone PyxlMoose and reproduce the errors, then work from there. Relying on the code snippets isn't enough.
Formula for converting XY position to an index
Somebody in the comments suggested a formula for converting an XYPosition
to an index value, I came up with the following method which works:
fun convertXYPositionToIndex(xyPosition: XYPosition, spanCount: Int): Int {
val positionX = xyPosition.x
val positionY = xyPosition.y
return (spanCount - positionY) + (spanCount * (positionX - 1))
}
The only problem is - it increases the speed by around 50% but it's still taking around 10-15 seconds to fill in an area of 80 by 80 pixels, so it has helped to a large degree although it's still very slow. But thank you very much for the suggestion anyways, it has helped a lot :)
ANSWER
Answered 2021-Dec-29 at 08:28I think the performance issue is because of expandToNeighbors
method generates 4 points all the time. It becomes crucial on the border, where you'd better generate 3 (or even 2 on corner) points, so extra point is current position again. So first border point doubles following points count, second one doubles it again (now it's x4) and so on.
If I'm right, you saw not the slow method work, but it was called too often.
QUESTION
I switched from the deprecated GDPR Consent Library to the new User Messaging Platform, and used the code as stated in the documentation.
I noticed that when the user clicks on Manage Options then Confirm choices, ads will stop displaying altogether (Ad failed to load, no ad config), and I can't find anyway to check if the user didn't consent to the use of personal data.
This is problematic as my app relies purely on ads, and I will be losing money if ads don't show up, so I want to make it mandatory for users to consent to the use of their personal data, otherwise the app should be unusable.
I have made a test project on Github so everyone can test this behavior. If you are not using an emulator, then you need to change the "TEST_DEVICE_ID" to yours.
How can I achieve this?
ANSWER
Answered 2021-Nov-02 at 17:50I found a workaround for this, but this is no final official solution.
It seems that if a user consented to Personalized ads, a string in SharedPreferences
, which key is IABTCF_VendorConsents, will contain ones and zeros corresponding to some vendors (I think). If he didn't consent, this string will be equal to 0.
private val sp = PreferenceManager.getDefaultSharedPreferences(appContext)
fun consentedToPersonalizedAds() = sp.getString("IABTCF_VendorConsents", null) != "0"
QUESTION
We have a react-native application running in production with firebase phone auth. Lately, we received feedback from users owning new Huawei devices not being able to authenticate with their phone numbers using firebase.
Since a lot of users started having this issue, we decided to implement Huawei auth services only for devices under HarmonyOS and keep the regular firebase phone authentication for other users.
After integrating the Huawei App Gallery Connect Auth SDK in our react-native app, we are able to receive the OTP and sign the user in using credentialWithVerifyCode
and we are also able to retrieve the user's token using
idToken =(await (await AGCAuth.getInstance().currentUser()).getToken()).token;
The idToken is a JWT token that looks something like this
eyJhbGciOiJIUzUxMiJ9.eyJ0b2tlbiI6IjVCMzQ5OTM5ODBFNEYxRUQwNDBDOTBEMjA1Q0U4QTJCNzRFMTg3RkUyRDNDQzY4N0E3MUVCMUZFQ0VBMDZDQTEifQ.xtAXTzfpzqRHAvDP3fJjdctnNoFHFmqawWJBGqG4y3qBSeo1XNHFyNOPnL-V6BCmkpxGIO3eq2eYJShIJhad-A
The payload inside contains another token (Not JWT), but we don't think that is the problem, we also tried a token with all user information.
After sending the JWT to our .NET core 3.1 web API we are unable to validate the token using JwtBearerExtensions.AddJwtBearer
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(
JwtBearerDefaults.AuthenticationScheme,
o =>
{
o.Authority = "https://oauth-login.cloud.huawei.com";
});
the authority is set to https://oauth-login.cloud.huawei.com
which has the issuer set to https://accounts.huawei.com
which seems off because in firebase the issuer looks more like: https://securetoken.google.com/YOUR_PROJECT
The error we are getting is:
Microsoft.IdentityModel.Tokens.SecurityTokenInvalidSignatureException: IDX10503: Signature validation failed. Keys tried: '[PII is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'.
Exceptions caught:
'[PII is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'.
token: '[PII is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'.
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature(String token, TokenValidationParameters validationParameters)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateToken(String token, TokenValidationParameters validationParameters, SecurityToken& validatedToken)
at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync()
info: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler[7]
The error is happening probably because the Authority is not correct.
We can't figure out what is the problem, we are not able to find the authority we need to validate the token with.
ANSWER
Answered 2021-Nov-09 at 03:04You are advised to use the following method to validate the token.
try {
AuthAccessToken authAccessToken = agcAuth.verifyAccessToken(accessToken, true);
} catch (AGCAuthException e) {
if (e.getErrorCode() == AuthErrorCode.VERIFY_ACCESS_TOKEN_ACCESS_TOKEN_IS_NULL.getErrorCode()) {
// The user access token is empty.
} else if (e.getErrorCode() == AuthErrorCode.JWT_VERIFY_FAILED.getErrorCode()) {
// Failed to authenticate the user access token.
} else if (e.getErrorCode() == AuthErrorCode.JWT_EXPIRE.getErrorCode()) {
// The user access token has expired.
} else if (e.getErrorCode() == AuthErrorCode.JWT_REVOKED.getErrorCode()) {
// The user access token has been revoked.
}
For more details, pls kindly refer to this docs.
QUESTION
I was using def lifecycle_version = "2.3.1"
,
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version")
.
but I had to update version to def lifecycle_version = "2.4.0-alpha03"
to use some features, but ViewModelProvider.Factory
class doesn't work after I use def lifecycle_version = "2.4.0-alpha03"
.
class ViewModelFactory(private val param:String,private val param2: String) : ViewModelProvider.Factory {
override fun create(modelClass: Class): T {
return if (modelClass.isAssignableFrom(ReviewViewModel::class.java)) {
ReviewViewModel(param,param2) as T
}else if(modelClass.isAssignableFrom(FoodViewModel::class.java)){
FoodViewModel(param,param2) as T
else {
throw IllegalArgumentException()
}
}
}
the error messages are
! Class 'ViewModelFactory' is not abstract and does not implement abstract member public abstract fun create(modelClass: Class): T defined in androidx.lifecycle.ViewModelProvider.Factory
! 'create' overrides nothing
Thanks..
ANSWER
Answered 2021-Nov-09 at 03:38You've written:
override fun create(modelClass: Class): T {
But what the error message is saying is that create
must always have a non-null ViewModel
type. You need to change your method signature to remove the ?
that indicates nullability on your :
override fun create(modelClass: Class): T {
This is specifically called out in the Lifecycle 2.4 release notes:
Source-breaking change:
ViewModelProvider
has been rewritten in Kotlin.ViewModelProvider.Factory.create
method now longer allows nullable generic.
QUESTION
On Android 12,
If we open an activity
Go to the home screen of the phone to change the wallpaper
Switch back to our activity, the activity restarts.
It seems it is related to the Material You theming.
I would like to disable the restarting of activity when my app comes to the foreground. Is there a way?
ANSWER
Answered 2021-Oct-31 at 18:39It is a non-traditional configuration change. By "non-traditional", I mean that it cannot be blocked by android:configChanges
— your activity will be destroyed and recreated whether you like it or not.
If you have Configuration
objects from before and after the change, you can determine that this scenario occurred by calling diff()
on the newer Configuration
to compare it to the older one:
val diff = resources.configuration.diff(vm.originalConfiguration)
Log.d("WallpaperCCTest", "matches CONFIG_ASSETS_PATHS? ${(diff.toLong() and 0x80000000) != 0L}")
Here, vm.originalConfiguration
points to the older Configuration
, and we get the current Configuration
from the Resources
object. (diff.toLong() and 0x80000000) != 0L
will evaluate to true
if a wallpaper change or something similar triggered the configuration change. There are other edge cases for this — this whole "unblockable configuration change" thing came about when Sony got Google to add support for runtime resource overlays (RROs) back in 2017. So, some of those "change the system theme" apps might trigger this same unblockable configuration change.
As Nguyễn Hoài Nam notes, you can detect this from onConfigurationChanged()
of a custom Application
. Or, have your viewmodel hold onto the previous Configuration
and compare it with the current one in onCreate()
of your activity.
I have more on this issue in this blog post. AFAIK, there is no way to opt out of this configuration change, so if your app was designed to avoid configuration changes, you may be out of luck.
QUESTION
I create an app from the template, and populate with some Logs as below
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
MainApplication.something++
Log.d("Tracking", "onCreate ${MainApplication.something} ${Thread.currentThread()}")
setContentView(R.layout.activity_main)
}
override fun onStart() {
super.onStart()
Log.d("Tracking", "onStart")
}
override fun onResume() {
super.onResume()
Log.d("Tracking", "onResume")
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
Log.d("Tracking", "onConfigurationChanged")
}
override fun onPause() {
super.onPause()
Log.d("Tracking", "onPause")
}
override fun onStop() {
super.onStop()
Log.d("Tracking", "onStop")
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
Log.d("Tracking", "onSaveInstanceState")
}
override fun onDestroy() {
super.onDestroy()
Log.d("Tracking", "onDestroy")
}
}
and
class MainApplication: Application() {
companion object {
var something = 0
}
override fun onCreate() {
super.onCreate()
Log.d("Tracking", "Application onCreate $something ${Thread.currentThread()}")
}
}
When I run it, I notice that sometimes it launch 3 times. (some other times, it launch 2 times and sometimes 1 time).
The log as below
2021-11-04 23:55:31.409 7810-7810/com.example.empty D/Tracking: Application onCreate 0 Thread[main,5,main]
2021-11-04 23:55:31.445 7810-7810/com.example.empty D/Tracking: onCreate 1 Thread[main,5,main]
2021-11-04 23:55:31.409 7810-7810/com.example.empty D/Tracking: Application onCreate 0 Thread[main,5,main]
2021-11-04 23:55:31.445 7810-7810/com.example.empty D/Tracking: onCreate 1 Thread[main,5,main]
2021-11-04 23:55:31.508 7810-7810/com.example.empty D/Tracking: onStart
2021-11-04 23:55:31.445 7810-7810/com.example.empty D/Tracking: onCreate 1 Thread[main,5,main]
2021-11-04 23:55:31.509 7810-7810/com.example.empty D/Tracking: onResume
2021-11-04 23:55:31.508 7810-7810/com.example.empty D/Tracking: onStart
2021-11-04 23:55:31.509 7810-7810/com.example.empty D/Tracking: onResume
2021-11-04 23:55:31.509 7810-7810/com.example.empty D/Tracking: onResume
Initially, I thought it's the Activity gets launched 3 times, but then when I log out the Application onCreate, it also 3 times. I explicitly put a static variable something
, and clearly see that the Applications are 3 different instances (since the variable didn't get incremented across the 3 times).
To confirm it is not because configuration change or activity got killed (as per explained in https://stackoverflow.com/a/3588750/3286489), I also log out when onPause, onStop, onSavedInstanceState and onDestroy, none of them get called.
Does anyone know why did the application start 3 times when only launch once?
Note: I compiled on Android SDK 31 and 30, and Emulator API S and API 29, all of them have the same behavior. Also tried on the actual device Android 8.0. Same behavior shown.
Update I notice it only happens after the second compile. Perhaps it's just a repeated uncleared Log (though I have clear it).
ANSWER
Answered 2021-Nov-05 at 03:21Apparently, it's an Android Studio filtering issue. I'm having the Tracking
there to filter the log. And it shows multiple log of the same type.
When I remove a character from the log, then the filter get corrected. After putting back the character, the filter is still okay.
See the GIF below.
QUESTION
I'm starting to get errors when I am executing a test for a release variant which was always working fine. The code has always been executed in a Docker container so we can ensure that the build will always be clean.
Today for some reason with no changes at all to the code, I am starting to see errors on the test run:
Execution failed for task:
am:processReleaseResources'.
> A failure occurred while executing com.android.build.gradle.internal.tasks.Workers$ActionFacade
> Android resource linking failed
/.gradle/caches/transforms-2/files-2.1/ff28653768e2ccb1135467db3600af3a/core-1.7.0-alpha02/res/values/values.xml:105:5-114:25: AAPT: error: resource android:attr/lStar not found.
I've made some research about the error, but I only find errors related to another resource, but nothing about lStar. So far about, I have found that lStar is code added to Android 31 (Android 12) in particular, which makes no sense to me why it should start working on Android 12 if my compileSdkVersion value is 28.
What could be the cause of this error even though nothing has been changed?
ANSWER
Answered 2021-Sep-03 at 11:31I've found the issue and I was able to fix it.
The issue was that one of the external libraries the app depends on has a dependency on androidx.core:core-ktx:+
which meant that was always compiling with the latest version. My app is still working on SDK 28, but the latest version of androidx.core:core-ktx
has the minimal SDK of 31, which resulted in this conflict.
QUESTION
While upload my app in Google playstore I get this error; We've detected this app uses an unsupported version of Play billing. Please upgrade to Billing Library version 3 or newer to publish this app.
Below are the dependencies on my android studio app, which of them is the billing library to change?
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.google.android.material:material:1.1.0'
implementation 'com.github.AnchorFreePartner.hydra-sdk-android:sdk:3.1.1'
implementation 'com.github.AnchorFreePartner.hydra-sdk-android:openvpn:3.1.1'
implementation 'junit:junit:4.12'
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation 'com.android.support:multidex:1.0.3'
implementation 'com.jakewharton:butterknife:10.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.1'
implementation 'com.google.android.gms:play-services-ads:19.0.1'
implementation 'com.facebook.android:audience-network-sdk:5.6.1'
implementation 'com.onesignal:OneSignal:3.12.7@aar'
implementation 'com.google.firebase:firebase-analytics:17.3.0'
implementation 'com.google.android.gms:play-services-gcm:17.0.0'
implementation 'com.google.android.gms:play-services-location:17.0.0'
implementation 'com.anjlab.android.iab.v3:library:1.1.0'
implementation 'com.squareup.retrofit2:retrofit:2.6.2'
implementation 'com.google.code.gson:gson:2.8.5'
implementation 'com.squareup.retrofit2:converter-gson:2.6.2'
ANSWER
Answered 2021-Sep-10 at 12:03You are using this library for InApp purchasing but this library says we have updated to V3 but Google play store warn about this library that library is not updated to V3 or V4
implementation 'com.anjlab.android.iab.v3:library:1.1.0'
You should use Official documents for InApp Purchasing
Example V4build.gradle
implementation 'com.android.billingclient:billing:4.0.0'
strings.xml
Place Your ID Here
android.test.purchased
InAppPurchase.kt
import android.app.Activity
import android.content.Context
import android.widget.Toast
import com.android.billingclient.api.*
import java.io.IOException
import java.util.ArrayList
class InAppPurchase(context: Context) : PurchasesUpdatedListener{
private val mContext: Context = context
private var billingClient: BillingClient
var ackPurchase =
AcknowledgePurchaseResponseListener { billingResult ->
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
//if purchase is acknowledged
// Grant entitlement to the user. and restart activity
SharedPreferencesUtils.setPurchasedBillingValue(mContext, true)
showMessage("Item Purchased")
(mContext as Activity).recreate()
}
}
init {
billingClient =
BillingClient.newBuilder(mContext).enablePendingPurchases().setListener(this).build()
billingClient.startConnection(object : BillingClientStateListener {
override fun onBillingSetupFinished(billingResult: BillingResult) {
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
val queryPurchase = billingClient.queryPurchases(BillingClient.SkuType.INAPP)
val queryPurchases = queryPurchase.purchasesList
if (queryPurchases != null && queryPurchases.size > 0) {
handlePurchases(queryPurchases)
} else {
SharedPreferencesUtils.setPurchasedBillingValue(mContext, false)
}
}
}
override fun onBillingServiceDisconnected() {}
})
}
fun productPurchase() {
//check if service is already connected
if (billingClient.isReady) {
initiatePurchase()
} else {
billingClient =
BillingClient.newBuilder(mContext).enablePendingPurchases().setListener(this).build()
billingClient.startConnection(object : BillingClientStateListener {
override fun onBillingSetupFinished(billingResult: BillingResult) {
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
initiatePurchase()
} else {
showMessage("Error" + billingResult.debugMessage)
}
}
override fun onBillingServiceDisconnected() {}
})
}
}
private fun initiatePurchase() {
val skuList: MutableList = ArrayList()
skuList.add(mContext.resources.getString(R.string.product_id))
val params = SkuDetailsParams.newBuilder()
params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP)
billingClient.querySkuDetailsAsync(
params.build()
) { billingResult, skuDetailsList ->
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
if (skuDetailsList != null && skuDetailsList.size > 0) {
val flowParams = BillingFlowParams.newBuilder()
.setSkuDetails(skuDetailsList[0])
.build()
billingClient.launchBillingFlow(mContext as Activity, flowParams)
} else {
//try to add item/product id "purchase" inside managed product in google play console
showMessage("Purchase Item not Found")
}
} else {
showMessage(" Error " + billingResult.debugMessage)
}
}
}
/**
* Verifies that the purchase was signed correctly for this developer's public key.
*
* Note: It's strongly recommended to perform such check on your backend since hackers can
* replace this method with "constant true" if they decompile/rebuild your app.
*
*/
private fun verifyValidSignature(signedData: String, signature: String): Boolean {
return try {
// To get key go to Developer Console > Select your app > Development Tools > Services & APIs.
val base64Key = mContext.getString(R.string.license_key)
Security.verifyPurchase(base64Key, signedData, signature)
} catch (e: IOException) {
false
}
}
fun handlePurchases(purchases: List) {
for (purchase in purchases) {
//if item is purchased
if (mContext.getString(R.string.product_id) == purchase.skus[0] && purchase.purchaseState == Purchase.PurchaseState.PURCHASED) {
if (!verifyValidSignature(purchase.originalJson, purchase.signature)) {
// Invalid purchase
// show error to user
showMessage("Invalid Purchase")
return
}
// else purchase is valid
// if item is purchased and not acknowledged
if (!purchase.isAcknowledged) {
val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder()
.setPurchaseToken(purchase.purchaseToken)
.build()
billingClient.acknowledgePurchase(acknowledgePurchaseParams, ackPurchase)
} else {
// Grant entitlement to the user on item purchase
// restart activity
if (!SharedPreferencesUtils.getPurchasedBillingValue(mContext)) {
SharedPreferencesUtils.setPurchasedBillingValue(mContext,true)
showMessage("Item Purchased")
(mContext as Activity).recreate()
}
}
} else if (mContext.getString(R.string.product_id) == purchase.skus[0] && purchase.purchaseState == Purchase.PurchaseState.PENDING) {
showMessage("Purchase is Pending. Please complete Transaction")
} else if (mContext.getString(R.string.product_id) == purchase.skus[0] && purchase.purchaseState == Purchase.PurchaseState.UNSPECIFIED_STATE) {
SharedPreferencesUtils.setPurchasedBillingValue(mContext,false)
showMessage("Purchase Status Unknown")
}
}
}
private fun showMessage(message: String) {
(mContext as Activity).runOnUiThread{
Toast.makeText(mContext, message, Toast.LENGTH_SHORT).show()
}
}
override fun onPurchasesUpdated(billingResult: BillingResult, purchases: MutableList?) {
//if item newly purchased
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK && purchases != null) {
handlePurchases(purchases)
} else if (billingResult.responseCode == BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED) {
val queryAlreadyPurchasesResult =
billingClient.queryPurchases(BillingClient.SkuType.INAPP)
val alreadyPurchases = queryAlreadyPurchasesResult.purchasesList
alreadyPurchases?.let { handlePurchases(it) }
} else if (billingResult.responseCode == BillingClient.BillingResponseCode.USER_CANCELED) {
showMessage("Purchase Canceled")
} else {
showMessage("Error updated" + billingResult.debugMessage)
}
}
fun onDestroyBilling() {
billingClient.endConnection()
}
}
Security.kt
import android.text.TextUtils
import android.util.Base64
import java.io.IOException
import java.security.*
import java.security.spec.InvalidKeySpecException
import java.security.spec.X509EncodedKeySpec
/**
* Security-related methods. For a secure implementation, all of this code should be implemented on
* a server that communicates with the application on the device.
*/
object Security {
private const val KEY_FACTORY_ALGORITHM = "RSA"
private const val SIGNATURE_ALGORITHM = "SHA1withRSA"
/**
* Verifies that the data was signed with the given signature, and returns the verified
* purchase.
* @param base64PublicKey the base64-encoded public key to use for verifying.
* @param signedData the signed JSON string (signed, not encrypted)
* @param signature the signature for the data, signed with the private key
* @throws IOException if encoding algorithm is not supported or key specification
* is invalid
*/
@Throws(IOException::class)
fun verifyPurchase(
base64PublicKey: String, signedData: String,
signature: String
): Boolean {
if (TextUtils.isEmpty(signedData) || TextUtils.isEmpty(base64PublicKey)
|| TextUtils.isEmpty(signature)
) {
//Purchase verification failed: missing data
return false
}
val key = generatePublicKey(base64PublicKey)
return verify(key, signedData, signature)
}
/**
* Generates a PublicKey instance from a string containing the Base64-encoded public key.
*
* @param encodedPublicKey Base64-encoded public key
* @throws IOException if encoding algorithm is not supported or key specification
* is invalid
*/
@Throws(IOException::class)
fun generatePublicKey(encodedPublicKey: String): PublicKey {
return try {
val decodedKey = Base64.decode(encodedPublicKey, Base64.DEFAULT)
val keyFactory = KeyFactory.getInstance(KEY_FACTORY_ALGORITHM)
keyFactory.generatePublic(X509EncodedKeySpec(decodedKey))
} catch (e: NoSuchAlgorithmException) {
// "RSA" is guaranteed to be available.
throw RuntimeException(e)
} catch (e: InvalidKeySpecException) {
val msg = "Invalid key specification: $e"
throw IOException(msg)
}
}
/**
* Verifies that the signature from the server matches the computed signature on the data.
* Returns true if the data is correctly signed.
*
* @param publicKey public key associated with the developer account
* @param signedData signed data from server
* @param signature server signature
* @return true if the data and signature match
*/
private fun verify(publicKey: PublicKey, signedData: String, signature: String): Boolean {
val signatureBytes: ByteArray = try {
Base64.decode(signature, Base64.DEFAULT)
} catch (e: IllegalArgumentException) {
//Base64 decoding failed
return false
}
try {
val signatureAlgorithm = Signature.getInstance(SIGNATURE_ALGORITHM)
signatureAlgorithm.initVerify(publicKey)
signatureAlgorithm.update(signedData.toByteArray())
return signatureAlgorithm.verify(signatureBytes)
} catch (e: NoSuchAlgorithmException) {
// "RSA" is guaranteed to be available
throw RuntimeException(e)
} catch (e: InvalidKeyException) {
//Invalid key specification
} catch (e: SignatureException) {
//Signature exception
}
return false
}
}
MainActivity.kt
var inAppPurchase: InAppPurchase = InAppPurchase(this)
btnInApp.setOnClickListener {
inAppPurchase.productPurchase()
}
Community Discussions, Code Snippets contain sources that include Stack Exchange Network
Vulnerabilities
No vulnerabilities reported
Install AndroidUtilCode
You can use AndroidUtilCode like any standard Java library. Please include the the jar files in your classpath. You can also use any IDE and you can run and debug the AndroidUtilCode component as you would do with any other Java program. Best practice is to use a build tool that supports dependency management such as Maven or Gradle. For Maven installation, please refer maven.apache.org. For Gradle installation, please refer gradle.org .
Support
Find, review, and download reusable Libraries, Code Snippets, Cloud APIs from over 650 million Knowledge Items
Find more librariesExplore Kits - Develop, implement, customize Projects, Custom Functions and Applications with kandi kits
Save this library and start creating your kit
Share this Page