Browse Source

Move store sent option to account, added advanced option to use WebView

main
M66B 5 years ago
parent
commit
7307bc80c7
9 changed files with 98 additions and 49 deletions
  1. +9
    -3
      app/schemas/eu.faircode.email.DB/1.json
  2. +2
    -0
      app/src/main/java/eu/faircode/email/EntityAccount.java
  3. +8
    -1
      app/src/main/java/eu/faircode/email/FragmentAccount.java
  4. +13
    -10
      app/src/main/java/eu/faircode/email/FragmentMessage.java
  5. +11
    -5
      app/src/main/java/eu/faircode/email/FragmentOptions.java
  6. +26
    -22
      app/src/main/java/eu/faircode/email/ServiceSynchronize.java
  7. +11
    -2
      app/src/main/res/layout/fragment_account.xml
  8. +15
    -5
      app/src/main/res/layout/fragment_options.xml
  9. +3
    -1
      app/src/main/res/values/strings.xml

+ 9
- 3
app/schemas/eu.faircode.email.DB/1.json View File

@ -2,7 +2,7 @@
"formatVersion": 1, "formatVersion": 1,
"database": { "database": {
"version": 1, "version": 1,
"identityHash": "cd3cf378d6f71c13ba8beb38a8bf58cf",
"identityHash": "1d725017559c0a36b02ce447ecba45b9",
"entities": [ "entities": [
{ {
"tableName": "identity", "tableName": "identity",
@ -125,7 +125,7 @@
}, },
{ {
"tableName": "account", "tableName": "account",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `name` TEXT, `host` TEXT NOT NULL, `port` INTEGER NOT NULL, `user` TEXT NOT NULL, `password` TEXT NOT NULL, `primary` INTEGER NOT NULL, `synchronize` INTEGER NOT NULL, `seen_until` INTEGER, `state` TEXT, `error` TEXT)",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `name` TEXT, `host` TEXT NOT NULL, `port` INTEGER NOT NULL, `user` TEXT NOT NULL, `password` TEXT NOT NULL, `primary` INTEGER NOT NULL, `synchronize` INTEGER NOT NULL, `store_sent` INTEGER NOT NULL, `seen_until` INTEGER, `state` TEXT, `error` TEXT)",
"fields": [ "fields": [
{ {
"fieldPath": "id", "fieldPath": "id",
@ -175,6 +175,12 @@
"affinity": "INTEGER", "affinity": "INTEGER",
"notNull": true "notNull": true
}, },
{
"fieldPath": "store_sent",
"columnName": "store_sent",
"affinity": "INTEGER",
"notNull": true
},
{ {
"fieldPath": "seen_until", "fieldPath": "seen_until",
"columnName": "seen_until", "columnName": "seen_until",
@ -776,7 +782,7 @@
], ],
"setupQueries": [ "setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"cd3cf378d6f71c13ba8beb38a8bf58cf\")"
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"1d725017559c0a36b02ce447ecba45b9\")"
] ]
} }
} }

+ 2
- 0
app/src/main/java/eu/faircode/email/EntityAccount.java View File

@ -46,6 +46,8 @@ public class EntityAccount {
public Boolean primary; public Boolean primary;
@NonNull @NonNull
public Boolean synchronize; public Boolean synchronize;
@NonNull
public Boolean store_sent;
public Long seen_until; public Long seen_until;
public String state; public String state;
public String error; public String error;


+ 8
- 1
app/src/main/java/eu/faircode/email/FragmentAccount.java View File

@ -71,6 +71,7 @@ public class FragmentAccount extends FragmentEx {
private TextInputLayout tilPassword; private TextInputLayout tilPassword;
private CheckBox cbSynchronize; private CheckBox cbSynchronize;
private CheckBox cbPrimary; private CheckBox cbPrimary;
private CheckBox cbStoreSent;
private Button btnCheck; private Button btnCheck;
private ProgressBar pbCheck; private ProgressBar pbCheck;
private Spinner spDrafts; private Spinner spDrafts;
@ -104,6 +105,7 @@ public class FragmentAccount extends FragmentEx {
tilPassword = view.findViewById(R.id.tilPassword); tilPassword = view.findViewById(R.id.tilPassword);
cbSynchronize = view.findViewById(R.id.cbSynchronize); cbSynchronize = view.findViewById(R.id.cbSynchronize);
cbPrimary = view.findViewById(R.id.cbPrimary); cbPrimary = view.findViewById(R.id.cbPrimary);
cbStoreSent = view.findViewById(R.id.cbStoreSent);
btnCheck = view.findViewById(R.id.btnCheck); btnCheck = view.findViewById(R.id.btnCheck);
pbCheck = view.findViewById(R.id.pbCheck); pbCheck = view.findViewById(R.id.pbCheck);
spDrafts = view.findViewById(R.id.spDrafts); spDrafts = view.findViewById(R.id.spDrafts);
@ -365,6 +367,7 @@ public class FragmentAccount extends FragmentEx {
args.putString("password", tilPassword.getEditText().getText().toString()); args.putString("password", tilPassword.getEditText().getText().toString());
args.putBoolean("synchronize", cbSynchronize.isChecked()); args.putBoolean("synchronize", cbSynchronize.isChecked());
args.putBoolean("primary", cbPrimary.isChecked()); args.putBoolean("primary", cbPrimary.isChecked());
args.putBoolean("store_sent", cbStoreSent.isChecked());
args.putParcelable("drafts", drafts); args.putParcelable("drafts", drafts);
args.putParcelable("sent", sent); args.putParcelable("sent", sent);
args.putParcelable("all", all); args.putParcelable("all", all);
@ -380,6 +383,8 @@ public class FragmentAccount extends FragmentEx {
String user = args.getString("user"); String user = args.getString("user");
String password = args.getString("password"); String password = args.getString("password");
boolean synchronize = args.getBoolean("synchronize"); boolean synchronize = args.getBoolean("synchronize");
boolean primary = args.getBoolean("primary");
boolean store_sent = args.getBoolean("store_sent");
EntityFolder drafts = args.getParcelable("drafts"); EntityFolder drafts = args.getParcelable("drafts");
EntityFolder sent = args.getParcelable("sent"); EntityFolder sent = args.getParcelable("sent");
EntityFolder all = args.getParcelable("all"); EntityFolder all = args.getParcelable("all");
@ -433,7 +438,8 @@ public class FragmentAccount extends FragmentEx {
account.user = user; account.user = user;
account.password = password; account.password = password;
account.synchronize = synchronize; account.synchronize = synchronize;
account.primary = (account.synchronize && args.getBoolean("primary"));
account.primary = (account.synchronize && primary);
account.store_sent = store_sent;
if (!synchronize) if (!synchronize)
account.error = null; account.error = null;
@ -620,6 +626,7 @@ public class FragmentAccount extends FragmentEx {
tilPassword.getEditText().setText(account == null ? null : account.password); tilPassword.getEditText().setText(account == null ? null : account.password);
cbSynchronize.setChecked(account == null ? true : account.synchronize); cbSynchronize.setChecked(account == null ? true : account.synchronize);
cbPrimary.setChecked(account == null ? true : account.primary); cbPrimary.setChecked(account == null ? true : account.primary);
cbStoreSent.setChecked(account == null ? true : account.store_sent);
} else { } else {
int provider = savedInstanceState.getInt("provider"); int provider = savedInstanceState.getInt("provider");
spProvider.setTag(provider); spProvider.setTag(provider);


+ 13
- 10
app/src/main/java/eu/faircode/email/FragmentMessage.java View File

@ -22,6 +22,7 @@ package eu.faircode.email;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
@ -105,7 +106,9 @@ public class FragmentMessage extends FragmentEx {
// Get arguments // Get arguments
Bundle args = getArguments(); Bundle args = getArguments();
final long id = (args == null ? -1 : args.getLong("id")); final long id = (args == null ? -1 : args.getLong("id"));
debug = PreferenceManager.getDefaultSharedPreferences(getContext()).getBoolean("debug", false);
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
debug = prefs.getBoolean("debug", false);
// Get controls // Get controls
tvFrom = view.findViewById(R.id.tvFrom); tvFrom = view.findViewById(R.id.tvFrom);
@ -152,14 +155,7 @@ public class FragmentMessage extends FragmentEx {
if (link.length != 0) { if (link.length != 0) {
String url = link[0].getURL(); String url = link[0].getURL();
if (true) {
// https://developer.chrome.com/multidevice/android/customtabs
CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
builder.setToolbarColor(Helper.resolveColor(getContext(), R.attr.colorPrimary));
CustomTabsIntent customTabsIntent = builder.build();
customTabsIntent.launchUrl(getContext(), Uri.parse(url));
} else {
if (prefs.getBoolean("webview", false)) {
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putString("link", url); args.putString("link", url);
@ -169,6 +165,13 @@ public class FragmentMessage extends FragmentEx {
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction(); FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.content_frame, fragment).addToBackStack("webview"); fragmentTransaction.replace(R.id.content_frame, fragment).addToBackStack("webview");
fragmentTransaction.commit(); fragmentTransaction.commit();
} else {
// https://developer.chrome.com/multidevice/android/customtabs
CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
builder.setToolbarColor(Helper.resolveColor(getContext(), R.attr.colorPrimary));
CustomTabsIntent customTabsIntent = builder.build();
customTabsIntent.launchUrl(getContext(), Uri.parse(url));
} }
} }
return true; return true;
@ -812,7 +815,7 @@ public class FragmentMessage extends FragmentEx {
long id = args.getLong("id"); long id = args.getLong("id");
long target = args.getLong("target"); long target = args.getLong("target");
boolean close = false;
boolean close;
DB db = DB.getInstance(context); DB db = DB.getInstance(context);
try { try {


+ 11
- 5
app/src/main/java/eu/faircode/email/FragmentOptions.java View File

@ -22,17 +22,20 @@ package eu.faircode.email;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Bundle; import android.os.Bundle;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.text.method.LinkMovementMethod;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.CheckBox; import android.widget.CheckBox;
import android.widget.CompoundButton; import android.widget.CompoundButton;
import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
public class FragmentOptions extends FragmentEx { public class FragmentOptions extends FragmentEx {
private CheckBox cbStoreSent;
private CheckBox cbWebView;
private TextView tvCustomTabs;
private CheckBox cbDebug; private CheckBox cbDebug;
@Override @Override
@ -43,18 +46,19 @@ public class FragmentOptions extends FragmentEx {
View view = inflater.inflate(R.layout.fragment_options, container, false); View view = inflater.inflate(R.layout.fragment_options, container, false);
// Get controls // Get controls
cbStoreSent = view.findViewById(R.id.cbStoreSent);
cbWebView = view.findViewById(R.id.cbWebView);
tvCustomTabs = view.findViewById(R.id.tvCustomTabs);
cbDebug = view.findViewById(R.id.cbDebug); cbDebug = view.findViewById(R.id.cbDebug);
// Wire controls // Wire controls
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
cbStoreSent.setChecked(prefs.getBoolean("store_sent", false));
cbStoreSent.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
cbWebView.setChecked(prefs.getBoolean("webview", false));
cbWebView.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override @Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
prefs.edit().putBoolean("store_sent", checked).apply();
prefs.edit().putBoolean("webview", checked).apply();
} }
}); });
@ -66,6 +70,8 @@ public class FragmentOptions extends FragmentEx {
} }
}); });
tvCustomTabs.setMovementMethod(LinkMovementMethod.getInstance());
return view; return view;
} }
} }

+ 26
- 22
app/src/main/java/eu/faircode/email/ServiceSynchronize.java View File

@ -28,7 +28,6 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.ServiceConnection; import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.media.RingtoneManager; import android.media.RingtoneManager;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
import android.net.Network; import android.net.Network;
@ -329,6 +328,8 @@ public class ServiceSynchronize extends LifecycleService {
// - "This operation is not allowed on a closed folder" // - "This operation is not allowed on a closed folder"
// - can happen when syncing message // - can happen when syncing message
// MailConnectException
if (!(ex instanceof FolderClosedException) && !(ex instanceof IllegalStateException)) { if (!(ex instanceof FolderClosedException) && !(ex instanceof IllegalStateException)) {
String action = account + "/" + folder; String action = account + "/" + folder;
NotificationManager nm = getSystemService(NotificationManager.class); NotificationManager nm = getSystemService(NotificationManager.class);
@ -918,6 +919,7 @@ public class ServiceSynchronize extends LifecycleService {
private void doSend(Session isession, EntityMessage message, DB db) throws MessagingException, IOException { private void doSend(Session isession, EntityMessage message, DB db) throws MessagingException, IOException {
// Send message // Send message
EntityAccount account = db.account().getAccount(message.account);
EntityIdentity ident = db.identity().getIdentity(message.identity); EntityIdentity ident = db.identity().getIdentity(message.identity);
EntityMessage reply = (message.replying == null ? null : db.message().getMessage(message.replying)); EntityMessage reply = (message.replying == null ? null : db.message().getMessage(message.replying));
List<EntityAttachment> attachments = db.attachment().getAttachments(message.id); List<EntityAttachment> attachments = db.attachment().getAttachments(message.id);
@ -962,12 +964,12 @@ public class ServiceSynchronize extends LifecycleService {
message.ui_seen = true; message.ui_seen = true;
db.message().updateMessage(message); db.message().updateMessage(message);
// TODO: store sent setting per account
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
if (prefs.getBoolean("store_sent", false)) {
if (account.store_sent) {
EntityFolder sent = db.folder().getFolderByType(ident.account, EntityFolder.SENT); EntityFolder sent = db.folder().getFolderByType(ident.account, EntityFolder.SENT);
if (sent != null) { if (sent != null) {
// TODO: how to handle thread?
message.folder = sent.id;
message.uid = null;
db.message().updateMessage(message);
Log.i(Helper.TAG, "Appending sent msgid=" + message.msgid); Log.i(Helper.TAG, "Appending sent msgid=" + message.msgid);
EntityOperation.queue(db, message, EntityOperation.ADD); // Could already exist EntityOperation.queue(db, message, EntityOperation.ADD); // Could already exist
} }
@ -1184,24 +1186,26 @@ public class ServiceSynchronize extends LifecycleService {
} }
// Cleanup files // Cleanup files
File messages = new File(getFilesDir(), "messages");
for (File file : messages.listFiles())
if (file.isFile()) {
long id = Long.parseLong(file.getName());
if (db.message().countMessage(id) == 0) {
Log.i(Helper.TAG, "Cleanup message id=" + id);
file.delete();
File[] messages = new File(getFilesDir(), "messages").listFiles();
if (messages != null)
for (File file : messages)
if (file.isFile()) {
long id = Long.parseLong(file.getName());
if (db.message().countMessage(id) == 0) {
Log.i(Helper.TAG, "Cleanup message id=" + id);
file.delete();
}
} }
}
File attachments = new File(getFilesDir(), "attachments");
for (File file : attachments.listFiles())
if (file.isFile()) {
long id = Long.parseLong(file.getName());
if (db.attachment().countAttachment(id) == 0) {
Log.i(Helper.TAG, "Cleanup attachment id=" + id);
file.delete();
File[] attachments = new File(getFilesDir(), "attachments").listFiles();
if (attachments != null)
for (File file : attachments)
if (file.isFile()) {
long id = Long.parseLong(file.getName());
if (db.attachment().countAttachment(id) == 0) {
Log.i(Helper.TAG, "Cleanup attachment id=" + id);
file.delete();
}
} }
}
Log.w(Helper.TAG, folder.name + " statistics added=" + added + " updated=" + updated + " unchanged=" + unchanged); Log.w(Helper.TAG, folder.name + " statistics added=" + added + " updated=" + updated + " unchanged=" + unchanged);
} finally { } finally {
@ -1256,7 +1260,7 @@ public class ServiceSynchronize extends LifecycleService {
Log.i(Helper.TAG, folder.name + " found as id=" + dup.id + " uid=" + dup.uid + " msgid=" + msgid); Log.i(Helper.TAG, folder.name + " found as id=" + dup.id + " uid=" + dup.uid + " msgid=" + msgid);
dup.folder = folder.id; dup.folder = folder.id;
dup.uid = uid; dup.uid = uid;
if (outbox) // only now the uid is known
if (TextUtils.isEmpty(dup.thread)) // outbox: only now the uid is known
dup.thread = helper.getThreadId(uid); dup.thread = helper.getThreadId(uid);
db.message().updateMessage(dup); db.message().updateMessage(dup);
message = dup; message = dup;


+ 11
- 2
app/src/main/res/layout/fragment_account.xml View File

@ -176,6 +176,15 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/cbSynchronize" /> app:layout_constraintTop_toBottomOf="@id/cbSynchronize" />
<CheckBox
android:id="@+id/cbStoreSent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="@string/title_store_sent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/cbPrimary" />
<!-- check --> <!-- check -->
<Button <Button
@ -185,7 +194,7 @@
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:text="@string/title_check" android:text="@string/title_check"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/cbPrimary" />
app:layout_constraintTop_toBottomOf="@id/cbStoreSent" />
<ProgressBar <ProgressBar
android:id="@+id/pbCheck" android:id="@+id/pbCheck"
@ -205,7 +214,7 @@
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:src="@drawable/baseline_delete_24" android:src="@drawable/baseline_delete_24"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/cbPrimary" />
app:layout_constraintTop_toBottomOf="@id/cbStoreSent" />
<TextView <TextView
android:id="@+id/tvDrafts" android:id="@+id/tvDrafts"


+ 15
- 5
app/src/main/res/layout/fragment_options.xml View File

@ -12,17 +12,27 @@
android:layout_height="match_parent"> android:layout_height="match_parent">
<CheckBox <CheckBox
android:id="@+id/cbStoreSent"
android:id="@+id/cbWebView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:text="@string/title_advanced_store_sent"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:text="@string/title_advanced_webview"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:textColor="?android:attr/textColorSecondary" android:textColor="?android:attr/textColorSecondary"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tvCustomTabs"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:text="@string/title_advanced_customtabs"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/cbWebView" />
<CheckBox <CheckBox
android:id="@+id/cbDebug" android:id="@+id/cbDebug"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -30,9 +40,9 @@
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:text="@string/title_advanced_debug" android:text="@string/title_advanced_debug"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:textColor="?android:attr/textColorSecondary" android:textColor="?android:attr/textColorSecondary"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/cbStoreSent" />
app:layout_constraintTop_toBottomOf="@id/tvCustomTabs" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView> </ScrollView>

+ 3
- 1
app/src/main/res/values/strings.xml View File

@ -63,7 +63,8 @@
<string name="title_setup_dark_theme">Dark theme</string> <string name="title_setup_dark_theme">Dark theme</string>
<string name="title_advanced">Advanced options</string> <string name="title_advanced">Advanced options</string>
<string name="title_advanced_store_sent">Store sent messages</string>
<string name="title_advanced_webview">Use WebView</string>
<string name="title_advanced_customtabs">Instead of <a href="https://developer.chrome.com/multidevice/android/customtabs">Chrome Custom Tabs</a></string>
<string name="title_advanced_debug">Debug</string> <string name="title_advanced_debug">Debug</string>
<string name="title_identity_name">Your name</string> <string name="title_identity_name">Your name</string>
@ -82,6 +83,7 @@
<string name="title_port">Port number</string> <string name="title_port">Port number</string>
<string name="title_user">User name</string> <string name="title_user">User name</string>
<string name="title_password">Password</string> <string name="title_password">Password</string>
<string name="title_store_sent">Store sent messages (enable if needed only)</string>
<string name="title_synchronize_account">Synchronize (receive messages)</string> <string name="title_synchronize_account">Synchronize (receive messages)</string>
<string name="title_synchronize_identity">Synchronize (send messages)</string> <string name="title_synchronize_identity">Synchronize (send messages)</string>
<string name="title_primary_account">Primary (default account)</string> <string name="title_primary_account">Primary (default account)</string>


Loading…
Cancel
Save