yasme/src/main/java/de/fau/cs/mad/yasme/android/entities/Message.java
@DatabaseTable(tableName = DatabaseConstants.MESSAGE_TABLE)
public class Message implements Serializable {

    @DatabaseField(columnName = DatabaseConstants.MESSAGE_ID, id = true)
    private long id;

    @DatabaseField(columnName = DatabaseConstants.CHAT, canBeNull = false, foreign = true,
            foreignAutoCreate=true, foreignAutoRefresh=true)
    private Chat chat;

    @DatabaseField(columnName = DatabaseConstants.SENDER, foreign = true,
            foreignAutoCreate=true, foreignAutoRefresh=true)
    private User sender;

    @DatabaseField(columnName = DatabaseConstants.DATE, dataType = DataType.DATE_LONG)
    private Date dateSent;

    @DatabaseField(columnName = DatabaseConstants.MESSAGE)
    private String message;

    @DatabaseField(columnName = DatabaseConstants.MESSAGE_MESSAGEKEY_ID)
    private long messageKeyId;

    @DatabaseField(columnName = DatabaseConstants.AUTHENTICATED)
    private byte authenticated = 0;

    @DatabaseField(columnName = DatabaseConstants.ERROR_ID)
    private int errorId = MessageEncryption.ErrorType.OK;

    @DatabaseField(columnName = DatabaseConstants.READ)
    private boolean read = false;

    @DatabaseField(columnName = DatabaseConstants.SENT)
    private boolean sent = false;

    @DatabaseField(columnName = DatabaseConstants.RECEIVED)
    private boolean received = false;

    @DatabaseField(columnName = DatabaseConstants.MIMETYPE)
    private String mimeType
            = DatabaseManager.INSTANCE.getContext().getResources().getString(R.string.mime_image);

    private MessageKey messageKey;

    /**
     * Constructors *
     */
    public Message() {
        // ORMLite needs a no-arg constructor
    }

    public Message(long id, Date dateSent, User sender, String message, Chat chat, long messageKeyId) {
        this.id = id;
        this.dateSent = dateSent;
        this.sender = sender;
        this.message = message;
        this.chat = chat;
        this.messageKeyId = messageKeyId;
    }

    public Message(User sender, String message, Chat chat, long messageKeyId) {
        //Chat chat = new Chat();
        //chat.setId(chatId);

        this.chat = chat;
        this.sender = sender;
        this.message = message;
        this.messageKeyId = messageKeyId;
    }
    public Message(User sender, String message, Chat chat, long messageKeyId, String mimeType) {
        this.chat = chat;
        this.sender = sender;
        this.message = message;
        this.messageKeyId = messageKeyId;
        this.mimeType = mimeType;
    }
    /**
     * Getters *
     */
    @JsonIgnoreProperties({ "pw", "email", "name", "devices", "lastModified", "created" })
    public User getSender() {
        return sender;
    }

    public String getMessage() {
        return message;
    }

    public Date getDateSent() {
        return dateSent;
    }

    public MessageKey getMessageKey() {
        return messageKey;
    }

    @JsonIgnore
    public long getChatId() {
        return chat.getId();
    }

    @JsonIgnoreProperties({ "participants", "status", "name", "owner", "lastModified", "created", "profilePicture", "messages" })
    public Chat getChat() {
        return chat;
    }

    public long getId() {
        return id;
    }

    public long getMessageKeyId() {
        return messageKeyId;
    }

    @JsonIgnore
    public boolean getAuthenticity(){
        if (authenticated == 1) return true;
        else return false;
    }

    @JsonIgnore
    public int getErrorId() {
        return errorId;
    }

    public String getMimeType() {
        return this.mimeType;
    }

    @JsonIgnore
    public boolean wasRead() {
        return read;
    }

    @JsonIgnore
    public boolean wasSent() {
        return sent;
    }

    @JsonIgnore
    public boolean wasReceived() {
        return received;
    }

    /**
     * Setters
     */

    @JsonIgnore
    public void setChatAndId(long chatId) {
        this.chat = new Chat();
        this.chat.setId(chatId);
    }

    public void setChat(Chat chat) {
        this.chat = chat;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public void setDateSent(Date dateSent) {
        this.dateSent = dateSent;
    }

    public void setSender(User sender) {
        this.sender = sender;
    }

    public void setMessageKey(MessageKey messageKey) {
        this.messageKey = messageKey;
    }

    public void setId(long id) {
        this.id = id;
    }

    public void setMessageKeyId(long messageKeyId) {
        this.messageKeyId = messageKeyId;
    }

    @JsonIgnore
    public boolean setAuthenticity(boolean bool){
        if (bool){
            authenticated = 1;
            return true;
        }
        else {
            authenticated = 0;
            return false;
        }
    }

    @JsonIgnore
    public void setErrorId(int id) {
        this.errorId = id;
    }

    public void setRead(boolean read) {
        this.read = read;
    }

    public void setSent(boolean sent) {
        this.sent = sent;
    }

    public void setReceived(boolean received) {
        this.received = received;
    }

    public void setMimeType(String mimeType) {
        this.mimeType = mimeType;
    }
}
yasme/src/main/java/de/fau/cs/mad/yasme/android/asyncTasks/database/GetNewMessagesForChatTask.java
public class GetNewMessagesForChatTask extends AsyncTask<String, Void, Boolean> {
    private List<Message> messages;
    private MessageDAO messageDao;
    private long chatId;
    private long latestMessageId;
    private Class classToNotify;

    public GetNewMessagesForChatTask(long latestMessageId, long chatId, Class classToNotify) {
        messageDao = DatabaseManager.INSTANCE.getMessageDAO();
        this.chatId = chatId;
        this.latestMessageId = latestMessageId;
        this.classToNotify = classToNotify;
    }

    @Override
    protected Boolean doInBackground(String... params) {
        SpinnerObservable.getInstance().registerBackgroundTask(this);
        messages = messageDao.getNewMessagesByChat(chatId, latestMessageId);
        if (messages == null) {
            return false;
        }
        return true;
    }

    @Override
    protected void onPostExecute(Boolean success) {
        SpinnerObservable.getInstance().removeBackgroundTask(this);
        if (success) {
            // Notify
            if (classToNotify == ChatFragment.class) {
                ObservableRegistry.getObservable(ChatFragment.class).notifyFragments(messages);
            }
        } else {
            Log.w(this.getClass().getSimpleName(), "Getting new messages failed");
        }
    }
}
yasme/src/main/java/de/fau/cs/mad/yasme/android/storage/PictureManager.java
public enum PictureManager {
    INSTANCE;

    private Context mContext = DatabaseManager.INSTANCE.getContext();

    private Bitmap scaleBitmap(Bitmap bigBitmap, int newMaxSize) {
        float picScale = ((float) newMaxSize)
                / ((float) Math.max(bigBitmap.getHeight(), bigBitmap.getWidth()));
        int newHeight = (int) (bigBitmap.getHeight() * picScale);
        int newWidth = (int) (bigBitmap.getWidth() * picScale);
        Bitmap bitmap = Bitmap.createScaledBitmap(bigBitmap, newWidth, newHeight, false);
        return bitmap;
    }

    public byte[] scaledBitmapToByteArray(Bitmap bitmap, int newMaxSize) {
        float picScale = ((float) newMaxSize)
                / ((float) Math.max(bitmap.getHeight(), bitmap.getWidth()));
        int newHeight = (int) (bitmap.getHeight() * picScale);
        int newWidth = (int) (bitmap.getWidth() * picScale);
        Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, false);

        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
        return stream.toByteArray();
    }

    /**
     * Stores a given bitmap on internal storage
     *
     * @param user   for generating the filename
     * @param bitmap bitmap to be stored
     * @return path of the stored picture
     */
    public String storePicture(User user, Bitmap bitmap) throws IOException {
        // TODO Remove old picture
        // I think it will be replaced automatically

        // Create directory userPictures
        ContextWrapper cw = new ContextWrapper(mContext);
        File directory = cw.getDir("userPictures", Context.MODE_PRIVATE);

        // Generate file name
        String filename = user.getId() + "_profilePicture.jpg";

        // Concatenate directory and filename to path
        File path = new File(directory, filename);

        FileOutputStream fileOutputStream = new FileOutputStream(path);
        BufferedOutputStream bos = new BufferedOutputStream(fileOutputStream);

        // scale down bitmap
        Bitmap scaledBitmap = scaleBitmap(bitmap, 300);

        // Use the compress method on the BitMap object to write image to the OutputStream
        scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos);

        bos.flush();
        bos.close();
        fileOutputStream.close();

        Log.d(this.getClass().getSimpleName(), "Picture stored under: " + path.getAbsolutePath());
        return path.getAbsolutePath();
    }


    public static int calculateInSampleSize(
            BitmapFactory.Options options, int reqWidth, int reqHeight) {
        // Raw height and width of image
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {

            final int halfHeight = height / 2;
            final int halfWidth = width / 2;

            // Calculate the largest inSampleSize value that is a power of 2 and keeps both
            // height and width larger than the requested height and width.
            while ((halfHeight / inSampleSize) > reqHeight
                    && (halfWidth / inSampleSize) > reqWidth) {
                inSampleSize *= 2;
            }
        }
        return inSampleSize;
    }

    /**
     * Fetches the profilePicture from the storage sampled to a smaller size
     *
     * @param user      User
     * @param reqHeight int
     * @param reqWidth  int
     * @return profilePicture as a bitmap
     */
    public Bitmap getPicture(User user, int reqHeight, int reqWidth) {
        String path = user.getProfilePicture();
        if (path == null || path.isEmpty()) {
            return null;
        }
        File file = new File(path);
        if (!file.exists()) {
            return null;
        }

        // First decode with inJustDecodeBounds=true to check dimensions
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(path, options);

        // Calculate inSampleSize
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeFile(path, options);
    }

    public boolean deletePicture(User user) {
        String path = user.getProfilePicture();
        if (path == null || path.isEmpty()) {
            return false;
        }
        File file = new File(path);
        if (!file.exists()) {
            return false;
        }
        return file.delete();
    }

    /**
     * @param bitmap
     * @return converting bitmap and return a string
     */
    public String bitMapToString(Bitmap bitmap) {
        Bitmap btmp = scaleBitmap(bitmap, 512);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        btmp.compress(Bitmap.CompressFormat.PNG, 100, baos);
        byte[] b = baos.toByteArray();
        String temp = Base64.encodeToString(b, Base64.DEFAULT);
        return temp;
    }

    /**
     * @param encodedString
     * @return bitmap (from given string)
     */
    public Bitmap stringToBitMap(String encodedString) {
        try {
            byte[] encodeByte = Base64.decode(encodedString, Base64.DEFAULT);
            Bitmap bitmap = BitmapFactory.decodeByteArray(encodeByte, 0, encodeByte.length);
            return bitmap;
        } catch (Exception e) {
            Log.e(this.getClass().getSimpleName(), e.getMessage());
            return null;
        }
    }

    /**
     * Create a file Uri for saving an image or video
     */
    public static File getOutputMediaFilePath() throws IOException {
        File mediaStorageDir = new File(
                Environment.getExternalStorageDirectory(),
                "Yasme Pictures");

        // Create the storage directory if it does not exist
        if (!mediaStorageDir.exists()) {
            if (!mediaStorageDir.mkdirs()) {
                Log.d("MyCameraApp", "failed to create directory");
                return null;
            }
        }

        // Create a media file name
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
        File mediaFile = new File(mediaStorageDir.getPath() + File.separator +
                "IMG_" + timeStamp + ".jpg");

        return mediaFile;
    }
}
yasme/src/main/java/de/fau/cs/mad/yasme/android/ui/ChatAdapter.java
public class ChatAdapter extends ArrayAdapter<Message> {
    private final Context context;
    private List<Message> messages;

    /**
     * @see <a href="http://developer.android.com/design/style/color.html">Color palette used</a>
     * from K9 Mail
     */
    public final static int CONTACT_DUMMY_COLORS_ARGB[] = {
            0xff33B5E5,
            0xffAA66CC,
            0xff99CC00,
            0xffFFBB33,
            0xffFF4444,
            0xff0099CC,
            0xff9933CC,
            0xff669900,
            0xffFF8800,
            0xffCC0000
    };

    public ChatAdapter(Context context, int resource, List<Message> messages) {
        super(context, resource, messages);
        this.context = context;
        this.messages = messages;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        Message msg = messages.get(position);
        Boolean isSelf;
        View rowView;
        ImageView imageView;
        TextView textView;
        TextView dateView;
        ImageView profileImageView = null;
        LinearLayout textViews;
        TextView initial = null;

        LayoutInflater inflater = ((Activity) context).getLayoutInflater();
        if (msg == null) {
            Log.e(this.getClass().getSimpleName(), "msg == null");
            rowView = inflater.inflate(R.layout.chat_item_other, parent, false);
            textView = (TextView) rowView.findViewById(R.id.chat_item_message);
            textView.setText(context.getResources().getString(R.string.message_not_correctly_delivered));
            return rowView;
        }
        if (msg.getSender() == null) {
            Log.e(this.getClass().getSimpleName(), "sender == null");
            rowView = inflater.inflate(R.layout.chat_item_other, parent, false);
            textView = (TextView) rowView.findViewById(R.id.chat_item_message);
            textView.setText(context.getResources().getString(R.string.message_not_correctly_delivered));
            return rowView;
        }
        User user;
        try {
            user = DatabaseManager.INSTANCE.getUserDAO().get(msg.getSender().getId());
            //NPE occured - maybe DatabaseManager is not initialized yet
        } catch (NullPointerException npe) {
            Log.e(this.getClass().getSimpleName(), "NullPointerException occured");
            user = msg.getSender();
        }
        if (user == null) {
            Log.w(this.getClass().getSimpleName(), "User nicht in DB gefunden");
            user = msg.getSender();
        }
        String name = msg.getSender().getName();

        isSelf = user.getId() == DatabaseManager.INSTANCE.getUserId();
        if (isSelf) {
            rowView = inflater.inflate(R.layout.chat_item_own, parent, false);
        } else {
            rowView = inflater.inflate(R.layout.chat_item_other, parent, false);
            profileImageView = (ImageView) rowView.findViewById(R.id.chat_item_picture);
            initial = (TextView) rowView.findViewById(R.id.chat_item_picture_text);
        }
        textView = (TextView) rowView.findViewById(R.id.chat_item_message);
        dateView = (TextView) rowView.findViewById(R.id.chat_item_date);
        imageView = (ImageView) rowView.findViewById(R.id.image_content);
        textViews = (LinearLayout) rowView.findViewById(R.id.chat_item_text);

        if (msg.getMimeType() == null || msg.getMimeType()
                .compareTo(context.getResources().getString(R.string.mime_text)) == 0) {
            String text;
            if (msg.getErrorId() != MessageEncryption.ErrorType.OK) {
                switch (msg.getErrorId()) {
                    case MessageEncryption.ErrorType.DECRYPTION_FAILED:
                        text = context.getResources().getString(R.string.decryption_failed) + msg.getMessage();
                        break;
                    case MessageEncryption.ErrorType.AUTHENTICATION_FAILED:
                        text = context.getResources().getString(R.string.authentication_failed) + msg.getMessage();
                        break;
                    default:
                        text = context.getResources().getString(R.string.unknown_message_error) + msg.getMessage();
                        break;
                }
            } else {
                text = msg.getMessage();
            }
            imageView.setVisibility(View.GONE);
            textView.setText(text);
        } else if (msg.getMimeType().compareTo(context.getResources().getString(R.string.mime_image)) == 0) {
            Bitmap bitmap = PictureManager.INSTANCE.stringToBitMap(msg.getMessage());
            if (bitmap == null) {
                //error decoding bitmap
                //bitmap = BitmapFactory.decodeResource(context.getResources(), android.R.drawable.ic_delete);
                textView.setText(context.getResources().getString(R.string.decryption_failed));
                imageView.setVisibility(View.GONE);
            } else {
                imageView.setImageBitmap(bitmap);
                textView.setVisibility(View.GONE);
            }
        }

        String time = getDateOfMessage(msg);
        if (isSelf) {
            dateView.setText(time);
        } else {
            dateView.setText(name + ", " + time);
        }

        if (profileImageView != null && initial != null && !isSelf) {
            if (user.getProfilePicture() != null && !user.getProfilePicture().isEmpty()) {
                // load picture from local storage
                initial.setVisibility(View.GONE);
                profileImageView.setBackgroundColor(Color.TRANSPARENT);
                profileImageView.setImageBitmap(PictureManager.INSTANCE.getPicture(user, 40, 40));
            } else {
                // no local picture found. Set default pic
                profileImageView.setBackgroundColor(CONTACT_DUMMY_COLORS_ARGB
                        [(int) user.getId() % CONTACT_DUMMY_COLORS_ARGB.length]);
                initial.setText(user.getName().substring(0, 1).toUpperCase());
            }
            LayerDrawable d = (LayerDrawable) textViews.getBackground();
            GradientDrawable bgShape = (GradientDrawable) d.findDrawableByLayerId(R.id.chat_item_line);
            bgShape.setColor(CONTACT_DUMMY_COLORS_ARGB
                    [(int) user.getId() % CONTACT_DUMMY_COLORS_ARGB.length]);
        }

        rowView.requestFocus();
        return rowView;
    }

    private String getDateOfMessage(Message message) {
        String returnDate;

        Date dateSent = message.getDateSent();
        Date currentDate = new Date(System.currentTimeMillis());

        Calendar sent = new GregorianCalendar();
        sent.setTimeInMillis(dateSent.getTime());

        Calendar current = new GregorianCalendar();
        current.setTimeInMillis(currentDate.getTime());

        boolean today = dateEquals(sent, current);

        if (today) {
            returnDate = formatTime(dateSent) + " Uhr";
        } else {
            returnDate = formatDate(sent, current, dateSent) + " Uhr";
        }
        return returnDate;
    }

    public String formatTime(Date date) {
        SimpleDateFormat formatter = new SimpleDateFormat("HH:mm");
        formatter.setTimeZone(TimeZone.getTimeZone("GMT+2"));
        return formatter.format(date);
    }

    public String formatDate(Calendar calendar, Calendar current, Date date) {
        Calendar tmp = new GregorianCalendar(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH));
        tmp.add(Calendar.DATE, 1);
        if (dateEquals(tmp, current)) {
            return context.getResources().getString(R.string.yesterday) + ", " + formatTime(date);
        }
        tmp.add(Calendar.DATE, 6);
        if (tmp.compareTo(current) > 0) {
            int dayNumber = calendar.get(Calendar.DAY_OF_WEEK) % 7;

            String day = context.getResources().getStringArray(R.array.daysofweek)[dayNumber];
            return day + ", " + formatTime(date);
        }

        SimpleDateFormat formatter = new SimpleDateFormat("dd.MM.yyyy HH:mm");
        formatter.setTimeZone(TimeZone.getTimeZone("GMT+2"));
        return formatter.format(date);
    }


    public boolean dateEquals(Calendar first, Calendar second) {
        return (first.get(Calendar.YEAR) == second.get(Calendar.YEAR)) && (first.get(Calendar.DAY_OF_YEAR) == second.get(Calendar.DAY_OF_YEAR));
    }
}
yasme/src/main/java/de/fau/cs/mad/yasme/android/controller/Log.java
public class Log {
    static final boolean LOG_I = BuildConfig.DEBUG;
    static final boolean LOG_E = BuildConfig.DEBUG;
    static final boolean LOG_D = BuildConfig.DEBUG;
    static final boolean LOG_V = BuildConfig.DEBUG;
    static final boolean LOG_W = BuildConfig.DEBUG;

    public static void i(String tag, String string) {
        if (LOG_I) android.util.Log.i(tag, string);
    }
    public static void e(String tag, String string) {
        if (LOG_E) android.util.Log.e(tag, string);
    }
    public static void d(String tag, String string) {
        if (LOG_D) android.util.Log.d(tag, string);
    }
    public static void v(String tag, String string) {
        if (LOG_V) android.util.Log.v(tag, string);
    }
    public static void w(String tag, String string) {
        if (LOG_W) android.util.Log.w(tag, string);
    }
}
yasme/src/main/java/de/fau/cs/mad/yasme/android/entities/User.java
@JsonIgnoreProperties(ignoreUnknown = true)
@DatabaseTable(tableName = DatabaseConstants.USER_TABLE)
public class User implements Serializable {

    @DatabaseField(columnName = DatabaseConstants.USER_ID, id = true)
    private long id;

    @DatabaseField(columnName = DatabaseConstants.USER_NAME)
    private String name;

    //@DatabaseField(columnName = DatabaseConstants.USER_EMAIL)
    private String email;

    private String pw;

    @JsonIgnore
    private List<Device> devices;   // Just for convenience

    @DatabaseField(columnName = DatabaseConstants.USER_LAST_MODIFIED)
    private Date lastModified;

    @DatabaseField(columnName = DatabaseConstants.USER_CREATED)
    private Date created;

    @JsonIgnore
    @DatabaseField(columnName = DatabaseConstants.USER_PICTURE)
    private String profilePicture;


    @JsonIgnore
    @DatabaseField(columnName = DatabaseConstants.CONTACT)
    private int contactFlag = 0;


    public User(String pw, String name, String email) {
        this.pw = pw;
        this.name = name;
        this.email = email;
    }

    public User(String email, String pw) {
        this.email = email;
        this.pw = pw;
    }

    public User(String name, long id) {
        this.name = name;
        this.id = id;
    }

    public User(long id) {
        this.id = id;
    }

    public User(String name, String email, long id) {
        this.name = name;
        this.email = email;
        this.id = id;
    }

    public User() {
        // ORMLite needs a no-arg constructor
    }

    /*
     * Getters
     */

    @JsonIgnoreProperties({"id", "user", "publicKey", "product", "lastModified"})
    public List<Device> getDevices() {
        return devices;
    }

    public String getEmail() {
        return email;
    }

    public String getName() {
        return name;
    }

    public String getPw() {
        return pw;
    }

    public long getId() {
        return id;
    }

    public Date getLastModified() {
        return lastModified;
    }

    public Date getCreated() {
        return created;
    }

    public String getProfilePicture() {
        return profilePicture;
    }

    /*
     * Setters
     */

    public void setDevices(List<Device> devices) {
        this.devices = devices;
    }

    public void setPw(String pw) {
        this.pw = pw;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setId(long id) {
        this.id = id;
    }

    public void setLastModified(Date lastModified) {
        this.lastModified = lastModified;
    }

    public void setCreated(Date created) {
        this.created = created;
    }

    public void setProfilePicture(String profilePicture) {
        this.profilePicture = profilePicture;
    }

    @JsonIgnore
    public void addToContacts() {
        contactFlag = 1;
    }

    @JsonIgnore
    public void removeFromContacts() {
        contactFlag = 0;
    }

    @JsonIgnore
    public boolean isContact() {
        return contactFlag == 1;
    }

}
yasme/src/main/java/de/fau/cs/mad/yasme/android/controller/NotifiableFragment.java
public interface NotifiableFragment<T> {

    void notifyFragment(T value);
}
yasme/src/main/java/de/fau/cs/mad/yasme/android/ui/activities/ChatSettingsActivity.java
public class ChatSettingsActivity extends AbstractYasmeActivity {
    /**
     * The {@link android.support.v4.view.PagerAdapter} that will provide
     * fragments for each of the sections. We use a
     * {@link android.support.v13.app.FragmentPagerAdapter} derivative, which will keep every
     * loaded fragment in memory. If this becomes too memory intensive, it
     * may be best to switch to a
     * {@link android.support.v13.app.FragmentStatePagerAdapter}.
     */
    public static final String CHAT_ID = "chatId";

    ChatSettingsActivity.SectionsPagerAdapter mSectionsPagerAdapter;
    ViewPager mViewPager;

    private long chatId;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //progress bar in actionbar
        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);

        chatId = getIntent().getLongExtra(CHAT_ID, -1);
        if (chatId <= 0) {
            throw new IllegalArgumentException("chatId <= 0");
        }

        setContentView(R.layout.activity_contact);

        // Set up the action bar.
        final ActionBar actionBar = getActionBar();
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

        // Create the adapter that will return a fragment for each of the three
        // primary sections of the activity.
        mSectionsPagerAdapter = new SectionsPagerAdapter(getFragmentManager());

        // Set up the ViewPager with the sections adapter.
        mViewPager = (ViewPager) findViewById(R.id.pager);
        if(mViewPager == null) {
            Log.e(this.getClass().getSimpleName(), "ViewPager ist null");
        }
        mViewPager.setAdapter(mSectionsPagerAdapter);

        // When swiping between different sections, select the corresponding
        // tab. We can also use ActionBar.Tab#select() to do this if we have
        // a reference to the Tab.
        mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
            @Override
            public void onPageSelected(int position) {
                actionBar.setSelectedNavigationItem(position);
            }
        });

        // Create a tab listener that is called when the user changes tabs.
        ActionBar.TabListener tabListener = new ActionBar.TabListener() {
            public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
                // When the tab is selected, switch to the
                // corresponding page in the ViewPager.
                mViewPager.setCurrentItem(tab.getPosition());
            }

            public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
                // hide the given tab
            }

            public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
                // probably ignore this event
            }
        };

        // Add tabs, specifying the tab's text and TabListener
        for (int i = 0; i < mSectionsPagerAdapter.getCount(); i++) {
            actionBar.addTab(actionBar.newTab().setText(mSectionsPagerAdapter.getPageTitle(i))
                    .setTabListener(tabListener));
        }
    }



    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.chat, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int itemId = item.getItemId();
        if (itemId == android.R.id.home) {
            onBackPressed();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }


    @Override
    public void onBackPressed() {
        Intent intent = new Intent(this, ChatActivity.class);
        intent.putExtra(AbstractYasmeActivity.CHAT_ID, chatId);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        startActivity(intent);
    }

    /**
     * A {@link android.support.v13.app.FragmentPagerAdapter} that returns a fragment corresponding to
     * one of the sections/tabs/pages.
     */
    public class SectionsPagerAdapter extends FragmentPagerAdapter {

        public SectionsPagerAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {
            // getItem is called to instantiate the fragment for the given page.

            // It may be a better idea to pass the chat id and retrieve
            Bundle args = new Bundle();
            args.putLong(CHAT_ID, chatId);

            switch (position){
                case 0:
                    ChatSettingsInfo csi = new ChatSettingsInfo();
                    csi.setArguments(args);
                    return csi;
                case 1:
                    ChatSettingsAdd csa = new ChatSettingsAdd();
                    csa.setArguments(args);
                    return csa;
                case 2:
                    ChatSettingsRemove csr = new ChatSettingsRemove();
                    csr.setArguments(args);
                    return csr;
                default:
                    ChatSettingsInfo csid = new ChatSettingsInfo();
                    csid.setArguments(args);
                    return csid;
            }
        }


        @Override
        public int getCount() {
            return 3;
        }

        @Override
        public CharSequence getPageTitle(int position) {
            Locale l = Locale.getDefault();
            switch (position) {
                case 0:
                    return getString(R.string.settings_title_tab1).toUpperCase(l);
                case 1:
                    return getString(R.string.settings_title_tab2).toUpperCase(l);
                case 2:
                    return getString(R.string.settings_title_tab3).toUpperCase(l);

            }
            return null;
        }
    }
}
yasme/src/main/java/de/fau/cs/mad/yasme/android/storage/DatabaseManager.java
public enum DatabaseManager {
    INSTANCE;

    private boolean mInitialized = false;
    private boolean mDBInitialized = false;
    private DatabaseHelper mHelper;
    private Context mContext;
    private long mUserId = -1;
    private long mDeviceId = -1;
    private String mAccessToken = null;
    private long serverInfoUpdateTime = -1;
    private ServerInfo serverInfo = null;
    private SharedPreferences mSharedPreferences, mSettings;
    private String mUserEmail;
    private NewMessageNotificationManager notifier = null;

    private UserDAO userDAO;
    private ChatDAO chatDAO;
    private MessageDAO messageDAO;
    private MessageKeyDAO messageKeyDAO;
    private DeviceDAO deviceDAO;


    public void init(Context context, SharedPreferences sharedPreferences,
                     SharedPreferences settings, long userId) {
        mContext = context;
        mSharedPreferences = sharedPreferences;
        mSettings = settings;
        mUserId = userId;
        initDB(context, userId);
        initializeDAOs();
        mInitialized = true;
    }

    public void initDB(Context context, long userId) {
        mHelper = new DatabaseHelper(context, userId);
        mDBInitialized = true;
    }

    public boolean isInitialized() {
        return mInitialized;
    }

    public boolean isDBInitialized() {
        return mDBInitialized;
    }

    private DatabaseHelper getHelper() {
        return mHelper;
    }

    public SharedPreferences getSharedPreferences() {
        return mSharedPreferences;
    }

    public SharedPreferences getSettings() {
        return mSettings;
    }

    public Context getContext() {
        return mContext;
    }

    private void initializeDAOs() {
        UserDAOImpl.INSTANCE.setDatabaseHelper(mHelper);
        userDAO = UserDAOImpl.INSTANCE;

        ChatDAOImpl.INSTANCE.setDatabaseHelper(mHelper);
        chatDAO = ChatDAOImpl.INSTANCE;

        MessageDAOImpl.INSTANCE.setDatabaseHelper(mHelper);
        messageDAO = MessageDAOImpl.INSTANCE;

        MessageKeyDAOImpl.INSTANCE.setDatabaseHelper(mHelper);
        messageKeyDAO = MessageKeyDAOImpl.INSTANCE;

        DeviceDAOImpl.INSTANCE.setDatabaseHelper(mHelper);
        deviceDAO = DeviceDAOImpl.INSTANCE;
    }

    public UserDAO getUserDAO() {
        return userDAO;
    }

    public ChatDAO getChatDAO() {
        return chatDAO;
    }

    public MessageDAO getMessageDAO() {
        return messageDAO;
    }

    public MessageKeyDAO getMessageKeyDAO() {
        return messageKeyDAO;
    }

    public DeviceDAO getDeviceDAO() {
        return deviceDAO;
    }

    public long getUserId() {
        if (-1 == mUserId) {
            mUserId = getSharedPreferences().getLong(AbstractYasmeActivity.USER_ID, -1);
        }
        return mUserId;
    }

    public void setUserId(long mUserId) {
        this.mUserId = mUserId;
        SharedPreferences.Editor editor = getSharedPreferences().edit();
        editor.putLong(AbstractYasmeActivity.USER_ID, mUserId);
        editor.apply();
    }

    public long getDeviceId() {
        if (-1 == mDeviceId) {
            mDeviceId = getSharedPreferences().getLong(AbstractYasmeActivity.DEVICE_ID, -1);
        }
        return mDeviceId;
    }

    public void setDeviceId(long mDeviceId) {
        this.mDeviceId = mDeviceId;
        SharedPreferences.Editor editor = getSharedPreferences().edit();
        editor.putLong(AbstractYasmeActivity.DEVICE_ID, mDeviceId);
        editor.apply();
    }

    public String getAccessToken() {
        if (null == mAccessToken) {
            mAccessToken = getSharedPreferences().getString(AbstractYasmeActivity.ACCESSTOKEN, null);
        }
        return mAccessToken;
    }

    public void setAccessToken(String accessToken) {
        this.mAccessToken = accessToken;
        SharedPreferences.Editor editor = getSharedPreferences().edit();
        editor.putString(AbstractYasmeActivity.ACCESSTOKEN, mAccessToken);
        editor.commit();
    }

    public long getServerInfoUpdateTime() {
        if (-1 == serverInfoUpdateTime) {
            serverInfoUpdateTime = getSharedPreferences().getLong(AbstractYasmeActivity.SERVERINFOUPDATETIME, -1);
        }
        return serverInfoUpdateTime;
    }

    public void setServerInfoUpdateTime() {
        this.serverInfoUpdateTime = System.currentTimeMillis();
        SharedPreferences.Editor editor = getSharedPreferences().edit();
        editor.putLong(AbstractYasmeActivity.SERVERINFOUPDATETIME, serverInfoUpdateTime);
        editor.commit();
    }

    public ServerInfo getServerInfo() {
        return serverInfo;
    }

    public void setServerInfo(ServerInfo serverInfo) {
        this.serverInfo = serverInfo;
    }

    public String getUserEmail() {
        if (null == mUserEmail || "" == mUserEmail) {
            mUserEmail = getSharedPreferences().getString(AbstractYasmeActivity.USER_MAIL, null);
        }
        return mUserEmail;
    }

    public void setUserEmail(String mUserEmail) {
        this.mUserEmail = mUserEmail;
        SharedPreferences.Editor editor = getSharedPreferences().edit();
        editor.putString(AbstractYasmeActivity.USER_MAIL, mUserEmail);
        editor.commit();
    }

    public NewMessageNotificationManager getNotifier() {
        if (notifier == null) {
            notifier = new NewMessageNotificationManager();
        }
        return notifier;
    }
}
yasme/src/main/java/de/fau/cs/mad/yasme/android/controller/ObservableRegistry.java
public class ObservableRegistry {

    private static ArrayList<ObservableRegistryEntry> entries = new ArrayList<ObservableRegistryEntry>();

    public static <T extends NotifiableFragment<P>, P> FragmentObservable<T, P> getObservable(Class fragmentClass) {
        for (ObservableRegistryEntry entry : entries) {
            if (entry.check(fragmentClass)) {
                Log.d("ObserverRegistry","Returned existing observable");
                return (FragmentObservable<T,P>) entry.getObs(); // no idea how to solve this... 
            }
        }

        FragmentObservable<T, P> res = new FragmentObservable<T, P>();
        Log.d("ObserverRegistry","Created new observable");
        entries.add(new ObservableRegistryEntry<P>(res,fragmentClass));
        return res;
    }
}
yasme/src/main/java/de/fau/cs/mad/yasme/android/controller/FragmentObservable.java
public class FragmentObservable<T extends NotifiableFragment<P>,  P> {
    private Set<T> fragments;
    private P buffer;

    public FragmentObservable() {
        fragments = new HashSet<T>();
    }

    public void register(T fragment) {
        fragments.add(fragment);
        if (buffer != null) {
            notifyFragments(buffer);
        }
    }

    public void remove(T fragment) {
        fragments.remove(fragment);
    }

    //addIfNotExists
    public void notifyFragments(P parameter) {
        buffer = parameter;
        for (T fragment : fragments) {
            try {
                Log.d(this.getClass().getSimpleName(), "Notify fragment: " + fragment.getClass().getSimpleName());
                fragment.notifyFragment(parameter);
                buffer = null;
            } catch (Exception e) {
                Log.e(this.getClass().getSimpleName(), "Notify fragment failed: " + fragment.getClass().getSimpleName());
            }
        }
    }
}
yasme/src/main/java/de/fau/cs/mad/yasme/android/ui/AbstractYasmeActivity.java
public abstract class AbstractYasmeActivity extends Activity implements Toastable {
    public final static String USER_ID = "de.fau.cs.mad.yasme.android.USER_ID";
    public final static String USER_NAME = "de.fau.cs.mad.yasme.android.USER_NAME";
    public final static String USER_MAIL = "de.fau.cs.mad.yasme.android.USER_MAIL";
    public final static String USER_PW = "de.fau.cs.mad.yasme.android.USER_PW";
    public final static String DEVICE_ID = "de.fau.cs.mad.yasme.android.DEVICE_ID";
    public final static String PROFILE_PICTURE = "de.fau.cs.mad.yasme.android.PROFILE_PICTURE";

    public final static String CHAT_ID = "de.fau.cs.mad.yasme.android.CHAT_ID";
    public final static String LAST_MESSAGE_ID = "de.fau.cs.mad.yasme.android.LAST_MESSAGE_ID";

    public final static String ACCESSTOKEN = "de.fau.cs.mad.yasme.android.ACCESSTOKEN";
    public final static String SIGN_IN = "de.fau.cs.mad.yasme.android.SIGN_IN";

    public final static String SERVERINFOUPDATETIME = "de.fau.cs.mad.yasme.android.SERVERINFOUPDATETIME";
    public final static String SERVERMESSAGE = "de.fau.cs.mad.yasme.android.SERVERMESSAGE";

    public final static String STORAGE_PREFS = "de.fau.cs.mad.yasme.android.STORAGE_PREFS";
    public final static String SETTINGS_PREFS = "de.fau.cs.mad.yasme.android.SETTINGS_PREFS";
    public final static String DEVICE_PREFS = "de.fau.cs.mad.yasme.android.DEVICE_PREFS";
    public final static String PUSH_PREFS = "de.fau.cs.mad.yasme.android.PUSH_PREFS";

    public final static String NOTIFICATION_VIBRATE = "de.fau.cs.mad.yasme.android.NOTIFICATION_VIBRATE";
    public final static String NOTIFICATION_SOUND = "de.fau.cs.mad.yasme.android.NOTIFICATION_SOUND";


    //GCM Properties
    public static final String PROPERTY_REG_ID = "registration_id";
    public static final String PROPERTY_APP_VERSION = "appVersion";
    public static final String SENDER_ID = "688782154540"; //"104759172131";
    public static final int PLAY_SERVICES_RESOLUTION_REQUEST = 9000;
    public static final String TAG = "YasmeGCM";

    protected User selfUser;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (!ConnectionTask.isInitialized()) {
            String server = getResources().getString(R.string.server_host);
            if (BuildConfig.DEBUG) {
                server = getResources().getString(R.string.server_host_debug);
            }
            Log.d(getClass().getSimpleName(), "YASME-Server: " + server);
            ConnectionTask.initParams(getResources().getString(R.string.server_scheme),
                    server,
                    getResources().getString(R.string.server_port), getResources().getString(R.string.language), getVersion());
        }

        SharedPreferences storage = getSharedPreferences(STORAGE_PREFS, MODE_PRIVATE);
        SharedPreferences settings = getSharedPreferences(SETTINGS_PREFS, MODE_PRIVATE);
        Long userId = storage.getLong(USER_ID, 0);
        String userName = storage.getString(USER_NAME, "");
        String userMail = storage.getString(USER_MAIL, "");
        String userPw = storage.getString(USER_PW, "password");

        if(selfUser==null) {
            selfUser = new User();
        }
        selfUser.setId(userId);
        selfUser.setName(userName);
        selfUser.setEmail(userMail);
        selfUser.setPw(userPw);


        //Initialize databaseManager (once in application)
        if (!DatabaseManager.INSTANCE.isInitialized()) {
            DatabaseManager.INSTANCE.init(this, storage, settings, userId);
        }
        //Init QR-Code
        QR.init(false);

        String accessToken = DatabaseManager.INSTANCE.getAccessToken();
        if ((accessToken == null || accessToken.length() <= 0) && !this.getClass().equals(LoginActivity.class)) {
            Log.i(this.getClass().getSimpleName(), "Not logged in, starting login activity");
            Intent intent = new Intent(this, LoginActivity.class);
            startActivity(intent);
            finish();
            return;
        }
    }

    @Override
    public void onStart() {
        super.onStart();
        Toaster.getInstance().register(this);
        stopSpinning();
        SpinnerObservable.getInstance().registerActivity(this);
    }

    @Override
    public void onResume() {
        super.onResume();
        String accessToken = DatabaseManager.INSTANCE.getAccessToken();
        if ((accessToken == null || accessToken.length() <= 0) && !this.getClass().equals(LoginActivity.class)) {
            Log.i(this.getClass().getSimpleName(), "Not logged in, starting login activity");
            Intent intent = new Intent(this, LoginActivity.class);
            startActivity(intent);
            finish();
            return;
        }
    }

    @Override
    public void onStop() {
        super.onStop();
        Toaster.getInstance().remove(this);
        SpinnerObservable.getInstance().removeActivity(this);
    }

    public void setActionBarTitle(String title) {
        getActionBar().setTitle(title);
    }

    public void setActionBarTitle(String title, String subtitle) {
        getActionBar().setTitle(title);
        getActionBar().setSubtitle(subtitle);
    }

    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        Intent intent;
        int itemId = item.getItemId();
        switch (itemId) {
            case android.R.id.home:
                if (NavUtils.getParentActivityName(this) != null) {
                    NavUtils.navigateUpFromSameTask(this);
                }
                return true;
            case R.id.action_settings:
                intent = new Intent(this, SettingsActivity.class);
                intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                startActivity(intent);
                return true;
            case R.id.action_chats:
                intent = new Intent(this, ChatListActivity.class);
                intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                startActivity(intent);
                return true;
            case R.id.action_add_chat:
                intent = new Intent(this, InviteToChatActivity.class);
                intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
                startActivity(intent);
                return true;
            case R.id.action_contacts:
                intent = new Intent(this, ContactActivity.class);
                intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
                startActivity(intent);
                return true;
            default:
                return super.onOptionsItemSelected(item);

        }
    }

    public String getSelfName() {

        if(selfUser==null) {
            SharedPreferences storage = getSharedPreferences(STORAGE_PREFS, MODE_PRIVATE);
            Long userId = storage.getLong(USER_ID, 0);
            String userName = storage.getString(USER_NAME, "");
            String userMail = storage.getString(USER_MAIL, "");
            String userPw = storage.getString(USER_PW, "password");

            selfUser = new User();
            selfUser.setId(userId);
            selfUser.setName(userName);
            selfUser.setEmail(userMail);
            selfUser.setPw(userPw);
        }
        if (selfUser.getName().isEmpty()) {
            String name = getSharedPreferences(STORAGE_PREFS, MODE_PRIVATE).getString(USER_NAME, "");
            selfUser.setName(name);
        }
        return selfUser.getName();
    }

    public User getSelfUser() {
        return selfUser;
    }

    public String getUserMail() {
        if (selfUser == null || selfUser.getEmail().isEmpty()) {
            String mail = getSharedPreferences(STORAGE_PREFS, MODE_PRIVATE).getString(USER_MAIL, "");
            selfUser.setEmail(mail);
        }
        return selfUser.getEmail();
    }

    public long getUserId() {
        return selfUser.getId();
    }

    public String getOwnProfilePicture() {
        String path = selfUser.getProfilePicture();
        if (path == null || path.isEmpty()) {
            path = getSharedPreferences(STORAGE_PREFS, MODE_PRIVATE).getString(PROFILE_PICTURE, null);
        }
        return path;
    }

    public void setOwnProfilePicture(String ownProfilePicture) {
        selfUser.setProfilePicture(ownProfilePicture);
        SharedPreferences.Editor editor = getSharedPreferences(STORAGE_PREFS, MODE_PRIVATE).edit();
        editor.putString(AbstractYasmeActivity.PROFILE_PICTURE, ownProfilePicture);
        editor.commit();
    }

    public String getAccessToken() {
        return DatabaseManager.INSTANCE.getAccessToken();
    }

    public void toast(final int messageId, final int duration) {
        String text = getApplicationContext().getResources().getString(messageId);
        toast(text, duration, -1);
    }

    public void toast(final String text, final int duration) {
        toast(text, duration, -1);
    }

    public void toast(final String text, final int duration, final int gravity) {
        runOnUiThread(new Runnable() {
            public void run() {
                Toast toast = Toast.makeText(getApplicationContext(), text, duration);
                if (-1 != gravity) {
                    toast.setGravity(gravity, 0, 0);
                }
                // otherwise use default position
                Log.d(getClass().getSimpleName(), "Toast: " + text);
                toast.show();
            }
        });
    }

    public void startSpinning() {
        runOnUiThread(new Runnable() {
            public void run() {
                setProgressBarIndeterminateVisibility(true);
            }
        });
    }

    public void stopSpinning() {
        runOnUiThread(new Runnable() {
            public void run() {
                setProgressBarIndeterminateVisibility(false);
            }
        });
    }

    public int getVersion() {
        try {
            return getPackageManager().getPackageInfo(getPackageName(), 0).versionCode;
        } catch (Exception e) {
            Log.e(this.getClass().getSimpleName(), e.getMessage());
            return 0;
        }
    }

}
yasme/src/main/java/de/fau/cs/mad/yasme/android/controller/Toaster.java
public class Toaster {
    private static Toaster instance;
    private Context context;
    private Set<Toastable> toastables = new HashSet<>();

    private Toaster() {
        context = DatabaseManager.INSTANCE.getContext();
    }

    public static Toaster getInstance() {
        if (instance == null) {
            instance = new Toaster();
        }
        return instance;
    }

    public void register(Toastable toast) {
        toastables.add(toast);
    }

    public void remove(Toastable toast) {
        toastables.remove(toast);
    }

    public void toast(int id, int duration) {
        for (Toastable toast : toastables) {
            toast.toast(id,duration);
        }
    }

    public void toast(String text, int duration) {
        for (Toastable toast : toastables) {
            toast.toast(text, duration);
        }
    }

    public void toast(String text, int duration, int gravity) {
        for (Toastable toast : toastables) {
            toast.toast(text, duration, gravity);
        }
    }
}
yasme/src/main/java/de/fau/cs/mad/yasme/android/entities/Chat.java
@JsonIgnoreProperties(ignoreUnknown = true)
@DatabaseTable(tableName = DatabaseConstants.CHAT_TABLE)
public class Chat implements Serializable {

    @DatabaseField(columnName = DatabaseConstants.CHAT_ID, id = true)
    private long id;

    private List<User> participants;

    @DatabaseField(columnName = DatabaseConstants.NAME_CHANGED)
    private boolean nameChanged = false;

    @DatabaseField(columnName = DatabaseConstants.STATUS_CHANGED)
    private boolean statusChanged = false;

    @DatabaseField(columnName = DatabaseConstants.CHAT_STATUS)
    private String status;

    @DatabaseField(columnName = DatabaseConstants.CHAT_NAME)
    private String name;

    @DatabaseField(columnName = DatabaseConstants.OWNER, foreign = true)
    private User owner;

    @DatabaseField(columnName = DatabaseConstants.CHAT_LAST_MODIFIED, dataType = DataType.DATE)
    private Date lastModified;

    @DatabaseField(columnName = DatabaseConstants.CHAT_CREATED)
    private Date created;

    @JsonIgnore
    private String profilePicture;

    @JsonIgnore
    @ForeignCollectionField(columnName = DatabaseConstants.MESSAGES)
    private Collection<Message> messages;

    @JsonIgnore
    @DatabaseField(columnName = DatabaseConstants.LAST_MESSAGE, foreign = true)
    private Message lastMessage;

    /**
     * Constructors *
     */
    public Chat(long id) {
        this.id = id;
        this.participants = new ArrayList<User>();
        this.messages = new ArrayList<Message>();
    }

    /**
     * It is needed to set an id after calling this constructor!
     *
     * @param owner
     * @param status
     * @param name
     */
    public Chat(User owner, String status, String name) {
        new Chat(0, new ArrayList<User>(), status, name, owner, new ArrayList<Message>());
    }

    public Chat(long id, List<User> participants, String status, String name,
                User owner) {
        new Chat(id, participants, status, name, owner, new ArrayList<Message>());
    }

    public Chat() {
        // ORMLite needs a no-arg constructor
    }

    public Chat(long id, List<User> participants, String status, String name, User owner,
                Collection<Message> messages) {
        this.id = id;
        this.participants = participants;
        this.status = status;
        this.name = name;
        this.owner = owner;
        this.messages = messages;
    }

    public Chat(Chat origin) throws IllegalAccessException {
        Field[] fields = this.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.set(this, field.get(origin));
        }
    }

    /**
     * Getters *
     */
    public long getId() {
        return id;
    }

    /**
     * Setters *
     */
    public void setId(long id) {
        this.id = id;
    }

    public int size() {
        if (null == participants) {
            Log.e(((Object) this).getClass().getSimpleName(), "Participants are null");
            return -1;
        }
        return participants.size();
    }

    @JsonIgnoreProperties({"pw", "email", "name", "devices", "lastModified", "created"})
    public ArrayList<User> getParticipants() {
        if (participants == null) {
            participants = new ArrayList<>();
            User mDummy = new User("Dummy", 12);
            participants.add(mDummy);
            // Without cast IntelliJ is not happy
            Log.e(((Object) this).getClass().getSimpleName(), "Participants are null");
        }
        return new ArrayList<User>(participants);
    }

    public void setParticipants(List<User> participants) {
        this.participants = participants;
    }

    public void setNameChanged(boolean nameChanged) {
        this.nameChanged = nameChanged;
    }

    public void setStatusChanged(boolean statusChanged) {
        this.statusChanged = statusChanged;
    }

    public boolean addParticipant(User newUser) {
        if (participants == null) {
            Log.e(((Object) this).getClass().getSimpleName(), "Participants are null");
            return false;
        }
        participants.add(newUser);
        return true;
    }

    public boolean removeParticipant(User delUser) {
        if (participants == null) {
            Log.e(((Object) this).getClass().getSimpleName(), "Participants are null, sry.");
            return false;
        }
        boolean ret = false;
        for (User u : participants) {
            if (u.getId() != delUser.getId()) {
                continue;
            }
            ret = participants.remove(u);
            break;
        }
        return true;
    }

    public String getStatus() {
        if (!this.statusChanged || this.status == null || this.status.isEmpty()) {
            return (getNumberOfParticipants() + " YASMEs");
        }
        return this.status;
    }

    public void setStatus(String status, boolean isNotDefault) {
        this.statusChanged = isNotDefault;
        this.status = status;
    }

    public boolean getStatusChanged() {
        return this.statusChanged;
    }

    public boolean getNameChanged() {
        return this.nameChanged;
    }

    @JsonProperty("name")
    public String getName() {
        if (this.nameChanged && this.name != null && this.name.length() > 0) {
            return name;
        }
        // Create a new name
        String returnName = "";
        try {
            int size = getParticipants().size();
            for (int i = 0; i < size; i++) {
                returnName += participants.get(i).getName();
                if (i < size - 1) {
                    returnName += ", ";
                }
            }
        } catch (Exception e) {
            Log.d(this.getClass().getSimpleName(), e.getMessage());
        }
        return returnName;
    }

    public void setName(String name, boolean isNotDefault) {
        this.nameChanged = isNotDefault;
        this.name = name;
    }

    public Date getLastModified() {
        return lastModified;
    }

    public void setLastModified(Timestamp lastModified) {
        this.lastModified = lastModified;
    }

    public Date getCreated() {
        return created;
    }

    public void setCreated(Timestamp created) {
        this.created = created;
    }

    public String getProfilePicture() {
        return profilePicture;
    }

    public void setProfilePicture(String profilePicture) {
        this.profilePicture = profilePicture;
    }

    @JsonIgnoreProperties({"pw", "email", "name", "devices", "lastModified", "created"})
    public User getOwner() {
        if (owner == null) {
            owner = new User("Dummy", 12);
        }
        return owner;
    }

    public void setOwner(User owner) {
        this.owner = owner;
    }

    @JsonIgnore
    public int getNumberOfParticipants() {
        if (participants != null) {
            return participants.size();
        }
        return 0;
    }

    @JsonIgnore
    public ArrayList<Message> getMessages() {
        if (messages == null) {
            return new ArrayList<Message>();
        }
        return new ArrayList<Message>(messages);
    }

    public void setMessages(ArrayList<Message> messages) {
        this.messages = messages;
    }

    public void setLastModified(Date lastModified) {
        this.lastModified = lastModified;
    }

    /**
     * Other Methods
     */
    @JsonIgnore
    public boolean isOwner(long userId) {
        if (owner.getId() == userId) {
            return true;
        }
        return false;
    }

    public Message getLastMessage() {
        return lastMessage;
    }

    public void setLastMessage(Message lastMessage) {
        this.lastMessage = lastMessage;
    }

    public Chat clone() {
        try {
            return new Chat(this);
        } catch (IllegalAccessException e) {
            Log.e(this.getClass().getSimpleName(), e.getMessage());
            return null;
        }
    }
}
yasme/src/main/java/de/fau/cs/mad/yasme/android/encryption/MessageEncryption.java
public class MessageEncryption {
    Chat chat;
    User creator;
    private DatabaseManager db = DatabaseManager.INSTANCE;

    /**
     * initialize Encryption for the given chat
     *
     * @param chat chat the encryption should be initialized for
     * @param creator user who is using the chat
     */
    public MessageEncryption(Chat chat, User creator) {
        this.chat = chat;
        this.creator = creator;
    }

    /**
     * For Encryption the latest generated key is needed
     * This method load the latest key from database
     *
     * @return MessageKey-Object containing the latest AES-Key with the corresponding InitalVector
     */
    public MessageKey getCurrentKey() {
        try {
            //Log.d(this.getClass().getSimpleName(),"Try to use a local Key");
            MessageKey key = db.getMessageKeyDAO().getCurrentKeyByChat(chat.getId());
            if (key != null) {
                //Log.d(this.getClass().getSimpleName(),"... success");
                return key;
            } else {
                //Log.d(this.getClass().getSimpleName(),"... failed");
                return generateKey();
            }
        }
        catch (Exception e){
            Log.e(this.getClass().getSimpleName(),"... failed with exception");
            return null;
        }
    }

    /**
     * generate a random AES-Key/InitialVector and encode it to base64
     * call method sendKey: send it to the server and save it locally
     *
     * @return MessageKey containing the generated AES-Key/Inital Vector and the Keyid the server has assigned
     */
    public MessageKey generateKey() {
        //Log.d(this.getClass().getSimpleName(),"Generate Key");
        Toaster.getInstance().toast(R.string.generate_key, Toast.LENGTH_LONG);

        // Generate key
        AESEncryption aes = new AESEncryption();
        String key = aes.getKeyinBase64();
        String iv = aes.getIVinBase64();

        return sendKey(key, iv, true);
    }

    /**
     * load AES-Key and Initial Vector for given KeyId
     *
     * @param keyId KeyId of the key that is needed
     * @return MessageKey-Object containing the needed AES-Key/InitalVector
     */
    public MessageKey getKey(long keyId) {
        //Log.d(getClass().getSimpleName(), "Get key from DB: " + keyId);
        return db.getMessageKeyDAO().get(keyId);
    }

    /**
     * encrypt a Message with the latest generated AES-Key loaded from database
     *
     * @param message message that should be encrypted
     * @return message-object containing the encrypted message and the used KeyId
     */
    public Message encrypt(Message message) {
        return encrypt(message,getCurrentKey());
    }

    /**
     * encrypt a Message and force to generate a new AES-Key
     *
     * @param message message that should be encrypted
     * @return message-object containing the encrypted message and the used KeyId
     */
    public Message encryptGenerated(Message message) {
        return encrypt(message,generateKey());
    }

    /**
     * encrypt a message with a given MessageKey
     *
     * @param message message that should be encrypted
     * @param messageKey messageKey that should be used for encryption
     * @return message-object containing the encrypted message and the used KeyId
     */
    private Message encrypt (Message message, MessageKey messageKey) {
        if (messageKey == null) {
            Log.e(getClass().getSimpleName(), "Message could not be encrypted");
            Toaster.getInstance().toast(R.string.key_generation_failed,Toast.LENGTH_LONG);
            return null;
        }
        AESEncryption aes = new AESEncryption(messageKey);
        message.setMessage(aes.encrypt(message.getMessage()));
        message.setMessageKeyId(messageKey.getId());
        return message;
    }

    /**
     * decrypt a message using the KeyId that is stored in the message-object
     * check, if the used Key is confidable
     *
     * @param message message that should be decrypted
     * @return message-object containing the decrypted message
     */
    public Message decrypt(Message message) {
        MessageKey messageKey = getKey(message.getMessageKeyId());
        if (messageKey == null) {
            message.setMessage("");
            message.setErrorId(ErrorType.DECRYPTION_FAILED);
            return message;
        }
        AESEncryption aes = new AESEncryption(messageKey);
        message.setMessage(aes.decrypt(message.getMessage()));

        //is the message successfully authenticated?
        message.setAuthenticity(messageKey.getAuthenticity());
        if (message.getErrorId() == 0 && !messageKey.getAuthenticity()) {
            message.setErrorId(ErrorType.AUTHENTICATION_FAILED);
        }

        return message;
    }

    /**
     * get all devices the generated messagekey need to be sent to (all devices from all participants in the chat)
     * that is necessary because a messagekey needs to be encrypted using RSA for every single device the key is sent to
     *
     * @param local define, if the devices should be loaded from local storage or if the devices should be loaded from the server
     * @return list of all devices
     */
    private List<Device> getRecipientDevices(boolean local) {
        if (local) {
            //Log.d(this.getClass().getSimpleName(),"Get local stored devices");
            List<Device> devices = new ArrayList<>();
            //Log.d(this.getClass().getSimpleName(),"Generate keys for " + chat.getParticipants().size() + " users");
            for (User user : chat.getParticipants()) {
                //Log.d(this.getClass().getSimpleName(),"Add Devices for User " + user.getId());
                for (Device device : DatabaseManager.INSTANCE.getDeviceDAO().getAll(user)) {
                    //Log.d(this.getClass().getSimpleName(),"Local-device: " + device.getId());
                    //Log.d(this.getClass().getSimpleName(),"Add Device" + device.getId() + " from User " + device.getUser().getName());
                    devices.add(device);
                }
            }
            return devices;
        } else {
            try {
                //Log.d(this.getClass().getSimpleName(),"Get devices from Server");
                return ChatTask.getInstance().getAllDevicesForChat(chat.getId());
            } catch (Exception e) {
                Log.e(this.getClass().getSimpleName(),e.getMessage());
                return new ArrayList<>();
            }

        }
    }


    /**
     * this method send a generated Key/InitalVector to all devices of the chat participants
     * for every device the key needs to be encrypted using the public RSA-Key from the recipientDevice
     * this encrypted key also is signed by the private key of the creatorDevice (sender)
     *
     * when the server received the encrypted key for all known devices, he returns a messageKey-Object with an assigned KeyId
     * now the method saves the generated Key with this KeyId on the local database
     *
     * @param key generated AES-Key encoded in base64
     * @param iv generated InitalVector encoded in base64
     * @param local define, if the devices should be loaded from local storage or if the devices should be loaded from the server
     * @return  MessageKey-Object containing the assigned KeyId and Timestamp for the generated Key
     *          null, if sending/saving key was not successful
     */
    public MessageKey sendKey(String key, String iv, boolean local) {
        try {
            long deviceId = DatabaseManager.INSTANCE.getDeviceId();
            Device sender = new Device(deviceId);
            ArrayList<MessageKey> messageKeys = new ArrayList<MessageKey>();
            List<Device> devices = getRecipientDevices(local);
            if (local && devices.size() == 0) {
                local = false;
                devices = getRecipientDevices(false);
            }

            if (!local) {
                RefreshTask refreshTask = new RefreshTask(RefreshTask.RefreshType.CHAT, chat.getId(), true);
                refreshTask.execute();
            }

            for (Device recipientDevice : devices) {
                //Log.d(this.getClass().getSimpleName(),"Send Key for Device" + recipientDevice.getId() + " from User " + recipientDevice.getUser().getName());


                // Do not store the key on the server for the creating device
                if (recipientDevice.getId() == deviceId) {
                    //Log.d(this.getClass().getSimpleName(),"Ignored Key for Device" + recipientDevice.getId());
                    continue;
                }

                //Log.d(this.getClass().getSimpleName(),"Generate Key for Device" + recipientDevice.getId());

                MessageKey messageKey = new MessageKey(0, sender, recipientDevice, chat, key, iv);
                Log.d(getClass().getSimpleName(), "MessageKey1: " + messageKey.getMessageKey());
                KeyEncryption keyEncryption = new KeyEncryption();
                Log.d(getClass().getSimpleName(), "MessageKey2: " + messageKey.getMessageKey());
                MessageKey messageKeyEncrypted = keyEncryption.encrypt(messageKey);
                Log.d(getClass().getSimpleName(), "MessageKey3: " + messageKeyEncrypted.getMessageKey());
                //Log.d(this.getClass().getSimpleName(), "MessageKey has successfully been encrypted.");
                MessageKey messageKeySigned = keyEncryption.sign(messageKeyEncrypted);
                Log.d(getClass().getSimpleName(), "MessageKey4: " + messageKeySigned.getMessageKey());
                //Log.d(this.getClass().getSimpleName(), "MessageKey has successfully been signed.");

                messageKeys.add(messageKeySigned);
                //Log.d(this.getClass().getSimpleName(),"Key from " + deviceId + " for device " + recipientDevice.getId() + "created");
            }

            MessageKey result = MessageKeyTask.getInstance().saveKeys(messageKeys);

            if (result == null) {
                //Log.d(this.getClass().getSimpleName(),"Error sending key to server");
                return null;
            }

            MessageKey messageKey = new MessageKey(result.getId(),sender,sender,chat,key,iv);
            messageKey.setCreated(result.getCreated());

            //Log.d(this.getClass().getSimpleName(),"Key was send to server, id is: "+ messageKey.getId());

            // If you can trust yourself
            messageKey.setAuthenticity(true);
            db.getMessageKeyDAO().addIfNotExists(messageKey);
            return messageKey;

        } catch (IncompleteKeyException e) {
            if (local) {
                Log.e(this.getClass().getSimpleName(),"Create key");
                return sendKey(key,iv,false);
            } else {
                Log.e(this.getClass().getSimpleName(),"Key was not sent to server");
                return null;
            }
        } catch (Exception e) {
            Log.e(this.getClass().getSimpleName(),"Failed to send key: "+e.getMessage());
            return null;
        }
    }

    public static class ErrorType {
        public static final int OK = 0;
        public static final int DECRYPTION_FAILED = 10;
        public static final int AUTHENTICATION_FAILED = 20;
    }
}
yasme/src/main/java/de/fau/cs/mad/yasme/android/EditTextWithImage.java
public class EditTextWithImage {
    private EditText et;
    private ImageView iv;
    private Drawable button;

    public EditTextWithImage(final Context context) {
        et = new EditText(context);
        iv = new ImageView(context);
        iv.setVisibility(View.GONE);
        button = context.getResources().getDrawable(R.drawable.ic_action_camera);
        button.setBounds(0, 0, button.getIntrinsicWidth(), button.getIntrinsicHeight());
        et.setCompoundDrawables(null, null, button, null);

        et.addTextChangedListener(new TextWatcher() {
            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                et.setCompoundDrawables(null, null, button, null);
            }

            @Override
            public void afterTextChanged(Editable arg0) {

            }

            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }
        });
    }

    public ImageView getImageView() {
        return iv;
    }

    public int getIntrinsicWidth() {
        return button.getIntrinsicWidth();
    }

    public EditText getEditText() {
        return et;
    }
}
yasme/src/main/java/de/fau/cs/mad/yasme/android/ui/activities/ChatActivity.java
public class ChatActivity extends AbstractYasmeActivity {

    private NewMessageNotificationManager notifier;

    @Override
    public void onResume() {
        super.onResume();
        notifier = DatabaseManager.INSTANCE.getNotifier();
        notifier.clearMessages();
    }

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

        //progress bar in actionbar
        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);

		setContentView(R.layout.activity_with_single_fragment);

        getActionBar().setDisplayHomeAsUpEnabled(true);

		if (savedInstanceState == null) {
			getFragmentManager().beginTransaction()
					.add(R.id.singleFragmentContainer, new ChatFragment(), "chatFragment").commit();
		}
    }

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {

		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.chat, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// Handle action bar item clicks here. The action bar will
		// automatically handle clicks on the Home/Up button, so long
		// as you specify a parent activity in AndroidManifest.xml.
        Intent intent;
        int itemId = item.getItemId();
        switch (itemId) {
            case android.R.id.home:
                if (NavUtils.getParentActivityName(this) != null) {
                    NavUtils.navigateUpFromSameTask(this);
                }
                return true;
        }
        return super.onOptionsItemSelected(item);
    }
}
yasme/src/main/java/de/fau/cs/mad/yasme/android/asyncTasks/server/GetMessageTask.java
public class GetMessageTask extends AsyncTask<Object, Void, Boolean> {

    private List<Message> messages;
    private long lastMessageId;
    private NewMessageNotificationManager notifier;
    private Class classToNotify;

    public GetMessageTask(Class classToNotify) {
        notifier = DatabaseManager.INSTANCE.getNotifier();
        this.classToNotify = classToNotify;
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        SpinnerObservable.getInstance().registerBackgroundTask(this);
    }

    /**
     * @return Returns true if it was successful, otherwise false
     */
    @Override
    protected Boolean doInBackground(Object... params) {
        lastMessageId = DatabaseManager.INSTANCE.getSharedPreferences()
                .getLong(AbstractYasmeActivity.LAST_MESSAGE_ID, 0L);
        Log.d(this.getClass().getSimpleName(), "Last message id: " + lastMessageId);
        try {
            messages = MessageTask.getInstance().getMessages(lastMessageId);
        } catch (RestServiceException e) {
            Log.e(this.getClass().getSimpleName(), e.getMessage());
        }

        if (messages == null) {
            Log.w(this.getClass().getSimpleName(), "messages is null!");
            return false;
        }
        if (messages.isEmpty()) {
            Log.i(this.getClass().getSimpleName(), "messages is empty");
            return true;
        }

        //add new messages to DB
        Log.d(this.getClass().getSimpleName(), "Number of messages to store in DB: " + messages.size());
        for (Message message : messages) {
            // Log.d(this.getClass().getSimpleName(), message.getMessage() + " " + message.getId());

            // Refresh Sender-User-Data
            User sender = message.getSender();
            User dbUser = DatabaseManager.INSTANCE.getUserDAO().get(sender.getId());
            Log.d(getClass().getSimpleName(), "Sender lastMod: " + sender.getLastModified());
            if (dbUser == null || dbUser.getLastModified() == null || sender.getLastModified() == null || sender.getLastModified().compareTo(dbUser.getLastModified()) > 0) {
                Log.d(getClass().getSimpleName(), "Sender has to be refreshed");
                RefreshTask refreshTask = new RefreshTask(RefreshTask.RefreshType.USER, sender.getId(), true);
                refreshTask.execute();
            }

            // Store MessageKey if message contains one
            storeMessageKey(message);
            // Decrypt message
            message = decrypt(message);

            if (null == DatabaseManager.INSTANCE.getMessageDAO().addIfNotExists(message)) {//changed from addOrUpdate

                Log.e(this.getClass().getSimpleName(), "Storing a message in database failed");
                return false;
            } else {
                //Log.d(getClass().getSimpleName(), "Message successfully stored");
                if (message.getChat() != null) {
                    //Log.d(getClass().getSimpleName(), "Get Chat from DB");
                    ChatDAO chatDAO = DatabaseManager.INSTANCE.getChatDAO();
                    Chat chat = chatDAO.get(message.getChat().getId());
                    if (chat != null) {
                        chat.setLastMessage(message);
                        chatDAO.addOrUpdate(chat);
                        //Log.d(getClass().getSimpleName(), "Set lastMessage for chat " + chat.getId() +": " + message.getId());
                    } else {
                        Log.e(getClass().getSimpleName(), "Chat not found in DB");
                    }
                } else {
                    Log.e(getClass().getSimpleName(), "Chat not found in Message");
                }
            }
            lastMessageId = Math.max(lastMessageId, message.getId());
        }
        return true;
    }

    @Override
    protected void onPostExecute(final Boolean success) {
        SpinnerObservable.getInstance().removeBackgroundTask(this);
        if (!success) {
            Log.e(this.getClass().getSimpleName(), "No success");
            return;
        } else {
            //Log.i(this.getClass().getSimpleName(), "UpdateDB successfull, Messages stored");

            int size = messages.size();
            if (size > 0) {
                // Store lastMessageId
                SharedPreferences.Editor editor = DatabaseManager.INSTANCE.getSharedPreferences().edit();
                editor.putLong(AbstractYasmeActivity.LAST_MESSAGE_ID, lastMessageId);
                editor.commit();

                if (!(size == 1 && messages.get(0).getSender().getId() == DatabaseManager.INSTANCE.getUserId())) {
                    notifier.mNotify(size, messages.get(size - 1).getChatId());
                }
            }

            if (classToNotify == ChatFragment.class) {
                ObservableRegistry.getObservable(ChatFragment.class).notifyFragments(null);
            } else if (classToNotify == ChatListFragment.class) {
                ObservableRegistry.getObservable(ChatListFragment.class).notifyFragments(null);
            }
        }
    }

    private Message decrypt(Message message) {
        Chat chat = DatabaseManager.INSTANCE.getChatDAO().get(message.getChat().getId());
        User sender = DatabaseManager.INSTANCE.getUserDAO().get(message.getSender().getId());
        message.setChat(chat);
        message.setSender(sender);

        // Decrypt
        MessageEncryption messageEncryption = new MessageEncryption(chat, sender);
        message = messageEncryption.decrypt(message);
        return message;
    }

    private void storeMessageKey(Message message) {
        MessageKey messageKeyEncrypted = message.getMessageKey();
        if (messageKeyEncrypted == null) {
            return;
        }
        Device creator;
        try {
            creator = DeviceTask.getInstance().getDevice(messageKeyEncrypted.getCreatorDevice().getId());
        } catch (Exception e) {
            Log.e(getClass().getSimpleName(), "CreatorDevice is null");
            creator = null;
        }
        messageKeyEncrypted.setCreatorDevice(creator);
        if (creator != null) {
            //Log.d(getClass().getSimpleName(), "Creator publicKey: " + messageKeyEncrypted.getCreatorDevice().getPublicKey());
        }

        KeyEncryption keyEncryption = new KeyEncryption();

        //verify the signature of the key and save authenticity-status in messageKeyEncrypted
        if (messageKeyEncrypted.setAuthenticity(keyEncryption.verify(messageKeyEncrypted))) {
            Log.d(this.getClass().getSimpleName(), "MessageKey has successfully been verified");
        } else {
            Log.d(this.getClass().getSimpleName(), "MessageKey could not be verified");
        }

        //decrypt the key with RSA
        MessageKey messageKey = keyEncryption.decrypt(messageKeyEncrypted);
        if (messageKey != null && DatabaseManager.INSTANCE.getMessageKeyDAO().addIfNotExists(messageKey) != null) {
            try {
                MessageKeyTask.getInstance().deleteKey(messageKey.getId());
            } catch (Exception e) {
                Log.e(this.getClass().getSimpleName(), e.getMessage());
            }

            // For Developer-Devices only
            if (DebugManager.INSTANCE.isDebugMode()) {
                //Log.d(getClass().getSimpleName(), "Store messageKey to external storage");
                DebugManager.INSTANCE.storeMessageKeyToExternalStorage(messageKey);
            }
        }
        Log.d(this.getClass().getSimpleName(), "Key " + messageKey.getId() + " extracted from Message and saved");
    }
}
yasme/src/main/java/de/fau/cs/mad/yasme/android/asyncTasks/server/SendMessageTask.java
public class SendMessageTask extends AsyncTask<String, Void, Boolean> {

    private AsyncTask<Object,Void,Boolean> onPostExecute; // could also be a GetMessageTask
    private Chat chat;
    private User sender;
    private Mime type;

    public SendMessageTask(Chat chat, User sender, AsyncTask<Object,Void,Boolean> onPostExecute, Mime type) {
        this.sender = sender;
        this.chat = chat;
        this.onPostExecute = onPostExecute;
        this.type = type;
    }


    protected void onPreExecute() {
        super.onPreExecute();
        SpinnerObservable.getInstance().registerBackgroundTask(this);
    }

    /**
     * @param msgs
     * @return true on success and false on error
     */
    protected Boolean doInBackground(String... msgs) {
        for (String msgText : msgs) {
            if (null == msgText) {
                Log.e(this.getClass().getSimpleName(), "Received message is null!");
                continue;
            }

            if (sendMessage(msgText) == null) {
                return false;
            }
        }
        return true;
    }

    protected void onPostExecute(final Boolean success) {
        SpinnerObservable.getInstance().removeBackgroundTask(this);
        if (success) {
            if (null != this.onPostExecute) {
                onPostExecute.execute(); // onPostExecute is a GetMessageTask
                // onPostExecute async task will call notify the registered fragments
            }
        } else {
            Log.e(this.getClass().getSimpleName(), "Send failed");
        }
    }

    private Message sendMessage(String text) {
        try {
            // At first, try with an old key
            return sendMessage(text, false);
        } catch (KeyOutdatedException koe) {
            try {
                // If key is outdated, retry with a generated key
                return sendMessage(text, true);
            } catch (Exception e) {
                Log.e(this.getClass().getSimpleName(),e.getMessage());
                return null;
            }
        }
    }

    private Message sendMessage(String text, Boolean forceKeyGeneration) throws KeyOutdatedException {
        // Create message
        Message message;
        if (type == Mime.PLAIN) {
            message = new Message(sender, text, chat, 0,
                    DatabaseManager.INSTANCE.getContext().getResources().getString(R.string.mime_text));
        } else if (type == Mime.IMAGE) {
            message = new Message(sender, text, chat, 0,
                    DatabaseManager.INSTANCE.getContext().getResources().getString(R.string.mime_image));
        } else {
            Log.e(this.getClass().getSimpleName(), "Error during creating a new message");
            return null;
        }

        // Encrypt
        MessageEncryption messageEncryption = new MessageEncryption(chat, sender);
        if (forceKeyGeneration) {
            message = messageEncryption.encryptGenerated(message);
        } else {
            message = messageEncryption.encrypt(message);
        }

        if (message == null) {
            return null;
        }

        // Send
        return  MessageTask.getInstance().sendMessage(message);
    }

    public enum Mime {
        PLAIN, //"text/plain"
        IMAGE; //"media/image"
    }
}


Question: package de.fau.cs.mad.yasme.android.ui.fragments;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Fragment;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.media.ThumbnailUtils;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.Toast;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import de.fau.cs.mad.yasme.android.EditTextWithImage;
import de.fau.cs.mad.yasme.android.R;
import de.fau.cs.mad.yasme.android.asyncTasks.database.GetNewMessagesForChatTask;
import de.fau.cs.mad.yasme.android.asyncTasks.server.GetMessageTask;
import de.fau.cs.mad.yasme.android.asyncTasks.server.SendMessageTask;
import de.fau.cs.mad.yasme.android.controller.FragmentObservable;
import de.fau.cs.mad.yasme.android.controller.Log;
import de.fau.cs.mad.yasme.android.controller.NotifiableFragment;
import de.fau.cs.mad.yasme.android.controller.ObservableRegistry;
import de.fau.cs.mad.yasme.android.controller.Toaster;
import de.fau.cs.mad.yasme.android.encryption.MessageEncryption;
import de.fau.cs.mad.yasme.android.entities.Chat;
import de.fau.cs.mad.yasme.android.entities.Message;
import de.fau.cs.mad.yasme.android.entities.User;
import de.fau.cs.mad.yasme.android.storage.DatabaseManager;
import de.fau.cs.mad.yasme.android.storage.PictureManager;
import de.fau.cs.mad.yasme.android.ui.AbstractYasmeActivity;
import de.fau.cs.mad.yasme.android.ui.ChatAdapter;
import de.fau.cs.mad.yasme.android.ui.activities.ChatActivity;
import de.fau.cs.mad.yasme.android.ui.activities.ChatSettingsActivity;




/**
 * Created by Martin Sturm <martin.sturm@fau.de> on 21.06.2014.
 */
public class ChatFragment extends Fragment implements NotifiableFragment<List<Message>> {
    public static final String RESTORE_LATEST_MESSAGE_ON_DISPLAY = "LATEST_MESSAGE_ON_DISPLAY";
    public static final String RESTORE_CHAT_ID = "CHAT_ID";
    public static final String RESTORE_IMAGE_URI = "imageUri";

    private ChatAdapter mAdapter;
    private Chat chat;
    private AtomicLong latestMessageOnDisplay;
    private Context mContext;

    //UI references
    private Button imageCancel;
    private ImageView imageView;
    private EditText editMessage;
    private ListView list;

    private static final int RESULT_LOAD_IMAGE = 100;
    private static final int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 200;

    private Uri imageUri;
    private Bitmap bitmap;

    public ChatFragment() {
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Retrain instance variables!
        // setRetainInstance(true);

        ChatActivity activity = (ChatActivity) getActivity();
        mContext = DatabaseManager.INSTANCE.getContext();

        Intent intent = activity.getIntent();
        long chatId = intent.getLongExtra(AbstractYasmeActivity.CHAT_ID, -1);
        if (chatId <= 0) {
            throw new ExceptionInInitializerError("chatId <= 0");
        }

        //add the fragments own menu items
        setHasOptionsMenu(true);

        //trying to get chat with chatId from local DB
        try {
            chat = DatabaseManager.INSTANCE.getChatDAO().get(chatId);
            // Assuming that the messages are sorted by id
            latestMessageOnDisplay = new AtomicLong(0);
        } catch (NullPointerException e) {
            // Occurs when new chat has been generated, but id hasn't been returned by the server yet
            chat = null;
            Log.e(this.getClass().getSimpleName(), "get chat from DB failed");
        }
        if (chat == null) {
Answer:             Toaster.getInstance().toast(R.string.unable_open_chat, Toast.LENGTH_SHORT);