diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index cc29b586ea..0159c99a75 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -94,6 +94,12 @@
+
+
diff --git a/app/src/main/java/eu/faircode/email/ActivitySignature.java b/app/src/main/java/eu/faircode/email/ActivitySignature.java
new file mode 100644
index 0000000000..ae8b312763
--- /dev/null
+++ b/app/src/main/java/eu/faircode/email/ActivitySignature.java
@@ -0,0 +1,232 @@
+package eu.faircode.email;
+
+/*
+ This file is part of FairEmail.
+
+ FairEmail 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.
+
+ FairEmail 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 FairEmail. If not, see .
+
+ Copyright 2018-2020 by Marcel Bokhorst (M66B)
+*/
+
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.text.Html;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.style.ImageSpan;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
+
+import com.google.android.material.bottomnavigation.BottomNavigationView;
+
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+
+public class ActivitySignature extends ActivityBase {
+ private EditTextCompose etText;
+ private BottomNavigationView style_bar;
+ private BottomNavigationView bottom_navigation;
+
+ private static final int REQUEST_IMAGE = 1;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ getSupportActionBar().setSubtitle(getString(R.string.title_edit_signature));
+ setContentView(R.layout.activity_signature);
+
+ etText = findViewById(R.id.etText);
+ style_bar = findViewById(R.id.style_bar);
+ bottom_navigation = findViewById(R.id.bottom_navigation);
+
+ etText.setSelectionListener(new EditTextCompose.ISelection() {
+ @Override
+ public void onSelected(boolean selection) {
+ style_bar.setVisibility(selection ? View.VISIBLE : View.GONE);
+ }
+ });
+
+ style_bar.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
+ @Override
+ public boolean onNavigationItemSelected(@NonNull MenuItem item) {
+ return onActionStyle(item.getItemId());
+ }
+ });
+
+ bottom_navigation.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
+ @Override
+ public boolean onNavigationItemSelected(@NonNull MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.action_insert_image:
+ insertImage();
+ return true;
+ case R.id.action_delete:
+ delete();
+ return true;
+ case R.id.action_save:
+ save();
+ return true;
+ default:
+ return false;
+ }
+ }
+ });
+
+ style_bar.setVisibility(View.GONE);
+
+ setResult(RESULT_CANCELED, new Intent());
+
+ load();
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ setIntent(intent);
+ load();
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ try {
+ switch (requestCode) {
+ case REQUEST_IMAGE:
+ if (resultCode == RESULT_OK && data != null)
+ onImageSelected(data.getData());
+ break;
+ }
+ } catch (Throwable ex) {
+ Log.e(ex);
+ }
+ }
+
+ private void load() {
+ String html = getIntent().getStringExtra("html");
+ if (html == null)
+ etText.setText(null);
+ else
+ etText.setText(HtmlHelper.fromHtml(html, new Html.ImageGetter() {
+ @Override
+ public Drawable getDrawable(String source) {
+ return getDrawableByUri(Uri.parse(source));
+ }
+ }, null));
+ }
+
+ private void delete() {
+ Intent result = new Intent();
+ result.putExtra("html", (String) null);
+ setResult(RESULT_OK, result);
+ finish();
+ }
+
+ private void save() {
+ etText.clearComposingText();
+ String html = HtmlHelper.toHtml(etText.getText());
+ Intent result = new Intent();
+ result.putExtra("html", html);
+ setResult(RESULT_OK, result);
+ finish();
+ }
+
+ private void insertImage() {
+ Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
+ intent.setType("image/*");
+ Helper.openAdvanced(intent);
+ startActivityForResult(intent, REQUEST_IMAGE);
+ }
+
+ private boolean onActionStyle(int action) {
+ Log.i("Style action=" + action);
+
+ if (action == R.id.menu_link) {
+ Uri uri = null;
+
+ ClipboardManager cbm = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
+ if (cbm != null && cbm.hasPrimaryClip()) {
+ String link = cbm.getPrimaryClip().getItemAt(0).coerceToText(this).toString();
+ uri = Uri.parse(link);
+ if (uri.getScheme() == null)
+ uri = null;
+ }
+
+ View view = LayoutInflater.from(this).inflate(R.layout.dialog_insert_link, null);
+ EditText etLink = view.findViewById(R.id.etLink);
+ TextView tvInsecure = view.findViewById(R.id.tvInsecure);
+
+ etLink.setText(uri == null ? "https://" : uri.toString());
+ tvInsecure.setVisibility(View.GONE);
+
+ new AlertDialog.Builder(this)
+ .setView(view)
+ .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ String link = etLink.getText().toString();
+ StyleHelper.apply(R.id.menu_link, etText, link);
+ }
+ })
+ .setNegativeButton(android.R.string.cancel, null)
+ .show();
+
+ return true;
+ } else
+ return StyleHelper.apply(action, etText);
+ }
+
+ private void onImageSelected(Uri uri) {
+ getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+ int start = etText.getSelectionStart();
+ SpannableStringBuilder ssb = new SpannableStringBuilder(etText.getText());
+ ssb.insert(start, " ");
+ ImageSpan is = new ImageSpan(getDrawableByUri(uri), uri.toString(), ImageSpan.ALIGN_BASELINE);
+ ssb.setSpan(is, start, start + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ etText.setText(ssb);
+ }
+
+ private Drawable getDrawableByUri(Uri uri) {
+ Drawable d;
+ try {
+ Log.i("Loading image source=" + uri);
+ InputStream inputStream = getContentResolver().openInputStream(uri);
+ d = Drawable.createFromStream(inputStream, uri.toString());
+ } catch (FileNotFoundException ex) {
+ Log.w(ex);
+ d = getResources().getDrawable(R.drawable.baseline_broken_image_24);
+ }
+
+ int w = Helper.dp2pixels(this, d.getIntrinsicWidth());
+ int h = Helper.dp2pixels(this, d.getIntrinsicHeight());
+
+ d.setBounds(0, 0, w, h);
+ return d;
+ }
+}
diff --git a/app/src/main/java/eu/faircode/email/FragmentIdentity.java b/app/src/main/java/eu/faircode/email/FragmentIdentity.java
index 26a89d686e..3b4a15e0be 100644
--- a/app/src/main/java/eu/faircode/email/FragmentIdentity.java
+++ b/app/src/main/java/eu/faircode/email/FragmentIdentity.java
@@ -21,17 +21,14 @@ package eu.faircode.email;
import android.accounts.Account;
import android.accounts.AccountManager;
-import android.app.Dialog;
import android.app.NotificationManager;
import android.content.Context;
-import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.text.Editable;
-import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.method.LinkMovementMethod;
@@ -57,7 +54,6 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.appcompat.app.AlertDialog;
import androidx.constraintlayout.widget.Group;
import androidx.lifecycle.Lifecycle;
@@ -86,8 +82,7 @@ public class FragmentIdentity extends FragmentBase {
private EditText etDisplay;
private ViewButtonColor btnColor;
private TextView tvColorPro;
- private EditText etSignature;
- private Button btnHtml;
+ private Button btnSignature;
private Button btnAdvanced;
private Spinner spProvider;
@@ -135,12 +130,13 @@ public class FragmentIdentity extends FragmentBase {
private int auth = EmailService.AUTH_TYPE_PASSWORD;
private String provider = null;
private String certificate = null;
+ private String signature = null;
private boolean saving = false;
private static final int REQUEST_COLOR = 1;
private static final int REQUEST_SAVE = 2;
private static final int REQUEST_DELETE = 3;
- private static final int REQUEST_HTML = 4;
+ private static final int REQUEST_SIGNATURE = 4;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -172,8 +168,7 @@ public class FragmentIdentity extends FragmentBase {
etDisplay = view.findViewById(R.id.etDisplay);
btnColor = view.findViewById(R.id.btnColor);
tvColorPro = view.findViewById(R.id.tvColorPro);
- etSignature = view.findViewById(R.id.etSignature);
- btnHtml = view.findViewById(R.id.btnHtml);
+ btnSignature = view.findViewById(R.id.btnSignature);
btnAdvanced = view.findViewById(R.id.btnAdvanced);
spProvider = view.findViewById(R.id.spProvider);
@@ -337,36 +332,12 @@ public class FragmentIdentity extends FragmentBase {
Helper.linkPro(tvColorPro);
- etSignature.addTextChangedListener(new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- }
-
- @Override
- public void afterTextChanged(Editable editable) {
- SpannableStringBuilder ssb = new SpannableStringBuilder(editable);
- Helper.clearComposingText(ssb);
- if (TextUtils.isEmpty(editable.toString()))
- etSignature.setTag(null);
- else
- etSignature.setTag(HtmlHelper.toHtml(ssb));
- }
- });
-
- btnHtml.setOnClickListener(new View.OnClickListener() {
+ btnSignature.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- Bundle args = new Bundle();
- args.putString("html", (String) etSignature.getTag());
-
- FragmentDialogHtml fragment = new FragmentDialogHtml();
- fragment.setArguments(args);
- fragment.setTargetFragment(FragmentIdentity.this, REQUEST_HTML);
- fragment.show(getParentFragmentManager(), "identity:html");
+ Intent intent = new Intent(getContext(), ActivitySignature.class);
+ intent.putExtra("html", signature);
+ startActivityForResult(intent, REQUEST_SIGNATURE);
}
});
@@ -589,8 +560,6 @@ public class FragmentIdentity extends FragmentBase {
name = hint.toString();
}
- etSignature.clearComposingText();
-
Bundle args = new Bundle();
args.putLong("id", id);
args.putString("name", name);
@@ -614,7 +583,7 @@ public class FragmentIdentity extends FragmentBase {
args.putString("realm", etRealm.getText().toString());
args.putString("fingerprint", cbTrust.isChecked() ? (String) cbTrust.getTag() : null);
args.putBoolean("use_ip", cbUseIp.isChecked());
- args.putString("signature", (String) etSignature.getTag());
+ args.putString("signature", signature);
args.putBoolean("synchronize", cbSynchronize.isChecked());
args.putBoolean("primary", cbPrimary.isChecked());
@@ -1006,7 +975,7 @@ public class FragmentIdentity extends FragmentBase {
outState.putInt("fair:advanced", grpAdvanced.getVisibility());
outState.putInt("fair:auth", auth);
outState.putString("fair:authprovider", provider);
- outState.putString("fair:html", (String) etSignature.getTag());
+ outState.putString("fair:html", signature);
super.onSaveInstanceState(outState);
}
@@ -1033,9 +1002,7 @@ public class FragmentIdentity extends FragmentBase {
etDisplay.setText(identity == null ? null : identity.display);
btnColor.setColor(identity == null ? null : identity.color);
- String signature = (identity == null ? null : identity.signature);
- etSignature.setText(TextUtils.isEmpty(signature) ? null : HtmlHelper.fromHtml(signature));
- etSignature.setTag(signature);
+ signature = (identity == null ? null : identity.signature);
etHost.setText(identity == null ? null : identity.host);
rgEncryption.check(identity != null && identity.starttls ? R.id.radio_starttls : R.id.radio_ssl);
@@ -1092,7 +1059,7 @@ public class FragmentIdentity extends FragmentBase {
grpAdvanced.setVisibility(savedInstanceState.getInt("fair:advanced"));
auth = savedInstanceState.getInt("fair:auth");
provider = savedInstanceState.getString("fair:authprovider");
- etSignature.setTag(savedInstanceState.getString("fair:html"));
+ signature = savedInstanceState.getString("fair:html");
}
Helper.setViewsEnabled(view, true);
@@ -1257,9 +1224,9 @@ public class FragmentIdentity extends FragmentBase {
if (resultCode == RESULT_OK)
onDelete();
break;
- case REQUEST_HTML:
- if (resultCode == RESULT_OK && data != null)
- onHtml(data.getBundleExtra("args"));
+ case REQUEST_SIGNATURE:
+ if (resultCode == RESULT_OK)
+ onHtml(data.getExtras());
break;
}
} catch (Throwable ex) {
@@ -1302,46 +1269,6 @@ public class FragmentIdentity extends FragmentBase {
}
private void onHtml(Bundle args) {
- String html = args.getString("html");
- etSignature.setText(HtmlHelper.fromHtml(html));
- etSignature.setTag(html);
- }
-
- public static class FragmentDialogHtml extends FragmentDialogBase {
- private EditText etHtml;
-
- @Override
- public void onSaveInstanceState(@NonNull Bundle outState) {
- outState.putString("fair:html", etHtml.getText().toString());
- super.onSaveInstanceState(outState);
- }
-
- @NonNull
- @Override
- public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
- String html;
- if (savedInstanceState == null)
- html = getArguments().getString("html");
- else
- html = savedInstanceState.getString("fair:html");
-
- View dview = LayoutInflater.from(getContext()).inflate(R.layout.dialog_signature, null);
- etHtml = dview.findViewById(R.id.etHtml);
- etHtml.setText(html);
-
- return new AlertDialog.Builder(getContext())
- .setTitle(R.string.title_edit_html)
- .setView(dview)
- .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- String html = etHtml.getText().toString();
- getArguments().putString("html", html);
- sendResult(RESULT_OK);
- }
- })
- .setNegativeButton(android.R.string.cancel, null)
- .create();
- }
+ signature = args.getString("html");
}
}
diff --git a/app/src/main/res/layout/activity_signature.xml b/app/src/main/res/layout/activity_signature.xml
new file mode 100644
index 0000000000..43eee26224
--- /dev/null
+++ b/app/src/main/res/layout/activity_signature.xml
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_identity.xml b/app/src/main/res/layout/fragment_identity.xml
index 96291dc37c..3166af283c 100644
--- a/app/src/main/res/layout/fragment_identity.xml
+++ b/app/src/main/res/layout/fragment_identity.xml
@@ -149,39 +149,18 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvColorHint" />
-
-
-
-
+ app:layout_constraintTop_toBottomOf="@id/tvColorPro" />
+ app:layout_constraintTop_toBottomOf="@id/btnSignature" />
@@ -727,7 +706,7 @@
app:constraint_referenced_ids="
tvName,etName,tvEmail,etEmail,tvDisplay,etDisplay,
tvColor,btnColor,tvColorHint,tvColorPro,
- tvSignature,etSignature,btnHtml,
+ btnSignature,
btnAdvanced,btnSave" />
+
diff --git a/app/src/main/res/menu/action_signature_style.xml b/app/src/main/res/menu/action_signature_style.xml
new file mode 100644
index 0000000000..362a05b77f
--- /dev/null
+++ b/app/src/main/res/menu/action_signature_style.xml
@@ -0,0 +1,27 @@
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 62f7528aeb..53c64941b8 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -501,7 +501,6 @@
Used to differentiate folders
Frequency of refreshing the connection for push messages or frequency of checking for new messages
Disable this only in case of empty messages or corrupt attachments
- Signature text
Color
Synchronize manually
Separate notifications
@@ -867,6 +866,10 @@
Previous
Next
+ Edit signature
+ Signature text
+ Insert image
+
Edit template
Reply template
Template name