/*
* Kontalk Android client
* Copyright (C) 2017 Kontalk Devteam <devteam@kontalk.org>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.kontalk.message;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.List;
import org.kontalk.crypto.Coder;
import org.kontalk.provider.MyMessages.Messages;
import org.kontalk.provider.MyMessages.Threads.Conversations;
import android.content.AsyncQueryHandler;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Parcelable;
/**
* An abstract message.
* FIXME it should be a {@link Parcelable}
* @author Daniele Ricci
* @version 2.0
*/
@Deprecated
public abstract class LegacyAbstractMessage<T> {
public static final int USERID_LENGTH = 40;
public static final int USERID_LENGTH_RESOURCE = 48;
private static final String[] MESSAGE_LIST_PROJECTION = {
Messages._ID,
Messages.MESSAGE_ID,
Messages.PEER,
Messages.DIRECTION,
Messages.TIMESTAMP,
Messages.SERVER_TIMESTAMP,
Messages.STATUS_CHANGED,
Messages.STATUS,
Messages.ENCRYPTED,
Messages.SECURITY_FLAGS,
Messages.BODY_MIME,
Messages.BODY_CONTENT,
Messages.BODY_LENGTH,
};
// these indexes matches MESSAGE_LIST_PROJECTION
public static final int COLUMN_ID = 0;
public static final int COLUMN_MESSAGE_ID = 1;
public static final int COLUMN_REAL_ID = 2;
public static final int COLUMN_PEER = 3;
public static final int COLUMN_DIRECTION = 4;
public static final int COLUMN_TIMESTAMP = 5;
public static final int COLUMN_SERVER_TIMESTAMP = 6;
public static final int COLUMN_STATUS_CHANGED = 7;
public static final int COLUMN_STATUS = 8;
public static final int COLUMN_ENCRYPTED = 9;
public static final int COLUMN_SECURITY = 10;
public static final int COLUMN_BODY_MIME = 11;
public static final int COLUMN_BODY_CONTENT = 12;
public static final int COLUMN_BODY_LENGTH = 13;
public static final String MSG_ID = "org.kontalk.message.id";
public static final String MSG_SENDER = "org.kontalk.message.sender";
public static final String MSG_MIME = "org.kontalk.message.mime";
public static final String MSG_CONTENT = "org.kontalk.message.content";
public static final String MSG_RECIPIENTS = "org.kontalk.message.recipients";
public static final String MSG_GROUP = "org.kontalk.message.group";
public static final String MSG_TIMESTAMP = "org.kontalk.message.timestamp";
protected Context mContext;
protected long databaseId;
protected String id;
protected String sender;
protected long timestamp;
protected long serverTimestamp;
protected long statusChanged;
protected int status;
protected boolean encrypted;
protected int security;
/**
* Recipients (outgoing) - will contain one element for incoming
*/
protected List<String> recipients;
/**
* Recipients (incoming) - will be null for outgoing
*/
protected List<String> group;
/** Remote fetch URL (if any). */
protected String fetchUrl;
/** Local file {@link Uri}. */
protected Uri localUri;
/** Preview file path. */
protected File previewFile;
/** Message length (original file length for media messages). */
protected long length;
/** Message components. */
protected List<MessageComponent<?>> mComponents;
public LegacyAbstractMessage(Context context, String id, long timestamp, String sender, boolean encrypted, List<String> group) {
this(context, id, timestamp, sender, encrypted);
this.group = group;
}
public LegacyAbstractMessage(Context context, String id, long timestamp, String sender, boolean encrypted) {
this.mContext = context;
this.id = id;
this.sender = sender;
this.recipients = new ArrayList<String>();
// will be updated if necessary
this.timestamp = System.currentTimeMillis();
this.serverTimestamp = timestamp;
if (encrypted) {
this.encrypted = encrypted;
// only basic encryption for now
this.security = Coder.SECURITY_BASIC;
}
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getSender(boolean generic) {
return (generic && sender.length() > USERID_LENGTH) ?
sender.substring(0, USERID_LENGTH) : sender;
}
public String getSender() {
return getSender(false);
}
public List<String> getRecipients() {
return recipients;
}
public void addRecipient(String userId) {
recipients.add(userId);
}
public List<String> getGroup() {
return group;
}
public long getTimestamp() {
return timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
public long getStatusChanged() {
return statusChanged;
}
public void setStatusChanged(long statusChanged) {
this.statusChanged = statusChanged;
}
public long getServerTimestamp() {
return serverTimestamp;
}
public int getStatus() {
return status;
}
public long getLength() {
return length;
}
public void setLength(long length) {
this.length = length;
}
@Override
public String toString() {
return getClass().getSimpleName() + ": id=" + id;
}
public int getDirection() {
return (sender != null) ?
Messages.DIRECTION_IN : Messages.DIRECTION_OUT;
}
public void setDatabaseId(long databaseId) {
this.databaseId = databaseId;
}
public long getDatabaseId() {
return databaseId;
}
/**
* Returns a rapid text representation of the message.
* The returned value is useful for notification tickers.
* @return text representation of this message
* @throws UnsupportedEncodingException
*/
public abstract String getTextContent() throws UnsupportedEncodingException;
/**
* Binary contents of this message.
* Used for storing data representation into the database.
* @return the byte contents of this message
*/
public abstract byte[] getBinaryContent();
/** A sample text content from class name and mime type. */
public static String getSampleTextContent(Class<? extends LegacyAbstractMessage> klass,
String mime) {
String cname = klass.getSimpleName();
return cname.substring(0, cname.length() - "Message".length()) +
": " + mime;
}
/** Sets a URL for fetching big contents. */
public void setFetchUrl(String url) {
fetchUrl = url;
}
public String getFetchUrl() {
return fetchUrl;
}
/*
public void setFetched(boolean fetched) {
this.fetched = fetched;
}
public boolean isFetched() {
return this.fetched;
}
*/
/** Sets a pointer to the local resource. */
public void setLocalUri(Uri uri) {
localUri = uri;
}
public Uri getLocalUri() {
return localUri;
}
/** Sets a pointer to the preview resource. */
public void setPreviewFile(File file) {
previewFile = file;
}
public File getPreviewFile() {
return previewFile;
}
public boolean isEncrypted() {
return encrypted;
}
public void setEncrypted() {
encrypted = true;
}
public int getSecurityFlags() {
return security;
}
public void setSecurityFlags(int flags) {
this.security = flags;
}
/** Decrypts the message. */
public abstract void decrypt(Coder coder) throws GeneralSecurityException;
protected void populateFromCursor(Cursor c) {
// be sure to stick to our projection array
databaseId = c.getLong(COLUMN_ID);
id = c.getString(COLUMN_MESSAGE_ID);
//realId = c.getString(COLUMN_REAL_ID);
timestamp = c.getLong(COLUMN_TIMESTAMP);
statusChanged = c.getLong(COLUMN_STATUS_CHANGED);
status = c.getInt(COLUMN_STATUS);
recipients = new ArrayList<String>();
encrypted = (c.getShort(COLUMN_ENCRYPTED) > 0);
security = c.getInt(COLUMN_SECURITY);
serverTimestamp = c.getLong(COLUMN_SERVER_TIMESTAMP);
/*
fetchUrl = c.getString(COLUMN_FETCH_URL);
length = c.getLong(COLUMN_LENGTH);
String _localUri = c.getString(COLUMN_LOCAL_URI);
// load local uri
if (_localUri != null && _localUri.length() > 0)
localUri = Uri.parse(_localUri);
*/
String peer = c.getString(COLUMN_PEER);
int direction = c.getInt(COLUMN_DIRECTION);
if (direction == Messages.DIRECTION_OUT) {
// we are the origin
sender = null;
recipients.add(peer);
}
else {
sender = peer;
// we are the origin - no recipient
}
// TODO groups??
}
/** Clears all local fields for recycle. */
protected void clear() {
// clear all fields
mContext = null;
databaseId = 0;
id = null;
sender = null;
timestamp = 0;
serverTimestamp = 0;
statusChanged = 0;
status = 0;
encrypted = false;
security = 0;
}
/** Release this message for later use in the global pool. */
public abstract void recycle();
public static LegacyAbstractMessage fromCursor(Context context, Cursor cursor) {
// TODO
return null;
}
public static void startQuery(AsyncQueryHandler handler, int token, long threadId) {
// cancel previous operations
handler.cancelOperation(token);
handler.startQuery(token, null,
ContentUris.withAppendedId(Conversations.CONTENT_URI, threadId),
MESSAGE_LIST_PROJECTION, null, null, Messages.DEFAULT_SORT_ORDER);
}
public static String buildMediaFilename(LegacyAbstractMessage msg) {
// TODO
return null;
}
/** Still unused.
public static void startQuery(AsyncQueryHandler handler, int token, String peer) {
// cancel previous operations
handler.cancelOperation(token);
handler.startQuery(token, null, Messages.CONTENT_URI,
MESSAGE_LIST_PROJECTION, "peer = ?", new String[] { peer },
Messages.DEFAULT_SORT_ORDER);
}
*/
}