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: