aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java
diff options
context:
space:
mode:
authorChristian Schneppe <christian@pix-art.de>2017-05-17 21:58:46 +0200
committerChristian Schneppe <christian@pix-art.de>2017-05-17 21:58:46 +0200
commit5a60dab1d5ead7a94d9d86cecc563210fed4b4bf (patch)
tree383ecee02d5fd06737fb7d4cfbfb2360c3c441ba /src/main/java
parentd1449161398a099db7f3c5e10ccfc07dd8cffe3b (diff)
use mam reference instead of timestamp
Diffstat (limited to '')
-rw-r--r--src/main/java/de/pixart/messenger/entities/Conversation.java28
-rw-r--r--src/main/java/de/pixart/messenger/generator/IqGenerator.java4
-rw-r--r--src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java16
-rw-r--r--src/main/java/de/pixart/messenger/services/MessageArchiveService.java90
-rw-r--r--src/main/java/de/pixart/messenger/services/XmppConnectionService.java17
-rw-r--r--src/main/java/de/pixart/messenger/ui/ConversationFragment.java2
-rw-r--r--src/main/java/de/pixart/messenger/ui/adapter/MessageAdapter.java7
-rw-r--r--src/main/java/de/pixart/messenger/xmpp/mam/MamReference.java69
8 files changed, 156 insertions, 77 deletions
diff --git a/src/main/java/de/pixart/messenger/entities/Conversation.java b/src/main/java/de/pixart/messenger/entities/Conversation.java
index 15dc844b1..d95894d00 100644
--- a/src/main/java/de/pixart/messenger/entities/Conversation.java
+++ b/src/main/java/de/pixart/messenger/entities/Conversation.java
@@ -31,6 +31,7 @@ import de.pixart.messenger.services.XmppConnectionService;
import de.pixart.messenger.xmpp.chatstate.ChatState;
import de.pixart.messenger.xmpp.jid.InvalidJidException;
import de.pixart.messenger.xmpp.jid.Jid;
+import de.pixart.messenger.xmpp.mam.MamReference;
public class Conversation extends AbstractEntity implements Blockable, Comparable<Conversation> {
@@ -363,12 +364,16 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
return this.mFirstMamReference;
}
- public void setLastClearHistory(long time) {
- setAttribute("ATTRIBUTE_LAST_CLEAR_HISTORY", String.valueOf(time));
+ public void setLastClearHistory(long time, String reference) {
+ if (reference != null) {
+ setAttribute(ATTRIBUTE_LAST_CLEAR_HISTORY, String.valueOf(time) + ":" + reference);
+ } else {
+ setAttribute(ATTRIBUTE_LAST_CLEAR_HISTORY, String.valueOf(time));
+ }
}
- public long getLastClearHistory() {
- return getLongAttribute("ATTRIBUTE_LAST_CLEAR_HISTORY", 0);
+ public MamReference getLastClearHistory() {
+ return MamReference.fromAttribute(getAttribute(ATTRIBUTE_LAST_CLEAR_HISTORY));
}
public List<Jid> getAcceptedCryptoTargets() {
@@ -482,11 +487,10 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
if (this.messages.size() == 0) {
Message message = new Message(this, "", Message.ENCRYPTION_NONE);
message.setType(Message.TYPE_STATUS);
- message.setTime(Math.max(getCreated(), getLastClearHistory()));
+ message.setTime(Math.max(getCreated(), getLastClearHistory().getTimestamp()));
return message;
} else {
- Message message = this.messages.get(this.messages.size() - 1);
- return message;
+ return this.messages.get(this.messages.size() - 1);
}
}
@@ -854,19 +858,19 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
}
}
- public long getLastMessageTransmitted() {
- final long last_clear = getLastClearHistory();
- long last_received = 0;
+ public MamReference getLastMessageTransmitted() {
+ final MamReference lastClear = getLastClearHistory();
+ MamReference lastReceived = new MamReference(0);
synchronized (this.messages) {
for (int i = this.messages.size() - 1; i >= 0; --i) {
Message message = this.messages.get(i);
if (message.getStatus() == Message.STATUS_RECEIVED || message.isCarbon()) {
- last_received = message.getTimeSent();
+ lastReceived = new MamReference(message.getTimeSent(),message.getServerMsgId());
break;
}
}
}
- return Math.max(last_clear, last_received);
+ return MamReference.max(lastClear,lastReceived);
}
public void setMutedTill(long value) {
diff --git a/src/main/java/de/pixart/messenger/generator/IqGenerator.java b/src/main/java/de/pixart/messenger/generator/IqGenerator.java
index 301f4d178..927d5d07c 100644
--- a/src/main/java/de/pixart/messenger/generator/IqGenerator.java
+++ b/src/main/java/de/pixart/messenger/generator/IqGenerator.java
@@ -246,7 +246,9 @@ public class IqGenerator extends AbstractGenerator {
} else if (mam.getWith() != null) {
data.put("with", mam.getWith().toString());
}
- data.put("start", getTimestamp(mam.getStart()));
+ if (mam.getStart() != 0) {
+ data.put("start", getTimestamp(mam.getStart()));
+ }
data.put("end", getTimestamp(mam.getEnd()));
data.submit();
query.addChild(data);
diff --git a/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java b/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java
index d6e45f5fe..ddf516f93 100644
--- a/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java
+++ b/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java
@@ -10,7 +10,6 @@ import android.database.sqlite.SQLiteOpenHelper;
import android.os.Environment;
import android.util.Base64;
import android.util.Log;
-import android.util.Pair;
import org.json.JSONException;
import org.json.JSONObject;
@@ -51,6 +50,7 @@ import de.pixart.messenger.entities.Roster;
import de.pixart.messenger.entities.ServiceDiscoveryResult;
import de.pixart.messenger.xmpp.jid.InvalidJidException;
import de.pixart.messenger.xmpp.jid.Jid;
+import de.pixart.messenger.xmpp.mam.MamReference;
public class DatabaseBackend extends SQLiteOpenHelper {
@@ -841,7 +841,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
return db.delete(Message.TABLENAME, where, whereArgs) > 0;
}
- public Pair<Long, String> getLastMessageReceived(Account account) {
+ public MamReference getLastMessageReceived(Account account) {
Cursor cursor = null;
try {
SQLiteDatabase db = this.getReadableDatabase();
@@ -852,7 +852,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
return null;
} else {
cursor.moveToFirst();
- return new Pair<>(cursor.getLong(0), cursor.getString(1));
+ return new MamReference(cursor.getLong(0), cursor.getString(1));
}
} catch (Exception e) {
return null;
@@ -877,23 +877,23 @@ public class DatabaseBackend extends SQLiteOpenHelper {
return time;
}
- public Pair<Long, String> getLastClearDate(Account account) {
+ public MamReference getLastClearDate(Account account) {
SQLiteDatabase db = this.getReadableDatabase();
String[] columns = {Conversation.ATTRIBUTES};
String selection = Conversation.ACCOUNT + "=?";
String[] args = {account.getUuid()};
Cursor cursor = db.query(Conversation.TABLENAME, columns, selection, args, null, null, null);
- long maxClearDate = 0;
+ MamReference maxClearDate = new MamReference(0);
while (cursor.moveToNext()) {
try {
- final JSONObject jsonObject = new JSONObject(cursor.getString(0));
- maxClearDate = Math.max(maxClearDate, jsonObject.getLong(Conversation.ATTRIBUTE_LAST_CLEAR_HISTORY));
+ final JSONObject o = new JSONObject(cursor.getString(0));
+ maxClearDate = MamReference.max(maxClearDate, MamReference.fromAttribute(o.getString(Conversation.ATTRIBUTE_LAST_CLEAR_HISTORY)));
} catch (Exception e) {
//ignored
}
}
cursor.close();
- return new Pair<>(maxClearDate, null);
+ return maxClearDate;
}
private Cursor getCursorForSession(Account account, AxolotlAddress contact) {
diff --git a/src/main/java/de/pixart/messenger/services/MessageArchiveService.java b/src/main/java/de/pixart/messenger/services/MessageArchiveService.java
index 5a5b0e9a1..e90bb8d07 100644
--- a/src/main/java/de/pixart/messenger/services/MessageArchiveService.java
+++ b/src/main/java/de/pixart/messenger/services/MessageArchiveService.java
@@ -1,7 +1,6 @@
package de.pixart.messenger.services;
import android.util.Log;
-import android.util.Pair;
import java.math.BigInteger;
import java.util.ArrayList;
@@ -19,6 +18,7 @@ import de.pixart.messenger.xml.Element;
import de.pixart.messenger.xmpp.OnAdvancedStreamFeaturesLoaded;
import de.pixart.messenger.xmpp.OnIqPacketReceived;
import de.pixart.messenger.xmpp.jid.Jid;
+import de.pixart.messenger.xmpp.mam.MamReference;
import de.pixart.messenger.xmpp.stanzas.IqPacket;
public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
@@ -46,34 +46,26 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
}
}
}
- final Pair<Long, String> lastMessageReceived = mXmppConnectionService.databaseBackend.getLastMessageReceived(account);
- final Pair<Long, String> lastClearDate = mXmppConnectionService.databaseBackend.getLastClearDate(account);
- long startCatchup;
- final String reference;
- if (lastMessageReceived != null && lastMessageReceived.first >= lastClearDate.first) {
- startCatchup = lastMessageReceived.first;
- reference = lastMessageReceived.second;
- } else {
- startCatchup = lastClearDate.first;
- reference = null;
- }
- startCatchup = Math.max(startCatchup,mXmppConnectionService.getAutomaticMessageDeletionDate());
+ MamReference mamReference = MamReference.max(
+ mXmppConnectionService.databaseBackend.getLastMessageReceived(account),
+ mXmppConnectionService.databaseBackend.getLastClearDate(account)
+ );
+ mamReference = MamReference.max(mamReference, mXmppConnectionService.getAutomaticMessageDeletionDate());
long endCatchup = account.getXmppConnection().getLastSessionEstablished();
final Query query;
- if (startCatchup == 0) {
+ if (mamReference.getTimestamp() == 0) {
return;
- } else if (endCatchup - startCatchup >= Config.MAM_MAX_CATCHUP) {
- startCatchup = endCatchup - Config.MAM_MAX_CATCHUP;
+ } else if (endCatchup - mamReference.getTimestamp() >= Config.MAM_MAX_CATCHUP) {
+ long startCatchup = endCatchup - Config.MAM_MAX_CATCHUP;
List<Conversation> conversations = mXmppConnectionService.getConversations();
for (Conversation conversation : conversations) {
- if (conversation.getMode() == Conversation.MODE_SINGLE && conversation.getAccount() == account && startCatchup > conversation.getLastMessageTransmitted()) {
+ if (conversation.getMode() == Conversation.MODE_SINGLE && conversation.getAccount() == account && startCatchup > conversation.getLastMessageTransmitted().getTimestamp()) {
this.query(conversation, startCatchup, true);
}
}
- query = new Query(account, startCatchup, endCatchup);
+ query = new Query(account, new MamReference(startCatchup), endCatchup);
} else {
- query = new Query(account, startCatchup, endCatchup);
- query.reference = reference;
+ query = new Query(account, mamReference, endCatchup);
}
synchronized (this.queries) {
this.queries.add(query);
@@ -82,9 +74,9 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
}
public void catchupMUC(final Conversation conversation) {
- if (conversation.getLastMessageTransmitted() < 0 && conversation.countMessages() == 0) {
+ if (conversation.getLastMessageTransmitted().getTimestamp() < 0 && conversation.countMessages() == 0) {
query(conversation,
- 0,
+ new MamReference(0),
System.currentTimeMillis(),
true);
} else {
@@ -96,9 +88,9 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
}
public Query query(final Conversation conversation) {
- if (conversation.getLastMessageTransmitted() < 0 && conversation.countMessages() == 0) {
+ if (conversation.getLastMessageTransmitted().getTimestamp() < 0 && conversation.countMessages() == 0) {
return query(conversation,
- 0,
+ new MamReference(0),
System.currentTimeMillis(),
false);
} else {
@@ -113,22 +105,27 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
return this.query(conversation, conversation.getLastMessageTransmitted(), end, allowCatchup);
}
- public Query query(Conversation conversation, long start, long end, boolean allowCatchup) {
+ public Query query(Conversation conversation, MamReference start, long end, boolean allowCatchup) {
synchronized (this.queries) {
final Query query;
- final long startActual = Math.max(start, mXmppConnectionService.getAutomaticMessageDeletionDate());
- if (start == 0) {
+ final MamReference startActual = MamReference.max(start, mXmppConnectionService.getAutomaticMessageDeletionDate());
+ if (start.getTimestamp() == 0) {
query = new Query(conversation, startActual, end, false);
+ query.reference = conversation.getFirstMamReference();
} else {
- long maxCatchup = Math.max(startActual, System.currentTimeMillis() - Config.MAM_MAX_CATCHUP);
- if (maxCatchup > startActual) {
- Query reverseCatchup = new Query(conversation, startActual, maxCatchup, false);
- this.queries.add(reverseCatchup);
- this.execute(reverseCatchup);
+ if (allowCatchup) {
+ MamReference maxCatchup = MamReference.max(startActual, System.currentTimeMillis() - Config.MAM_MAX_CATCHUP);
+ if (maxCatchup.greaterThan(startActual)) {
+ Query reverseCatchup = new Query(conversation, startActual, maxCatchup.getTimestamp(), false);
+ this.queries.add(reverseCatchup);
+ this.execute(reverseCatchup);
+ }
+ query = new Query(conversation, maxCatchup, end, allowCatchup);
+ } else {
+ query = new Query(conversation, startActual, end, false);
}
- query = new Query(conversation, maxCatchup, end, allowCatchup);
}
- if (start > end) {
+ if (start.greaterThan(end)) {
return null;
}
this.queries.add(query);
@@ -304,27 +301,26 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
private boolean catchup = true;
- public Query(Conversation conversation, long start, long end) {
- this(conversation.getAccount(), start, end);
+ public Query(Conversation conversation, MamReference start, long end, boolean catchup) {
+ this(conversation.getAccount(), catchup ? start : start.timeOnly(), end);
this.conversation = conversation;
- }
-
- public Query(Conversation conversation, long start, long end, boolean catchup) {
- this(conversation, start, end);
this.pagingOrder = catchup ? PagingOrder.NORMAL : PagingOrder.REVERSE;
this.catchup = catchup;
}
- public Query(Account account, long start, long end) {
+ public Query(Account account, MamReference start, long end) {
this.account = account;
- this.start = start;
+ if (start.getReference() != null) {
+ this.reference = start.getReference();
+ } else {
+ this.start = start.getTimestamp();
+ }
this.end = end;
this.queryId = new BigInteger(50, mXmppConnectionService.getRNG()).toString(32);
}
private Query page(String reference) {
- Query query = new Query(this.account, this.start, this.end);
- query.reference = reference;
+ Query query = new Query(this.account, new MamReference(this.start, reference), this.end);
query.conversation = conversation;
query.totalCount = totalCount;
query.actualCount = actualCount;
@@ -444,8 +440,10 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
builder.append(getWith().toString());
}
}
- builder.append(", start=");
- builder.append(AbstractGenerator.getTimestamp(this.start));
+ if (this.start != 0) {
+ builder.append(", start=");
+ builder.append(AbstractGenerator.getTimestamp(this.start));
+ }
builder.append(", end=");
builder.append(AbstractGenerator.getTimestamp(this.end));
builder.append(", order="+pagingOrder.toString());
diff --git a/src/main/java/de/pixart/messenger/services/XmppConnectionService.java b/src/main/java/de/pixart/messenger/services/XmppConnectionService.java
index 7bbc9aa78..f8842953d 100644
--- a/src/main/java/de/pixart/messenger/services/XmppConnectionService.java
+++ b/src/main/java/de/pixart/messenger/services/XmppConnectionService.java
@@ -140,6 +140,7 @@ import de.pixart.messenger.xmpp.jid.Jid;
import de.pixart.messenger.xmpp.jingle.JingleConnectionManager;
import de.pixart.messenger.xmpp.jingle.OnJinglePacketReceived;
import de.pixart.messenger.xmpp.jingle.stanzas.JinglePacket;
+import de.pixart.messenger.xmpp.mam.MamReference;
import de.pixart.messenger.xmpp.pep.Avatar;
import de.pixart.messenger.xmpp.stanzas.IqPacket;
import de.pixart.messenger.xmpp.stanzas.MessagePacket;
@@ -1754,10 +1755,10 @@ public class XmppConnectionService extends Service {
callback.onMoreMessagesLoaded(messages.size(), conversation);
} else if (conversation.hasMessagesLeftOnServer()
&& account.isOnlineAndConnected()
- && conversation.getLastClearHistory() == 0) {
+ && conversation.getLastClearHistory().getTimestamp() == 0) {
if ((conversation.getMode() == Conversation.MODE_SINGLE && account.getXmppConnection().getFeatures().mam())
|| (conversation.getMode() == Conversation.MODE_MULTI && conversation.getMucOptions().mamSupport())) {
- MessageArchiveService.Query query = getMessageArchiveService().query(conversation, 0, timestamp, false);
+ MessageArchiveService.Query query = getMessageArchiveService().query(conversation, new MamReference(0), timestamp, false);
if (query != null) {
query.setCallback(callback);
callback.informUser(R.string.fetching_history_from_server);
@@ -2417,7 +2418,7 @@ public class XmppConnectionService extends Service {
x.addChild("history").setAttribute("maxchars", "0");
} else {
// Fallback to muc history
- x.addChild("history").setAttribute("since", PresenceGenerator.getTimestamp(conversation.getLastMessageTransmitted()));
+ x.addChild("history").setAttribute("since", PresenceGenerator.getTimestamp(conversation.getLastMessageTransmitted().getTimestamp()));
}
sendPresencePacket(account, packet);
if (onConferenceJoined != null) {
@@ -3836,15 +3837,19 @@ public class XmppConnectionService extends Service {
}
public void clearConversationHistory(final Conversation conversation) {
- long clearDate;
+ final long clearDate;
+ final String reference;
if (conversation.countMessages() > 0) {
- clearDate = conversation.getLatestMessage().getTimeSent() + 1000;
+ Message latestMessage = conversation.getLatestMessage();
+ clearDate = latestMessage.getTimeSent() + 1000;
+ reference = latestMessage.getServerMsgId();
} else {
clearDate = System.currentTimeMillis();
+ reference = null;
}
conversation.clearMessages();
conversation.setHasMessagesLeftOnServer(false); //avoid messages getting loaded through mam
- conversation.setLastClearHistory(clearDate);
+ conversation.setLastClearHistory(clearDate, reference);
Runnable runnable = new Runnable() {
@Override
public void run() {
diff --git a/src/main/java/de/pixart/messenger/ui/ConversationFragment.java b/src/main/java/de/pixart/messenger/ui/ConversationFragment.java
index 342e52451..084c27a99 100644
--- a/src/main/java/de/pixart/messenger/ui/ConversationFragment.java
+++ b/src/main/java/de/pixart/messenger/ui/ConversationFragment.java
@@ -1354,7 +1354,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
private boolean showLoadMoreMessages(final Conversation c) {
final boolean mam = hasMamSupport(c);
final MessageArchiveService service = activity.xmppConnectionService.getMessageArchiveService();
- return mam && (c.getLastClearHistory() != 0 || (c.countMessages() == 0 && c.messagesLoaded.get() && c.hasMessagesLeftOnServer() && !service.queryInProgress(c)));
+ return mam && (c.getLastClearHistory().getTimestamp() != 0 || (c.countMessages() == 0 && c.messagesLoaded.get() && c.hasMessagesLeftOnServer() && !service.queryInProgress(c)));
}
private boolean hasMamSupport(final Conversation c) {
diff --git a/src/main/java/de/pixart/messenger/ui/adapter/MessageAdapter.java b/src/main/java/de/pixart/messenger/ui/adapter/MessageAdapter.java
index a2c837ea0..c451015cf 100644
--- a/src/main/java/de/pixart/messenger/ui/adapter/MessageAdapter.java
+++ b/src/main/java/de/pixart/messenger/ui/adapter/MessageAdapter.java
@@ -77,6 +77,7 @@ import de.pixart.messenger.utils.CryptoHelper;
import de.pixart.messenger.utils.GeoHelper;
import de.pixart.messenger.utils.Patterns;
import de.pixart.messenger.utils.UIHelper;
+import de.pixart.messenger.xmpp.mam.MamReference;
import ezvcard.Ezvcard;
import ezvcard.VCard;
import nl.changer.audiowife.AudioWife;
@@ -745,16 +746,16 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
}
private void loadMoreMessages(Conversation conversation) {
- conversation.setLastClearHistory(0);
+ conversation.setLastClearHistory(0, null);
activity.xmppConnectionService.updateConversation(conversation);
conversation.setHasMessagesLeftOnServer(true);
conversation.setFirstMamReference(null);
- long timestamp = conversation.getLastMessageTransmitted();
+ long timestamp = conversation.getLastMessageTransmitted().getTimestamp();
if (timestamp == 0) {
timestamp = System.currentTimeMillis();
}
conversation.messagesLoaded.set(true);
- MessageArchiveService.Query query = activity.xmppConnectionService.getMessageArchiveService().query(conversation, 0, timestamp, false);
+ MessageArchiveService.Query query = activity.xmppConnectionService.getMessageArchiveService().query(conversation, new MamReference(0), timestamp, false);
if (query != null) {
Toast.makeText(activity, R.string.fetching_history_from_server, Toast.LENGTH_LONG).show();
} else {
diff --git a/src/main/java/de/pixart/messenger/xmpp/mam/MamReference.java b/src/main/java/de/pixart/messenger/xmpp/mam/MamReference.java
new file mode 100644
index 000000000..788e04889
--- /dev/null
+++ b/src/main/java/de/pixart/messenger/xmpp/mam/MamReference.java
@@ -0,0 +1,69 @@
+package de.pixart.messenger.xmpp.mam;
+
+public class MamReference {
+
+ private final long timestamp;
+ private final String reference;
+
+ public MamReference(long timestamp) {
+ this.timestamp = timestamp;
+ this.reference = null;
+ }
+
+ public MamReference(long timestamp, String reference) {
+ this.timestamp = timestamp;
+ this.reference = reference;
+ }
+
+ public long getTimestamp() {
+ return timestamp;
+ }
+
+ public String getReference() {
+ return reference;
+ }
+
+ public boolean greaterThan(MamReference b) {
+ return timestamp > b.getTimestamp();
+ }
+
+ public boolean greaterThan(long b) {
+ return timestamp > b;
+ }
+
+ public static MamReference max(MamReference a, MamReference b) {
+ if (a != null && b != null) {
+ return a.timestamp > b.timestamp ? a : b;
+ } else if (a != null) {
+ return a;
+ } else {
+ return b;
+ }
+ }
+
+ public static MamReference max(MamReference a, long b) {
+ return max(a,new MamReference(b));
+ }
+
+ public static MamReference fromAttribute(String attr) {
+ if (attr == null) {
+ return new MamReference(0);
+ } else {
+ String[] attrs = attr.split(":");
+ try {
+ long timestamp = Long.parseLong(attrs[0]);
+ if (attrs.length >= 2) {
+ return new MamReference(timestamp,attrs[1]);
+ } else {
+ return new MamReference(timestamp);
+ }
+ } catch (Exception e) {
+ return new MamReference(0);
+ }
+ }
+ }
+
+ public MamReference timeOnly() {
+ return reference == null ? this : new MamReference(timestamp);
+ }
+} \ No newline at end of file