expert setting to manually change presence

This commit is contained in:
Daniel Gultsch 2016-04-22 21:25:06 +02:00
parent 195b745efc
commit 1901abd05f
40 changed files with 683 additions and 79 deletions

View File

@ -87,6 +87,12 @@
android:label="@string/create_account"
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"/>

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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) {

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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();

View File

@ -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();

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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) {

View File

@ -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 {

View File

@ -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);
}
}

View File

@ -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) {

View File

@ -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();
}
}
}

View File

@ -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();

View File

@ -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);
}
}

View File

@ -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()) {

View File

@ -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 {

View File

@ -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);
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 337 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 431 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 285 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 578 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 894 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 519 B

View File

@ -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>

View File

@ -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>

View File

@ -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" />

View File

@ -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>

View File

@ -1,5 +1,11 @@
<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"

View File

@ -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>

View File

@ -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>

View File

@ -191,16 +191,24 @@
android:title="@string/pref_display_enter_key"/>
</PreferenceCategory>
<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"