diff options
Diffstat (limited to 'src/main/java/de/thedevstack/conversationsplus')
9 files changed, 397 insertions, 13 deletions
diff --git a/src/main/java/de/thedevstack/conversationsplus/ConversationsPlusApplication.java b/src/main/java/de/thedevstack/conversationsplus/ConversationsPlusApplication.java index d902e8d4..4b11bb4a 100644 --- a/src/main/java/de/thedevstack/conversationsplus/ConversationsPlusApplication.java +++ b/src/main/java/de/thedevstack/conversationsplus/ConversationsPlusApplication.java @@ -8,6 +8,7 @@ import android.preference.PreferenceManager; import java.io.File; import de.thedevstack.conversationsplus.utils.ImageUtil; + import eu.siacs.conversations.R; import eu.siacs.conversations.persistance.FileBackend; import eu.siacs.conversations.utils.SerialSingleThreadExecutor; @@ -32,7 +33,7 @@ public class ConversationsPlusApplication extends Application { ConversationsPlusApplication.instance = this; ConversationsPlusPreferences.init(PreferenceManager.getDefaultSharedPreferences(getAppContext())); ImageUtil.initBitmapCache(); - FileBackend.createNoMedia(); + FileBackend.init(); } /** diff --git a/src/main/java/de/thedevstack/conversationsplus/persistance/CursorHelper.java b/src/main/java/de/thedevstack/conversationsplus/persistance/CursorHelper.java new file mode 100644 index 00000000..7e3fdab0 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/persistance/CursorHelper.java @@ -0,0 +1,203 @@ +package de.thedevstack.conversationsplus.persistance; + +import android.database.Cursor; + +/** + * Created by steckbrief on 15.04.2016. + */ +public abstract class CursorHelper { + + static double getDouble(Cursor cursor, String columnName) { + if (null == cursor) { + return Double.MIN_VALUE; + } + int columnIndex = getColumnIndex(cursor, columnName); + if (columnIndex < 0) { + return Double.MIN_VALUE; + } + return getDouble(cursor, columnIndex); + } + + static String getString(Cursor cursor, String columnName) { + if (null == cursor) { + return null; + } + int columnIndex = getColumnIndex(cursor, columnName); + if (columnIndex < 0) { + return null; + } + return getString(cursor, columnIndex); + } + + static float getFloat(Cursor cursor, String columnName) { + if (null == cursor) { + return Float.MIN_VALUE; + } + int columnIndex = getColumnIndex(cursor, columnName); + if (columnIndex < 0) { + return Float.MIN_VALUE; + } + return getFloat(cursor, columnIndex); + } + + static int getInt(Cursor cursor, String columnName) { + if (null == cursor) { + return Integer.MIN_VALUE; + } + int columnIndex = getColumnIndex(cursor, columnName); + if (columnIndex < 0) { + return Integer.MIN_VALUE; + } + return getInt(cursor, columnIndex); + } + + static long getLong(Cursor cursor, String columnName) { + if (null == cursor) { + return Long.MIN_VALUE; + } + int columnIndex = getColumnIndex(cursor, columnName); + if (columnIndex < 0) { + return Long.MIN_VALUE; + } + return getLong(cursor, columnIndex); + } + + static int getShort(Cursor cursor, String columnName) { + if (null == cursor) { + return Short.MIN_VALUE; + } + int columnIndex = getColumnIndex(cursor, columnName); + if (columnIndex < 0) { + return Short.MIN_VALUE; + } + return getShort(cursor, columnIndex); + } + + static byte[] getBlob(Cursor cursor, String columnName) { + if (null == cursor) { + return null; + } + int columnIndex = getColumnIndex(cursor, columnName); + if (columnIndex < 0) { + return null; + } + return getBlob(cursor, columnIndex); + } + + /** + * Returns the zero-based index for the given column name, or -1 if the column doesn't exist. + * If you expect the column to exist use {@link Cursor#getColumnIndexOrThrow(String)} instead, which + * will make the error more clear. + * + * @param columnName the name of the target column. + * @return the zero-based column index for the given column name, or -1 if + * the column name does not exist. + * @see Cursor#getColumnIndexOrThrow(String) + */ + static int getColumnIndex(Cursor cursor, String columnName) { + return cursor.getColumnIndex(columnName); + } + + /** + * Returns the value of the requested column as a byte array. + * + * <p>The result and whether this method throws an exception when the + * column value is null or the column type is not a blob type is + * implementation-defined. + * + * @param columnIndex the zero-based index of the target column. + * @return the value of that column as a byte array. + */ + static byte[] getBlob(Cursor cursor, int columnIndex) { + return cursor.getBlob(columnIndex); + } + + /** + * Returns the value of the requested column as a String. + * + * <p>The result and whether this method throws an exception when the + * column value is null or the column type is not a string type is + * implementation-defined. + * + * @param columnIndex the zero-based index of the target column. + * @return the value of that column as a String. + */ + static String getString(Cursor cursor, int columnIndex) { + return cursor.getString(columnIndex); + } + + /** + * Returns the value of the requested column as a short. + * + * <p>The result and whether this method throws an exception when the + * column value is null, the column type is not an integral type, or the + * integer value is outside the range [<code>Short.MIN_VALUE</code>, + * <code>Short.MAX_VALUE</code>] is implementation-defined. + * + * @param columnIndex the zero-based index of the target column. + * @return the value of that column as a short. + */ + static short getShort(Cursor cursor, int columnIndex) { + return cursor.getShort(columnIndex); + } + + /** + * Returns the value of the requested column as an int. + * + * <p>The result and whether this method throws an exception when the + * column value is null, the column type is not an integral type, or the + * integer value is outside the range [<code>Integer.MIN_VALUE</code>, + * <code>Integer.MAX_VALUE</code>] is implementation-defined. + * + * @param columnIndex the zero-based index of the target column. + * @return the value of that column as an int. + */ + static int getInt(Cursor cursor, int columnIndex) { + return cursor.getInt(columnIndex); + } + + /** + * Returns the value of the requested column as a long. + * + * <p>The result and whether this method throws an exception when the + * column value is null, the column type is not an integral type, or the + * integer value is outside the range [<code>Long.MIN_VALUE</code>, + * <code>Long.MAX_VALUE</code>] is implementation-defined. + * + * @param columnIndex the zero-based index of the target column. + * @return the value of that column as a long. + */ + static long getLong(Cursor cursor, int columnIndex) { + return cursor.getLong(columnIndex); + } + + /** + * Returns the value of the requested column as a float. + * + * <p>The result and whether this method throws an exception when the + * column value is null, the column type is not a floating-point type, or the + * floating-point value is not representable as a <code>float</code> value is + * implementation-defined. + * + * @param columnIndex the zero-based index of the target column. + * @return the value of that column as a float. + */ + static float getFloat(Cursor cursor, int columnIndex) { + return cursor.getFloat(columnIndex); + } + + /** + * Returns the value of the requested column as a double. + * + * <p>The result and whether this method throws an exception when the + * column value is null, the column type is not a floating-point type, or the + * floating-point value is not representable as a <code>double</code> value is + * implementation-defined. + * + * @param columnIndex the zero-based index of the target column. + * @return the value of that column as a double. + */ + static double getDouble(Cursor cursor, int columnIndex) { + return cursor.getDouble(columnIndex); + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/persistance/MessageDatabaseAccess.java b/src/main/java/de/thedevstack/conversationsplus/persistance/MessageDatabaseAccess.java new file mode 100644 index 00000000..ba5f4a2c --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/persistance/MessageDatabaseAccess.java @@ -0,0 +1,60 @@ +package de.thedevstack.conversationsplus.persistance; + +import android.content.ContentValues; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; + +import de.thedevstack.android.logcat.Logging; + +import eu.siacs.conversations.entities.Message; +import eu.siacs.conversations.xmpp.jid.InvalidJidException; +import eu.siacs.conversations.xmpp.jid.Jid; + +/** + * Created by steckbrief on 15.04.2016. + */ +public class MessageDatabaseAccess { + public static final String TABLE_NAME_ADDITIONAL_PARAMETERS = "message_parameters"; + public static final String COLUMN_NAME_MSG_PARAMS_HTTPUPLOAD = "httpupload"; + public static final String COLUMN_NAME_MSG_PARAMS_MSGUUID = "message_uuid"; + public static final String COLUMN_NAME_MSG_PARAMS_TREATASDOWNLOADABLE_DECISION = "treatasdownloadable_decision"; + + public static final String TABLE_ADDITIONAL_PARAMETERS_CREATE_V0 = "CREATE TABLE " + MessageDatabaseAccess.TABLE_NAME_ADDITIONAL_PARAMETERS + " (" + + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID + " TEXT, " + + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_HTTPUPLOAD + " INTEGER DEFAULT 0, " + + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_TREATASDOWNLOADABLE_DECISION + " TEXT DEFAULT 'NOT_DECIDED', " + + "FOREIGN KEY(" + MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID + ") REFERENCES " + Message.TABLENAME + "(" + Message.UUID + ") ON DELETE CASCADE)"; + + public static ContentValues getAdditionalParametersContentValues(Message message) { + ContentValues additionalParameters = new ContentValues(); + additionalParameters.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID, message.getUuid()); + additionalParameters.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_HTTPUPLOAD, message.isHttpUploaded() ? 1 : 0); + additionalParameters.put(MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_TREATASDOWNLOADABLE_DECISION, message.treatAsDownloadable().name()); + + return additionalParameters; + } + + public static void populateMessageParametersFromCursor(Cursor cursor, Message message) { + boolean isHttpUploaded = CursorHelper.getInt(cursor, COLUMN_NAME_MSG_PARAMS_HTTPUPLOAD) == 1; + message.setHttpUploaded(isHttpUploaded); + String downloadable = CursorHelper.getString(cursor, COLUMN_NAME_MSG_PARAMS_TREATASDOWNLOADABLE_DECISION); + Message.Decision treatAsDownloadable = Message.Decision.NOT_DECIDED; + try { + treatAsDownloadable = Message.Decision.valueOf(downloadable); + } catch (IllegalArgumentException e) { + // Should only happen if the database is corrupted, but to be on the save side catch it here + Logging.e("db.msg", "Unknown Decision for treatAsDownloadable found: '" + downloadable + "'"); + } + message.setTreatAsDownloadable(treatAsDownloadable); + } + + public static void populateMessageParameters(SQLiteDatabase db, Message message) { + Cursor paramsCursor = db.query(MessageDatabaseAccess.TABLE_NAME_ADDITIONAL_PARAMETERS, + null, MessageDatabaseAccess.COLUMN_NAME_MSG_PARAMS_MSGUUID + "=?", + new String[] {message.getUuid()}, + null, null, null, null); + paramsCursor.moveToNext(); + MessageDatabaseAccess.populateMessageParametersFromCursor(paramsCursor, message); + paramsCursor.close(); + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/persistance/observers/FileDeletionObserver.java b/src/main/java/de/thedevstack/conversationsplus/persistance/observers/FileDeletionObserver.java new file mode 100644 index 00000000..a313c8b1 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/persistance/observers/FileDeletionObserver.java @@ -0,0 +1,46 @@ +package de.thedevstack.conversationsplus.persistance.observers; + +import android.os.FileObserver; + +import de.thedevstack.conversationsplus.utils.MessageUtil; +import de.thedevstack.conversationsplus.utils.UiUpdateHelper; +import de.thedevstack.conversationsplus.utils.XmppConnectionServiceAccessor; + +import eu.siacs.conversations.entities.Conversation; +import eu.siacs.conversations.entities.Message; +import eu.siacs.conversations.entities.Transferable; +import eu.siacs.conversations.entities.TransferablePlaceholder; + +/** + * Observer to mark messages containing files which are deleted. + */ +public class FileDeletionObserver extends FileObserver { + public FileDeletionObserver(String path) { + super(path, FileObserver.DELETE); + } + + @Override + public void onEvent(int event, String path) { + if (null != path) { + markFileDeleted(path.split("\\.")[0]); + } + } + + private void markFileDeleted(String uuid) { + if (null != XmppConnectionServiceAccessor.xmppConnectionService) { + for (Conversation conversation : XmppConnectionServiceAccessor.xmppConnectionService.getConversations()) { + Message message = conversation.findMessageWithFileAndUuid(uuid); + if (message != null) { + message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED)); + final int s = message.getStatus(); + if (s == Message.STATUS_WAITING || s == Message.STATUS_OFFERED || s == Message.STATUS_UNSEND) { + MessageUtil.markMessage(message, Message.STATUS_SEND_FAILED); + } else { + UiUpdateHelper.updateConversationUi(); + } + return; + } + } + } + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/dialogs/MessageDetailsDialog.java b/src/main/java/de/thedevstack/conversationsplus/ui/dialogs/MessageDetailsDialog.java index e5a478e8..4f6cffb4 100644 --- a/src/main/java/de/thedevstack/conversationsplus/ui/dialogs/MessageDetailsDialog.java +++ b/src/main/java/de/thedevstack/conversationsplus/ui/dialogs/MessageDetailsDialog.java @@ -9,6 +9,7 @@ import java.util.Date; import de.thedevstack.android.logcat.Logging; import de.thedevstack.conversationsplus.ConversationsPlusColors; +import de.thedevstack.conversationsplus.utils.ui.TextViewUtil; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Conversation; @@ -60,11 +61,11 @@ public class MessageDetailsDialog extends AbstractAlertDialog { view.findViewById(R.id.dlgMsgDetFileTable).setVisibility(View.VISIBLE); if (null != message.getFileParams()) { Message.FileParams params = message.getFileParams(); - TextView tvFilesize = (TextView) view.findViewById(R.id.dlgMsgDetFileSize); - tvFilesize.setText(UIHelper.getHumanReadableFileSize(params.size)); + TextViewUtil.setText(view, R.id.dlgMsgDetFileSize, UIHelper.getHumanReadableFileSize(params.size)); } - TextView mimetype = (TextView) view.findViewById(R.id.dlgMsgDetFileMimeType); - mimetype.setText(message.getMimeType()); + TextViewUtil.setText(view, R.id.dlgMsgDetFileMimeType, message.getMimeType()); + + TextViewUtil.setText(view, R.id.dlgMsgDetFileHttpUploaded, message.isHttpUploaded() ? R.string.cplus_yes : R.string.cplus_no); } } @@ -108,7 +109,6 @@ public class MessageDetailsDialog extends AbstractAlertDialog { * @param message the message to display in dialog */ protected void displayMessageTypeInfo(View view, Message message) { - TextView msgTypeTextView = (TextView) view.findViewById(R.id.dlgMsgDetMsgType); int msgTypeResId; switch (message.getType()) { case Message.TYPE_PRIVATE: @@ -127,7 +127,7 @@ public class MessageDetailsDialog extends AbstractAlertDialog { default: msgTypeResId = R.string.dlg_msg_details_msg_type_text; } - msgTypeTextView.setText(msgTypeResId); + TextViewUtil.setText(view, R.id.dlgMsgDetMsgType, msgTypeResId); } /** @@ -147,10 +147,8 @@ public class MessageDetailsDialog extends AbstractAlertDialog { if (conversation.getMode() == Conversation.MODE_MULTI) { // Change label of sending and receiving party to MUC terminology - TextView senderLabel = (TextView) view.findViewById(R.id.dlgMsgDetLblSender); - senderLabel.setText(R.string.dlg_msg_details_sender_nick); - TextView receipientLabel = (TextView) view.findViewById(R.id.dlgMsgDetLblReceipient); - receipientLabel.setText(R.string.dlg_msg_details_receipient_nick); + TextViewUtil.setText(view, R.id.dlgMsgDetLblSender, R.string.dlg_msg_details_sender_nick); + TextViewUtil.setText(view, R.id.dlgMsgDetLblReceipient, R.string.dlg_msg_details_receipient_nick); // Get own nick for MUC me = conversation.getMucOptions().getActualNick(); @@ -174,7 +172,6 @@ public class MessageDetailsDialog extends AbstractAlertDialog { * @param message the message to display in dialog */ protected void displayMessageSentTime(View view, Message message) { - TextView timeSent = (TextView) view.findViewById(R.id.dlgMsgDetTimeSent); - timeSent.setText(DateFormat.format("dd.MM.yyyy kk:mm:ss", new Date(message.getTimeSent()))); + TextViewUtil.setText(view, R.id.dlgMsgDetTimeSent, DateFormat.format("dd.MM.yyyy kk:mm:ss", new Date(message.getTimeSent()))); } } diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/MessageUtil.java b/src/main/java/de/thedevstack/conversationsplus/utils/MessageUtil.java index f8310206..afb8387d 100644 --- a/src/main/java/de/thedevstack/conversationsplus/utils/MessageUtil.java +++ b/src/main/java/de/thedevstack/conversationsplus/utils/MessageUtil.java @@ -6,8 +6,11 @@ import java.net.URL; import java.util.regex.Matcher; import java.util.regex.Pattern; +import de.thedevstack.conversationsplus.ConversationsPlusApplication; + import eu.siacs.conversations.entities.DownloadableFile; import eu.siacs.conversations.entities.Message; +import eu.siacs.conversations.persistance.DatabaseBackend; import eu.siacs.conversations.persistance.FileBackend; /** @@ -15,6 +18,17 @@ import eu.siacs.conversations.persistance.FileBackend; */ public final class MessageUtil { + public static void markMessage(Message message, int status) { + if (status == Message.STATUS_SEND_FAILED + && (message.getStatus() == Message.STATUS_SEND_RECEIVED || message + .getStatus() == Message.STATUS_SEND_DISPLAYED)) { + return; + } + message.setStatus(status); + DatabaseBackend.getInstance(ConversationsPlusApplication.getAppContext()).updateMessage(message); + UiUpdateHelper.updateConversationUi(); + } + public static boolean wasHighlightedOrPrivate(final Message message) { final String nick = message.getConversation().getMucOptions().getActualNick(); final Pattern highlight = generateNickHighlightPattern(nick); diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/XmppConnectionServiceAccessor.java b/src/main/java/de/thedevstack/conversationsplus/utils/XmppConnectionServiceAccessor.java new file mode 100644 index 00000000..20cd7361 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/utils/XmppConnectionServiceAccessor.java @@ -0,0 +1,32 @@ +package de.thedevstack.conversationsplus.utils; + +import de.thedevstack.android.logcat.Logging; + +import eu.siacs.conversations.services.XmppConnectionService; + +/** + * Accessor utility to access XmppConnectionService without having to pass the XmppConnectionService every time. + */ +public final class XmppConnectionServiceAccessor { + public static XmppConnectionService xmppConnectionService; + + /** + * Initializes the XmppConnectionService. + * This method needs to be called once in XmppConnectionService#onCreate. + * @param xmppConnectionService + */ + public static void initXmppConnectionService(XmppConnectionService xmppConnectionService) { + if (null == XmppConnectionServiceAccessor.xmppConnectionService) { + XmppConnectionServiceAccessor.xmppConnectionService = xmppConnectionService; + } else { + Logging.e("XmppConnectionServiceAccessor", "XMPP Connection Service already instantiated."); + } + } + + /** + * Avoid instantiation + */ + private XmppConnectionServiceAccessor() { + // avoid instantiation + } +} diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/ui/TextViewUtil.java b/src/main/java/de/thedevstack/conversationsplus/utils/ui/TextViewUtil.java index bb08014b..a775dad6 100644 --- a/src/main/java/de/thedevstack/conversationsplus/utils/ui/TextViewUtil.java +++ b/src/main/java/de/thedevstack/conversationsplus/utils/ui/TextViewUtil.java @@ -1,12 +1,28 @@ package de.thedevstack.conversationsplus.utils.ui; import android.support.annotation.StringRes; +import android.view.View; import android.widget.TextView; /** * Created by steckbrief on 29.03.2016. */ public final class TextViewUtil { + + public static void setText(View parentView, int textViewId, CharSequence text) { + TextView tv = (TextView) parentView.findViewById(textViewId); + if (null != tv) { + tv.setText(text); + } + } + + public static void setText(View parentView, int textViewId, int textResId) { + TextView tv = (TextView) parentView.findViewById(textViewId); + if (null != tv) { + tv.setText(textResId); + } + } + public static void enable(TextView tv) { setColorEnabledAndTextResId(tv, null, true, null); } diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/httpuploadim/HttpUploadHint.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/httpuploadim/HttpUploadHint.java new file mode 100644 index 00000000..7868a2f5 --- /dev/null +++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/httpuploadim/HttpUploadHint.java @@ -0,0 +1,15 @@ +package de.thedevstack.conversationsplus.xmpp.httpuploadim; + +import eu.siacs.conversations.xml.Element; + +/** + * Created by steckbrief on 17.04.2016. + */ +public class HttpUploadHint extends Element { + public static final String NAMESPACE = "urn:xmpp:hints"; + public static final String ELEMENT_NAME = "httpupload"; + + public HttpUploadHint() { + super(ELEMENT_NAME, NAMESPACE); + } +} |