Reputation: 21
first time I use billingclient, but I have a problem, by the console it gives me the error: RecyclerView billingClient: No adapter attached; skipping layout and sometimes it loads the RecyclerView and sometimes it doesn't, that is, I enter the activity and sometimes it loads it and many times it doesn't
I leave the code for an expert to guide me and I can help myself to see where I have a fault
suscripcion.java
private BillingClient billingClient;
private AcknowledgePurchaseResponseListener acknowledgePurchaseResponseListener;
private RecyclerView recyclerView_sub;
private DocumentReference documentReference;
private FirebaseFirestore mFirestore;
private FirebaseUser user;
private FirebaseAuth fAuth;
private String userID;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_suscripcion);
mFirestore = FirebaseFirestore.getInstance();
fAuth = FirebaseAuth.getInstance();
user = fAuth.getCurrentUser();
if (user !=null)
{
userID = fAuth.getCurrentUser().getUid();
}
documentReference = mFirestore.collection("users").document(userID);
setupSubClient();
iniciador();
txtclose = findViewById(R.id.txtclosesub);
txtclose.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
startActivity(new Intent(suscripcion.this, MainActivity.class));
}
});
}
private void iniciador() {
statuspremium = findViewById(R.id.statuspremium);
rangopremium = findViewById(R.id.rangopremium);
recyclerView_sub = findViewById(R.id.recyclerview_sub);
recyclerView_sub.setHasFixedSize(true);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView_sub.setLayoutManager(layoutManager);
recyclerView_sub.addItemDecoration(new DividerItemDecoration(this, layoutManager.getOrientation()));
}
private void loadAllSubPackage() {
if (billingClient.isReady())
{
SkuDetailsParams params = SkuDetailsParams.newBuilder()
.setSkusList(Arrays.asList("vf_sub_unmes", "vf_sub_tresmes"))
.setType(BillingClient.SkuType.SUBS)
.build();
billingClient.querySkuDetailsAsync(params, new SkuDetailsResponseListener() {
@Override
public void onSkuDetailsResponse(@NonNull BillingResult billingResult, @Nullable List<SkuDetails> list) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK)
{
MySubAdapter adapter = new MySubAdapter(suscripcion.this, list, billingClient);
recyclerView_sub.setAdapter(adapter);
}else
{
Log.d("ERROR", "onSkuDetailsResponse: " + billingResult.getResponseCode());
}
}
});
}else
{
Log.d("ERROR", "loadAllSubPackage: Billing cliente not ready" );
}
}
private void setupSubClient() {
acknowledgePurchaseResponseListener = new AcknowledgePurchaseResponseListener() {
@Override
public void onAcknowledgePurchaseResponse(@NonNull BillingResult billingResult) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK)
{
Log.d("ERROR", "onAcknowledgePurchaseResponse: " +
billingResult.getResponseCode());
// rangopremium.setVisibility(View.VISIBLE);
// tiemposub.setVisibility(View.VISIBLE);
}
}
};
billingClient = BillingClientSetup.getInstance(this, this);
billingClient.startConnection(new BillingClientStateListener() {
@Override
public void onBillingServiceDisconnected() {
Log.d("ERROR", "onBillingServiceDisconnected: " );
}
@Override
public void onBillingSetupFinished(@NonNull BillingResult billingResult) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK)
{
Log.d("ERROR", "onBillingSetupFinished: " +
billingResult.getResponseCode());
// //Query
// List<Purchase> purchases = billingClient.queryPurchases(BillingClient.SkuType.SUBS)
// .getPurchasesList();
billingClient.queryPurchasesAsync(BillingClient.SkuType.SUBS, new PurchasesResponseListener() {
@Override
public void onQueryPurchasesResponse(@NonNull BillingResult billingResult, @NonNull List<Purchase> list) {
if (list.size() > 0)
{
for (Purchase purchase:list)
handleSubAlreadyPurchase(purchase);
Log.d("ERROR", "onBillingSetupFinished: " +
billingResult.getResponseCode());
}else
{
loadAllSubPackage();
documentReference.update("premium", "no");
documentReference.update("status", "user");
statuspremium.setVisibility(View.VISIBLE);
Log.d("ERROR", "onBillingSetupFinished: " +
billingResult.getResponseCode());
// rangopremium.setVisibility(View.GONE);
// tiemposub.setVisibility(View.GONE);
// statuspremium.setVisibility(View.VISIBLE);
// recyclerView_sub.setVisibility(View.VISIBLE);
}
}
});
}
else
{
Log.d("ERROR", "onBillingSetupFinished: " +
billingResult.getResponseCode());
}
}
});
}
private void handleSubAlreadyPurchase(Purchase purchases) {
if (purchases.getPurchaseState() == Purchase.PurchaseState.PURCHASED)
{
rangopremium.setVisibility(View.VISIBLE);
Log.d("ERROR", "handleSubAlreadyPurchase: ");
if (!purchases.isAcknowledged())
{
Log.d("ERROR", "handleSubAlreadyPurchase: ");
AcknowledgePurchaseParams acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder()
.setPurchaseToken(purchases.getPurchaseToken())
.build();
billingClient.acknowledgePurchase(acknowledgePurchaseParams, acknowledgePurchaseResponseListener);
}else
{
Log.d("ERROR", "handleSubAlreadyPurchase: ");
}
}
if (purchases.getPurchaseState() == Purchase.PurchaseState.UNSPECIFIED_STATE){
Log.d("ERROR", "handleSubAlreadyPurchase: ");
}
}
@Override
public void onPurchasesUpdated(@NonNull BillingResult billingResult, @Nullable List<Purchase> list) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && list !=null)
{
rangopremium.setVisibility(View.VISIBLE);
statuspremium.setVisibility(View.GONE);
recyclerView_sub.setVisibility(View.GONE);
documentReference.update("premium", "si");
documentReference.update("status", "Vinorrero");
Log.d("ERROR", "onPurchasesUpdated: " + billingResult.getResponseCode());
for (Purchase purchase:list)
handleSubAlreadyPurchase(purchase);
}else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED)
{
Log.d("ERROR", "onPurchasesUpdated: " + billingResult.getResponseCode());
rangopremium.setVisibility(View.GONE);
statuspremium.setVisibility(View.VISIBLE);
recyclerView_sub.setVisibility(View.VISIBLE);
documentReference.update("premium", "no");
documentReference.update("status", "user");
}else
{
Log.d("ERROR", "onPurchasesUpdated: " + billingResult.getResponseCode());
}
}
MySubAdapter.java
public class MySubAdapter extends RecyclerView.Adapter<MySubAdapter.MyViewHolder> {
AppCompatActivity appCompatActivity;
List<SkuDetails> skuDetailsList;
BillingClient billingClient;
public MySubAdapter(AppCompatActivity appCompatActivity, List<SkuDetails> skuDetailsList, BillingClient billingClient) {
this.appCompatActivity = appCompatActivity;
this.skuDetailsList = skuDetailsList;
this.billingClient = billingClient;
}
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new MyViewHolder(LayoutInflater.from(appCompatActivity.getBaseContext())
.inflate(R.layout.listado_sub, parent, false));
}
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
holder.txt_sub_name.setText(skuDetailsList.get(position).getTitle());
holder.txt_precio_sub.setText(skuDetailsList.get(position).getPrice());
holder.setListener(new IReciclerClickListener() {
@Override
public void onClick(View view, int position) {
BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
.setSkuDetails(skuDetailsList.get(position))
.build();
int reponse = billingClient.launchBillingFlow(appCompatActivity, billingFlowParams)
.getResponseCode();
switch(reponse)
{
case BillingClient.BillingResponseCode.BILLING_UNAVAILABLE:
Log.d("ERROR11", "BILLING_UNAVAILABLE");
break;
case BillingClient.BillingResponseCode.DEVELOPER_ERROR:
Log.d("ERROR12", "DEVELOPER_ERROR");
break;
case BillingClient.BillingResponseCode.FEATURE_NOT_SUPPORTED:
Log.d("ERROR13", "FEATURE_NOT_SUPPORTED");
break;
case BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED:
Log.d("ERROR14", "ITEM_ALREADY_OWNED");
break;
case BillingClient.BillingResponseCode.SERVICE_DISCONNECTED:
Log.d("ERROR15", "SERVICE_DISCONNECTED");
break;
case BillingClient.BillingResponseCode.SERVICE_TIMEOUT:
Log.d("ERROR16", "SERVICE_TIMEOUT");
break;
case BillingClient.BillingResponseCode.ITEM_UNAVAILABLE:
Log.d("ERROR17", "ITEM_UNAVAILABLE");
break;
default:
break;
}
}
});
}
@Override
public int getItemCount() {
return skuDetailsList.size();
}
public static class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
TextView txt_sub_name, txt_precio_sub;
IReciclerClickListener listener;
public void setListener(IReciclerClickListener listener) {
this.listener = listener;
}
public MyViewHolder(@NonNull View itemView) {
super(itemView);
txt_sub_name = itemView.findViewById(R.id.txt_sub_name);
txt_precio_sub = itemView.findViewById(R.id.txt_precio_sub);
itemView.setOnClickListener(this);
}
@Override
public void onClick(View v) {
listener.onClick(v, getAdapterPosition());
}
}
listado_sub.xml
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="@+id/card_sub"
android:layout_width="match_parent"
android:layout_height="wrap_content"
card_view:cardBackgroundColor="@android:color/transparent"
card_view:cardElevation="0dp"
android:theme="@style/Theme.MaterialComponents.Light"
android:layout_margin="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:layout_marginStart="10dp"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@drawable/btn_desing"
android:padding="16dp">
<TextView
android:id="@+id/txt_sub_name"
android:text="Titulo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:textColor="@color/colorPrimary"
android:textSize="16sp"/>
<TextView
android:id="@+id/txt_precio_sub"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_gravity="center"
android:text="Precio"
android:textAppearance="?attr/textAppearanceBody2"
android:textColor="?android:attr/textColorSecondary"/>
</LinearLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
Activity_suscripcion.xlm
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/Snackbar_apoyo"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/fondocurva"
android:orientation="vertical"
android:layout_gravity="center"
android:gravity="center">
<TextView
android:id="@+id/txtclosesub"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="end"
android:layout_marginBottom="20dp"
android:layout_marginEnd="20dp"
android:layout_marginTop="20dp"
android:background="@drawable/circulo"
android:gravity="center"
android:text="@string/equis"
android:textColor="@android:color/background_light"
android:textStyle="bold" />
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/statuspremium"
style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:gravity="center"
android:shadowColor="@color/negro"
android:shadowDx="2"
android:shadowDy="5"
android:visibility="gone"
android:shadowRadius="10"
android:text="@string/no_premium"
android:textColor="@color/colorAccent"
android:textSize="25sp"
android:textStyle="bold|italic" />
<com.hanks.htextview.rainbow.RainbowTextView
android:id="@+id/rangopremium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:visibility="gone"
android:textSize="25sp"
android:textStyle="bold"
android:text="PREMIUM"
app:colorSpace="150dp"
app:colorSpeed="10dp"/>
<ImageView
android:contentDescription="@string/app_name"
android:layout_width="200dp"
android:layout_height="200dp"
android:src="@drawable/vinoayuda"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/haste_premium"
android:textSize="18sp"
android:textColor="@color/colorAccent"
android:layout_gravity="center"
android:gravity="center"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:layout_marginBottom="20dp"
android:textStyle="bold"
android:fontFamily="sans-serif-smallcaps"
android:shadowColor="@color/negro"
android:shadowRadius="10"
android:shadowDx="2"
android:shadowDy="5"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/usuario_premium_garantizamos"
android:textSize="14sp"
android:textColor="@color/blanco"
android:layout_gravity="center"
android:gravity="center"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:layout_marginBottom="20dp"
android:textStyle="bold"
android:fontFamily="sans-serif-smallcaps"
app:drawableLeftCompat="@drawable/ic_baseline_error_24" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerview_sub"
android:layout_width="match_parent"
android:layout_height="0dp"
android:visibility="visible"
android:layout_weight="1"
android:layout_marginBottom="20dp">
</androidx.recyclerview.widget.RecyclerView>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</LinearLayout>
Upvotes: 0
Views: 137
Reputation: 57
I know that's quite an old topic but I'm posting my solution here cause I faced the same issue and perhaps it will help someone.
I don't know the reason why but I had to change my architecture. Instead of putting all the specific Billing's function call in the activity, I followed a "MVVM like" implementation. All my Billing calls are in a dedicated BillingClientWrapper class called by a BillingViewModel class that is called by my Activity. So it follows like that:
Activity → BillingViewModel → BillingClientWrapper
I used the official Billing's GitHub repo which has a similar approach: https://github.com/android/play-billing-samples
At the date of my post the implementation in the repo was done with Billing v4 and we are at v5. A bit of adaptation is needed cause some methods are deprecated.
Upvotes: 0