aboutsummaryrefslogtreecommitdiffstats
path: root/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'src/main')
-rw-r--r--src/main/AndroidManifest.xml6
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Account.java35
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Bookmark.java13
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Contact.java30
-rw-r--r--src/main/java/eu/siacs/conversations/entities/ListItem.java6
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Presence.java43
-rw-r--r--src/main/java/eu/siacs/conversations/entities/PresenceTemplate.java55
-rw-r--r--src/main/java/eu/siacs/conversations/parser/PresenceParser.java3
-rw-r--r--src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java44
-rw-r--r--src/main/java/eu/siacs/conversations/services/XmppConnectionService.java40
-rw-r--r--src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java2
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java2
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java11
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java2
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ConversationFragment.java12
-rw-r--r--src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java14
-rw-r--r--src/main/java/eu/siacs/conversations/ui/SetPresenceActivity.java200
-rw-r--r--src/main/java/eu/siacs/conversations/ui/SettingsActivity.java6
-rw-r--r--src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java4
-rw-r--r--src/main/java/eu/siacs/conversations/ui/XmppActivity.java15
-rw-r--r--src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java2
-rw-r--r--src/main/java/eu/siacs/conversations/utils/UIHelper.java17
-rw-r--r--src/main/res/drawable-hdpi/ic_account_box_white_24dp.pngbin0 -> 337 bytes
-rw-r--r--src/main/res/drawable-hdpi/ic_announcement_white_24dp.pngbin0 -> 251 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_account_box_white_24dp.pngbin0 -> 290 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_announcement_white_24dp.pngbin0 -> 214 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_account_box_white_24dp.pngbin0 -> 431 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_announcement_white_24dp.pngbin0 -> 285 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_account_box_white_24dp.pngbin0 -> 578 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_announcement_white_24dp.pngbin0 -> 355 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_account_box_white_24dp.pngbin0 -> 894 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_announcement_white_24dp.pngbin0 -> 519 bytes
-rw-r--r--src/main/res/layout/activity_set_presence.xml72
-rw-r--r--src/main/res/layout/presence_template.xml49
-rw-r--r--src/main/res/layout/simple_list_item.xml26
-rw-r--r--src/main/res/menu/change_presence.xml17
-rw-r--r--src/main/res/menu/editaccount.xml6
-rw-r--r--src/main/res/values/arrays.xml8
-rw-r--r--src/main/res/values/strings.xml10
-rw-r--r--src/main/res/xml/preferences.xml12
40 files changed, 683 insertions, 79 deletions
diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index 83f1c5177..182a8ee9e 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -88,6 +88,12 @@
android:screenOrientation="portrait"
android:launchMode="singleTask"/>
<activity
+ android:name=".ui.SetPresenceActivity"
+ android:label="@string/change_presence"
+ android:configChanges="orientation|screenSize"
+ android:windowSoftInputMode="stateHidden|adjustResize"
+ android:launchMode="singleTask"/>
+ <activity
android:name=".ui.SettingsActivity"
android:label="@string/title_activity_settings"/>
<activity
diff --git a/src/main/java/eu/siacs/conversations/entities/Account.java b/src/main/java/eu/siacs/conversations/entities/Account.java
index 2356ffb94..c172f0e73 100644
--- a/src/main/java/eu/siacs/conversations/entities/Account.java
+++ b/src/main/java/eu/siacs/conversations/entities/Account.java
@@ -43,6 +43,8 @@ public class Account extends AbstractEntity {
public static final String DISPLAY_NAME = "display_name";
public static final String HOSTNAME = "hostname";
public static final String PORT = "port";
+ public static final String STATUS = "status";
+ public static final String STATUS_MESSAGE = "status_message";
public static final String PINNED_MECHANISM_KEY = "pinned_mechanism";
@@ -168,15 +170,18 @@ public class Account extends AbstractEntity {
private final Roster roster = new Roster(this);
private List<Bookmark> bookmarks = new CopyOnWriteArrayList<>();
private final Collection<Jid> blocklist = new CopyOnWriteArraySet<>();
+ private Presence.Status presenceStatus = Presence.Status.ONLINE;
+ private String presenceStatusMessage = null;
public Account(final Jid jid, final String password) {
this(java.util.UUID.randomUUID().toString(), jid,
- password, 0, null, "", null, null, null, 5222);
+ password, 0, null, "", null, null, null, 5222, Presence.Status.ONLINE, null);
}
private Account(final String uuid, final Jid jid,
- final String password, final int options, final String rosterVersion, final String keys,
- final String avatar, String displayName, String hostname, int port) {
+ final String password, final int options, final String rosterVersion, final String keys,
+ final String avatar, String displayName, String hostname, int port,
+ final Presence.Status status, String statusMessage) {
this.uuid = uuid;
this.jid = jid;
if (jid.isBareJid()) {
@@ -194,6 +199,8 @@ public class Account extends AbstractEntity {
this.displayName = displayName;
this.hostname = hostname;
this.port = port;
+ this.presenceStatus = status;
+ this.presenceStatusMessage = statusMessage;
}
public static Account fromCursor(final Cursor cursor) {
@@ -212,7 +219,9 @@ public class Account extends AbstractEntity {
cursor.getString(cursor.getColumnIndex(AVATAR)),
cursor.getString(cursor.getColumnIndex(DISPLAY_NAME)),
cursor.getString(cursor.getColumnIndex(HOSTNAME)),
- cursor.getInt(cursor.getColumnIndex(PORT)));
+ cursor.getInt(cursor.getColumnIndex(PORT)),
+ Presence.Status.fromShowString(cursor.getString(cursor.getColumnIndex(STATUS))),
+ cursor.getString(cursor.getColumnIndex(STATUS_MESSAGE)));
}
public boolean isOptionSet(final int option) {
@@ -287,6 +296,22 @@ public class Account extends AbstractEntity {
return getXmppConnection() != null && getStatus().isError() && getXmppConnection().getAttempt() >= 3;
}
+ public void setPresenceStatus(Presence.Status status) {
+ this.presenceStatus = status;
+ }
+
+ public Presence.Status getPresenceStatus() {
+ return this.presenceStatus;
+ }
+
+ public void setPresenceStatusMessage(String message) {
+ this.presenceStatusMessage = message;
+ }
+
+ public String getPresenceStatusMessage() {
+ return this.presenceStatusMessage;
+ }
+
public String getResource() {
return jid.getResourcepart();
}
@@ -347,6 +372,8 @@ public class Account extends AbstractEntity {
values.put(DISPLAY_NAME, displayName);
values.put(HOSTNAME, hostname);
values.put(PORT, port);
+ values.put(STATUS, presenceStatus.toShowString());
+ values.put(STATUS_MESSAGE, presenceStatusMessage);
return values;
}
diff --git a/src/main/java/eu/siacs/conversations/entities/Bookmark.java b/src/main/java/eu/siacs/conversations/entities/Bookmark.java
index 088dfd8ae..8d97751c1 100644
--- a/src/main/java/eu/siacs/conversations/entities/Bookmark.java
+++ b/src/main/java/eu/siacs/conversations/entities/Bookmark.java
@@ -1,5 +1,7 @@
package eu.siacs.conversations.entities;
+import android.content.Context;
+
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
@@ -76,7 +78,7 @@ public class Bookmark extends Element implements ListItem {
}
@Override
- public List<Tag> getTags() {
+ public List<Tag> getTags(Context context) {
ArrayList<Tag> tags = new ArrayList<Tag>();
for (Element element : getChildren()) {
if (element.getName().equals("group") && element.getContent() != null) {
@@ -114,7 +116,8 @@ public class Bookmark extends Element implements ListItem {
}
}
- public boolean match(String needle) {
+ @Override
+ public boolean match(Context context, String needle) {
if (needle == null) {
return true;
}
@@ -122,12 +125,12 @@ public class Bookmark extends Element implements ListItem {
final Jid jid = getJid();
return (jid != null && jid.toString().contains(needle)) ||
getDisplayName().toLowerCase(Locale.US).contains(needle) ||
- matchInTag(needle);
+ matchInTag(context, needle);
}
- private boolean matchInTag(String needle) {
+ private boolean matchInTag(Context context, String needle) {
needle = needle.toLowerCase(Locale.US);
- for (Tag tag : getTags()) {
+ for (Tag tag : getTags(context)) {
if (tag.getName().toLowerCase(Locale.US).contains(needle)) {
return true;
}
diff --git a/src/main/java/eu/siacs/conversations/entities/Contact.java b/src/main/java/eu/siacs/conversations/entities/Contact.java
index 691fc3e45..e608688ba 100644
--- a/src/main/java/eu/siacs/conversations/entities/Contact.java
+++ b/src/main/java/eu/siacs/conversations/entities/Contact.java
@@ -1,6 +1,7 @@
package eu.siacs.conversations.entities;
import android.content.ContentValues;
+import android.content.Context;
import android.database.Cursor;
import org.json.JSONArray;
@@ -141,25 +142,14 @@ public class Contact implements ListItem, Blockable {
}
@Override
- public List<Tag> getTags() {
+ public List<Tag> getTags(Context context) {
final ArrayList<Tag> tags = new ArrayList<>();
for (final String group : getGroups()) {
tags.add(new Tag(group, UIHelper.getColorForName(group)));
}
- switch (getMostAvailableStatus()) {
- case CHAT:
- case ONLINE:
- tags.add(new Tag("online", 0xff259b24));
- break;
- case AWAY:
- tags.add(new Tag("away", 0xffff9800));
- break;
- case XA:
- tags.add(new Tag("not available", 0xfff44336));
- break;
- case DND:
- tags.add(new Tag("dnd", 0xfff44336));
- break;
+ Presence.Status status = getMostAvailableStatus();
+ if (status != Presence.Status.OFFLINE) {
+ tags.add(UIHelper.getTagForStatus(context, status));
}
if (isBlocked()) {
tags.add(new Tag("blocked", 0xff2e2f3b));
@@ -167,7 +157,7 @@ public class Contact implements ListItem, Blockable {
return tags;
}
- public boolean match(String needle) {
+ public boolean match(Context context, String needle) {
if (needle == null || needle.isEmpty()) {
return true;
}
@@ -175,7 +165,7 @@ public class Contact implements ListItem, Blockable {
String[] parts = needle.split("\\s+");
if (parts.length > 1) {
for(int i = 0; i < parts.length; ++i) {
- if (!match(parts[i])) {
+ if (!match(context, parts[i])) {
return false;
}
}
@@ -183,13 +173,13 @@ public class Contact implements ListItem, Blockable {
} else {
return jid.toString().contains(needle) ||
getDisplayName().toLowerCase(Locale.US).contains(needle) ||
- matchInTag(needle);
+ matchInTag(context, needle);
}
}
- private boolean matchInTag(String needle) {
+ private boolean matchInTag(Context context, String needle) {
needle = needle.toLowerCase(Locale.US);
- for (Tag tag : getTags()) {
+ for (Tag tag : getTags(context)) {
if (tag.getName().toLowerCase(Locale.US).contains(needle)) {
return true;
}
diff --git a/src/main/java/eu/siacs/conversations/entities/ListItem.java b/src/main/java/eu/siacs/conversations/entities/ListItem.java
index 22aedd4b3..178df2d16 100644
--- a/src/main/java/eu/siacs/conversations/entities/ListItem.java
+++ b/src/main/java/eu/siacs/conversations/entities/ListItem.java
@@ -1,5 +1,7 @@
package eu.siacs.conversations.entities;
+import android.content.Context;
+
import java.util.List;
import eu.siacs.conversations.xmpp.jid.Jid;
@@ -11,7 +13,7 @@ public interface ListItem extends Comparable<ListItem> {
Jid getJid();
- List<Tag> getTags();
+ List<Tag> getTags(Context context);
final class Tag {
private final String name;
@@ -31,5 +33,5 @@ public interface ListItem extends Comparable<ListItem> {
}
}
- boolean match(final String needle);
+ boolean match(Context context, final String needle);
}
diff --git a/src/main/java/eu/siacs/conversations/entities/Presence.java b/src/main/java/eu/siacs/conversations/entities/Presence.java
index 442f1bcac..485adaa13 100644
--- a/src/main/java/eu/siacs/conversations/entities/Presence.java
+++ b/src/main/java/eu/siacs/conversations/entities/Presence.java
@@ -17,41 +17,46 @@ public class Presence implements Comparable {
case XA: return "xa";
case DND: return "dnd";
}
-
return null;
}
+
+ public static Status fromShowString(String show) {
+ if (show == null) {
+ return ONLINE;
+ } else {
+ switch (show.toLowerCase(Locale.US)) {
+ case "away":
+ return AWAY;
+ case "xa":
+ return XA;
+ case "dnd":
+ return DND;
+ case "chat":
+ return CHAT;
+ default:
+ return ONLINE;
+ }
+ }
+ }
}
protected final Status status;
protected ServiceDiscoveryResult disco;
protected final String ver;
protected final String hash;
+ protected final String message;
- private Presence(Status status, String ver, String hash) {
+ private Presence(Status status, String ver, String hash, String message) {
this.status = status;
this.ver = ver;
this.hash = hash;
+ this.message = message;
}
- public static Presence parse(String show, Element caps) {
+ public static Presence parse(String show, Element caps, String message) {
final String hash = caps == null ? null : caps.getAttribute("hash");
final String ver = caps == null ? null : caps.getAttribute("ver");
- if (show == null) {
- return new Presence(Status.ONLINE, ver, hash);
- } else {
- switch (show.toLowerCase(Locale.US)) {
- case "away":
- return new Presence(Status.AWAY, ver, hash);
- case "xa":
- return new Presence(Status.XA, ver, hash);
- case "dnd":
- return new Presence(Status.DND, ver, hash);
- case "chat":
- return new Presence(Status.CHAT, ver, hash);
- default:
- return new Presence(Status.ONLINE, ver, hash);
- }
- }
+ return new Presence(Status.fromShowString(show), ver, hash, message);
}
public int compareTo(Object other) {
diff --git a/src/main/java/eu/siacs/conversations/entities/PresenceTemplate.java b/src/main/java/eu/siacs/conversations/entities/PresenceTemplate.java
new file mode 100644
index 000000000..dd45d7e46
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/entities/PresenceTemplate.java
@@ -0,0 +1,55 @@
+package eu.siacs.conversations.entities;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+
+
+public class PresenceTemplate extends AbstractEntity {
+
+ public static final String TABELNAME = "presence_templates";
+ public static final String LAST_USED = "last_used";
+ public static final String MESSAGE = "message";
+ public static final String STATUS = "status";
+
+ private long lastUsed = 0;
+ private String statusMessage;
+ private Presence.Status status = Presence.Status.ONLINE;
+
+ public PresenceTemplate(Presence.Status status, String statusMessage) {
+ this.status = status;
+ this.statusMessage = statusMessage;
+ this.lastUsed = System.currentTimeMillis();
+ this.uuid = java.util.UUID.randomUUID().toString();
+ }
+
+ private PresenceTemplate() {
+
+ }
+
+ @Override
+ public ContentValues getContentValues() {
+ ContentValues values = new ContentValues();
+ values.put(LAST_USED, lastUsed);
+ values.put(MESSAGE, statusMessage);
+ values.put(STATUS, status.toShowString());
+ values.put(UUID, uuid);
+ return values;
+ }
+
+ public static PresenceTemplate fromCursor(Cursor cursor) {
+ PresenceTemplate template = new PresenceTemplate();
+ template.uuid = cursor.getString(cursor.getColumnIndex(UUID));
+ template.lastUsed = cursor.getLong(cursor.getColumnIndex(LAST_USED));
+ template.statusMessage = cursor.getString(cursor.getColumnIndex(MESSAGE));
+ template.status = Presence.Status.fromShowString(cursor.getString(cursor.getColumnIndex(STATUS)));
+ return template;
+ }
+
+ public Presence.Status getStatus() {
+ return status;
+ }
+
+ public String getStatusMessage() {
+ return statusMessage;
+ }
+}
diff --git a/src/main/java/eu/siacs/conversations/parser/PresenceParser.java b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java
index c2782d232..5c5ca6bef 100644
--- a/src/main/java/eu/siacs/conversations/parser/PresenceParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java
@@ -196,7 +196,8 @@ public class PresenceParser extends AbstractParser implements
final String show = packet.findChildContent("show");
final Element caps = packet.findChild("c", "http://jabber.org/protocol/caps");
- final Presence presence = Presence.parse(show, caps);
+ final String message = packet.findChildContent("status");
+ final Presence presence = Presence.parse(show, caps, message);
contact.updatePresence(resource, presence);
if (presence.hasCaps() && Config.REQUEST_DISCO) {
mXmppConnectionService.fetchCaps(account, from, presence);
diff --git a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
index 04c532382..1ccc1e412 100644
--- a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
+++ b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
@@ -41,6 +41,7 @@ import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Message;
+import eu.siacs.conversations.entities.PresenceTemplate;
import eu.siacs.conversations.entities.Roster;
import eu.siacs.conversations.entities.ServiceDiscoveryResult;
import eu.siacs.conversations.xmpp.jid.InvalidJidException;
@@ -51,7 +52,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
private static DatabaseBackend instance = null;
private static final String DATABASE_NAME = "history";
- private static final int DATABASE_VERSION = 25;
+ private static final int DATABASE_VERSION = 26;
private static String CREATE_CONTATCS_STATEMENT = "create table "
+ Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, "
@@ -73,6 +74,14 @@ public class DatabaseBackend extends SQLiteOpenHelper {
+ "UNIQUE(" + ServiceDiscoveryResult.HASH + ", "
+ ServiceDiscoveryResult.VER + ") ON CONFLICT REPLACE);";
+ private static String CREATE_PRESENCE_TEMPLATES_STATEMENT = "CREATE TABLE "
+ + PresenceTemplate.TABELNAME + "("
+ + PresenceTemplate.UUID + " TEXT, "
+ + PresenceTemplate.LAST_USED + " NUMBER,"
+ + PresenceTemplate.MESSAGE + " TEXT,"
+ + PresenceTemplate.STATUS + " TEXT,"
+ + "UNIQUE("+PresenceTemplate.MESSAGE + "," +PresenceTemplate.STATUS+") ON CONFLICT REPLACE);";
+
private static String CREATE_PREKEYS_STATEMENT = "CREATE TABLE "
+ SQLiteAxolotlStore.PREKEY_TABLENAME + "("
+ SQLiteAxolotlStore.ACCOUNT + " TEXT, "
@@ -175,6 +184,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
db.execSQL(CREATE_PREKEYS_STATEMENT);
db.execSQL(CREATE_SIGNED_PREKEYS_STATEMENT);
db.execSQL(CREATE_IDENTITIES_STATEMENT);
+ db.execSQL(CREATE_PRESENCE_TEMPLATES_STATEMENT);
}
@Override
@@ -331,6 +341,10 @@ public class DatabaseBackend extends SQLiteOpenHelper {
db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.HOSTNAME + " TEXT");
db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.PORT + " NUMBER DEFAULT 5222");
}
+ if (oldVersion < 26 && newVersion >= 26) {
+ db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.STATUS + " TEXT");
+ db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.STATUS_MESSAGE + " TEXT");
+ }
/* Any migrations that alter the Account table need to happen BEFORE this migration, as it
* depends on account de-serialization.
*/
@@ -380,6 +394,10 @@ public class DatabaseBackend extends SQLiteOpenHelper {
if (oldVersion < 25 && newVersion >= 25) {
db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.OOB + " INTEGER");
}
+
+ if (oldVersion < 26 && newVersion >= 26) {
+ db.execSQL(CREATE_PRESENCE_TEMPLATES_STATEMENT);
+ }
}
public static synchronized DatabaseBackend getInstance(Context context) {
@@ -430,6 +448,30 @@ public class DatabaseBackend extends SQLiteOpenHelper {
return result;
}
+ public void insertPresenceTemplate(PresenceTemplate template) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ db.insert(PresenceTemplate.TABELNAME, null, template.getContentValues());
+ }
+
+ public List<PresenceTemplate> getPresenceTemplates() {
+ ArrayList<PresenceTemplate> templates = new ArrayList<>();
+ SQLiteDatabase db = this.getReadableDatabase();
+ Cursor cursor = db.query(PresenceTemplate.TABELNAME,null,null,null,null,null,PresenceTemplate.LAST_USED+" desc");
+ while (cursor.moveToNext()) {
+ templates.add(PresenceTemplate.fromCursor(cursor));
+ }
+ cursor.close();
+ return templates;
+ }
+
+ public void deletePresenceTemplate(PresenceTemplate template) {
+ Log.d(Config.LOGTAG,"deleting presence template with uuid "+template.getUuid());
+ SQLiteDatabase db = this.getWritableDatabase();
+ String where = PresenceTemplate.UUID+"=?";
+ String[] whereArgs = {template.getUuid()};
+ db.delete(PresenceTemplate.TABELNAME,where,whereArgs);
+ }
+
public CopyOnWriteArrayList<Conversation> getConversations(int status) {
CopyOnWriteArrayList<Conversation> list = new CopyOnWriteArrayList<>();
SQLiteDatabase db = this.getReadableDatabase();
diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
index 5040664ab..4aee6079e 100644
--- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
+++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
@@ -73,6 +73,7 @@ import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.MucOptions;
import eu.siacs.conversations.entities.MucOptions.OnRenameListener;
import eu.siacs.conversations.entities.Presence;
+import eu.siacs.conversations.entities.PresenceTemplate;
import eu.siacs.conversations.entities.Roster;
import eu.siacs.conversations.entities.ServiceDiscoveryResult;
import eu.siacs.conversations.entities.Transferable;
@@ -629,6 +630,10 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
return getPreferences().getBoolean("xa_on_silent_mode", false);
}
+ private boolean manuallyChangePresence() {
+ return getPreferences().getBoolean("manually_change_presence", false);
+ }
+
private boolean treatVibrateAsSilent() {
return getPreferences().getBoolean("treat_vibrate_as_silent", false);
}
@@ -762,7 +767,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
public void toggleScreenEventReceiver() {
- if (awayWhenScreenOff()) {
+ if (awayWhenScreenOff() && !manuallyChangePresence()) {
final IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
registerReceiver(this.mEventReceiver, filter);
@@ -2998,7 +3003,17 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
public void sendPresence(final Account account) {
- sendPresencePacket(account, mPresenceGenerator.selfPresence(account, getTargetPresence()));
+ PresencePacket packet;
+ if (manuallyChangePresence()) {
+ packet = mPresenceGenerator.selfPresence(account, account.getPresenceStatus());
+ String message = account.getPresenceStatusMessage();
+ if (message != null && !message.isEmpty()) {
+ packet.addChild(new Element("status").setContent(message));
+ }
+ } else {
+ packet = mPresenceGenerator.selfPresence(account, getTargetPresence());
+ }
+ sendPresencePacket(account, packet);
}
public void refreshAllPresences() {
@@ -3229,6 +3244,27 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
return pending;
}
+ public void changeStatus(Account account, Presence.Status status, String statusMessage) {
+ databaseBackend.insertPresenceTemplate(new PresenceTemplate(status, statusMessage));
+ changeStatusReal(account, status, statusMessage);
+ }
+
+ private void changeStatusReal(Account account, Presence.Status status, String statusMessage) {
+ account.setPresenceStatus(status);
+ account.setPresenceStatusMessage(statusMessage);
+ databaseBackend.updateAccount(account);
+ if (!account.isOptionSet(Account.OPTION_DISABLED)) {
+ sendPresence(account);
+ }
+ }
+
+ public void changeStatus(Presence.Status status, String statusMessage) {
+ databaseBackend.insertPresenceTemplate(new PresenceTemplate(status, statusMessage));
+ for(Account account : getAccounts()) {
+ changeStatusReal(account, status, statusMessage);
+ }
+ }
+
public interface OnMamPreferencesFetched {
void onPreferencesFetched(Element prefs);
void onPreferencesFetchFailed();
diff --git a/src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java b/src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java
index 5a85c17bd..abec8ed76 100644
--- a/src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java
@@ -49,7 +49,7 @@ public class BlocklistActivity extends AbstractSearchableListItemActivity implem
if (account != null) {
for (final Jid jid : account.getBlocklist()) {
final Contact contact = account.getRoster().getContact(jid);
- if (contact.match(needle) && contact.isBlocked()) {
+ if (contact.match(this, needle) && contact.isBlocked()) {
getListItems().add(contact);
}
}
diff --git a/src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java b/src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java
index c5357a5ef..1698343b0 100644
--- a/src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java
@@ -144,7 +144,7 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity {
for (final Contact contact : account.getRoster().getContacts()) {
if (contact.showInRoster() &&
!filterContacts.contains(contact.getJid().toBareJid().toString())
- && contact.match(needle)) {
+ && contact.match(this, needle)) {
getListItems().add(contact);
}
}
diff --git a/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java
index a27570704..d74203592 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java
@@ -643,17 +643,6 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
}
}
- @SuppressWarnings("deprecation")
- @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
- private void setListItemBackgroundOnView(View view) {
- int sdk = android.os.Build.VERSION.SDK_INT;
- if (sdk < android.os.Build.VERSION_CODES.JELLY_BEAN) {
- view.setBackgroundDrawable(getResources().getDrawable(R.drawable.greybackground));
- } else {
- view.setBackground(getResources().getDrawable(R.drawable.greybackground));
- }
- }
-
private void viewPgpKey(User user) {
PgpEngine pgp = xmppConnectionService.getPgpEngine();
if (pgp != null) {
diff --git a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java
index e4fc59fab..308bc742f 100644
--- a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java
@@ -439,7 +439,7 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
keys.setVisibility(View.GONE);
}
- List<ListItem.Tag> tagList = contact.getTags();
+ List<ListItem.Tag> tagList = contact.getTags(this);
if (tagList.size() == 0 || !this.showDynamicTags) {
tags.setVisibility(View.GONE);
} else {
diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
index 095bf58fa..23b68e3b0 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
@@ -474,9 +474,15 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
}
} else {
Account account = message.getConversation().getAccount();
- Intent intent = new Intent(activity, EditAccountActivity.class);
- intent.putExtra("jid", account.getJid().toBareJid().toString());
- intent.putExtra("fingerprint", message.getFingerprint());
+ Intent intent;
+ if (activity.manuallyChangePresence()) {
+ intent = new Intent(activity, SetPresenceActivity.class);
+ intent.putExtra(SetPresenceActivity.EXTRA_ACCOUNT, account.getJid().toBareJid().toString());
+ } else {
+ intent = new Intent(activity, EditAccountActivity.class);
+ intent.putExtra("jid", account.getJid().toBareJid().toString());
+ intent.putExtra("fingerprint", message.getFingerprint());
+ }
startActivity(intent);
}
}
diff --git a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
index b2c7f1a91..04824193b 100644
--- a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
@@ -476,6 +476,9 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
final MenuItem clearDevices = menu.findItem(R.id.action_clear_devices);
final MenuItem renewCertificate = menu.findItem(R.id.action_renew_certificate);
final MenuItem mamPrefs = menu.findItem(R.id.action_mam_prefs);
+ final MenuItem changePresence = menu.findItem(R.id.action_change_presence);
+
+ changePresence.setVisible(manuallyChangePresence());
renewCertificate.setVisible(mAccount != null && mAccount.getPrivateKeyAlias() != null);
@@ -536,8 +539,8 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
protected void onBackendConnected() {
if (this.jidToEdit != null) {
this.mAccount = xmppConnectionService.findAccountByJid(jidToEdit);
- this.mInitMode |= this.mAccount.isOptionSet(Account.OPTION_REGISTER);
if (this.mAccount != null) {
+ this.mInitMode |= this.mAccount.isOptionSet(Account.OPTION_REGISTER);
if (this.mAccount.getPrivateKeyAlias() != null) {
this.mPassword.setHint(R.string.authenticate_with_certificate);
if (this.mInitMode) {
@@ -593,6 +596,9 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
case R.id.action_renew_certificate:
renewCertificate();
break;
+ case R.id.action_change_presence:
+ changePresence();
+ break;
}
return super.onOptionsItemSelected(item);
}
@@ -601,6 +607,12 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
KeyChain.choosePrivateKeyAlias(this, this, null, null, null, -1, null);
}
+ private void changePresence() {
+ Intent intent = new Intent(this, SetPresenceActivity.class);
+ intent.putExtra(SetPresenceActivity.EXTRA_ACCOUNT,mAccount.getJid().toBareJid().toString());
+ startActivity(intent);
+ }
+
@Override
public void alias(String alias) {
if (alias != null) {
diff --git a/src/main/java/eu/siacs/conversations/ui/SetPresenceActivity.java b/src/main/java/eu/siacs/conversations/ui/SetPresenceActivity.java
new file mode 100644
index 000000000..5fe9c5d8c
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/ui/SetPresenceActivity.java
@@ -0,0 +1,200 @@
+package eu.siacs.conversations.ui;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.ScrollView;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import android.util.Log;
+
+import java.util.List;
+import java.util.concurrent.RunnableFuture;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import eu.siacs.conversations.Config;
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.entities.ListItem;
+import eu.siacs.conversations.entities.Presence;
+import eu.siacs.conversations.entities.PresenceTemplate;
+import eu.siacs.conversations.utils.UIHelper;
+
+public class SetPresenceActivity extends XmppActivity implements View.OnClickListener {
+
+ //data
+ protected Account mAccount;
+ private List<PresenceTemplate> mTemplates;
+
+ //UI Elements
+ protected ScrollView mScrollView;
+ protected EditText mStatusMessage;
+ protected Spinner mShowSpinner;
+ protected CheckBox mAllAccounts;
+ protected LinearLayout mTemplatesView;
+
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_set_presence);
+ mScrollView = (ScrollView) findViewById(R.id.scroll_view);
+ mShowSpinner = (Spinner) findViewById(R.id.presence_show);
+ ArrayAdapter adapter = ArrayAdapter.createFromResource(this,
+ R.array.presence_show_options,
+ R.layout.simple_list_item);
+ mShowSpinner.setAdapter(adapter);
+ mShowSpinner.setSelection(1);
+ mStatusMessage = (EditText) findViewById(R.id.presence_status_message);
+ mAllAccounts = (CheckBox) findViewById(R.id.all_accounts);
+ mTemplatesView = (LinearLayout) findViewById(R.id.templates);
+ final Button changePresence = (Button) findViewById(R.id.change_presence);
+ changePresence.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ executeChangePresence();
+ }
+ });
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.change_presence, menu);
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem item) {
+ if (item.getItemId() == R.id.action_account_details) {
+ if (mAccount != null) {
+ switchToAccount(mAccount);
+ }
+ return true;
+ } else {
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ private void executeChangePresence() {
+ Presence.Status status = getStatusFromSpinner();
+ boolean allAccounts = mAllAccounts.isChecked();
+ String statusMessage = mStatusMessage.getText().toString().trim();
+ if (allAccounts) {
+ xmppConnectionService.changeStatus(status, statusMessage);
+ } else if (mAccount != null) {
+ xmppConnectionService.changeStatus(mAccount, status, statusMessage);
+ }
+ finish();
+ }
+
+ private Presence.Status getStatusFromSpinner() {
+ switch (mShowSpinner.getSelectedItemPosition()) {
+ case 0:
+ return Presence.Status.CHAT;
+ case 2:
+ return Presence.Status.AWAY;
+ case 3:
+ return Presence.Status.XA;
+ case 4:
+ return Presence.Status.DND;
+ default:
+ return Presence.Status.ONLINE;
+ }
+ }
+
+ private void setStatusInSpinner(Presence.Status status) {
+ switch(status) {
+ case AWAY:
+ mShowSpinner.setSelection(2);
+ break;
+ case XA:
+ mShowSpinner.setSelection(3);
+ break;
+ case CHAT:
+ mShowSpinner.setSelection(0);
+ break;
+ case DND:
+ mShowSpinner.setSelection(4);
+ break;
+ default:
+ mShowSpinner.setSelection(1);
+ break;
+ }
+ }
+
+ @Override
+ protected void refreshUiReal() {
+
+ }
+
+ @Override
+ void onBackendConnected() {
+ mAccount = extractAccount(getIntent());
+ if (mAccount != null) {
+ setStatusInSpinner(mAccount.getPresenceStatus());
+ String message = mAccount.getPresenceStatusMessage();
+ if (mStatusMessage.getText().length() == 0 && message != null) {
+ mStatusMessage.append(message);
+ }
+ mTemplates = xmppConnectionService.databaseBackend.getPresenceTemplates();
+ }
+ redrawTemplates();
+ }
+
+ private void redrawTemplates() {
+ if (mTemplates == null || mTemplates.size() == 0) {
+ mTemplatesView.setVisibility(View.GONE);
+ } else {
+ mTemplatesView.removeAllViews();
+ mTemplatesView.setVisibility(View.VISIBLE);
+ LayoutInflater inflater = getLayoutInflater();
+ for (PresenceTemplate template : mTemplates) {
+ View templateLayout = inflater.inflate(R.layout.presence_template, mTemplatesView, false);
+ templateLayout.setTag(template);
+ setListItemBackgroundOnView(templateLayout);
+ templateLayout.setOnClickListener(this);
+ TextView message = (TextView) templateLayout.findViewById(R.id.presence_status_message);
+ TextView status = (TextView) templateLayout.findViewById(R.id.status);
+ ImageButton button = (ImageButton) templateLayout.findViewById(R.id.delete_button);
+ button.setTag(template);
+ button.setOnClickListener(this);
+ ListItem.Tag tag = UIHelper.getTagForStatus(this, template.getStatus());
+ status.setText(tag.getName());
+ status.setBackgroundColor(tag.getColor());
+ message.setText(template.getStatusMessage());
+ mTemplatesView.addView(templateLayout);
+ }
+ }
+ }
+
+ @Override
+ public void onClick(View v) {
+ PresenceTemplate template = (PresenceTemplate) v.getTag();
+ if (template == null) {
+ return;
+ }
+ if (v.getId() == R.id.presence_template) {
+ setStatusInSpinner(template.getStatus());
+ mStatusMessage.getEditableText().clear();
+ mStatusMessage.getEditableText().append(template.getStatusMessage());
+ new Handler().post(new Runnable() {
+ @Override
+ public void run() {
+ mScrollView.smoothScrollTo(0,0);
+ }
+ });
+ } else if (v.getId() == R.id.delete_button) {
+ xmppConnectionService.databaseBackend.deletePresenceTemplate(template);
+ mTemplates.remove(template);
+ redrawTemplates();
+ }
+ }
+}
diff --git a/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java b/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java
index 750a74212..6eef7eb3a 100644
--- a/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java
@@ -161,7 +161,8 @@ public class SettingsActivity extends XmppActivity implements
"xa_on_silent_mode",
"away_when_screen_off",
"allow_message_correction",
- "treat_vibrate_as_silent");
+ "treat_vibrate_as_silent",
+ "manually_change_presence");
if (name.equals("resource")) {
String resource = preferences.getString("resource", "mobile")
.toLowerCase(Locale.US);
@@ -182,7 +183,8 @@ public class SettingsActivity extends XmppActivity implements
xmppConnectionService.toggleForegroundService();
} else if (resendPresence.contains(name)) {
if (xmppConnectionServiceBound) {
- if (name.equals("away_when_screen_off")) {
+ if (name.equals("away_when_screen_off")
+ || name.equals("manually_change_presence")) {
xmppConnectionService.toggleScreenEventReceiver();
}
xmppConnectionService.refreshAllPresences();
diff --git a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java
index 7d650e5bb..4a59da21e 100644
--- a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java
@@ -738,7 +738,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
for (Contact contact : account.getRoster().getContacts()) {
Presence p = contact.getPresences().getMostAvailablePresence();
Presence.Status s = p == null ? Presence.Status.OFFLINE : p.getStatus();
- if (contact.showInRoster() && contact.match(needle)
+ if (contact.showInRoster() && contact.match(this, needle)
&& (!this.mHideOfflineContacts
|| (needle != null && !needle.trim().isEmpty())
|| s.compareTo(Presence.Status.OFFLINE) < 0)) {
@@ -756,7 +756,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
for (Account account : xmppConnectionService.getAccounts()) {
if (account.getStatus() != Account.State.DISABLED) {
for (Bookmark bookmark : account.getBookmarks()) {
- if (bookmark.match(needle)) {
+ if (bookmark.match(this, needle)) {
this.conferences.add(bookmark);
}
}
diff --git a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java
index 7d70b20fe..17086b7e1 100644
--- a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java
@@ -536,6 +536,17 @@ public abstract class XmppActivity extends Activity {
}
}
+ @SuppressWarnings("deprecation")
+ @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+ protected void setListItemBackgroundOnView(View view) {
+ int sdk = android.os.Build.VERSION.SDK_INT;
+ if (sdk < android.os.Build.VERSION_CODES.JELLY_BEAN) {
+ view.setBackgroundDrawable(getResources().getDrawable(R.drawable.greybackground));
+ } else {
+ view.setBackground(getResources().getDrawable(R.drawable.greybackground));
+ }
+ }
+
protected void choosePgpSignId(Account account) {
xmppConnectionService.getPgpEngine().chooseKey(account, new UiCallback<Account>() {
@Override
@@ -1006,6 +1017,10 @@ public abstract class XmppActivity extends Activity {
return getPreferences().getString("picture_compression", "auto").equals("never");
}
+ protected boolean manuallyChangePresence() {
+ return getPreferences().getBoolean("manually_change_presence", false);
+ }
+
protected void unregisterNdefPushMessageCallback() {
NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
if (nfcAdapter != null && nfcAdapter.isEnabled()) {
diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java
index da8e39101..02c54b77e 100644
--- a/src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java
+++ b/src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java
@@ -62,7 +62,7 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
ImageView picture = (ImageView) view.findViewById(R.id.contact_photo);
LinearLayout tagLayout = (LinearLayout) view.findViewById(R.id.tags);
- List<ListItem.Tag> tags = item.getTags();
+ List<ListItem.Tag> tags = item.getTags(activity);
if (tags.size() == 0 || !this.showDynamicTags) {
tagLayout.setVisibility(View.GONE);
} else {
diff --git a/src/main/java/eu/siacs/conversations/utils/UIHelper.java b/src/main/java/eu/siacs/conversations/utils/UIHelper.java
index add3d80cb..5ab058d14 100644
--- a/src/main/java/eu/siacs/conversations/utils/UIHelper.java
+++ b/src/main/java/eu/siacs/conversations/utils/UIHelper.java
@@ -14,7 +14,9 @@ import java.util.Locale;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
+import eu.siacs.conversations.entities.ListItem;
import eu.siacs.conversations.entities.Message;
+import eu.siacs.conversations.entities.Presence;
import eu.siacs.conversations.entities.Transferable;
import eu.siacs.conversations.xmpp.jid.Jid;
@@ -267,4 +269,19 @@ public class UIHelper {
body = body.replace("?","").replace("¿","");
return LOCATION_QUESTIONS.contains(body);
}
+
+ public static ListItem.Tag getTagForStatus(Context context, Presence.Status status) {
+ switch (status) {
+ case CHAT:
+ return new ListItem.Tag(context.getString(R.string.presence_chat), 0xff259b24);
+ case AWAY:
+ return new ListItem.Tag(context.getString(R.string.presence_away), 0xffff9800);
+ case XA:
+ return new ListItem.Tag(context.getString(R.string.presence_xa), 0xfff44336);
+ case DND:
+ return new ListItem.Tag(context.getString(R.string.presence_dnd), 0xfff44336);
+ default:
+ return new ListItem.Tag(context.getString(R.string.presence_online), 0xff259b24);
+ }
+ }
}
diff --git a/src/main/res/drawable-hdpi/ic_account_box_white_24dp.png b/src/main/res/drawable-hdpi/ic_account_box_white_24dp.png
new file mode 100644
index 000000000..2f0f491d6
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_account_box_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-hdpi/ic_announcement_white_24dp.png b/src/main/res/drawable-hdpi/ic_announcement_white_24dp.png
new file mode 100644
index 000000000..034702001
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_announcement_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-mdpi/ic_account_box_white_24dp.png b/src/main/res/drawable-mdpi/ic_account_box_white_24dp.png
new file mode 100644
index 000000000..895b2ebf0
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_account_box_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-mdpi/ic_announcement_white_24dp.png b/src/main/res/drawable-mdpi/ic_announcement_white_24dp.png
new file mode 100644
index 000000000..e1039350b
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_announcement_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xhdpi/ic_account_box_white_24dp.png b/src/main/res/drawable-xhdpi/ic_account_box_white_24dp.png
new file mode 100644
index 000000000..1fbad8ca1
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_account_box_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xhdpi/ic_announcement_white_24dp.png b/src/main/res/drawable-xhdpi/ic_announcement_white_24dp.png
new file mode 100644
index 000000000..b1e9aa6ef
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_announcement_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxhdpi/ic_account_box_white_24dp.png b/src/main/res/drawable-xxhdpi/ic_account_box_white_24dp.png
new file mode 100644
index 000000000..56cbb9566
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_account_box_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxhdpi/ic_announcement_white_24dp.png b/src/main/res/drawable-xxhdpi/ic_announcement_white_24dp.png
new file mode 100644
index 000000000..d56a8f8e0
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_announcement_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_account_box_white_24dp.png b/src/main/res/drawable-xxxhdpi/ic_account_box_white_24dp.png
new file mode 100644
index 000000000..a543c76b9
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_account_box_white_24dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_announcement_white_24dp.png b/src/main/res/drawable-xxxhdpi/ic_announcement_white_24dp.png
new file mode 100644
index 000000000..3731138a3
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_announcement_white_24dp.png
Binary files differ
diff --git a/src/main/res/layout/activity_set_presence.xml b/src/main/res/layout/activity_set_presence.xml
new file mode 100644
index 000000000..8195092ff
--- /dev/null
+++ b/src/main/res/layout/activity_set_presence.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:background="@color/grey200"
+ android:id="@+id/scroll_view">
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="@dimen/activity_horizontal_margin"
+ android:layout_marginRight="@dimen/activity_horizontal_margin"
+ android:layout_marginTop="@dimen/activity_vertical_margin"
+ android:layout_marginBottom="@dimen/activity_vertical_margin"
+ android:background="@drawable/infocard_border"
+ android:padding="@dimen/infocard_padding"
+ android:orientation="vertical">
+ <EditText
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="textMultiLine"
+ android:hint="@string/status_message"
+ android:id="@+id/presence_status_message"
+ android:textColor="@color/black87"
+ android:layout_marginBottom="8dp"
+ android:textSize="?attr/TextSizeBody"/>
+ <Spinner
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/presence_show"
+ android:layout_gravity="center_horizontal"/>
+ <CheckBox
+ android:layout_marginTop="16dp"
+ android:layout_marginBottom="16dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/all_accounts_on_this_device"
+ android:id="@+id/all_accounts"
+ android:textColor="@color/black87"
+ android:textSize="?attr/TextSizeBody"/>
+ <Button
+ android:id="@+id/change_presence"
+ style="?android:attr/borderlessButtonStyle"
+ android:layout_marginRight="-8dp"
+ android:layout_marginBottom="-8dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right"
+ android:text="@string/change_presence"
+ android:textColor="@color/accent"/>
+ </LinearLayout>
+ <LinearLayout
+ android:id="@+id/templates"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="@dimen/activity_horizontal_margin"
+ android:layout_marginRight="@dimen/activity_horizontal_margin"
+ android:layout_marginTop="@dimen/activity_vertical_margin"
+ android:layout_marginBottom="@dimen/activity_vertical_margin"
+ android:background="@drawable/infocard_border"
+ android:padding="@dimen/infocard_padding"
+ android:orientation="vertical"
+ android:divider="?android:dividerHorizontal"
+ android:showDividers="middle">
+ </LinearLayout>
+ </LinearLayout>
+</ScrollView> \ No newline at end of file
diff --git a/src/main/res/layout/presence_template.xml b/src/main/res/layout/presence_template.xml
new file mode 100644
index 000000000..aa4ded5a6
--- /dev/null
+++ b/src/main/res/layout/presence_template.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="?android:attr/activatedBackgroundIndicator"
+ android:paddingTop="8dp"
+ android:paddingLeft="8dp"
+ android:paddingBottom="8dp"
+ android:id="@+id/presence_template">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_centerVertical="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_toLeftOf="@+id/delete_button"
+ android:layout_toStartOf="@+id/delete_button"
+ android:layout_marginRight="8dp">
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/presence_status_message"
+ android:textColor="@color/black87"
+ android:textSize="?attr/TextSizeBody"/>
+ <TextView
+ android:id="@+id/status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingBottom="1dp"
+ android:paddingLeft="4dp"
+ android:paddingRight="4dp"
+ android:paddingTop="1dp"
+ android:textAllCaps="true"
+ android:textColor="@color/white"
+ android:textSize="?attr/TextSizeInfo"
+ android:layout_marginTop="4dp"/>
+ </LinearLayout>
+ <ImageButton
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/delete_button"
+ android:layout_centerVertical="true"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
+ android:background="?android:selectableItemBackground"
+ android:padding="@dimen/image_button_padding"
+ android:src="?attr/icon_remove"/>
+</RelativeLayout> \ No newline at end of file
diff --git a/src/main/res/layout/simple_list_item.xml b/src/main/res/layout/simple_list_item.xml
new file mode 100644
index 000000000..8cbc1f923
--- /dev/null
+++ b/src/main/res/layout/simple_list_item.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2006 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/text1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textColor="@color/black87"
+ android:textSize="?attr/TextSizeBody"
+ android:gravity="center_vertical"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall" />
diff --git a/src/main/res/menu/change_presence.xml b/src/main/res/menu/change_presence.xml
new file mode 100644
index 000000000..f3dfadfd0
--- /dev/null
+++ b/src/main/res/menu/change_presence.xml
@@ -0,0 +1,17 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:id="@+id/action_account_details"
+ android:title="@string/account_details"
+ android:showAsAction="always"
+ android:icon="@drawable/ic_account_box_white_24dp"/>
+ <item
+ android:id="@+id/action_accounts"
+ android:orderInCategory="90"
+ android:showAsAction="never"
+ android:title="@string/action_accounts"/>
+ <item
+ android:id="@+id/action_settings"
+ android:orderInCategory="100"
+ android:showAsAction="never"
+ android:title="@string/action_settings"/>
+</menu> \ No newline at end of file
diff --git a/src/main/res/menu/editaccount.xml b/src/main/res/menu/editaccount.xml
index 9f06fc4c4..ef9f17323 100644
--- a/src/main/res/menu/editaccount.xml
+++ b/src/main/res/menu/editaccount.xml
@@ -1,6 +1,12 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
+ android:id="@+id/action_change_presence"
+ android:showAsAction="always"
+ android:title="@string/change_presence"
+ android:icon="@drawable/ic_announcement_white_24dp"/>
+
+ <item
android:id="@+id/action_show_qr_code"
android:showAsAction="never"
android:title="@string/show_qr_code"/>
diff --git a/src/main/res/values/arrays.xml b/src/main/res/values/arrays.xml
index 2058162ce..5a6353273 100644
--- a/src/main/res/values/arrays.xml
+++ b/src/main/res/values/arrays.xml
@@ -75,4 +75,12 @@
<item>@string/contacts</item>
<item>@string/always</item>
</string-array>
+
+ <string-array name="presence_show_options">
+ <item>@string/presence_chat</item>
+ <item>@string/presence_online</item>
+ <item>@string/presence_away</item>
+ <item>@string/presence_xa</item>
+ <item>@string/presence_dnd</item>
+ </string-array>
</resources>
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index 445a4a83a..7b96240fd 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -625,4 +625,14 @@
<string name="create_account">Create Account</string>
<string name="use_own_provider">Use my own provider</string>
<string name="pick_your_username">Pick your username</string>
+ <string name="pref_manually_change_presence">Manually change presence</string>
+ <string name="pref_manually_change_presence_summary">Touch your avatar to change your presence</string>
+ <string name="change_presence">Change Presence</string>
+ <string name="status_message">Status message</string>
+ <string name="all_accounts_on_this_device">Set for all accounts on this device</string>
+ <string name="presence_chat">Free for Chat</string>
+ <string name="presence_online">Online</string>
+ <string name="presence_away">Away</string>
+ <string name="presence_xa">Not Available</string>
+ <string name="presence_dnd">Busy</string>
</resources>
diff --git a/src/main/res/xml/preferences.xml b/src/main/res/xml/preferences.xml
index d1ba6b7f3..63dc71dbd 100644
--- a/src/main/res/xml/preferences.xml
+++ b/src/main/res/xml/preferences.xml
@@ -193,14 +193,22 @@
<PreferenceCategory android:title="@string/pref_presence_settings">
<CheckBoxPreference
android:defaultValue="false"
+ android:key="manually_change_presence"
+ android:title="@string/pref_manually_change_presence"
+ android:summary="@string/pref_manually_change_presence_summary"
+ android:disableDependentsState="true"/>
+ <CheckBoxPreference
+ android:defaultValue="false"
android:key="away_when_screen_off"
android:summary="@string/pref_away_when_screen_off_summary"
- android:title="@string/pref_away_when_screen_off"/>
+ android:title="@string/pref_away_when_screen_off"
+ android:dependency="manually_change_presence"/>
<CheckBoxPreference
android:defaultValue="false"
android:key="xa_on_silent_mode"
android:summary="@string/pref_xa_on_silent_mode_summary"
- android:title="@string/pref_xa_on_silent_mode"/>
+ android:title="@string/pref_xa_on_silent_mode"
+ android:dependency="manually_change_presence"/>
<CheckBoxPreference
android:dependency="xa_on_silent_mode"
android:defaultValue="false"