Merge branch 'donation_google' into develop
# Conflicts: # .gitignore
This commit is contained in:
commit
fd400f025e
|
@ -12,3 +12,4 @@
|
||||||
/app/google_full/
|
/app/google_full/
|
||||||
/app/google_acad/
|
/app/google_acad/
|
||||||
/app/src/google_donation/res/values/data.xml
|
/app/src/google_donation/res/values/data.xml
|
||||||
|
/keystore.properties
|
||||||
|
|
|
@ -1,7 +1,21 @@
|
||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
apply plugin: "androidx.navigation.safeargs"
|
apply plugin: "androidx.navigation.safeargs"
|
||||||
|
|
||||||
|
|
||||||
|
def keystorePropertiesFile = rootProject.file("keystore.properties")
|
||||||
|
def keystoreProperties = new Properties()
|
||||||
|
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|
signingConfigs {
|
||||||
|
full_version {
|
||||||
|
storeFile file(keystoreProperties['storeFile'])
|
||||||
|
storePassword keystoreProperties['storePassword']
|
||||||
|
keyPassword keystoreProperties['keyPassword']
|
||||||
|
keyAlias keystoreProperties['keyAlias']
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
compileSdkVersion 30
|
compileSdkVersion 30
|
||||||
buildToolsVersion "30.0.2"
|
buildToolsVersion "30.0.2"
|
||||||
|
|
||||||
|
@ -9,8 +23,8 @@ android {
|
||||||
|
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 30
|
targetSdkVersion 30
|
||||||
versionCode 34
|
versionCode 35
|
||||||
versionName "1.11.0"
|
versionName "1.11.1-test"
|
||||||
multiDexEnabled true
|
multiDexEnabled true
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
|
@ -22,6 +36,7 @@ android {
|
||||||
release {
|
release {
|
||||||
minifyEnabled false
|
minifyEnabled false
|
||||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
|
signingConfig signingConfigs.full_version
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
compileOptions {
|
compileOptions {
|
||||||
|
@ -117,27 +132,27 @@ android {
|
||||||
sourceSets {
|
sourceSets {
|
||||||
fdroid_peertube_apps_educ {
|
fdroid_peertube_apps_educ {
|
||||||
res.srcDirs = ['src/main/res', 'src/acad/res']
|
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 {
|
google_peertube_apps_educ {
|
||||||
res.srcDirs = ['src/main/res', 'src/acad/res']
|
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 {
|
fdroid_full {
|
||||||
res.srcDirs = ['src/main/res', 'src/full/res']
|
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 {
|
google_full {
|
||||||
res.srcDirs = ['src/main/res', 'src/full/res']
|
res.srcDirs = ['src/main/res', 'src/full/res', 'src/google_donation/res']
|
||||||
java.srcDirs = ['src/main/java', 'src/full/java']
|
java.srcDirs = ['src/main/java', 'src/full/java', 'src/google_donation/java']
|
||||||
}
|
}
|
||||||
queermotion {
|
queermotion {
|
||||||
res.srcDirs = ['src/main/res', 'src/queermotion/res']
|
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 {
|
bittube {
|
||||||
res.srcDirs = ['src/main/res', 'src/bittube/res']
|
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 'com.fasterxml.jackson.core:jackson-core:2.12.0'
|
||||||
implementation 'org.slf4j:slf4j-simple:1.7.30'
|
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'
|
fdroid_peertube_apps_educImplementation 'org.matomo.sdk:tracker:4.1.2'
|
||||||
google_peertube_apps_educImplementation 'org.matomo.sdk:tracker:4.1.2'
|
google_peertube_apps_educImplementation 'org.matomo.sdk:tracker:4.1.2'
|
||||||
|
|
|
@ -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 <http://www.gnu.org/licenses>. */
|
||||||
|
|
||||||
|
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<Purchase> 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<Purchase> queryPurchases() {
|
||||||
|
Purchase.PurchasesResult purchasesResult = billingClient.queryPurchases(BillingClient.SkuType.SUBS);
|
||||||
|
List<Purchase> purchases = purchasesResult.getPurchasesList();
|
||||||
|
List<String> isSubscriptions = new ArrayList<>();
|
||||||
|
HashMap<String, Purchase> 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<Purchase> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses>. */
|
||||||
|
|
||||||
|
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<RecyclerView.ViewHolder> {
|
||||||
|
|
||||||
|
private final List<SkuDetails> skuDetails;
|
||||||
|
private final BillingClient billingClient;
|
||||||
|
private Context context;
|
||||||
|
private final boolean isSubscription;
|
||||||
|
|
||||||
|
public DonationButtonAdapter(List<SkuDetails> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses>. */
|
||||||
|
|
||||||
|
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<RecyclerView.ViewHolder> {
|
||||||
|
|
||||||
|
private final List<SkuDetails> skuDetailsList;
|
||||||
|
private final BillingClient billingClient;
|
||||||
|
private Context context;
|
||||||
|
private final HashMap<String, Purchase> map;
|
||||||
|
|
||||||
|
public DonationHistoryAdapter(List<SkuDetails> SkuDetailsList, HashMap<String, Purchase> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses>. */
|
||||||
|
|
||||||
|
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<String> 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses>. */
|
||||||
|
|
||||||
|
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<SkuDetails> skuDetailsList, HashMap<String, Purchase> 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
|
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 <http://www.gnu.org/licenses>.
|
||||||
|
-->
|
||||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
tools:context=".ShowChannelActivity">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
app:theme="@style/ThemeOverlay.AppCompat.ActionBar">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.CollapsingToolbarLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
app:contentScrim="?attr/colorPrimary"
|
||||||
|
app:expandedTitleMarginEnd="64dp"
|
||||||
|
app:expandedTitleMarginStart="48dp"
|
||||||
|
app:layout_scrollFlags="scroll|exitUntilCollapsed">
|
||||||
|
|
||||||
|
|
||||||
|
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.tabs.TabLayout
|
||||||
|
android:id="@+id/tablayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:tabGravity="fill"
|
||||||
|
app:tabMode="fixed"
|
||||||
|
app:tabSelectedTextColor="?colorAccent"
|
||||||
|
app:tabTextColor="@android:color/white" />
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
<androidx.viewpager.widget.ViewPager
|
||||||
|
android:id="@+id/viewpager"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="5dp"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/button_donation"
|
||||||
|
style="@style/Widget.AppCompat.Button.Colored"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:paddingStart="40dp"
|
||||||
|
android:paddingTop="15dp"
|
||||||
|
android:paddingEnd="40dp"
|
||||||
|
android:paddingBottom="15dp"
|
||||||
|
android:textSize="25sp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
|
||||||
|
android:layout_margin="10dp">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="10dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/product_title"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/cancel_donation"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/product_name"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/cancel_donation"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/product_title" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/product_info"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/cancel_donation"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/product_name" />
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/cancel_donation"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:contentDescription="@string/cancel"
|
||||||
|
android:src="@drawable/ic_baseline_delete_24"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
|
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 <http://www.gnu.org/licenses>.
|
||||||
|
-->
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:paddingLeft="@dimen/fab_margin"
|
||||||
|
android:paddingRight="@dimen/fab_margin">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="20dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:id="@+id/donation_text" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/lv_products"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:divider="@null"
|
||||||
|
android:scrollbars="none" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- Main Loader -->
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/loader"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center"
|
||||||
|
android:visibility="gone">
|
||||||
|
|
||||||
|
<com.github.ybq.android.spinkit.SpinKitView xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
style="@style/progress"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
app:SpinKit_Color="?colorAccent" />
|
||||||
|
</RelativeLayout>
|
||||||
|
</RelativeLayout>
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
|
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 <http://www.gnu.org/licenses>.
|
||||||
|
-->
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:paddingLeft="@dimen/fab_margin"
|
||||||
|
android:paddingRight="@dimen/fab_margin">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="20dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/donations_description" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/lv_purchases"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:divider="@null"
|
||||||
|
android:scrollbars="none" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- Main Loader -->
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/loader"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center"
|
||||||
|
android:visibility="gone">
|
||||||
|
|
||||||
|
<com.github.ybq.android.spinkit.SpinKitView xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
style="@style/progress"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
app:SpinKit_Color="?colorAccent" />
|
||||||
|
</RelativeLayout>
|
||||||
|
</RelativeLayout>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
package="app.fedilab.fedilabtube">
|
package="app.fedilab.fedilabtube">
|
||||||
|
|
||||||
|
<uses-permission android:name="com.android.vending.BILLING" />
|
||||||
<application
|
<application
|
||||||
android:name=".FedilabTube"
|
android:name=".FedilabTube"
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
|
@ -30,5 +30,10 @@
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".DonationActivity"
|
||||||
|
android:configChanges="orientation|screenSize"
|
||||||
|
android:label="@string/support_the_app"
|
||||||
|
android:windowSoftInputMode="stateAlwaysHidden" />
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
|
@ -687,7 +687,11 @@ public class MainActivity extends AppCompatActivity implements ChromeCastsListen
|
||||||
MenuItem incognitoItem = menu.findItem(R.id.action_incognito);
|
MenuItem incognitoItem = menu.findItem(R.id.action_incognito);
|
||||||
MenuItem instanceItem = menu.findItem(R.id.action_change_instance);
|
MenuItem instanceItem = menu.findItem(R.id.action_change_instance);
|
||||||
MenuItem accountItem = menu.findItem(R.id.action_account);
|
MenuItem accountItem = menu.findItem(R.id.action_account);
|
||||||
|
MenuItem donateItem = menu.findItem(R.id.action_donate);
|
||||||
|
|
||||||
|
if (BuildConfig.full_instances && BuildConfig.google_restriction) {
|
||||||
|
donateItem.setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
if (BuildConfig.surfing_mode && ((Helper.isLoggedIn(MainActivity.this) && typeOfConnection == NORMAL) || typeOfConnection == SURFING)) {
|
if (BuildConfig.surfing_mode && ((Helper.isLoggedIn(MainActivity.this) && typeOfConnection == NORMAL) || typeOfConnection == SURFING)) {
|
||||||
binding.instances.setVisibility(View.VISIBLE);
|
binding.instances.setVisibility(View.VISIBLE);
|
||||||
|
@ -847,6 +851,9 @@ public class MainActivity extends AppCompatActivity implements ChromeCastsListen
|
||||||
} else if (item.getItemId() == R.id.action_about) {
|
} else if (item.getItemId() == R.id.action_about) {
|
||||||
Intent intent = new Intent(MainActivity.this, AboutActivity.class);
|
Intent intent = new Intent(MainActivity.this, AboutActivity.class);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
|
} else if (item.getItemId() == R.id.action_donate) {
|
||||||
|
Intent intent = new Intent(MainActivity.this, DonationActivity.class);
|
||||||
|
startActivity(intent);
|
||||||
} else if (item.getItemId() == R.id.action_incognito) {
|
} else if (item.getItemId() == R.id.action_incognito) {
|
||||||
item.setChecked(!item.isChecked());
|
item.setChecked(!item.isChecked());
|
||||||
final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
|
final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="?attr/colorControlNormal"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M11.8,10.9c-2.27,-0.59 -3,-1.2 -3,-2.15 0,-1.09 1.01,-1.85 2.7,-1.85 1.78,0 2.44,0.85 2.5,2.1h2.21c-0.07,-1.72 -1.12,-3.3 -3.21,-3.81V3h-3v2.16c-1.94,0.42 -3.5,1.68 -3.5,3.61 0,2.31 1.91,3.46 4.7,4.13 2.5,0.6 3,1.48 3,2.41 0,0.69 -0.49,1.79 -2.7,1.79 -2.06,0 -2.87,-0.92 -2.98,-2.1h-2.2c0.12,2.19 1.76,3.42 3.68,3.83V21h3v-2.15c1.95,-0.37 3.5,-1.5 3.5,-3.55 0,-2.84 -2.43,-3.81 -4.7,-4.4z" />
|
||||||
|
</vector>
|
|
@ -67,4 +67,10 @@
|
||||||
android:icon="@drawable/ic_baseline_info_24"
|
android:icon="@drawable/ic_baseline_info_24"
|
||||||
android:title="@string/about_the_app"
|
android:title="@string/about_the_app"
|
||||||
app:showAsAction="ifRoom" />
|
app:showAsAction="ifRoom" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_donate"
|
||||||
|
android:icon="@drawable/ic_baseline_attach_money_24"
|
||||||
|
android:title="@string/make_a_donation"
|
||||||
|
android:visible="false"
|
||||||
|
app:showAsAction="ifRoom" />
|
||||||
</menu>
|
</menu>
|
||||||
|
|
|
@ -455,5 +455,21 @@
|
||||||
<string name="watermark">Watermark</string>
|
<string name="watermark">Watermark</string>
|
||||||
<string name="toast_code_error">An error occurred! The instance did not return an authorisation code!</string>
|
<string name="toast_code_error">An error occurred! The instance did not return an authorisation code!</string>
|
||||||
<string name="remote_account_from"><b>%1$s</b> remote account connected with the app.\n\nYou can proceed to some limited actions.</string>
|
<string name="remote_account_from"><b>%1$s</b> remote account connected with the app.\n\nYou can proceed to some limited actions.</string>
|
||||||
|
<string name="donate">Donate</string>
|
||||||
|
<string name="my_donations">My donations</string>
|
||||||
|
<string name="one_time_donation_text">Here, you can make a one time donation for supporting the development of the app. This action will not bring extra features!</string>
|
||||||
|
<string name="recurrent_donation_text">Here, you can make a recurrent donation for supporting the development of the app. This action will not bring extra features!</string>
|
||||||
|
<string name="make_a_donation">Make a donation</string>
|
||||||
|
<string name="donations_description">Here you will find the list of your recurrent donations made to support the development of the app! Thank you!</string>
|
||||||
|
<string name="support_the_app">Support the app</string>
|
||||||
|
<string name="donation_cancelled">Donation has been cancelled!</string>
|
||||||
|
<string name="donation_succeeded_null">Thank you for your donation!</string>
|
||||||
|
<string name="donation_succeeded">Thank you for your donation of %1$s!</string>
|
||||||
|
<string name="one_time">One time</string>
|
||||||
|
<string name="my_subscriptions">My subscription</string>
|
||||||
|
<string name="month">Month</string>
|
||||||
|
<string name="subscription_cancelled">Subscription cancelled!</string>
|
||||||
|
<string name="cancel_subscription">Cancel subscription</string>
|
||||||
|
<string name="cancel_subscription_confirm">Are you sure, you want to cancel that subscription?</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
|
@ -0,0 +1,20 @@
|
||||||
|
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 <http://www.gnu.org/licenses>. */
|
||||||
|
|
||||||
|
//Do nothing
|
||||||
|
public class DonationActivity {
|
||||||
|
|
||||||
|
}
|
|
@ -20,7 +20,19 @@ apply plugin: 'de.undercouch.download'
|
||||||
|
|
||||||
group='com.github.TorrentStream'
|
group='com.github.TorrentStream'
|
||||||
|
|
||||||
|
def keystorePropertiesFile = rootProject.file("keystore.properties")
|
||||||
|
def keystoreProperties = new Properties()
|
||||||
|
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|
signingConfigs {
|
||||||
|
full_version {
|
||||||
|
storeFile file(keystoreProperties['storeFile'])
|
||||||
|
storePassword keystoreProperties['storePassword']
|
||||||
|
keyAlias keystoreProperties['keyAlias']
|
||||||
|
keyPassword keystoreProperties['keyPassword']
|
||||||
|
}
|
||||||
|
}
|
||||||
compileSdkVersion 30
|
compileSdkVersion 30
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
|
@ -32,6 +44,7 @@ android {
|
||||||
release {
|
release {
|
||||||
minifyEnabled false
|
minifyEnabled false
|
||||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||||
|
signingConfig signingConfigs.full_version
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue