diff --git a/.gitignore b/.gitignore index 0f4f918..f4f1d03 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ /app/google_full/ /app/google_acad/ /app/src/google_donation/res/values/data.xml +/keystore.properties diff --git a/app/build.gradle b/app/build.gradle index 2a0cf72..bdff88a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,7 +1,21 @@ apply plugin: 'com.android.application' apply plugin: "androidx.navigation.safeargs" + +def keystorePropertiesFile = rootProject.file("keystore.properties") +def keystoreProperties = new Properties() +keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) + android { + signingConfigs { + full_version { + storeFile file(keystoreProperties['storeFile']) + storePassword keystoreProperties['storePassword'] + keyPassword keystoreProperties['keyPassword'] + keyAlias keystoreProperties['keyAlias'] + + } + } compileSdkVersion 30 buildToolsVersion "30.0.2" @@ -9,8 +23,8 @@ android { minSdkVersion 21 targetSdkVersion 30 - versionCode 34 - versionName "1.11.0" + versionCode 35 + versionName "1.11.1-test" multiDexEnabled true testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } @@ -22,6 +36,7 @@ android { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + signingConfig signingConfigs.full_version } } compileOptions { @@ -117,27 +132,27 @@ android { sourceSets { fdroid_peertube_apps_educ { res.srcDirs = ['src/main/res', 'src/acad/res'] - java.srcDirs = ['src/main/java', 'src/acad/java'] + java.srcDirs = ['src/main/java', 'src/acad/java', 'src/no_google_donation/java'] } google_peertube_apps_educ { res.srcDirs = ['src/main/res', 'src/acad/res'] - java.srcDirs = ['src/main/java', 'src/acad/java'] + java.srcDirs = ['src/main/java', 'src/acad/java', 'src/no_google_donation/java'] } fdroid_full { res.srcDirs = ['src/main/res', 'src/full/res'] - java.srcDirs = ['src/main/java', 'src/full/java'] + java.srcDirs = ['src/main/java', 'src/full/java', 'src/no_google_donation/java'] } google_full { - res.srcDirs = ['src/main/res', 'src/full/res'] - java.srcDirs = ['src/main/java', 'src/full/java'] + res.srcDirs = ['src/main/res', 'src/full/res', 'src/google_donation/res'] + java.srcDirs = ['src/main/java', 'src/full/java', 'src/google_donation/java'] } queermotion { res.srcDirs = ['src/main/res', 'src/queermotion/res'] - java.srcDirs = ['src/main/java', 'src/full/java'] + java.srcDirs = ['src/main/java', 'src/full/java', 'src/no_google_donation/java'] } bittube { res.srcDirs = ['src/main/res', 'src/bittube/res'] - java.srcDirs = ['src/main/java', 'src/full/java'] + java.srcDirs = ['src/main/java', 'src/full/java', 'src/no_google_donation/java'] } } } @@ -200,6 +215,7 @@ dependencies { implementation 'com.fasterxml.jackson.core:jackson-core:2.12.0' implementation 'org.slf4j:slf4j-simple:1.7.30' + google_fullImplementation "com.android.billingclient:billing:3.0.2" fdroid_peertube_apps_educImplementation 'org.matomo.sdk:tracker:4.1.2' google_peertube_apps_educImplementation 'org.matomo.sdk:tracker:4.1.2' diff --git a/app/src/google_donation/java/app/fedilab/fedilabtube/DonationActivity.java b/app/src/google_donation/java/app/fedilab/fedilabtube/DonationActivity.java new file mode 100644 index 0000000..f279033 --- /dev/null +++ b/app/src/google_donation/java/app/fedilab/fedilabtube/DonationActivity.java @@ -0,0 +1,278 @@ +package app.fedilab.fedilabtube; +/* Copyright 2021 Thomas Schneider + * + * This file is a part of TubeLab + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * TubeLab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with TubeLab; if not, + * see . */ + +import android.os.Bundle; +import android.view.MenuItem; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentStatePagerAdapter; +import androidx.viewpager.widget.PagerAdapter; +import androidx.viewpager.widget.ViewPager; + +import com.android.billingclient.api.AcknowledgePurchaseParams; +import com.android.billingclient.api.BillingClient; +import com.android.billingclient.api.BillingClientStateListener; +import com.android.billingclient.api.BillingResult; +import com.android.billingclient.api.ConsumeParams; +import com.android.billingclient.api.ConsumeResponseListener; +import com.android.billingclient.api.Purchase; +import com.android.billingclient.api.PurchasesUpdatedListener; +import com.android.billingclient.api.SkuDetailsParams; +import com.google.android.material.snackbar.Snackbar; +import com.google.android.material.tabs.TabLayout; + +import org.jetbrains.annotations.NotNull; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import app.fedilab.fedilabtube.databinding.ActivityDonationBinding; + +import app.fedilab.fedilabtube.fragment.MySubscriptionDonationsFragment; +import app.fedilab.fedilabtube.fragment.DonationsFragment; + + +public class DonationActivity extends AppCompatActivity implements PurchasesUpdatedListener { + + + DonationsFragment donationsFragment; + DonationsFragment subscriptionDonationsFragment; + MySubscriptionDonationsFragment mySubscriptionDonationsFragment; + private ActivityDonationBinding binding; + private BillingClient billingClient; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + binding = ActivityDonationBinding.inflate(getLayoutInflater()); + View view = binding.getRoot(); + setContentView(view); + billingClient = BillingClient.newBuilder(this) + .setListener(this) + .enablePendingPurchases() + .build(); + billingClient.startConnection(new BillingClientStateListener() { + @Override + public void onBillingSetupFinished(@NotNull BillingResult billingResult) { + if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) { + // The BillingClient is ready. You can query purchases here. + donationsFragment.initialized(billingClient); + subscriptionDonationsFragment.initialized(billingClient); + + List purchases = queryPurchases(); + if (purchases != null) { + for (Purchase purchase : purchases) { + if (!purchase.isAutoRenewing()) { + ConsumeParams consumeParams = + ConsumeParams.newBuilder() + .setPurchaseToken(purchase.getPurchaseToken()) + .build(); + + ConsumeResponseListener listener = (billingResult1, purchaseToken) -> { + //noinspection StatementWithEmptyBody + if (billingResult1.getResponseCode() == BillingClient.BillingResponseCode.OK) { + // Handle the success of the consume operation. + } + }; + billingClient.consumeAsync(consumeParams, listener); + } + } + } + } + } + + @Override + public void onBillingServiceDisconnected() { + // Try to restart the connection on the next request to + // Google Play by calling the startConnection() method. + } + }); + if (getSupportActionBar() != null) + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + donationsFragment = new DonationsFragment(); + Bundle bundle1 = new Bundle(); + bundle1.putSerializable("isSubscriptions", false); + donationsFragment.setArguments(bundle1); + + + subscriptionDonationsFragment = new DonationsFragment(); + Bundle bundle2 = new Bundle(); + bundle2.putSerializable("isSubscriptions", true); + subscriptionDonationsFragment.setArguments(bundle2); + + mySubscriptionDonationsFragment = new MySubscriptionDonationsFragment(); + + binding.tablayout.addTab(binding.tablayout.newTab().setText(getString(R.string.one_time))); + binding.tablayout.addTab(binding.tablayout.newTab().setText(getString(R.string.subscriptions))); + binding.tablayout.addTab(binding.tablayout.newTab().setText(getString(R.string.my_subscriptions))); + binding.viewpager.setOffscreenPageLimit(3); + + PagerAdapter mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager()); + binding.viewpager.setAdapter(mPagerAdapter); + binding.viewpager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + + } + + @Override + public void onPageSelected(int position) { + TabLayout.Tab tab = binding.tablayout.getTabAt(position); + if (tab != null) + tab.select(); + } + + @Override + public void onPageScrollStateChanged(int state) { + + } + }); + + binding.tablayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { + @Override + public void onTabSelected(TabLayout.Tab tab) { + binding.viewpager.setCurrentItem(tab.getPosition()); + } + + @Override + public void onTabUnselected(TabLayout.Tab tab) { + + } + + @Override + public void onTabReselected(TabLayout.Tab tab) { + } + }); + + } + + private List queryPurchases() { + Purchase.PurchasesResult purchasesResult = billingClient.queryPurchases(BillingClient.SkuType.SUBS); + List purchases = purchasesResult.getPurchasesList(); + List isSubscriptions = new ArrayList<>(); + HashMap map = new HashMap<>(); + if (purchases != null) { + for (Purchase purchase : purchases) { + try { + if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED) { + JSONObject purchaseJson = new JSONObject(purchase.getOriginalJson()); + String productId = purchaseJson.getString("productId"); + isSubscriptions.add(productId); + map.put(productId, purchase); + } + } catch (JSONException e) { + e.printStackTrace(); + } + } + SkuDetailsParams.Builder paramsSub = SkuDetailsParams.newBuilder(); + paramsSub.setSkusList(isSubscriptions).setType(BillingClient.SkuType.SUBS); + billingClient.querySkuDetailsAsync(paramsSub.build(), + (billingResult2, skuDetailsList) -> mySubscriptionDonationsFragment.initialized(skuDetailsList, map, billingClient)); + } else { + mySubscriptionDonationsFragment.initialized(new ArrayList<>(), map, billingClient); + } + return purchases; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == android.R.id.home) { + finish(); + return true; + } + return super.onOptionsItemSelected(item); + } + + @Override + public void onPurchasesUpdated(@NonNull BillingResult billingResult, @Nullable List purchases) { + String message; + if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK + && purchases != null) { + for (Purchase purchase : purchases) { + if (!purchase.isAutoRenewing()) { + ConsumeParams consumeParams = + ConsumeParams.newBuilder() + .setPurchaseToken(purchase.getPurchaseToken()) + .build(); + + ConsumeResponseListener listener = (billingResult1, purchaseToken) -> { + }; + billingClient.consumeAsync(consumeParams, listener); + } else { + if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED) { + if (!purchase.isAcknowledged()) { + AcknowledgePurchaseParams acknowledgePurchaseParams = + AcknowledgePurchaseParams.newBuilder() + .setPurchaseToken(purchase.getPurchaseToken()) + .build(); + billingClient.acknowledgePurchase(acknowledgePurchaseParams, b -> { + }); + } + } + + queryPurchases(); + } + } + message = getString(R.string.donation_succeeded_null); + } else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED) { + message = getString(R.string.donation_cancelled); + } else { + message = getString(R.string.toast_error); + } + View parentLayout = findViewById(android.R.id.content); + Snackbar snackbar = Snackbar.make(parentLayout, message, Snackbar.LENGTH_INDEFINITE); + snackbar.setAction(R.string.close, view -> snackbar.dismiss()); + snackbar.show(); + } + + + /** + * Pager adapter for the 2 fragments + */ + private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter { + + ScreenSlidePagerAdapter(FragmentManager fm) { + super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT); + } + + @NotNull + @Override + public Fragment getItem(int position) { + if (position == 0) { + return donationsFragment; + } else if (position == 1) { + return subscriptionDonationsFragment; + } else { + return mySubscriptionDonationsFragment; + } + } + + + @Override + public int getCount() { + return 3; + } + } +} diff --git a/app/src/google_donation/java/app/fedilab/fedilabtube/drawable/DonationButtonAdapter.java b/app/src/google_donation/java/app/fedilab/fedilabtube/drawable/DonationButtonAdapter.java new file mode 100644 index 0000000..37f482e --- /dev/null +++ b/app/src/google_donation/java/app/fedilab/fedilabtube/drawable/DonationButtonAdapter.java @@ -0,0 +1,91 @@ +package app.fedilab.fedilabtube.drawable; +/* Copyright 2021 Thomas Schneider + * + * This file is a part of TubeLab + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * TubeLab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with TubeLab; if not, + * see . */ + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import com.android.billingclient.api.BillingClient; +import com.android.billingclient.api.BillingFlowParams; +import com.android.billingclient.api.SkuDetails; + +import java.util.List; +import java.util.Locale; + +import app.fedilab.fedilabtube.DonationActivity; +import app.fedilab.fedilabtube.R; +import app.fedilab.fedilabtube.databinding.DrawerDonationBinding; + + +public class DonationButtonAdapter extends RecyclerView.Adapter { + + private final List skuDetails; + private final BillingClient billingClient; + private Context context; + private final boolean isSubscription; + + public DonationButtonAdapter(List skuDetails, BillingClient billingClient, boolean subscription) { + this.isSubscription = subscription; + this.skuDetails = skuDetails; + this.billingClient = billingClient; + } + + @NonNull + @Override + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + context = parent.getContext(); + DrawerDonationBinding itemBinding = DrawerDonationBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false); + return new ViewHolder(itemBinding); + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) { + final ViewHolder holder = (ViewHolder) viewHolder; + SkuDetails skuDetail = skuDetails.get(position); + String currency = skuDetail.getPriceCurrencyCode(); + String price = skuDetail.getPrice(); + if (isSubscription) { + holder.binding.buttonDonation.setText(String.format(Locale.getDefault(), "%s %s / %s", price, currency, context.getString(R.string.month))); + } else { + holder.binding.buttonDonation.setText(String.format(Locale.getDefault(), "%s %s", price, currency)); + } + + holder.binding.buttonDonation.setOnClickListener(v -> { + BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder() + .setSkuDetails(skuDetail) + .build(); + billingClient.launchBillingFlow((DonationActivity) context, billingFlowParams); + }); + } + + + @Override + public int getItemCount() { + return skuDetails.size(); + } + + static class ViewHolder extends RecyclerView.ViewHolder { + DrawerDonationBinding binding; + + ViewHolder(DrawerDonationBinding itemView) { + super(itemView.getRoot()); + binding = itemView; + } + } +} diff --git a/app/src/google_donation/java/app/fedilab/fedilabtube/drawable/DonationHistoryAdapter.java b/app/src/google_donation/java/app/fedilab/fedilabtube/drawable/DonationHistoryAdapter.java new file mode 100644 index 0000000..95ef2d7 --- /dev/null +++ b/app/src/google_donation/java/app/fedilab/fedilabtube/drawable/DonationHistoryAdapter.java @@ -0,0 +1,128 @@ +package app.fedilab.fedilabtube.drawable; +/* Copyright 2021 Thomas Schneider + * + * This file is a part of TubeLab + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * TubeLab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with TubeLab; if not, + * see . */ + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; +import androidx.recyclerview.widget.RecyclerView; + +import com.android.billingclient.api.BillingClient; +import com.android.billingclient.api.ConsumeParams; +import com.android.billingclient.api.ConsumeResponseListener; +import com.android.billingclient.api.Purchase; +import com.android.billingclient.api.SkuDetails; +import org.json.JSONException; +import org.json.JSONObject; +import java.util.HashMap; +import java.util.List; +import app.fedilab.fedilabtube.R; +import app.fedilab.fedilabtube.databinding.DrawerMyDonationBinding; +import es.dmoral.toasty.Toasty; + + +public class DonationHistoryAdapter extends RecyclerView.Adapter { + + private final List skuDetailsList; + private final BillingClient billingClient; + private Context context; + private final HashMap map; + + public DonationHistoryAdapter(List SkuDetailsList, HashMap map, BillingClient billingClient) { + this.skuDetailsList = SkuDetailsList; + this.billingClient = billingClient; + this.map = map; + } + + + @NonNull + @Override + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + context = parent.getContext(); + DrawerMyDonationBinding itemBinding = DrawerMyDonationBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false); + return new ViewHolder(itemBinding); + + } + + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) { + final ViewHolder holder = (ViewHolder) viewHolder; + SkuDetails skuDetails = skuDetailsList.get(position); + holder.binding.productTitle.setText(skuDetails.getTitle()); + holder.binding.productName.setText(skuDetails.getDescription()); + holder.binding.productInfo.setText(skuDetails.getOriginalPrice()); + + holder.binding.cancelDonation.setOnClickListener(v -> { + + AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context); + dialogBuilder.setMessage(R.string.cancel_subscription_confirm); + + dialogBuilder.setPositiveButton(R.string.cancel_subscription, (dialog, id) -> { + JSONObject skudetailsJson; + try { + skudetailsJson = new JSONObject(skuDetails.getOriginalJson()); + String productId = skudetailsJson.getString("productId"); + if (map.containsKey(productId)) { + Purchase purchase = map.get(productId); + if (purchase != null) { + ConsumeParams consumeParams = + ConsumeParams.newBuilder() + .setPurchaseToken(purchase.getPurchaseToken()) + .build(); + if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED) { + ConsumeResponseListener listener = (billingResult1, purchaseToken) -> { + if (billingResult1.getResponseCode() == BillingClient.BillingResponseCode.OK) { + Toasty.success(context, context.getString(R.string.subscription_cancelled), Toasty.LENGTH_LONG).show(); + } + skuDetailsList.remove(skuDetails); + notifyDataSetChanged(); + }; + billingClient.consumeAsync(consumeParams, listener); + } + + } + } + } catch (JSONException e) { + e.printStackTrace(); + } + dialog.dismiss(); + }); + dialogBuilder.setNegativeButton(R.string.cancel, (dialog, id) -> dialog.dismiss()); + AlertDialog alertDialogLogoutAccount = dialogBuilder.create(); + alertDialogLogoutAccount.show(); + }); + } + + + @Override + public int getItemCount() { + return skuDetailsList.size(); + } + + + static class ViewHolder extends RecyclerView.ViewHolder { + DrawerMyDonationBinding binding; + + ViewHolder(DrawerMyDonationBinding itemView) { + super(itemView.getRoot()); + binding = itemView; + } + } + +} diff --git a/app/src/google_donation/java/app/fedilab/fedilabtube/fragment/DonationsFragment.java b/app/src/google_donation/java/app/fedilab/fedilabtube/fragment/DonationsFragment.java new file mode 100644 index 0000000..099c860 --- /dev/null +++ b/app/src/google_donation/java/app/fedilab/fedilabtube/fragment/DonationsFragment.java @@ -0,0 +1,112 @@ +package app.fedilab.fedilabtube.fragment; +/* Copyright 2021 Thomas Schneider + * + * This file is a part of TubeLab + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * TubeLab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with TubeLab; if not, + * see . */ + +import android.content.Context; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.LinearLayoutManager; + +import com.android.billingclient.api.BillingClient; +import com.android.billingclient.api.SkuDetailsParams; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import app.fedilab.fedilabtube.R; +import app.fedilab.fedilabtube.databinding.FragmentDonationsBinding; +import app.fedilab.fedilabtube.drawable.DonationButtonAdapter; + + +public class DonationsFragment extends Fragment { + + public static final String[] donations = {"1", "2", "5", "10"}; + private FragmentDonationsBinding binding; + private View rootView; + private Context context; + private boolean isSubscriptions; + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + binding = FragmentDonationsBinding.inflate(LayoutInflater.from(context)); + rootView = binding.getRoot(); + context = getContext(); + Bundle bundle = this.getArguments(); + if (bundle != null) { + isSubscriptions = bundle.getBoolean("isSubscriptions", false); + } + int donationText; + if (isSubscriptions) { + donationText = R.string.recurrent_donation_text; + } else { + donationText = R.string.one_time_donation_text; + } + binding.donationText.setText(donationText); + binding.loader.setVisibility(View.VISIBLE); + binding.lvProducts.setVisibility(View.GONE); + return rootView; + } + + public void initialized(BillingClient bc) { + + + List donationsList = new ArrayList<>(); + for (String val : donations) { + donationsList.add("tubelab_donation_" + val + (isSubscriptions ? "_s" : "")); + } + SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder(); + if (isSubscriptions) { + params.setSkusList(donationsList).setType(BillingClient.SkuType.SUBS); + } else { + params.setSkusList(donationsList).setType(BillingClient.SkuType.INAPP); + } + bc.querySkuDetailsAsync(params.build(), + (billingResult, skuDetailsList) -> { + binding.loader.setVisibility(View.GONE); + binding.lvProducts.setVisibility(View.VISIBLE); + if (skuDetailsList != null) { + Collections.sort(skuDetailsList, (obj1, obj2) -> obj1.getPrice().compareTo(obj2.getPrice())); + } + DonationButtonAdapter donationButtonAdapter = new DonationButtonAdapter(skuDetailsList, bc, isSubscriptions); + binding.lvProducts.setAdapter(donationButtonAdapter); + binding.lvProducts.setLayoutManager(new LinearLayoutManager(context)); + }); + + + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + rootView = null; + } + + @Override + public void onCreate(Bundle saveInstance) { + super.onCreate(saveInstance); + } + + @Override + public void onAttach(@NonNull Context context) { + super.onAttach(context); + this.context = context; + } +} diff --git a/app/src/google_donation/java/app/fedilab/fedilabtube/fragment/MySubscriptionDonationsFragment.java b/app/src/google_donation/java/app/fedilab/fedilabtube/fragment/MySubscriptionDonationsFragment.java new file mode 100644 index 0000000..0300157 --- /dev/null +++ b/app/src/google_donation/java/app/fedilab/fedilabtube/fragment/MySubscriptionDonationsFragment.java @@ -0,0 +1,80 @@ +package app.fedilab.fedilabtube.fragment; +/* Copyright 2021 Thomas Schneider + * + * This file is a part of TubeLab + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * TubeLab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with TubeLab; if not, + * see . */ + +import android.content.Context; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.LinearLayoutManager; + +import com.android.billingclient.api.BillingClient; +import com.android.billingclient.api.Purchase; +import com.android.billingclient.api.SkuDetails; + +import java.util.HashMap; +import java.util.List; + +import app.fedilab.fedilabtube.databinding.FragmentMyDonationsBinding; +import app.fedilab.fedilabtube.drawable.DonationHistoryAdapter; + +public class MySubscriptionDonationsFragment extends Fragment { + + private FragmentMyDonationsBinding binding; + private View rootView; + private Context context; + + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + binding = FragmentMyDonationsBinding.inflate(LayoutInflater.from(context)); + rootView = binding.getRoot(); + context = getContext(); + binding.loader.setVisibility(View.VISIBLE); + binding.lvPurchases.setVisibility(View.GONE); + return rootView; + } + + public void initialized(List skuDetailsList, HashMap map, BillingClient bc) { + binding.loader.setVisibility(View.GONE); + binding.lvPurchases.setVisibility(View.VISIBLE); + + DonationHistoryAdapter donationHistoryAdapter = new DonationHistoryAdapter(skuDetailsList, map, bc); + binding.lvPurchases.setAdapter(donationHistoryAdapter); + binding.lvPurchases.setLayoutManager(new LinearLayoutManager(context)); + + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + rootView = null; + } + + @Override + public void onCreate(Bundle saveInstance) { + super.onCreate(saveInstance); + } + + @Override + public void onAttach(@NonNull Context context) { + super.onAttach(context); + this.context = context; + } +} diff --git a/app/src/google_donation/res/layout/activity_donation.xml b/app/src/google_donation/res/layout/activity_donation.xml new file mode 100644 index 0000000..9c07a5a --- /dev/null +++ b/app/src/google_donation/res/layout/activity_donation.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/google_donation/res/layout/drawer_donation.xml b/app/src/google_donation/res/layout/drawer_donation.xml new file mode 100644 index 0000000..4f8fff0 --- /dev/null +++ b/app/src/google_donation/res/layout/drawer_donation.xml @@ -0,0 +1,23 @@ + + + +