diff --git a/app/src/main/java/eu/faircode/email/ActivityView.java b/app/src/main/java/eu/faircode/email/ActivityView.java
index fd36dfa5..dd339f08 100644
--- a/app/src/main/java/eu/faircode/email/ActivityView.java
+++ b/app/src/main/java/eu/faircode/email/ActivityView.java
@@ -24,12 +24,14 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.preference.PreferenceManager;
+import android.provider.Settings;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MenuItem;
@@ -42,11 +44,14 @@ import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
+import com.google.android.material.snackbar.Snackbar;
+
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
+import java.security.NoSuchAlgorithmException;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
@@ -68,6 +73,7 @@ import androidx.lifecycle.Observer;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
public class ActivityView extends ActivityBase implements FragmentManager.OnBackStackChangedListener {
+ private View view;
private DrawerLayout drawerLayout;
private ListView drawerList;
private ActionBarDrawerToggle drawerToggle;
@@ -83,12 +89,14 @@ public class ActivityView extends ActivityBase implements FragmentManager.OnBack
static final String ACTION_VIEW_MESSAGE = BuildConfig.APPLICATION_ID + ".VIEW_MESSAGE";
static final String ACTION_EDIT_FOLDER = BuildConfig.APPLICATION_ID + ".EDIT_FOLDER";
static final String ACTION_STORE_ATTACHMENT = BuildConfig.APPLICATION_ID + ".STORE_ATTACHMENT";
+ static final String ACTION_ACTIVATE_PRO = BuildConfig.APPLICATION_ID + ".ACTIVATE_PRO";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_view);
+ view = LayoutInflater.from(this).inflate(R.layout.activity_view, null);
+ setContentView(view);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(true);
@@ -130,6 +138,9 @@ public class ActivityView extends ActivityBase implements FragmentManager.OnBack
case R.string.menu_faq:
onMenuFAQ();
break;
+ case R.string.menu_pro:
+ onMenuPro();
+ break;
case R.string.menu_privacy:
onMenuPrivacy();
break;
@@ -180,6 +191,10 @@ public class ActivityView extends ActivityBase implements FragmentManager.OnBack
if (getIntentFAQ().resolveActivity(getPackageManager()) != null)
drawerArray.add(new DrawerItem(ActivityView.this, R.layout.item_drawer, R.drawable.baseline_question_answer_24, R.string.menu_faq));
+ Intent pro = getIntentPro();
+ if (pro == null || pro.resolveActivity(getPackageManager()) != null)
+ drawerArray.add(new DrawerItem(ActivityView.this, R.layout.item_drawer, R.drawable.baseline_monetization_on_24, R.string.menu_pro));
+
if (getIntentPrivacy().resolveActivity(getPackageManager()) != null)
drawerArray.add(new DrawerItem(ActivityView.this, R.layout.item_drawer, R.drawable.baseline_account_box_24, R.string.menu_privacy));
@@ -327,6 +342,7 @@ public class ActivityView extends ActivityBase implements FragmentManager.OnBack
iff.addAction(ACTION_VIEW_MESSAGE);
iff.addAction(ACTION_EDIT_FOLDER);
iff.addAction(ACTION_STORE_ATTACHMENT);
+ iff.addAction(ACTION_ACTIVATE_PRO);
lbm.registerReceiver(receiver, iff);
if (newIntent) {
@@ -388,13 +404,22 @@ public class ActivityView extends ActivityBase implements FragmentManager.OnBack
}
}
+ private String getChallenge() throws NoSuchAlgorithmException {
+ String android_id = Settings.System.getString(getContentResolver(), Settings.Secure.ANDROID_ID);
+ return Helper.sha256(android_id);
+ }
+
+ private String getResponse() throws NoSuchAlgorithmException {
+ return Helper.sha256(BuildConfig.APPLICATION_ID + getChallenge());
+ }
+
private void checkIntent(Intent intent) {
Log.i(Helper.TAG, "View intent=" + intent + " action=" + intent.getAction());
String action = intent.getAction();
- intent.setAction(null);
- setIntent(intent);
-
if ("unseen".equals(action)) {
+ intent.setAction(null);
+ setIntent(intent);
+
Bundle args = new Bundle();
args.putLong("time", new Date().getTime());
@@ -438,6 +463,20 @@ public class ActivityView extends ActivityBase implements FragmentManager.OnBack
return intent;
}
+ private Intent getIntentPro() {
+ if (Helper.isPlayStoreInstall(this))
+ return null;
+
+ try {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setData(Uri.parse("https://email.faircode.eu/pro/?challenge=" + getChallenge()));
+ return intent;
+ } catch (NoSuchAlgorithmException ex) {
+ Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex));
+ return null;
+ }
+ }
+
private Intent getIntentOtherApps() {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("https://play.google.com/store/apps/dev?id=8420080860664580239"));
@@ -478,6 +517,13 @@ public class ActivityView extends ActivityBase implements FragmentManager.OnBack
startActivity(getIntentFAQ());
}
+ private void onMenuPro() {
+ if (Helper.isPlayStoreInstall(this)) {
+
+ } else
+ startActivity(getIntentPro());
+ }
+
private void onMenuPrivacy() {
startActivity(getIntentPrivacy());
}
@@ -616,6 +662,31 @@ public class ActivityView extends ActivityBase implements FragmentManager.OnBack
create.setType(intent.getStringExtra("type"));
create.putExtra(Intent.EXTRA_TITLE, intent.getStringExtra("name"));
startActivityForResult(create, (int) intent.getLongExtra("id", -1));
+
+ } else if (ACTION_ACTIVATE_PRO.equals(intent.getAction())) {
+ try {
+ Uri data = intent.getParcelableExtra("uri");
+ String challenge = getChallenge();
+ String response = data.getQueryParameter("response");
+ Log.i(Helper.TAG, "Challenge=" + challenge);
+ Log.i(Helper.TAG, "Response=" + response);
+ String expected = getResponse();
+ if (expected.equals(response)) {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ActivityView.this);
+ prefs.edit().putBoolean("pro", true).apply();
+ Log.i(Helper.TAG, "Response valid");
+ Snackbar.make(view, R.string.title_pro_valid, Snackbar.LENGTH_LONG).show();
+ } else {
+ Log.i(Helper.TAG, "Response invalid");
+ Snackbar.make(view, R.string.title_pro_invalid, Snackbar.LENGTH_LONG).show();
+ }
+
+ intent.setData(null);
+ setIntent(intent);
+ } catch (NoSuchAlgorithmException ex) {
+ Log.e(Helper.TAG, Log.getStackTraceString(ex));
+ Toast.makeText(ActivityView.this, ex.getMessage(), Toast.LENGTH_LONG).show();
+ }
}
}
};
diff --git a/app/src/main/java/eu/faircode/email/FragmentMessage.java b/app/src/main/java/eu/faircode/email/FragmentMessage.java
index 0c4112f5..e2a53d67 100644
--- a/app/src/main/java/eu/faircode/email/FragmentMessage.java
+++ b/app/src/main/java/eu/faircode/email/FragmentMessage.java
@@ -82,6 +82,7 @@ import androidx.constraintlayout.widget.Group;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.Observer;
+import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@@ -187,8 +188,15 @@ public class FragmentMessage extends FragmentEx {
URLSpan[] link = buffer.getSpans(off, off, URLSpan.class);
if (link.length != 0) {
String url = link[0].getURL();
+ Uri uri = Uri.parse(url);
- if (prefs.getBoolean("webview", false)) {
+ if (BuildConfig.APPLICATION_ID.equals(uri.getHost()) && "/activate/".equals(uri.getPath())) {
+ LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(getContext());
+ lbm.sendBroadcast(
+ new Intent(ActivityView.ACTION_ACTIVATE_PRO)
+ .putExtra("uri", uri));
+
+ } else if (prefs.getBoolean("webview", false)) {
Bundle args = new Bundle();
args.putString("link", url);
diff --git a/app/src/main/java/eu/faircode/email/Helper.java b/app/src/main/java/eu/faircode/email/Helper.java
index e66e0437..eb12f8d1 100644
--- a/app/src/main/java/eu/faircode/email/Helper.java
+++ b/app/src/main/java/eu/faircode/email/Helper.java
@@ -42,6 +42,8 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
import javax.mail.Address;
import javax.mail.internet.InternetAddress;
@@ -163,4 +165,27 @@ public class Helper {
in.close();
}
}
+
+ static boolean isPlayStoreInstall(Context context) {
+ if (false && BuildConfig.DEBUG)
+ return true;
+ try {
+ return "com.android.vending".equals(context.getPackageManager().getInstallerPackageName(context.getPackageName()));
+ } catch (Throwable ex) {
+ Log.e(TAG, Log.getStackTraceString(ex));
+ return false;
+ }
+ }
+
+ static String sha256(String data) throws NoSuchAlgorithmException {
+ return sha256(data.getBytes());
+ }
+
+ static String sha256(byte[] data) throws NoSuchAlgorithmException {
+ byte[] bytes = MessageDigest.getInstance("SHA-256").digest(data);
+ StringBuilder sb = new StringBuilder();
+ for (byte b : bytes)
+ sb.append(String.format("%02x", b));
+ return sb.toString();
+ }
}
diff --git a/app/src/main/res/drawable/baseline_monetization_on_24.xml b/app/src/main/res/drawable/baseline_monetization_on_24.xml
new file mode 100644
index 00000000..5cc09e55
--- /dev/null
+++ b/app/src/main/res/drawable/baseline_monetization_on_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 99bda0f8..4ea3d588 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -35,6 +35,7 @@
Operations
Legend
FAQ
+ Pro features
Privacy
About
Other apps
@@ -175,6 +176,9 @@
Connected
Closing
+ All pro features activated
+ Invalid response
+
Debug info
Please describe the problem and indicate the time of the problem: