mirror of
https://github.com/M66B/FairEmail.git
synced 2025-02-22 14:11:00 +00:00
Added IAB
This commit is contained in:
parent
597c789ee5
commit
1cc45d6b44
7 changed files with 158 additions and 3 deletions
3
FAQ.md
3
FAQ.md
|
@ -11,7 +11,8 @@ Frequently Asked Questions
|
|||
* View network connections (ACCESS_NETWORK_STATE): to monitor internet connectivity changes
|
||||
* Run at startup (RECEIVE_BOOT_COMPLETED): to start monitoring on device start
|
||||
* Optional: read your contacts (READ_CONTACTS): to autocomplete addresses
|
||||
* ... (FOREGROUND_SERVICE): to run a foreground service on Android 9 Pie and later, see also the next question.
|
||||
* ... (BILLING): to offer in-app purchases
|
||||
* ... (FOREGROUND_SERVICE): to run a foreground service on Android 9 Pie and later, see also the next question
|
||||
|
||||
<a name="FAQ2"></a>
|
||||
**(2) Why is there a permanent notification shown?**
|
||||
|
|
|
@ -55,6 +55,8 @@ dependencies {
|
|||
// https://mvnrepository.com/artifact/androidx.room/room-runtime
|
||||
// https://mvnrepository.com/artifact/androidx.paging/paging-runtime
|
||||
|
||||
// https://developer.android.com/google/play/billing/billing_library_releases_notes
|
||||
|
||||
// https://javaee.github.io/javamail/
|
||||
// https://jsoup.org/
|
||||
// http://www.freeutils.net/source/jcharset/
|
||||
|
@ -70,6 +72,7 @@ dependencies {
|
|||
def lifecycle_version = "2.0.0-rc01"
|
||||
def room_version = "2.0.0-rc01"
|
||||
def paging_version = "2.0.0-rc01"
|
||||
def billingclient_version = "1.1"
|
||||
def javamail_version = "1.6.0"
|
||||
def jsoup_version = "1.11.3"
|
||||
def jcharset_version = "2.0"
|
||||
|
@ -88,6 +91,8 @@ dependencies {
|
|||
|
||||
implementation "androidx.paging:paging-runtime:$paging_version"
|
||||
|
||||
implementation "com.android.billingclient:billing:$billingclient_version"
|
||||
|
||||
implementation "com.sun.mail:android-mail:$javamail_version"
|
||||
implementation "com.sun.mail:android-activation:$javamail_version"
|
||||
|
||||
|
|
3
app/proguard-rules.pro
vendored
3
app/proguard-rules.pro
vendored
|
@ -27,6 +27,9 @@
|
|||
#AndroidX
|
||||
-keep class androidx.appcompat.app.AppCompatViewInflater { <init>(...); }
|
||||
|
||||
#IAB
|
||||
-keep class com.android.vending.billing.**
|
||||
|
||||
#JavaMail
|
||||
-dontshrink
|
||||
-keep class javax.** {*;}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="com.android.vending.BILLING" />
|
||||
|
||||
<application
|
||||
android:name=".ApplicationEx"
|
||||
|
|
|
@ -29,6 +29,7 @@ import android.content.res.Configuration;
|
|||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.provider.Settings;
|
||||
|
@ -44,6 +45,11 @@ import android.widget.ListView;
|
|||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.android.billingclient.api.BillingClient;
|
||||
import com.android.billingclient.api.BillingClientStateListener;
|
||||
import com.android.billingclient.api.BillingFlowParams;
|
||||
import com.android.billingclient.api.Purchase;
|
||||
import com.android.billingclient.api.PurchasesUpdatedListener;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
|
@ -72,11 +78,12 @@ import androidx.lifecycle.Lifecycle;
|
|||
import androidx.lifecycle.Observer;
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
|
||||
public class ActivityView extends ActivityBase implements FragmentManager.OnBackStackChangedListener {
|
||||
public class ActivityView extends ActivityBase implements FragmentManager.OnBackStackChangedListener, PurchasesUpdatedListener {
|
||||
private View view;
|
||||
private DrawerLayout drawerLayout;
|
||||
private ListView drawerList;
|
||||
private ActionBarDrawerToggle drawerToggle;
|
||||
private BillingClient billingClient;
|
||||
|
||||
private boolean newIntent = false;
|
||||
|
||||
|
@ -307,6 +314,9 @@ public class ActivityView extends ActivityBase implements FragmentManager.OnBack
|
|||
}
|
||||
}.load(this, new Bundle());
|
||||
|
||||
billingClient = BillingClient.newBuilder(this).setListener(this).build();
|
||||
billingClient.startConnection(billingClientStateListener);
|
||||
|
||||
checkIntent(getIntent());
|
||||
}
|
||||
|
||||
|
@ -349,6 +359,9 @@ public class ActivityView extends ActivityBase implements FragmentManager.OnBack
|
|||
newIntent = false;
|
||||
getSupportFragmentManager().popBackStack("unified", 0);
|
||||
}
|
||||
|
||||
if (billingClient.isReady())
|
||||
queryPurchases();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -366,6 +379,7 @@ public class ActivityView extends ActivityBase implements FragmentManager.OnBack
|
|||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
billingClient.endConnection();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
|
@ -518,8 +532,22 @@ public class ActivityView extends ActivityBase implements FragmentManager.OnBack
|
|||
}
|
||||
|
||||
private void onMenuPro() {
|
||||
if (Helper.isPlayStoreInstall(this)) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
if (prefs.getBoolean("pro", false)) {
|
||||
Snackbar.make(view, R.string.title_pro_activated, Snackbar.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (Helper.isPlayStoreInstall(this)) {
|
||||
BillingFlowParams flowParams = BillingFlowParams.newBuilder()
|
||||
.setSku(BuildConfig.APPLICATION_ID + ".pro")
|
||||
.setType(BillingClient.SkuType.INAPP)
|
||||
.build();
|
||||
int responseCode = billingClient.launchBillingFlow(ActivityView.this, flowParams);
|
||||
String text = Helper.getBillingResponseText(responseCode);
|
||||
Log.i(Helper.TAG, "IAB launch billing flow response=" + text);
|
||||
if (responseCode != BillingClient.BillingResponse.OK)
|
||||
Snackbar.make(view, text, Snackbar.LENGTH_LONG).show();
|
||||
} else
|
||||
startActivity(getIntentPro());
|
||||
}
|
||||
|
@ -755,4 +783,68 @@ public class ActivityView extends ActivityBase implements FragmentManager.OnBack
|
|||
}.load(this, args);
|
||||
}
|
||||
}
|
||||
|
||||
private BillingClientStateListener billingClientStateListener = new BillingClientStateListener() {
|
||||
private int backoff = 4; // seconds
|
||||
|
||||
@Override
|
||||
public void onBillingSetupFinished(@BillingClient.BillingResponse int responseCode) {
|
||||
String text = Helper.getBillingResponseText(responseCode);
|
||||
Log.i(Helper.TAG, "IAB connected response=" + text);
|
||||
if (responseCode == BillingClient.BillingResponse.OK) {
|
||||
backoff = 4;
|
||||
queryPurchases();
|
||||
} else
|
||||
Snackbar.make(view, text, Snackbar.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBillingServiceDisconnected() {
|
||||
backoff *= 2;
|
||||
Log.i(Helper.TAG, "IAB disconnected retry in " + backoff + " s");
|
||||
new Handler().postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!billingClient.isReady())
|
||||
billingClient.startConnection(billingClientStateListener);
|
||||
}
|
||||
}, backoff * 1000L);
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onPurchasesUpdated(int responseCode, @android.support.annotation.Nullable List<Purchase> purchases) {
|
||||
String text = Helper.getBillingResponseText(responseCode);
|
||||
Log.i(Helper.TAG, "IAB purchases updated response=" + text);
|
||||
if (responseCode == BillingClient.BillingResponse.OK)
|
||||
checkPurchases(purchases);
|
||||
else
|
||||
Snackbar.make(view, text, Snackbar.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
private void queryPurchases() {
|
||||
Purchase.PurchasesResult result = billingClient.queryPurchases(BillingClient.SkuType.INAPP);
|
||||
String text = Helper.getBillingResponseText(result.getResponseCode());
|
||||
Log.i(Helper.TAG, "IAB query purchases response=" + text);
|
||||
if (result.getResponseCode() == BillingClient.BillingResponse.OK)
|
||||
checkPurchases(result.getPurchasesList());
|
||||
else
|
||||
Snackbar.make(view, text, Snackbar.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
private void checkPurchases(List<Purchase> purchases) {
|
||||
if (purchases != null) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
editor.remove("pro");
|
||||
for (Purchase purchase : purchases) {
|
||||
Log.i(Helper.TAG, "IAB SKU=" + purchase.getSku());
|
||||
if ((BuildConfig.APPLICATION_ID + ".pro").equals(purchase.getSku())) {
|
||||
editor.putBoolean("pro", true);
|
||||
Log.i(Helper.TAG, "IAB pro features activated");
|
||||
}
|
||||
}
|
||||
editor.apply();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import android.widget.EditText;
|
|||
import android.widget.ImageView;
|
||||
import android.widget.Spinner;
|
||||
|
||||
import com.android.billingclient.api.BillingClient;
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
|
@ -188,4 +189,55 @@ public class Helper {
|
|||
sb.append(String.format("%02x", b));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
static String getBillingResponseText(@BillingClient.BillingResponse int responseCode) {
|
||||
switch (responseCode) {
|
||||
case BillingClient.BillingResponse.BILLING_UNAVAILABLE:
|
||||
// Billing API version is not supported for the type requested
|
||||
return "BILLING_UNAVAILABLE";
|
||||
|
||||
case BillingClient.BillingResponse.DEVELOPER_ERROR:
|
||||
// Invalid arguments provided to the API.
|
||||
return "DEVELOPER_ERROR";
|
||||
|
||||
case BillingClient.BillingResponse.ERROR:
|
||||
// Fatal error during the API action
|
||||
return "ERROR";
|
||||
|
||||
case BillingClient.BillingResponse.FEATURE_NOT_SUPPORTED:
|
||||
// Requested feature is not supported by Play Store on the current device.
|
||||
return "FEATURE_NOT_SUPPORTED";
|
||||
|
||||
case BillingClient.BillingResponse.ITEM_ALREADY_OWNED:
|
||||
// Failure to purchase since item is already owned
|
||||
return "ITEM_ALREADY_OWNED";
|
||||
|
||||
case BillingClient.BillingResponse.ITEM_NOT_OWNED:
|
||||
// Failure to consume since item is not owned
|
||||
return "ITEM_NOT_OWNED";
|
||||
|
||||
case BillingClient.BillingResponse.ITEM_UNAVAILABLE:
|
||||
// Requested product is not available for purchase
|
||||
return "ITEM_UNAVAILABLE";
|
||||
|
||||
case BillingClient.BillingResponse.OK:
|
||||
// Success
|
||||
return "OK";
|
||||
|
||||
case BillingClient.BillingResponse.SERVICE_DISCONNECTED:
|
||||
// Play Store service is not connected now - potentially transient state.
|
||||
return "SERVICE_DISCONNECTED";
|
||||
|
||||
case BillingClient.BillingResponse.SERVICE_UNAVAILABLE:
|
||||
// Network connection is down
|
||||
return "SERVICE_UNAVAILABLE";
|
||||
|
||||
case BillingClient.BillingResponse.USER_CANCELED:
|
||||
// User pressed back or canceled a dialog
|
||||
return "USER_CANCELED";
|
||||
|
||||
default:
|
||||
return Integer.toString(responseCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -176,6 +176,7 @@
|
|||
<string name="title_legend_connected">Connected</string>
|
||||
<string name="title_legend_closing">Closing</string>
|
||||
|
||||
<string name="title_pro_activated">All pro features are activated</string>
|
||||
<string name="title_pro_valid">All pro features activated</string>
|
||||
<string name="title_pro_invalid">Invalid response</string>
|
||||
|
||||
|
|
Loading…
Reference in a new issue