Skip to content
Commits on Source (101)
......@@ -36,4 +36,8 @@ proguard/
*.el
.DS_Store
.externalNativeBuild
\ No newline at end of file
.externalNativeBuild
# Node
node_modules
package-lock.json
......@@ -40,7 +40,7 @@ before_script:
lintAll:
stage: test
except:
- master
- main
script:
- ./gradlew --console=plain lint
- ./gradlew --console=plain checkstyle
......@@ -48,7 +48,7 @@ lintAll:
lintRelease:
stage: test
only:
- master
- main
script:
- ./gradlew --console=plain lintRelease
- ./gradlew --console=plain checkstyle
......@@ -56,21 +56,21 @@ lintRelease:
testAll:
stage: test
except:
- master
- main
script:
- ./gradlew --console=plain test
testRelease:
stage: test
only:
- master
- main
script:
- ./gradlew --console=plain testReleaseUnitTest
buildDebug:
stage: build
except:
- master
- main
script:
- ./gradlew assembleDebug
artifacts:
......@@ -81,7 +81,7 @@ buildDebug:
buildRelease:
stage: build
only:
- master
- main
script:
- ./gradlew assembleRelease
artifacts:
......@@ -92,12 +92,12 @@ buildRelease:
release:
stage: deploy
only:
- master
- main
before_script:
- apt-get update -qq && apt-get install -y -qq pandoc wget
- wget --quiet --output-document=/tmp/nvm-install.sh https://raw.githubusercontent.com/creationix/nvm/v0.33.6/install.sh
- bash /tmp/nvm-install.sh
- export NVM_DIR="$HOME/.nvm" &&. "$NVM_DIR/nvm.sh" && nvm install 8
- export NVM_DIR="$HOME/.nvm" &&. "$NVM_DIR/nvm.sh" && nvm install --lts
- eval $(ssh-agent -s)
- ssh-add <(echo "$GIT_SSH_KEY")
- mkdir -p ~/.ssh && ssh-keyscan $GITLAB_URL >> ~/.ssh/known_hosts
......@@ -109,5 +109,5 @@ release:
- semantic-release
- git fetch
- git checkout develop
- git rebase origin/master
- git rebase origin/main
- git push git@${GITLAB_URL}:${CI_PROJECT_PATH}.git develop
[submodule "colorpicker"]
path = colorpicker
url = https://github.com/M66B/colorpicker.git
branches:
- main
plugins:
- "@semantic-release/commit-analyzer"
- "@semantic-release/release-notes-generator"
......
......@@ -3,6 +3,24 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.4.0](https://framagit.org/dystopia-project/simple-email/compare/v1.3.0...v1.4.0) (2020-11-09)
### Bug Fixes
* compatibly with SDK API 21 ([9005f7c](https://framagit.org/dystopia-project/simple-email/commit/9005f7c69aad4f7734133f3f636d4bcfd97f5ead))
* crash on uninitialized cursor instance ([8071dc3](https://framagit.org/dystopia-project/simple-email/commit/8071dc32cc50166938f005294e1a360d995f2eaf)), closes [#15](https://framagit.org/dystopia-project/simple-email/issues/15)
* gmail authentication over non-oAuth ([15cc220](https://framagit.org/dystopia-project/simple-email/commit/15cc220fb064cc2d2032b73d89f263e37ae159fd))
* wrong format on compose email addresses ([d4a1c8c](https://framagit.org/dystopia-project/simple-email/commit/d4a1c8ca204c0a7c6dfdd9583031f90a92183577))
* **ui:** improve message items view to compact and unread ([7389734](https://framagit.org/dystopia-project/simple-email/commit/738973499224ef09f4bc7d831ff7b7b49685d7cd))
* **ui:** increase distance on refreshable ([ce661ca](https://framagit.org/dystopia-project/simple-email/commit/ce661ca32b4d672eb54c9e095ae9bfdff4b0efeb))
### Features
* improve swipe actions in archive/trash folders ([0f76b2b](https://framagit.org/dystopia-project/simple-email/commit/0f76b2bd006dc2a7c8e46129b5513ef474107e12))
* update color picker dialog ([c1a2380](https://framagit.org/dystopia-project/simple-email/commit/c1a2380d2c42c1232498a1eafc8cb32ec8b035d9))
# [1.3.0](https://framagit.org/dystopia-project/simple-email/compare/v1.2.1...v1.3.0) (2018-12-08)
......
......@@ -5,7 +5,7 @@ We're thrilled you're interested in contributing to SimpleEmail! There are lots
## Documentation
Contributions to this document and the frequently asked questions
are preferred in the form of [pull requests][pull-requests]).
are preferred in the form of [pull requests][pull-requests].
## Translations
......@@ -14,7 +14,7 @@ are preferred in the form of [pull requests][pull-requests]).
## Source code
Source code contributions are welcome, there's always work to be done on the SimpleEmail codebase, building from source code is
Source code contributions are welcome, there's always work to be done on the SimpleEmail codebase, building from source code is
straightforward with `./gradlew assembleDebug` Here are the general rules:
* Write good commit messages, we follow the [conventional commits][commits]
......@@ -25,5 +25,6 @@ straightforward with `./gradlew assembleDebug` Here are the general rules:
* When submitting changes, you confirm that your code is licensed under the terms of the [GNU General Public License v3](https://www.gnu.org/licenses/gpl-3.0.html).
* Please test (compile and run) your code before you submit changes
[issue]: https://framagit.org/dystopia-project/simple-email/issues
[commits]: https://www.conventionalcommits.org/en/v1.0.0-beta.2/
[pull-requests]: https://framagit.org/dystopia-project/simple-email/merge_requests
......@@ -19,11 +19,9 @@ Alternatively, you can download directly from GitLab [Releases][gitlab-releases]
The focus of SimpleEmail is be a privacy-friendly email app with a good UX/UI and with a community development model.
SimpleEmail is minimalistic by design, so you can concentrate on reading and writing messages, it starts a foreground
SimpleEmail is minimalistic by design, so you can concentrate on reading and writing messages, it starts a foreground
service with a low priority status bar notification to make sure you'll never miss a new email.
*This project has been forked from [FairEmail](https://framagit.org/dystopia-project/simple-email/blob/master/docs/FAQ.md#what-is-the-difference-between-simpleemail-and-fairemail).*
## Features
* 100% [Free Software][free-software]
......@@ -78,10 +76,6 @@ SHA1: 7F:AB:59:CD:12:A1:11:E7:6B:12:9D:71:70:5E:21:76:D9:0E:59:C0
SHA256: A7:A9:0A:5F:14:ED:00:57:56:49:9A:53:4A:13:1A:F0:64:0A:C4:DF:62:2F:76:35:F6:51:69:D8:C9:E9:19:F2
```
## Compatibility
SimpleEmail requires at least Android 6 Marshmallow.
## Frequently asked questions
See [here][faqs] for a list of often asked questions.
......@@ -91,10 +85,14 @@ See [here][faqs] for a list of often asked questions.
* For support on SimpleEmail, please open a [issue][]
* For support on authorizing accounts you should contact your provider.
## Compatibility
SimpleEmail requires at least Android 6 Marshmallow.
## Contributing
Whether you have ideas, translations, features you think are missing, design or code changes, help is always welcome!.
You can submit [pull requests][pull-requests] to this repository, or submit translations using Weblate.
You can submit [pull requests][pull-requests] to this repository, or submit translations using [Weblate](https://hosted.weblate.org/projects/simple-email/).
To get started, take a look at [CONTRIBUTING.md](https://framagit.org/dystopia-project/simple-email/blob/develop/CONTRIBUTING.md)
......@@ -107,7 +105,7 @@ SimpleEmail uses:
* [JCharset](http://www.freeutils.net/source/jcharset/). under [GNU General Public License](http://www.freeutils.net/source/jcharset/#license)
* [Android Support Library](https://developer.android.com/tools/support-library/). under [Apache license](https://android.googlesource.com/platform/frameworks/support/+/master/LICENSE.txt).
* [Android Architecture Components](https://developer.android.com/topic/libraries/architecture/). under [Apache license](https://github.com/googlesamples/android-architecture-components/blob/master/LICENSE).
* [colorpicker](https://android.googlesource.com/platform/frameworks/opt/colorpicker). under [Apache license](https://android.googlesource.com/platform/frameworks/opt/colorpicker/+/master/src/com/android/colorpicker/ColorPickerDialog.java).
* [colorpicker](https://github.com/QuadFlask/colorpicker). under [Apache license](https://github.com/QuadFlask/colorpicker#license).
* [dnsjava](http://www.xbill.org/dnsjava/). under [BSD License](https://sourceforge.net/p/dnsjava/code/HEAD/tree/trunk/LICENSE).
* [OpenPGP API library](https://github.com/open-keychain/openpgp-api). under [Apache License 2.0](https://github.com/open-keychain/openpgp-api/blob/master/LICENSE).
......@@ -115,23 +113,7 @@ SimpleEmail uses:
[![GNU General Public License version 3](https://www.gnu.org/graphics/gplv3-127x51.png)](https://framagit.org/dystopia-project/simple-email/blob/master/LICENSE)
Copyright 2018, Marcel Bokhorst (M66B)
Copyright 2018, Distopico (dystopia project) and contributors
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.
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
* All assets and docs are under [CC BY-SA 4.0](http://creativecommons.org/licenses/by-sa/4.0/),
* All assets and docs are under [CC BY-SA 4.0](http://creativecommons.org/licenses/by-sa/4.0/),
for the license of the image used in the [assets](/assets) please see the metadata under the SVG file.
[free-software]: https://www.gnu.org/philosophy/free-sw.html
......
apply plugin: 'com.android.application'
apply plugin: "com.android.application"
apply plugin: "kotlin-android"
apply plugin: "kotlin-android-extensions"
apply from: "${rootDir}/jdee.gradle"
android {
compileSdkVersion 28
compileSdkVersion 30
defaultConfig {
applicationId "org.dystopia.email"
minSdkVersion 23
targetSdkVersion 28
versionCode 115
versionName "1.3.0"
minSdkVersion 21
targetSdkVersion 30
versionCode 116
versionName "1.4.0"
archivesBaseName = "SimpleEmail-v$versionName"
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
......@@ -56,6 +58,9 @@ repositories {
jcenter()
maven {
url "https://repo1.maven.org/maven2/"
}
maven {
url "https://jitpack.io"
}
}
......@@ -67,6 +72,8 @@ dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
def androidx_version = "1.0.0"
def appcompat_version = "1.2.0"
def fragment_version = "1.3.0-beta01"
def constraintlayout_version = "1.1.3"
def lifecycle_version = "2.0.0"
def room_version = "2.0.0"
......@@ -76,9 +83,12 @@ dependencies {
def jcharset_version = "2.0"
def dnsjava_version = "2.1.8"
def openpgp_version = "12.0"
def colorpicker_version = "0.0.15"
// https://mvnrepository.com/artifact/androidx.appcompat/appcompat
implementation "androidx.appcompat:appcompat:$androidx_version"
implementation "androidx.appcompat:appcompat:$appcompat_version"
// https://mvnrepository.com/artifact/androidx.fragment/fragment
implementation "androidx.fragment:fragment:$fragment_version"
// https://mvnrepository.com/artifact/androidx.annotation/annotation
implementation "androidx.annotation:annotation:$androidx_version"
// https://mvnrepository.com/artifact/androidx.recyclerview/recyclerview
......@@ -120,5 +130,8 @@ dependencies {
implementation "org.sufficientlysecure:openpgp-api:$openpgp_version"
// https://android.googlesource.com/platform/frameworks/opt/colorpicker
implementation project(path: ':colorpicker', configuration: 'default')
implementation "com.github.QuadFlask:colorpicker:$colorpicker_version"
// Kotlin support
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
......@@ -8,6 +8,9 @@
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission
android:name="android.permission.USE_CREDENTIALS"
android:maxSdkVersion="22" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-feature
......
......@@ -17,7 +17,7 @@ package org.dystopia.email;
along with FairEmail. If not, see <http://www.gnu.org/licenses/>.
Copyright 2018, Marcel Bokhorst (M66B)
Copyright 2018, Distopico (dystopia project) <distopico@riseup.net> and contributors
Copyright 2018-2020, Distopico (dystopia project) <distopico@riseup.net> and contributors
*/
import android.content.SharedPreferences;
......
......@@ -17,7 +17,7 @@ package org.dystopia.email;
along with FairEmail. If not, see <http://www.gnu.org/licenses/>.
Copyright 2018, Marcel Bokhorst (M66B)
Copyright 2018, Distopico (dystopia project) <distopico@riseup.net> and contributors
Copyright 2018-2020, Distopico (dystopia project) <distopico@riseup.net> and contributors
*/
import android.app.Activity;
......@@ -717,6 +717,11 @@ public class ActivityView extends ActivityBase
return intent;
}
/**
* Get Intent for invite to use SimpleEmail
*
* @return Intent with share/send action
*/
private Intent getIntentInvite() {
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
......@@ -1071,8 +1076,10 @@ public class ActivityView extends ActivityBase
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.i(
Helper.TAG,
"View onActivityResult request=" + requestCode + " result=" + resultCode + " data=" + data);
Helper.TAG,
"View onActivityResult request=" + requestCode + " result=" + resultCode + " data=" + data);
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == Activity.RESULT_OK) {
if (requestCode == REQUEST_ATTACHMENT) {
if (data != null) {
......@@ -1102,7 +1109,7 @@ public class ActivityView extends ActivityBase
fos.write(buffer, 0, read);
}
} catch (FileNotFoundException ex) {
Log.w(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex));
Log.w(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex));
} finally {
try {
if (pfd != null) {
......@@ -1133,7 +1140,7 @@ public class ActivityView extends ActivityBase
@Override
protected void onLoaded(Bundle args, Void data) {
Toast.makeText(ActivityView.this, R.string.title_attachment_saved, Toast.LENGTH_LONG)
.show();
.show();
}
@Override
......
......@@ -17,7 +17,7 @@ package org.dystopia.email;
along with FairEmail. If not, see <http://www.gnu.org/licenses/>.
Copyright 2018, Marcel Bokhorst (M66B)
Copyright 2018, Distopico (dystopia project) <distopico@riseup.net> and contributors
Copyright 2018-2020, Distopico (dystopia project) <distopico@riseup.net> and contributors
*/
import android.Manifest;
......@@ -319,18 +319,25 @@ public class AdapterMessage extends PagedListAdapter<TupleMessageEx, AdapterMess
ivFlagged.setVisibility(message.count - message.unflagged > 0 ? View.VISIBLE : View.GONE);
}
Address[] addresses = null;
if (EntityFolder.DRAFTS.equals(message.folderType)
|| EntityFolder.OUTBOX.equals(message.folderType)
|| EntityFolder.SENT.equals(message.folderType)) {
tvFrom.setText(MessageHelper.getFormattedAddresses(message.to, show_expanded));
addresses = message.to;
tvTime.setText(
DateUtils.getRelativeTimeSpanString(
context, message.sent == null ? message.received : message.sent));
} else {
tvFrom.setText(MessageHelper.getFormattedAddresses(message.from, show_expanded));
addresses = message.from;
tvTime.setText(DateUtils.getRelativeTimeSpanString(context, message.received));
}
if (compact && show_expanded) {
tvFrom.setText(MessageHelper.getFormattedAddresses(addresses, null));
} else {
tvFrom.setText(MessageHelper.getFormattedAddresses(addresses, show_expanded ? MessageHelper.ADDRESS_FULL : MessageHelper.ADDRESS_NAME));
}
tvSize.setText(
message.size == null ? null : Helper.humanReadableByteCount(message.size, true));
tvSize.setAlpha(message.content ? 1.0f : 0.5f);
......@@ -360,13 +367,13 @@ public class AdapterMessage extends PagedListAdapter<TupleMessageEx, AdapterMess
ivThread.setVisibility(View.VISIBLE);
}
if (!compact) {
tvFrom.setMaxLines(show_expanded ? Integer.MAX_VALUE : 1);
tvSubject.setMaxLines(show_expanded ? Integer.MAX_VALUE : 1);
}
tvFrom.setMaxLines(show_expanded ? Integer.MAX_VALUE : 1);
tvSubject.setMaxLines(show_expanded ? Integer.MAX_VALUE : 1);
tvSummary.setVisibility(View.GONE);
if (message.content && !show_expanded) {
if (message.content
&& !show_expanded
&& (!compact || viewType == ViewType.THREAD)) {
try {
String body = message.read(context);
Document doc = Jsoup.parse(body);
......@@ -374,7 +381,7 @@ public class AdapterMessage extends PagedListAdapter<TupleMessageEx, AdapterMess
int limit = compact ? 60 : 120;
tvSummary.setText(plainText.substring(0, Math.min(plainText.length(), limit)) + "...");
tvSummary.setVisibility(View.VISIBLE);
} catch(IOException ex) {
} catch (IOException ex) {
Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex));
}
}
......@@ -431,10 +438,14 @@ public class AdapterMessage extends PagedListAdapter<TupleMessageEx, AdapterMess
tvCount.setTypeface(null, typeface);
int colorUnseen = Helper.resolveColor(context, R.attr.colorUnread);
int colorSecondary = Helper.resolveColor(context, android.R.attr.textColorSecondary);
Drawable backgroundSeen = Helper.resolveDrawable(context, R.attr.drawableItemBackground);
Drawable backgroundUnseen = Helper.resolveDrawable(context, R.attr.drawableItemUnreadBackground);
tvSubject.setTextColor(colorUnseen);
tvFrom.setTextColor(colorUnseen);
tvTime.setTextColor(colorUnseen);
tvSummary.setTextColor(Helper.resolveColor(context, android.R.attr.textColorSecondary));
tvSummary.setTextColor(colorSecondary);
itemView.setBackground(!message.ui_seen && !show_expanded ? backgroundUnseen : backgroundSeen);
grpExpanded.setVisibility(
viewType == ViewType.THREAD && show_expanded ? View.VISIBLE : View.GONE);
......@@ -470,11 +481,11 @@ public class AdapterMessage extends PagedListAdapter<TupleMessageEx, AdapterMess
tvTimeEx.setText(df.format(new Date(message.received)));
}
tvFromEx.setText(MessageHelper.getFormattedAddresses(message.from, true));
tvTo.setText(MessageHelper.getFormattedAddresses(message.to, true));
tvReplyTo.setText(MessageHelper.getFormattedAddresses(message.reply, true));
tvCc.setText(MessageHelper.getFormattedAddresses(message.cc, true));
tvBcc.setText(MessageHelper.getFormattedAddresses(message.bcc, true));
tvFromEx.setText(MessageHelper.getFormattedAddresses(message.from, MessageHelper.ADDRESS_FULL));
tvTo.setText(MessageHelper.getFormattedAddresses(message.to, MessageHelper.ADDRESS_FULL));
tvReplyTo.setText(MessageHelper.getFormattedAddresses(message.reply, MessageHelper.ADDRESS_FULL));
tvCc.setText(MessageHelper.getFormattedAddresses(message.cc, MessageHelper.ADDRESS_FULL));
tvBcc.setText(MessageHelper.getFormattedAddresses(message.bcc, MessageHelper.ADDRESS_FULL));
tvSubjectEx.setText(message.subject);
tvHeaders.setText(show_headers ? message.headers : null);
......@@ -649,7 +660,7 @@ public class AdapterMessage extends PagedListAdapter<TupleMessageEx, AdapterMess
ContactsContract.CommonDataKinds.Email.ADDRESS + " = ?",
new String[] {email},
null);
if (cursor.moveToNext()) {
if (cursor != null && cursor.moveToNext()) {
int colContactId =
cursor.getColumnIndex(ContactsContract.CommonDataKinds.Photo.CONTACT_ID);
int colLookupKey = cursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY);
......@@ -1257,7 +1268,7 @@ public class AdapterMessage extends PagedListAdapter<TupleMessageEx, AdapterMess
lbm.sendBroadcast(
new Intent(ActivityView.ACTION_VIEW_FULL)
.putExtra("id", data.message.id)
.putExtra("from", MessageHelper.getFormattedAddresses(data.message.from, true)));
.putExtra("from", MessageHelper.getFormattedAddresses(data.message.from, MessageHelper.ADDRESS_FULL)));
}
private void onDecrypt(ActionData data) {
......
package org.dystopia.email;
/*
This file is part of SimpleEmail.
SimpleEmail 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.
SimpleEmail 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 SimpleEmail. If not, see <http://www.gnu.org/licenses/>.
Copyright 2018-2020, Distopico (dystopia project) <distopico@riseup.net> and contributors
*/
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Color;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import com.flask.colorpicker.ColorPickerView;
import com.flask.colorpicker.OnColorChangedListener;
import com.flask.colorpicker.builder.ColorPickerClickListener;
import com.flask.colorpicker.builder.ColorPickerDialogBuilder;
import static android.app.Activity.RESULT_OK;
public class ColorDialogFragment extends DialogFragment {
public static final String DIALOG_COLOR = "dialog:color:select";
private boolean sent = false;
private int color;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
color = savedInstanceState.getInt("dialog:color");
}
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
outState.putInt("dialog:color", color);
super.onSaveInstanceState(outState);
}
@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
Bundle args = getArguments();
color = savedInstanceState == null ? args.getInt("color") : savedInstanceState.getInt("dialog:color");
String title = args.getString("title");
boolean reset = args.getBoolean("reset", false);
boolean hasColor = color != Color.TRANSPARENT;
Context context = getContext();
int editTextColor = Helper.resolveColor(context, android.R.attr.editTextColor);
ColorPickerDialogBuilder builder = ColorPickerDialogBuilder
.with(context)
.setTitle(title)
.showColorEdit(true)
.setColorEditTextColor(editTextColor)
.wheelType(ColorPickerView.WHEEL_TYPE.FLOWER)
.density(6)
.lightnessSliderOnly()
.setOnColorChangedListener(new OnColorChangedListener() {
@Override
public void onColorChanged(int selectedColor) {
color = selectedColor;
}
})
.setPositiveButton(android.R.string.ok, new ColorPickerClickListener() {
@Override
public void onClick(DialogInterface dialog, int selectedColor, Integer[] allColors) {
getArguments().putInt("color", selectedColor);
sendResult(RESULT_OK);
}
});
if (!hasColor) {
builder.initialColor(Helper.resolveColor(getContext(), android.R.attr.colorPrimary));
} else {
builder.initialColor(color);
}
if (reset) {
int negativeText = !hasColor ? R.string.title_cancel : R.string.title_reset;
builder.setNegativeButton(negativeText, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
getArguments().putInt("color", Color.TRANSPARENT);
sendResult(RESULT_OK);
}
});
}
return builder.build();
}
protected void sendResult(int resultCode) {
if (sent) {
return;
}
sent = true;
Bundle args = getArguments();
if (args == null) {
args = new Bundle();
}
args.putInt("resultCode", resultCode);
getParentFragmentManager().setFragmentResult(DIALOG_COLOR, args);
dismiss();
}
}
package org.dystopia.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 <http://www.gnu.org/licenses/>.
Copyright 2018, Marcel Bokhorst (M66B)
*/
import android.content.Context;
import android.database.Cursor;
import android.text.TextUtils;
......@@ -19,25 +38,6 @@ import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
/*
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 <http://www.gnu.org/licenses/>.
Copyright 2018, Marcel Bokhorst (M66B)
*/
// https://developer.android.com/topic/libraries/architecture/room.html
@Database(
......@@ -95,7 +95,7 @@ public abstract class DB extends RoomDatabase {
Cursor cursor = null;
try {
cursor = db.query(command, new Object[0]);
if (cursor.moveToNext()) {
if (cursor != null && cursor.moveToNext()) {
return cursor.getString(0);
} else {
return null;
......
......@@ -17,7 +17,7 @@ package org.dystopia.email;
along with FairEmail. If not, see <http://www.gnu.org/licenses/>.
Copyright 2018, Marcel Bokhorst (M66B)
Copyright 2018, Distopico (dystopia project) <distopico@riseup.net> and contributors
Copyright 2018-2020, Distopico (dystopia project) <distopico@riseup.net> and contributors
*/
import androidx.lifecycle.LiveData;
......
......@@ -17,7 +17,7 @@ package org.dystopia.email;
along with FairEmail. If not, see <http://www.gnu.org/licenses/>.
Copyright 2018, Marcel Bokhorst (M66B)
Copyright 2018, Distopico (dystopia project) <distopico@riseup.net> and contributors
Copyright 2018-2020, Distopico (dystopia project) <distopico@riseup.net> and contributors
*/
import androidx.lifecycle.LiveData;
......
......@@ -17,7 +17,7 @@ package org.dystopia.email;
along with FairEmail. If not, see <http://www.gnu.org/licenses/>.
Copyright 2018, Marcel Bokhorst (M66B)
Copyright 2018, Distopico (dystopia project) <distopico@riseup.net> and contributors
Copyright 2018-2020, Distopico (dystopia project) <distopico@riseup.net> and contributors
*/
import android.app.usage.UsageStatsManager;
......@@ -26,7 +26,6 @@ import android.content.Intent;
import android.net.ConnectivityManager;
import android.os.Build;
import android.os.Bundle;
import android.os.PowerManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
......@@ -36,6 +35,9 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentTransaction;
import org.dystopia.email.util.CompatibilityUtils;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
......@@ -69,7 +71,9 @@ public class FragmentAbout extends FragmentEx {
btnLog = view.findViewById(R.id.btnLog);
btnDebugInfo = view.findViewById(R.id.btnDebugInfo);
tvVersion.setText(getString(R.string.title_version, BuildConfig.VERSION_NAME));
int version = R.string.title_version;
String versionName = getString(version, BuildConfig.VERSION_NAME);
tvVersion.setText(versionName);
btnLog.setOnClickListener(
new View.OnClickListener() {
......@@ -126,8 +130,7 @@ public class FragmentAbout extends FragmentEx {
sb.append(String.format(locale, "Id: %s\r\n", Build.ID));
sb.append("\r\n");
PowerManager pm = getContext().getSystemService(PowerManager.class);
boolean ignoring = pm.isIgnoringBatteryOptimizations(BuildConfig.APPLICATION_ID);
boolean ignoring = CompatibilityUtils.isIgnoringOptimizations(getContext());
sb.append(String.format(locale, "Battery optimizations: %b\r\n", !ignoring));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
......
......@@ -17,32 +17,35 @@ package org.dystopia.email;
along with FairEmail. If not, see <http://www.gnu.org/licenses/>.
Copyright 2018, Marcel Bokhorst (M66B)
Copyright 2018, Distopico (dystopia project) <distopico@riseup.net> and contributors
Copyright 2018-2020, Distopico (dystopia project) <distopico@riseup.net> and contributors
*/
import static android.accounts.AccountManager.newChooseAccountIntent;
import static android.app.Activity.RESULT_OK;
import android.Manifest;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AccountManagerCallback;
import android.accounts.AccountManagerFuture;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.text.Editable;
import android.text.Html;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.method.LinkMovementMethod;
import android.text.util.Linkify;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
......@@ -54,18 +57,18 @@ import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.ScrollView;
import android.widget.Spinner;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.constraintlayout.widget.Group;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.FragmentResultListener;
import androidx.lifecycle.Observer;
import com.android.colorpicker.ColorPickerDialog;
import com.android.colorpicker.ColorPickerSwatch;
import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.textfield.TextInputLayout;
import com.sun.mail.imap.IMAPFolder;
......@@ -102,13 +105,14 @@ public class FragmentAccount extends FragmentEx {
private TextInputLayout tilPassword;
private Button btnAuthorize;
private TextView tvGmailNote;
private Button btnAdvanced;
private TextView tvName;
private EditText etName;
private View vwColor;
private ImageView ibColorDefault;
private ViewButtonColor btnColor;
private LinearLayout llContainerColor;
private EditText etSignature;
private CheckBox cbSynchronize;
......@@ -148,6 +152,13 @@ public class FragmentAccount extends FragmentEx {
// Get arguments
Bundle args = getArguments();
id = (args == null ? -1 : args.getLong("id", -1));
getParentFragmentManager().setFragmentResultListener(ColorDialogFragment.DIALOG_COLOR, this, new FragmentResultListener() {
@Override
public void onFragmentResult(@NonNull String requestKey, @NonNull Bundle result) {
setAccountColor(result);
}
});
}
@Override
......@@ -170,6 +181,7 @@ public class FragmentAccount extends FragmentEx {
btnAutoConfig = view.findViewById(R.id.btnAutoConfig);
etHost = view.findViewById(R.id.etHost);
llContainerColor = view.findViewById(R.id.llContainerColor);
etPort = view.findViewById(R.id.etPort);
cbStartTls = view.findViewById(R.id.cbStartTls);
cbInsecure = view.findViewById(R.id.cbInsecure);
......@@ -177,13 +189,12 @@ public class FragmentAccount extends FragmentEx {
tilPassword = view.findViewById(R.id.tilPassword);
btnAuthorize = view.findViewById(R.id.btnAuthorize);
tvGmailNote = view.findViewById(R.id.tvGmailNote);
btnAdvanced = view.findViewById(R.id.btnAdvanced);
etName = view.findViewById(R.id.etName);
btnColor = view.findViewById(R.id.btnColor);
tvName = view.findViewById(R.id.tvName);
vwColor = view.findViewById(R.id.vwColor);
ibColorDefault = view.findViewById(R.id.ibColorDefault);
etSignature = view.findViewById(R.id.etSignature);
cbSynchronize = view.findViewById(R.id.cbSynchronize);
......@@ -225,7 +236,18 @@ public class FragmentAccount extends FragmentEx {
cbInsecure.setVisibility(position == 1 && insecure ? View.VISIBLE : View.GONE);
grpAuthorize.setVisibility(position > 0 ? View.VISIBLE : View.GONE);
btnAuthorize.setVisibility(provider.type == null ? View.GONE : View.VISIBLE);
// TODO: since December 31, 2019 Gmail retricted the oAuth
// gmail scopes and it require privative google play services java libraries
// so, only with "insecure" app enabled is supported
// see: https://developers.google.com/terms/api-services-user-data-policy#additional-requirements-for-specific-api-scopes
//
// maybe with webview and a web library?
btnAuthorize.setVisibility(View.GONE);
tvGmailNote.setVisibility(View.GONE);
if (provider.type != null) {
tvGmailNote.setVisibility(View.VISIBLE);
tvGmailNote.setText(Html.fromHtml(getString(R.string.text_gmail_note)));
}
btnAdvanced.setVisibility(position > 0 ? View.VISIBLE : View.GONE);
if (position == 0) {
......@@ -349,6 +371,24 @@ public class FragmentAccount extends FragmentEx {
}
});
tvGmailNote.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
Spanned htmlMessage = Html.fromHtml(getString(R.string.message_gmail_note));
final SpannableString dialogMessage = new SpannableString(htmlMessage);
Linkify.addLinks(dialogMessage, Linkify.WEB_URLS);
AlertDialog dialog = new DialogBuilderLifecycle(getContext(), getViewLifecycleOwner())
.setMessage(dialogMessage)
.setPositiveButton(android.R.string.ok, null)
.show();
((TextView) dialog.findViewById(android.R.id.message)).setMovementMethod(LinkMovementMethod.getInstance());
}
}
);
btnAdvanced.setOnClickListener(
new View.OnClickListener() {
@Override
......@@ -369,31 +409,19 @@ public class FragmentAccount extends FragmentEx {
}
});
vwColor.setBackgroundColor(color);
vwColor.setOnClickListener(
btnColor.setColor(color);
llContainerColor.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
int[] colors = getContext().getResources().getIntArray(R.array.colorPicker);
ColorPickerDialog colorPickerDialog = new ColorPickerDialog();
colorPickerDialog.initialize(
R.string.title_account_color, colors, color, 4, colors.length);
colorPickerDialog.setOnColorSelectedListener(
new ColorPickerSwatch.OnColorSelectedListener() {
@Override
public void onColorSelected(int color) {
setColor(color);
}
});
colorPickerDialog.show(getFragmentManager(), "colorpicker");
}
});
Bundle args = new Bundle();
args.putInt("color", btnColor.getColor());
args.putString("title", getString(R.string.title_color));
args.putBoolean("reset", true);
ibColorDefault.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
setColor(Color.TRANSPARENT);
ColorDialogFragment fragment = new ColorDialogFragment();
fragment.setArguments(args);
fragment.show(getParentFragmentManager(), "account:color");
}
});
......@@ -427,9 +455,7 @@ public class FragmentAccount extends FragmentEx {
args.putString("port", etPort.getText().toString());
args.putString("user", etUser.getText().toString());
args.putString("password", tilPassword.getEditText().getText().toString());
args.putInt(
"auth_type",
authorized == null ? Helper.AUTH_TYPE_PASSWORD : provider.getAuthType());
args.putInt("auth_type", provider.getAuthType());
new SimpleTask<CheckResult>() {
@Override
......@@ -464,14 +490,24 @@ public class FragmentAccount extends FragmentEx {
Session isession = Session.getInstance(props, null);
isession.setDebug(true);
IMAPStore istore = null;
try {
istore = (IMAPStore) isession.getStore(starttls ? "imap" : "imaps");
String originalPassword = password;
try {
if (auth_type == Helper.AUTH_TYPE_GMAIL) {
password = Helper.refreshToken(context, "com.google", user, password);
}
istore.connect(host, Integer.parseInt(port), user, password);
} catch (AuthenticationFailedException ex) {
// Try normal imap access with gmail allowed "insecure" app enabled
if (auth_type == Helper.AUTH_TYPE_GMAIL) {
password = Helper.refreshToken(context, "com.google", user, password);
istore.connect(host, Integer.parseInt(port), user, password);
auth_type = Helper.AUTH_TYPE_PASSWORD;
args.putInt("auth_type", auth_type);
props = MessageHelper.getSessionProperties(auth_type, insecure);
isession = Session.getInstance(props, null);
istore = (IMAPStore) isession.getStore(starttls ? "imap" : "imaps");
istore.connect(host, Integer.parseInt(port), user, originalPassword);
} else {
throw ex;
}
......@@ -1049,7 +1085,7 @@ public class FragmentAccount extends FragmentEx {
Helper.setViewsEnabled(view, true);
setColor(color);
btnColor.setColor(color);
etSignature.setHint(R.string.title_optional);
etSignature.setEnabled(true);
......@@ -1087,13 +1123,54 @@ public class FragmentAccount extends FragmentEx {
});
}
/**
* Set base account to button
* @param result - color dialog data from {@link FragmentResultListener} result callback
*/
private void setAccountColor(Bundle result) {
try {
int resultCode = result.getInt("resultCode");
if (resultCode == RESULT_OK) {
color = result.getInt("color");
btnColor.setColor(color);
}
} catch (Throwable ex) {
Log.e(Helper.TAG, "Set account color error: " + ex + "\n" + Log.getStackTraceString(ex));
}
}
private void selectAccount() {
Log.i(Helper.TAG, "Select account");
Provider provider = (Provider) spProvider.getSelectedItem();
if (provider.type != null) {
startActivityForResult(
newChooseAccountIntent(null, null, new String[] {provider.type}, null, null, null, null),
ActivitySetup.REQUEST_CHOOSE_ACCOUNT);
Intent intent = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
intent = newChooseAccountIntent(
null,
null,
new String[] {provider.type},
null,
null,
null,
null
);
} else {
intent = newChooseAccountIntent(
null,
null,
new String[]{provider.type},
false,
null,
null,
null,
null
);
}
PackageManager pm = getContext().getPackageManager();
if (intent.resolveActivity(pm) == null) { // system whitelisted
throw new IllegalArgumentException(getString(R.string.title_no_viewer, intent));
}
startActivityForResult(intent, ActivitySetup.REQUEST_CHOOSE_ACCOUNT);
}
}
......@@ -1112,7 +1189,7 @@ public class FragmentAccount extends FragmentEx {
Log.i(
Helper.TAG,
"Activity result request=" + requestCode + " result=" + resultCode + " data=" + data);
if (resultCode == Activity.RESULT_OK) {
if (resultCode == RESULT_OK) {
if (requestCode == ActivitySetup.REQUEST_CHOOSE_ACCOUNT) {
String name = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
String type = data.getStringExtra(AccountManager.KEY_ACCOUNT_TYPE);
......@@ -1149,7 +1226,7 @@ public class FragmentAccount extends FragmentEx {
}
}
},
null);
null);
break;
}
}
......@@ -1157,15 +1234,6 @@ public class FragmentAccount extends FragmentEx {
}
}
private void setColor(int color) {
FragmentAccount.this.color = color;
GradientDrawable border = new GradientDrawable();
border.setColor(color);
border.setStroke(5, Helper.resolveColor(getContext(), R.attr.colorSeparator));
vwColor.setBackground(border);
}
private void setFolders(List<EntityFolder> folders) {
final Collator collator = Collator.getInstance(Locale.getDefault());
collator.setStrength(Collator.SECONDARY); // Case insensitive, process accents etc
......
......@@ -17,7 +17,7 @@ package org.dystopia.email;
along with FairEmail. If not, see <http://www.gnu.org/licenses/>.
Copyright 2018, Marcel Bokhorst (M66B)
Copyright 2018, Distopico (dystopia project) <distopico@riseup.net> and contributors
Copyright 2018-2020, Distopico (dystopia project) <distopico@riseup.net> and contributors
*/
import static android.app.Activity.RESULT_OK;
......@@ -85,6 +85,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
......@@ -99,6 +100,8 @@ import javax.mail.Session;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import org.dystopia.email.util.CompatibilityUtils;
import org.openintents.openpgp.OpenPgpError;
import org.openintents.openpgp.util.OpenPgpApi;
import org.openintents.openpgp.util.OpenPgpServiceConnection;
......@@ -482,9 +485,9 @@ public class FragmentCompose extends FragmentEx {
break;
case R.id.menu_link:
Uri uri = null;
ClipboardManager cbm = getContext().getSystemService(ClipboardManager.class);
if (cbm.hasPrimaryClip()) {
String link = cbm.getPrimaryClip().getItemAt(0).coerceToText(getContext()).toString();
ClipboardManager clipboardManager = CompatibilityUtils.getClipboardManager(getContext());
if (clipboardManager.hasPrimaryClip()) {
String link = clipboardManager.getPrimaryClip().getItemAt(0).coerceToText(getContext()).toString();
uri = Uri.parse(link);
if (uri.getScheme() == null) {
uri = null;
......@@ -779,58 +782,93 @@ public class FragmentCompose extends FragmentEx {
}
private void handlePickContact(int requestCode, Intent data) {
Cursor cursor = null;
try {
Uri uri = data.getData();
if (uri != null) {
cursor =
getContext()
.getContentResolver()
.query(
uri,
new String[] {
ContactsContract.CommonDataKinds.Email.ADDRESS,
ContactsContract.Contacts.DISPLAY_NAME
},
null,
null,
null);
}
if (cursor != null && cursor.moveToFirst()) {
int colEmail = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.ADDRESS);
int colName = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);
String email = cursor.getString(colEmail);
String name = cursor.getString(colName);
String text = null;
if (requestCode == ActivityCompose.REQUEST_CONTACT_TO) {
text = etTo.getText().toString();
} else if (requestCode == ActivityCompose.REQUEST_CONTACT_CC) {
text = etCc.getText().toString();
} else if (requestCode == ActivityCompose.REQUEST_CONTACT_BCC) {
text = etBcc.getText().toString();
Uri uri = data.getData();
if (uri == null)
return;
Bundle args = new Bundle();
args.putLong("id", working);
args.putInt("requestCode", requestCode);
args.putParcelable("uri", uri);
new SimpleTask<EntityMessage>() {
@Override
protected EntityMessage onLoad(Context context, Bundle args) throws Throwable {
long id = args.getLong("id");
int requestCode = args.getInt("requestCode");
Uri uri = args.getParcelable("uri");
EntityMessage draft = null;
DB db = DB.getInstance(context);
try (Cursor cursor = context.getContentResolver().query(
uri,
new String[]{
ContactsContract.CommonDataKinds.Email.ADDRESS,
ContactsContract.Contacts.DISPLAY_NAME
},
null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
int colEmail = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.ADDRESS);
int colName = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);
String email = MessageHelper.sanitizeEmail(cursor.getString(colEmail));
String name = cursor.getString(colName);
try {
db.beginTransaction();
draft = db.message().getMessage(id);
if (draft == null)
return null;
Address[] address = null;
if (requestCode == ActivityCompose.REQUEST_CONTACT_TO) {
address = draft.to;
} else if (requestCode == ActivityCompose.REQUEST_CONTACT_CC) {
address = draft.cc;
} else if (requestCode == ActivityCompose.REQUEST_CONTACT_BCC) {
address = draft.bcc;
}
List<Address> list = new ArrayList<>();
if (address != null)
list.addAll(Arrays.asList(address));
list.add(new InternetAddress(email, name, StandardCharsets.UTF_8.name()));
if (requestCode == ActivityCompose.REQUEST_CONTACT_TO) {
draft.to = list.toArray(new Address[0]);
} else if (requestCode == ActivityCompose.REQUEST_CONTACT_CC) {
draft.cc = list.toArray(new Address[0]);
} else if (requestCode == ActivityCompose.REQUEST_CONTACT_BCC) {
draft.bcc = list.toArray(new Address[0]);
}
db.message().updateMessage(draft);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
}
InternetAddress address = new InternetAddress(email, name);
StringBuilder sb = new StringBuilder(text);
sb.append(address.toString().replace(",", "")).append(", ");
return draft;
}
if (requestCode == ActivityCompose.REQUEST_CONTACT_TO) {
etTo.setText(sb.toString());
} else if (requestCode == ActivityCompose.REQUEST_CONTACT_CC) {
etCc.setText(sb.toString());
} else if (requestCode == ActivityCompose.REQUEST_CONTACT_BCC) {
etBcc.setText(sb.toString());
@Override
protected void onLoaded(Bundle args, EntityMessage draft) {
if (draft != null) {
etTo.setText(MessageHelper.getAddressesCompose(draft.to));
etCc.setText(MessageHelper.getAddressesCompose(draft.cc));
etBcc.setText(MessageHelper.getAddressesCompose(draft.bcc));
}
}
} catch (Throwable ex) {
Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex));
Helper.unexpectedError(getContext(), ex);
} finally {
if (cursor != null) {
cursor.close();
@Override
protected void onException(Bundle args, Throwable ex) {
Helper.unexpectedError(getContext(), ex);
}
}
}.load(this, args);
}
private void handleAddAttachment(Intent data, final boolean image) {
......@@ -1121,7 +1159,7 @@ public class FragmentCompose extends FragmentEx {
String from = Helper.canonicalAddress(((InternetAddress) ref.from[0]).getAddress());
Log.i(
Helper.TAG,
"From=" + from + " to=" + MessageHelper.getFormattedAddresses(ref.to, false));
"From=" + from + " to=" + MessageHelper.getFormattedAddresses(ref.to, null));
for (EntityIdentity identity : identities) {
String email = Helper.canonicalAddress(identity.email);
if (from.equals(email)) {
......@@ -1229,7 +1267,7 @@ public class FragmentCompose extends FragmentEx {
String.format(
"<p>%s %s:</p><blockquote>%s</blockquote>",
Html.escapeHtml(new Date(time).toString()),
Html.escapeHtml(MessageHelper.getFormattedAddresses(draft.to, true)),
Html.escapeHtml(MessageHelper.getFormattedAddresses(draft.to, MessageHelper.ADDRESS_FULL)),
HtmlHelper.sanitize(ref.read(context)));
} else if ("forward".equals(action)) {
draft.subject = context.getString(R.string.title_subject_forward, ref.subject);
......@@ -1237,7 +1275,7 @@ public class FragmentCompose extends FragmentEx {
String.format(
"<p>%s %s:</p><blockquote>%s</blockquote>",
Html.escapeHtml(new Date(time).toString()),
Html.escapeHtml(MessageHelper.getFormattedAddresses(ref.from, true)),
Html.escapeHtml(MessageHelper.getFormattedAddresses(ref.from, MessageHelper.ADDRESS_FULL)),
HtmlHelper.sanitize(ref.read(context)));
}
......@@ -1328,9 +1366,9 @@ public class FragmentCompose extends FragmentEx {
setSubtitle(draft.account_name);
etTo.setText(MessageHelper.getFormattedAddresses(draft.to, true));
etCc.setText(MessageHelper.getFormattedAddresses(draft.cc, true));
etBcc.setText(MessageHelper.getFormattedAddresses(draft.bcc, true));
etTo.setText(MessageHelper.getFormattedAddresses(draft.to, MessageHelper.ADDRESS_FULL));
etCc.setText(MessageHelper.getFormattedAddresses(draft.cc, MessageHelper.ADDRESS_FULL));
etBcc.setText(MessageHelper.getFormattedAddresses(draft.bcc, MessageHelper.ADDRESS_FULL));
etSubject.setText(draft.subject);
etBody.setText(null);
......
......@@ -17,7 +17,7 @@ package org.dystopia.email;
along with FairEmail. If not, see <http://www.gnu.org/licenses/>.
Copyright 2018, Marcel Bokhorst (M66B)
Copyright 2018, Distopico (dystopia project) <distopico@riseup.net> and contributors
Copyright 2018-2020, Distopico (dystopia project) <distopico@riseup.net> and contributors
*/
import android.content.res.Configuration;
......@@ -32,6 +32,8 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Lifecycle;
import org.dystopia.email.util.CompatibilityUtils;
public class FragmentEx extends Fragment {
private String title = "";
private String subtitle = " ";
......@@ -113,10 +115,10 @@ public class FragmentEx extends Fragment {
public void onDetach() {
super.onDetach();
InputMethodManager im = getContext().getSystemService(InputMethodManager.class);
InputMethodManager inputMethodManager = CompatibilityUtils.getInputMethodManager(getContext());
View focused = getActivity().getCurrentFocus();
if (focused != null) {
im.hideSoftInputFromWindow(focused.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
inputMethodManager.hideSoftInputFromWindow(focused.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
}
}
......