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/ui/fragments/UserDetailsFragment.java
public class UserDetailsFragment
        extends DialogFragment
        implements View.OnClickListener,
        NotifiableFragment<UserDetailsFragment.UserDetailsFragmentParam> {

    private static final String ARG_USER = "userparam";
    private static final String ARG_USERNAME = "param1";
    private static final String ARG_USERMAIL = "param2";
    private static final String ARG_USERID = "param3";
    private static final String ARG_CONTACTBUTTON = "param4";


    private User contact;
    private User selfUser;

    private TextView contactName;
    private ImageView profilePicture;
    private Button startChat;
    private Button addContact;
    private UserDAO userDAO = DatabaseManager.INSTANCE.getUserDAO();
    private Context context = DatabaseManager.INSTANCE.getContext();

    private OnDetailsFragmentInteractionListener mListener;

    public UserDetailsFragment() {
        // Required empty public constructor
    }

    /**
     * Use this factory method to create a new instance of
     * this fragment using the provided parameters.
     *
     * @return A new instance of fragment UserDetailsFragment.
     */
    public static UserDetailsFragment newInstance(User theContact, Boolean addContactButton) {
        UserDetailsFragment fragment = new UserDetailsFragment();

        Bundle args = new Bundle();
        args.putSerializable(ARG_USER, theContact);
        args.putString(ARG_USERNAME, theContact.getName());
        args.putString(ARG_USERMAIL, theContact.getEmail());
        args.putString(ARG_USERID, String.valueOf(theContact.getId()));
        args.putBoolean(ARG_CONTACTBUTTON, addContactButton);
        fragment.setArguments(args);

        return fragment;
    }

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

        if (getArguments() != null) {
            contact = (User) getArguments().getSerializable(ARG_USER);
        }
        User user = DatabaseManager.INSTANCE.getUserDAO().get(contact.getId());
        if (user != null) {
            contact = user;
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        AbstractYasmeActivity activity = (AbstractYasmeActivity) getActivity();
        selfUser = activity.getSelfUser();

        View layout = inflater.inflate(R.layout.fragment_user_details, container, false);
        contactName = (TextView) layout.findViewById(R.id.contact_header);
        profilePicture = (ImageView) layout.findViewById(R.id.contact_details_profile_picture);
        startChat = (Button) layout.findViewById(R.id.contact_detail_newchat);
        addContact = (Button) layout.findViewById(R.id.contact_detail_addcontact);
        TextView initial = (TextView) layout.findViewById(R.id.contact_details_profile_picture_text);

        contactName.setText(contact.getName());
        boolean isSelf = (selfUser.getId() == contact.getId());

        // Show first letter of contact name as profile image
        if (profilePicture != null) {
            if (isSelf) {
                SharedPreferences storage = context
                        .getSharedPreferences(AbstractYasmeActivity.STORAGE_PREFS, Context.MODE_PRIVATE);
                contact = selfUser;
                contact.setProfilePicture(storage.getString(AbstractYasmeActivity.PROFILE_PICTURE, null));
            }

            if (contact.getProfilePicture() != null && !contact.getProfilePicture().isEmpty()) {
                // load picture from local storage
                initial.setVisibility(View.GONE);
                profilePicture.setBackgroundColor(Color.TRANSPARENT);
                profilePicture.setImageBitmap(PictureManager.INSTANCE.getPicture(contact, 300, 300));
            } else {
                // no local picture found. Set default pic
                profilePicture.setBackgroundColor(ChatAdapter.CONTACT_DUMMY_COLORS_ARGB
                        [(int) contact.getId() % ChatAdapter.CONTACT_DUMMY_COLORS_ARGB.length]);
                initial.setText(contact.getName().substring(0, 1).toUpperCase());
            }
        }

        // Don't show button to add self to contacts
        if (isSelf) {
            startChat.setVisibility(View.GONE);
            addContact.setVisibility(View.GONE);
        } else {
            startChat.setOnClickListener(this);
            addContact.setOnClickListener(this);
        }


        if (!getArguments().getBoolean(ARG_CONTACTBUTTON)) {
            addContact.setVisibility(View.GONE);
        }

        return layout;
    }


    @Override
    public void onStart() {
        super.onStart();
        //Register at observer
        Log.d(this.getClass().getSimpleName(), "Try to get ChatListObservableInstance");
        FragmentObservable<UserDetailsFragment, UserDetailsFragmentParam> obs = ObservableRegistry.getObservable(UserDetailsFragment.class);
        Log.d(this.getClass().getSimpleName(), "... successful");
        obs.register(this);
    }

    @Override
    public void onStop() {
        super.onStop();
        FragmentObservable<UserDetailsFragment, UserDetailsFragmentParam> obs =
                ObservableRegistry.getObservable(UserDetailsFragment.class);
        Log.d(this.getClass().getSimpleName(), "Remove from observer");
        obs.remove(this);
    }


    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            mListener = (OnDetailsFragmentInteractionListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString()
                    + " must implement OnFragmentInteractionListener");
        }
    }

    @Override
    public void onDetach() {
        super.onDetach();
        mListener = null;
    }

    @Override
    public void onClick(View v) {

        switch (v.getId()) {
            case R.id.contact_detail_newchat:

                // Add to contacts, but don't show toasts
                addToContacts(contact, false);
                Set<User> selectedUsers = new HashSet<>();
                selectedUsers.add(contact);
                // Don't dismiss. Wait until you're notified and than go to the chat view
                new CreateChatTask(selfUser, selectedUsers, this.getClass()).execute();
                break;

            case R.id.contact_detail_addcontact:

                addToContacts(contact, true);
                this.dismiss();
                break;
        }

    }


    private void addToContacts(User tappedUser, boolean showToast) {
        User userFromDb = userDAO.get(tappedUser.getId());
        if (null != userFromDb) {
            contact = userFromDb;
        }

        // Contact flag will be zero if the tapped user was not found in the database
        if (contact.isContact()) {
            if (showToast) {
                String toast = contact.getName() + " " + context.getText(R.string.contact_already_added) + ".";
                Toaster.getInstance().toast(toast, Toast.LENGTH_LONG);
            }
            return;
        }

        contact.addToContacts();
        userDAO.addOrUpdate(contact);
        RefreshTask refreshTask = new RefreshTask(RefreshTask.RefreshType.USER, contact.getId(), true);
        refreshTask.execute();
        Log.d(this.getClass().getSimpleName(), "contact added");

        if (showToast) {
            String toast = contact.getName() + " " + context.getText(R.string.contact_added_success) + ".";
            Toaster.getInstance().toast(toast, Toast.LENGTH_LONG);
        }

        // Refresh contact list in first tab
        new GetContactsTask(ContactListFragment.class).execute();
    }


    private void sendMail(String email) {
        Intent i = new Intent(Intent.ACTION_SEND);
        i.setType("message/rfc822");
        i.putExtra(Intent.EXTRA_EMAIL, new String[]{email});
        i.putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.contact_details_email_default_subject));
        i.putExtra(Intent.EXTRA_TEXT, context.getString(R.string.contact_details_email_default_body));
        try {
            startActivity(Intent.createChooser(i, context.getString(R.string.contact_details_compose_email_action)));
        } catch (android.content.ActivityNotFoundException ex) {
            Log.e(this.getClass().getSimpleName(), ex.getMessage());
            ex.printStackTrace();
        }

    }

    public void startChat(long chatId) {
        AbstractYasmeActivity activity = (AbstractYasmeActivity) getActivity();
        Intent intent = new Intent(activity, ChatActivity.class);
        intent.putExtra(activity.USER_MAIL, activity.getUserMail());
        intent.putExtra(activity.USER_ID, activity.getUserId());
        intent.putExtra(activity.CHAT_ID, chatId);
        intent.putExtra(activity.USER_NAME, activity.getSelfUser().getName());
        startActivity(intent);
    }

    @Override
    public void notifyFragment(UserDetailsFragmentParam param) {
        Log.d(super.getClass().getSimpleName(), "I have been notified. Yeeha!");
        if (param instanceof NewChatParam) {
            notifyFragment((NewChatParam) param);
        }
        if (param instanceof UserDetailsParam) {
            notifyFragment((UserDetailsParam) param);
        }
    }

    public void notifyFragment(NewChatParam newChatParam) {
        Log.d(super.getClass().getSimpleName(), "I have been notified with newChatParam");
        startChat(newChatParam.getChatId());
    }

    public void notifyFragment(UserDetailsParam userDetailsParam) {
        Log.d(super.getClass().getSimpleName(), "I have been notified with userDetailsParam");

    }

    public interface OnDetailsFragmentInteractionListener {
        public void onDetailsFragmentInteraction(User user, Integer buttonId);
    }

    //superclass of notifyParameters
    public static class UserDetailsFragmentParam {

    }

    public static class NewChatParam extends UserDetailsFragmentParam {
        private Long chatId;

        public NewChatParam(Long chatId) {
            this.chatId = chatId;
        }

        public Long getChatId() {
            return chatId;
        }
    }

    public static class UserDetailsParam extends UserDetailsFragmentParam {
        private Boolean success;
        private Long userId;

        public UserDetailsParam(Boolean success, Long userId) {
            this.success = success;
            this.userId = userId;
        }

        public Long getUserId() {
            return userId;
        }


        public Boolean getSuccess() {
            return success;
        }
    }
}
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/exception/RestServiceException.java
public class RestServiceException extends Exception {

	private static final long serialVersionUID = 1L;
	private int code;

	public RestServiceException(ErrorCode error) {

		this(error.getDescription(), error.getNumber());

	}

	public RestServiceException(String message, int code) {
		super(message);
		this.code = code;

		System.err.println("Code: " + this.code + "  " + "Message: "
				+ this.getMessage());
	}

    public int getCode() {
        return code;
    }
}
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/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/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/connection/ChatTask.java
public class ChatTask extends ConnectionTask {

    private static ChatTask instance;

    private ChatTask() {

        try {
            this.uri = new URIBuilder(baseURI).setPath(ConnectionTask.APIVERSION + "/chat").build();
        } catch (URISyntaxException e) {
            Log.e(this.getClass().getSimpleName(),e.getMessage());
        }
    }

    public static ChatTask getInstance() {
        if (instance == null) {
            synchronized (ChatTask.class) {
                if (null == instance) {
                    instance = new ChatTask();
                }
            }
        }
        return instance;
    }

    public List<Chat> getAllChatsForUser() throws RestServiceException {

        List<Chat> chats = new ArrayList<Chat>();
        try {
            HttpResponse httpResponse = executeRequest(Request.GET, "");

            String json = new BufferedReader(new InputStreamReader(
                    httpResponse.getEntity().getContent())).readLine();
            JSONArray jsonArray = new JSONArray(json);
            Log.d(this.getClass().getSimpleName(), json);

            for (int i = 0; i < jsonArray.length(); i++) {
                Chat chat = new ObjectMapper().readValue((jsonArray.getJSONObject(i)).
                        toString(), Chat.class);
                chats.add(chat);
            }
        } catch (JSONException e) {
            Log.e(this.getClass().getSimpleName(),e.getMessage());
        } catch (IOException e) {
            throw new RestServiceException(Error.CONNECTION_ERROR);
        }

        return chats;
    }

    public List<Device> getAllDevicesForChat(Long chatId) throws RestServiceException {
        String path = chatId + "/devices";
        List<Device> devices = new ArrayList<Device>();
        try {
            HttpResponse httpResponse = executeRequest(Request.GET, path);
            JSONArray jsonArray = new JSONArray(new BufferedReader(new InputStreamReader(
                    httpResponse.getEntity().getContent())).readLine());

            for (int i = 0; i < jsonArray.length(); i++) {
                Device device = new ObjectMapper().readValue((jsonArray.getJSONObject(i)).
                        toString(), Device.class);
                devices.add(device);
            }
        } catch (JSONException e) {
            Log.e(this.getClass().getSimpleName(),e.getMessage());
        } catch (IOException e) {
            throw new RestServiceException(Error.CONNECTION_ERROR);
        }

        return devices;
    }

    public void deleteChat(long chatId) throws RestServiceException {

        // remember: only the owner can delete the chat
        executeRequest(Request.DELETE, Long.toString(chatId));
        Log.d(this.getClass().getSimpleName(), "Chat deleted");
    }

    public Long createChatWithPar(Chat chat) throws RestServiceException {

        try {
            HttpResponse httpResponse = executeRequest(Request.POST, "", chat);

            JSONObject json = new JSONObject((new BufferedReader(
                    new InputStreamReader(httpResponse.getEntity()
                            .getContent(), "UTF-8")
            )).readLine());
            return Long.parseLong(json.getString("message"));

        } catch (IOException e) {
            throw new RestServiceException(Error.CONNECTION_ERROR);
        } catch (JSONException e) {
            throw new RestServiceException(Error.ERROR);
        }
    }

    public Chat getInfoOfChat(long chatId) throws RestServiceException {

        // note: only a participant of the chat shall get the chat object
        try {
            String path = chatId + "/info";
            HttpResponse httpResponse = executeRequest(Request.GET, path);
            return new ObjectMapper().readValue(new BufferedReader(new InputStreamReader(httpResponse.getEntity()
                    .getContent())).readLine(), Chat.class);
        } catch (IOException e) {
            throw new RestServiceException(Error.CONNECTION_ERROR);
        }
    }

    public void addParticipantToChat(long participantId, long chatId) throws RestServiceException {
        String path = "par/" + participantId + "/" + chatId;
        HttpResponse httpResponse = executeRequest(Request.PUT, path);
        int statusCode = httpResponse.getStatusLine().getStatusCode();
        if(statusCode != 200) {
            Log.e(this.getClass().getSimpleName(), "User " + participantId + " could not be added to chat " + chatId + " !");
            return;
        }
        //ChatDAO cDao = DatabaseManager.INSTANCE.getChatDAO();
        //Chat chat = cDao.get(chatId);
        //for(User u : chat.getParticipants()) Log.d("XXXXXXXXXXXXXXXXXXX","User: " + u.getName());
        //Log.d("XXXXXXXXXXXXXX","Now adding " + participantId);
        //chat.addParticipant(DatabaseManager.INSTANCE.getUserDAO().get(participantId));
        //for(User u : chat.getParticipants()) Log.d("XXXXXXXXXXXXXXXXXXX","User: " + u.getName());
        Log.d(this.getClass().getSimpleName(), "User " + participantId + " added to Chat!");
    }

    public void changeOwnerOfChat(long chatId, long newOwnerId) throws RestServiceException {
        String path = chatId + "/owner/" + newOwnerId;
        executeRequest(Request.PUT, path);
        Log.d(this.getClass().getSimpleName(), "Owner changed");
    }

    public void removeParticipantFromChat(long participantId, long chatId) throws RestServiceException {
        String path = "par/" + participantId + "/" + chatId;
        HttpResponse httpResponse = executeRequest(Request.DELETE, path);
        int statusCode = httpResponse.getStatusLine().getStatusCode();
        if(statusCode != 200) {
            Log.e(this.getClass().getSimpleName(), "User " + participantId + " could not be removed from chat " + chatId + " !");
            return;
        }
        Log.d(this.getClass().getSimpleName(), "User removed from Chat!");
    }

    public void removeOneSelfFromChat(long chatId)
            throws RestServiceException {
        String path = chatId + "/par/self";
        executeRequest(Request.DELETE, path);
        Log.d(this.getClass().getSimpleName(), "I´m out of Chat No. " + chatId);
    }

    public void updateChat(Chat chat) throws RestServiceException {
        // Copy the chat object first
        Chat clone = chat.clone();
        if (null == clone) {
            Log.e(this.getClass().getSimpleName(), "Cannot clone chat");
            return;
        }

        String path = clone.getId() + "/properties";
        clone.setId(-1); // Tell the server not to overwrite the properties with server data
        executeRequest(Request.PUT, path, clone);
        Log.d(this.getClass().getSimpleName(), "Chat updated");
    }
}
yasme/src/main/java/de/fau/cs/mad/yasme/android/controller/SpinnerObservable.java
public class SpinnerObservable {
    private Set<AsyncTask> backgroundTasks;
    private Set<AbstractYasmeActivity> activities;
    private boolean spinning = false;

    private static SpinnerObservable instance;

    public static SpinnerObservable getInstance() {
        if (instance == null) {
            instance = new SpinnerObservable();
        }
        return instance;
    }

    private SpinnerObservable() {
        backgroundTasks = new HashSet<>();
        activities = new HashSet<>();
    }

    public void registerActivity(AbstractYasmeActivity activity) {
        Log.d(getClass().getSimpleName(), "Register activity");
        activities.add(activity);
        if (spinning) {
            Log.d(getClass().getSimpleName(), "Start spinning");
            activity.startSpinning();
        } else {
            Log.d(getClass().getSimpleName(), "No spinning");
        }
    }

    public void removeActivity(AbstractYasmeActivity activity) {
        Log.d(getClass().getSimpleName(), "Remove activity");
        activities.remove(activity);
    }

    public void registerBackgroundTask(AsyncTask backgroundTask) {
        Log.d(getClass().getSimpleName(), "Register backgroundTask: " + backgroundTask.getClass().getSimpleName());
        backgroundTasks.add(backgroundTask);
        spinning = true;
        startSpinning();
    }

    public void removeBackgroundTask(AsyncTask backgroundTask) {
        Log.d(getClass().getSimpleName(), "Remove backgroundTask: " + backgroundTask.getClass().getSimpleName());
        backgroundTasks.remove(backgroundTask);
        if (backgroundTasks.size() == 0) {
            spinning = false;
            stopSpinning();
        }
    }

    public void startSpinning() {
        Log.d(getClass().getSimpleName(), "Start spinning");
        for (AbstractYasmeActivity activity : activities) {
            activity.startSpinning();
        }
    }

    public void stopSpinning() {
        Log.d(getClass().getSimpleName(), "Stop spinning");
        for (AbstractYasmeActivity activity : activities) {
            activity.stopSpinning();
        }
    }
}
yasme/src/main/java/de/fau/cs/mad/yasme/android/ui/fragments/InviteToChatFragment.java
public class InviteToChatFragment extends Fragment implements View.OnClickListener, NotifiableFragment<InviteToChatFragment.InviteToChatParam> {
    protected AbstractYasmeActivity activity;
    protected List<User> users;
    protected ListView chatPartners;
    protected Button startChat;
    protected UserAdapter adapter;
    protected TextView emptyContactsNotice;

    public InviteToChatFragment() {
    }

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

        //Register at observer
        Log.d(this.getClass().getSimpleName(), "Try to get ChatListObservableInstance");
        FragmentObservable<InviteToChatFragment, InviteToChatParam> obs =
                ObservableRegistry.getObservable(this.getClass());
        Log.d(this.getClass().getSimpleName(), "... successful");
        obs.register(this);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // Make sure that this fragment is registered before invoking the GetContactsTask
        FragmentObservable<InviteToChatFragment, InviteToChatParam> obs =
                ObservableRegistry.getObservable(this.getClass());
        obs.register(this);
        View rootView = inflater.inflate(R.layout.fragment_invite_to_chat, container, false);

        startChat = (Button) rootView.findViewById(R.id.inviteToChat_startChat);

        emptyContactsNotice = (TextView) rootView.findViewById(R.id.empty_contacts_notice);
        emptyContactsNotice.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(getActivity(), ContactActivity.class);
                // Message is immaterial
                intent.putExtra(ContactActivity.SEARCH_FOR_CONTACTS, "let me search for contacts");
                startActivity(intent);
            }
        });

        chatPartners = (ListView) rootView.findViewById(R.id.inviteToChat_usersList);
        // Only show the notice when the list view is empty
        chatPartners.setEmptyView(emptyContactsNotice);
        chatPartners.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);

        activity = (AbstractYasmeActivity) getActivity();
        adapter = new UserAdapter(activity, R.layout.user_item_checkbox, new ArrayList<User>());
		adapter.setNotifyOnChange(true);
        chatPartners.setAdapter(adapter);

        new GetContactsTask(this.getClass()).execute();
        return rootView;
    }

    /**
     * Will be called by the GetAllUsersTask after the list of users has been retrieved
     * @param contacts list
     */
    public void updateChatPartnersList(List<User> contacts) {
        users = contacts;
        Collections.sort(users, new Comparator<Object>() {
            @Override
            public int compare(Object o1, Object o2) {
                //Log.d(getClass().getSimpleName(), "Compare");
                User u1 = (User) o1;
                User u2 = (User) o2;
                if (u1.getName() == null && u2.getName() == null) {
                    return 0;
                }
                if (u1.getName() == null) {
                    return -1;
                }
                if (u2.getName() == null) {
                    return 1;
                }
                return u1.getName().toLowerCase().compareTo(u2.getName().toLowerCase());
            }
        });
        adapter.clear();
        adapter.addAll(users);
        adapter.notifyDataSetChanged();
        startChat.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        SparseBooleanArray checked = adapter.getSelectedContacts();
        Set<User> selectedUsers = new HashSet<>();

        if (checked.size() == 0) {
            Toast.makeText(activity, getString(R.string.toast_no_selection), Toast.LENGTH_LONG).show();
            return;
        }

        for (int i = 0; i < checked.size(); i++) {
            if (!checked.valueAt(i)) {
                continue;
            }
            // Item position in adapter
            int position = checked.keyAt(i);
            selectedUsers.add(users.get(position));
            //selectedUserNames.add(users.get(position).getName());
        }
        new CreateChatTask(activity.getSelfUser(), selectedUsers, this.getClass()).execute();
    }


    public void startChat(long chatId) {
        AbstractYasmeActivity activity = (AbstractYasmeActivity) getActivity();
        Intent intent = new Intent(activity, ChatActivity.class);
        intent.putExtra(activity.USER_MAIL, activity.getUserMail());
        intent.putExtra(activity.USER_ID, activity.getUserId());
        intent.putExtra(activity.CHAT_ID, chatId);
        intent.putExtra(activity.USER_NAME, activity.getSelfUser().getName());
        startActivity(intent);
        activity.finish();
    }

    @Override
    public void notifyFragment(InviteToChatParam inviteToChatParam) {
        Log.d(super.getClass().getSimpleName(), "I have been notified. Yeeha!");
        getActivity().setProgressBarIndeterminateVisibility(false); //progress bar off

        if (inviteToChatParam instanceof ChatRegisteredParam) {
            ChatRegisteredParam param = (ChatRegisteredParam) inviteToChatParam;
            startChat(param.getChatId());
        } else if (inviteToChatParam instanceof AllUsersFetchedParam) {
            AllUsersFetchedParam contactsFetchedParam = (AllUsersFetchedParam) inviteToChatParam;
            updateChatPartnersList(contactsFetchedParam.getAllUsers());
        }
    }

    @Override
    public void onStart() {
        super.onStart();
        //Register at observer
        FragmentObservable<InviteToChatFragment, InviteToChatParam> obs =
                ObservableRegistry.getObservable(this.getClass());
        obs.register(this);
    }

    @Override
    public void onStop() {
        super.onStop();
        //De-Register at observer
        FragmentObservable<InviteToChatFragment, InviteToChatParam> obs =
                ObservableRegistry.getObservable(this.getClass());
        obs.remove(this);
    }


    public static abstract class InviteToChatParam {
        protected Boolean success;

        public Boolean getSuccess() {
            return success;
        }
    }

    public static class ChatRegisteredParam extends InviteToChatParam {
        private Long chatId;

        public ChatRegisteredParam(Boolean success, Long chatId) {
            this.success = success;
            this.chatId = chatId;
        }

        public Long getChatId() {
            return this.chatId;
        }
    }

    public static class AllUsersFetchedParam extends InviteToChatParam {
        private List<User> allUsers;

        public AllUsersFetchedParam(Boolean success, List<User> allUsers) {
            this.success = success;
            this.allUsers = allUsers;
        }

        public List<User> getAllUsers() {
            return allUsers;
        }
    }
}
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);
        }
    }
}


Question: package de.fau.cs.mad.yasme.android.asyncTasks.server;
import android.os.AsyncTask;
import de.fau.cs.mad.yasme.android.controller.Log;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import de.fau.cs.mad.yasme.android.R;
import de.fau.cs.mad.yasme.android.connection.ChatTask;
import de.fau.cs.mad.yasme.android.controller.ObservableRegistry;
import de.fau.cs.mad.yasme.android.controller.SpinnerObservable;
import de.fau.cs.mad.yasme.android.controller.Toaster;
import de.fau.cs.mad.yasme.android.entities.Chat;
import de.fau.cs.mad.yasme.android.entities.User;
import de.fau.cs.mad.yasme.android.exception.RestServiceException;
import de.fau.cs.mad.yasme.android.storage.DatabaseManager;
import de.fau.cs.mad.yasme.android.ui.fragments.InviteToChatFragment;
import de.fau.cs.mad.yasme.android.ui.fragments.UserDetailsFragment;




/**
 * Created by Benedikt Lorch <benedikt.lorch@studium.fau.de> on 22.06.14.
 */
public class CreateChatTask extends AsyncTask<String, Void, Boolean> {

    private DatabaseManager databaseManager = DatabaseManager.INSTANCE;
    private User selfUser;
    private Set<User> selectedUsers;
    private long newChatId = -1;
    private Chat newChat;
    private Class classToNotify;

    public CreateChatTask(User selfUser, Set<User> selectedUsers, Class classToNotify) {
        this.selfUser = selfUser;
        this.selectedUsers = selectedUsers;
        this.classToNotify = classToNotify;
    }

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

    /**
     * @return Returns true if it was successful, otherwise false
     * @param params 0 is fragmentToNotify
     */
    @Override
    protected Boolean doInBackground(String... params) {

        // Add self to selected users and names
        if (!selectedUsers.contains(selfUser)) {
            selectedUsers.add(selfUser);
        }

        // selectedUsers contains at least the selfUser now
        if (selectedUsers.size() == 1) {
            Log.w(this.getClass().getSimpleName(), "Don't allow to create a chat which does only contain the self user");
            return false;
        }

        // Make sure that there is no such chat between the given participants yet
        List<Chat> matchingChats = databaseManager.getChatDAO().getByParticipantsExact(selectedUsers);
        if (null != matchingChats && matchingChats.size() > 0) {
            // Take first chat and open it
            newChatId = matchingChats.get(0).getId();
        } else {
            // No chat found in database. Create a new one

            // Concatenate chat name according to the participant's names
            String name = "";
            Iterator<User> iterator = selectedUsers.iterator();
            while (iterator.hasNext()) {
                name += iterator.next().getName();
                if (iterator.hasNext()) {
                    name += ", ";
                }
            }

            newChat = new Chat(selfUser, "Created: " + new Date().toString(), name);
            // Convert selected user's set to list as expected by chat entity
            List<User> participants = new ArrayList<>();
            participants.addAll(selectedUsers);
            newChat.setParticipants(participants);
            try {
                newChatId = ChatTask.getInstance().createChatWithPar(newChat);
            } catch (RestServiceException e) {
                Log.e(this.getClass().getSimpleName(), e.getMessage());
                return false;
            }

            if (newChatId < 0) {
                Log.e(this.getClass().getSimpleName(), "Chat was created at server. However, the new chat id is still " + newChatId);
                return false;
            }
            newChat.setId(newChatId);
            // If a new chat was created, store it in the internal database
            if (null == databaseManager.getChatDAO().addIfNotExists(newChat)) {
                Log.e(this.getClass().getSimpleName(), "Could not store new chat in database");
                return false;
            }
        }
        return true;
    }


    /**
     * Invokes the fragment's method to show the chat activity
     */
    protected void onPostExecute(final Boolean success) {
        SpinnerObservable.getInstance().removeBackgroundTask(this);
        if (newChatId == -1) {
            // Something went wrong
            Log.e(this.getClass().getSimpleName(), "newChatId is still -1.");
            Toaster.getInstance().toast(R.string.unable_create_chat, Toast.LENGTH_LONG);
        }

        if (success) {
Answer:             if(classToNotify == UserDetailsFragment.class) {