Input:
import logging
import operator
import pytest
from bloop.conditions import ConditionRenderer
from bloop.exceptions import InvalidModel, InvalidStream
from bloop.models import (
    BaseModel,
    Column,
    GlobalSecondaryIndex,
    IMeta,
    Index,
    LocalSecondaryIndex,
    bind_column,
    bind_index,
    model_created,
    object_modified,
    unbind,
    unpack_from_dynamodb,
)
from bloop.types import (
    UUID,
    Boolean,
    DateTime,
    Integer,
    String,
    Timestamp,
    Type,
)
from ..helpers.models import User, VectorModel

operations = [
    (operator.ne, "!="),
    (operator.eq, "=="),
    (operator.lt, "<"),
    (operator.le, "<="),
    (operator.gt, ">"),
    (operator.ge, ">=")
]

@pytest.fixture
def unpack_kwargs(engine):
    return {
        "attrs": {"name": {"S": "numberoverzero"}},
        "expected": {User.name, User.joined},
        "model": User,
        "engine": engine,
        "context": {"engine": engine, "extra": "foo"},
    }

# BASE MODEL =============================================================================================== BASE MODEL

def test_default_model_init():
    """Missing attributes are set to `None`"""
    user = User(id="user_id", email="user@domain.com")
    assert user.id == "user_id"
    assert user.email == "user@domain.com"
    assert not hasattr(user, "name")

def test_load_default_init(engine):
    """The default model loader uses cls.__new__(cls) method"""
    init_called = False

Context:
bloop/types.py
class Boolean(Type):
    python_type = bool
    backing_type = BOOLEAN

    def dynamo_load(self, value, *, context, **kwargs):
        if value is None:
            return None
        return bool(value)

    def dynamo_dump(self, value, *, context, **kwargs):
        if value is None:
            return None
        return bool(value)
bloop/models.py
class IMeta:
class BaseModel:
    class Meta(IMeta):
class Index:
class GlobalSecondaryIndex(Index):
class LocalSecondaryIndex(Index):
class Column(ComparisonMixin):
    class UNBOUND:
        class Meta(IMeta):
    def __init__(self, **attrs):
    def __init_subclass__(cls: type, **kwargs):
    def __repr__(self):
    def __init__(self, *, projection, hash_key=None, range_key=None, dynamo_name=None, **kwargs):
    def __copy__(self):
    def __set_name__(self, owner, name):
    def __repr__(self):
    def name(self):
    def dynamo_name(self):
    def hash_key(self):
    def range_key(self):
    def keys(self):
    def __set__(self, obj, value):
    def __get__(self, obj, type=None):
    def __delete__(self, obj):
    def __init__(
            self, *, projection,
            hash_key, range_key=None,
            read_units=None, write_units=None,
            dynamo_name=None, **kwargs):
    def __init__(self, *, projection, range_key, dynamo_name=None, strict=True, **kwargs):
    def hash_key(self):
    def read_units(self):
    def read_units(self, value):
    def write_units(self):
    def write_units(self, value):
    def __init__(self, typedef, hash_key=False, range_key=False, dynamo_name=None, default=missing):
    def __copy__(self):
    def __set_name__(self, owner, name):
    def __set__(self, obj, value):
    def __get__(self, obj, type=None):
    def __delete__(self, obj):
    def __repr__(self):
    def name(self):
    def dynamo_name(self):
def subclassof(obj, classinfo):
def instanceof(obj, classinfo):
def loaded_columns(obj: BaseModel):
def unpack_from_dynamodb(*, attrs, expected, model=None, obj=None, engine=None, context=None, **kwargs):
def validate_projection(projection):
def validate_stream(meta):
def validate_encryption(meta):
def validate_backups(meta):
def validate_billing(meta):
def validate_ttl(meta):
def unbound_repr(obj):
def setdefault(obj, field, default):
def ensure_hash(cls) -> None:
def initialize_meta(cls: type):
def bind_column(model, name, column, force=False, recursive=False, copy=False) -> Column:
def bind_index(model, name, index, force=False, recursive=True, copy=False) -> Index:
def refresh_index(meta, index) -> None:
def unbind(meta, name=None, dynamo_name=None) -> None:
bloop/types.py
class Integer(Number):
    """Truncates values when loading or dumping.

    For example, ``3.14`` in DynamoDB is loaded as ``3``. If a value is ``7.5``
    locally, it's stored in DynamoDB as ``7``.
    """
    python_type = int

    def dynamo_load(self, value, *, context, **kwargs):
        if value is None:
            return None
        number = super().dynamo_load(value, context=context, **kwargs)
        return int(number)

    def dynamo_dump(self, value, *, context, **kwargs):
        if value is None:
            return None
        value = int(value)
        return super().dynamo_dump(value, context=context, **kwargs)
bloop/exceptions.py
class InvalidModel(BloopException, ValueError):
    """This is not a valid Model."""
bloop/types.py
class DateTime(String):
    """Always stored in DynamoDB using the :data:`~bloop.types.FIXED_ISO8601_FORMAT` format.

    Naive datetimes (``tzinfo is None``) are not supported, and trying to use one will raise ``ValueError``.

    .. code-block:: python

        from datetime import datetime, timedelta, timezone

        class Model(Base):
            id = Column(Integer, hash_key=True)
            date = Column(DateTime)
        engine.bind()

        obj = Model(id=1, date=datetime.now(timezone.utc))
        engine.save(obj)

        one_day_ago = datetime.now(timezone.utc) - timedelta(days=1)

        query = engine.query(
            Model,
            key=Model.id==1,
            filter=Model.date >= one_day_ago)

        query.first().date

    .. note::

        To use common datetime libraries such as `arrow`_, `delorean`_, or `pendulum`_,
        see :ref:`DateTime and Timestamp Extensions <user-extensions-datetime>` in the user guide.  These
        are drop-in replacements and support non-utc timezones:

        .. code-block:: python

            from bloop import DateTime  # becomes:
            from bloop.ext.pendulum import DateTime

    .. _arrow: http://crsmithdev.com/arrow
    .. _delorean: https://delorean.readthedocs.io/en/latest/
    .. _pendulum: https://pendulum.eustace.io
    """
    python_type = datetime.datetime

    def dynamo_load(self, value, *, context, **kwargs):
        if value is None:
            return None
        dt = datetime.datetime.strptime(value, FIXED_ISO8601_FORMAT)
        # we assume all stored values are utc, so we simply force timezone to utc
        # without changing the day/time values
        return dt.replace(tzinfo=datetime.timezone.utc)

    def dynamo_dump(self, value, *, context, **kwargs):
        if value is None:
            return None
        if value.tzinfo is None:
            raise ValueError(
                "naive datetime instances are not supported.  You can set a timezone with either "
                "your_dt.replace(tzinfo=) or your_dt.astimezone(tz=).  WARNING: calling astimezone on a naive "
                "datetime will assume the naive datetime is in the system's timezone, even though "
                "datetime.utcnow() creates a naive object!  You almost certainly don't want to do that."
            )
        dt = value.astimezone(tz=datetime.timezone.utc)
        return dt.strftime(FIXED_ISO8601_FORMAT)
bloop/exceptions.py
class InvalidStream(BloopException, ValueError):
    """This is not a valid stream definition."""
bloop/types.py
class UUID(String):
    python_type = uuid.UUID

    def dynamo_load(self, value, *, context, **kwargs):
        if value is None:
            return None
        return uuid.UUID(value)

    def dynamo_dump(self, value, *, context, **kwargs):
        if value is None:
            return None
        return str(value)
tests/helpers/models.py
class VectorModel(BaseModel):
    name = Column(String, hash_key=True)
    list_str = Column(List(String))
    set_str = Column(Set(String))
    map_nested = Column(Map(**{
        "bytes": Binary,
        "str": String,
        "map": Map(**{
            "int": Integer,
            "str": String
        })
    }))
    some_int = Column(Integer)
    some_bytes = Column(Binary)
bloop/types.py
class Type:
    """Abstract base type."""

    python_type = None
    backing_type = None

    def supports_operation(self, operation: str) -> bool:
        """
        Used to ensure a conditional operation is supported by this type.

        By default, uses a hardcoded table of operations that maps to each backing DynamoDB type.

        You can override this method to implement your own conditional operators, or to dynamically
        adjust which operations your type supports.
        """
        return operation in OPERATION_SUPPORT_BY_TYPE[self.backing_type]

    def __init__(self):
        if not hasattr(self, "inner_typedef"):
            self.inner_typedef = self
        super().__init__()

    def __getitem__(self, key):
        raise RuntimeError(f"{self!r} does not support document paths")

    def dynamo_dump(self, value, *, context, **kwargs):
        """Converts a local value into a DynamoDB value.

        For example, to store a string enum as an integer:

        .. code-block:: python

            def dynamo_dump(self, value, *, context, **kwargs):
                colors = ["red", "blue", "green"]
                return colors.index(value.lower())
        """
        raise NotImplementedError

    def dynamo_load(self, value, *, context, **kwargs):
        """Converts a DynamoDB value into a local value.

        For example, to load a string enum from an integer:

        .. code-block:: python

            def dynamo_dump(self, value, *, context, **kwargs):
                colors = ["red", "blue", "green"]
                return colors[value]
        """
        raise NotImplementedError

    def _dump(self, value, **kwargs):
        """Entry point for serializing values.  Most custom types should use :func:`~bloop.types.Type.dynamo_dump`.

        This wraps the return value of :func:`~bloop.types.Type.dynamo_dump` in DynamoDB's wire format.
        For example, serializing a string enum to an int:

        .. code-block:: python

            value = "green"
            # dynamo_dump("green") = 2
            _dump(value) == {"N": 2}

        If a complex type calls this function with ``None``, it will forward ``None`` to
        :func:`~bloop.types.Type.dynamo_dump`.  This can happen when dumping eg. a sparse
        :class:`~.bloop.types.Map`, or a missing (not set) value.
        """
        wrapped = actions.wrap(value)
        value = self.dynamo_dump(wrapped.value, **kwargs)
        if value is None:
            return actions.wrap(None)
        else:
            value = {self.backing_type: value}
            return wrapped.type.new_action(value)

    def _load(self, value, **kwargs):
        """Entry point for deserializing values.  Most custom types should use :func:`~bloop.types.Type.dynamo_load`.

        This unpacks DynamoDB's wire format and calls :func:`~bloop.types.Type.dynamo_load` on the inner value.
        For example, deserializing an int to a string enum:

        .. code-block:: python

            value = {"N": 2}
            # dynamo_load(2) = "green"
            _load(value) == "green"

        If a complex type calls this function with ``None``, it will forward ``None`` to
        :func:`~bloop.types.Type.dynamo_load`.  This can happen when loading eg. a sparse :class:`~bloop.types.Map`.
        """
        if value is not None:
            value = next(iter(value.values()))
        return self.dynamo_load(value, **kwargs)

    def __repr__(self):
        # Render class python types by name
        python_type = self.python_type
        if isinstance(python_type, type):
            python_type = python_type.__name__

        return "<{}[{}:{}]>".format(
            self.__class__.__name__,
            self.backing_type, python_type
        )
tests/helpers/models.py
class User(BaseModel):
    id = Column(String, hash_key=True)
    age = Column(Integer)
    name = Column(String)
    email = Column(String)
    joined = Column(DateTime, dynamo_name="j")
    by_email = GlobalSecondaryIndex(hash_key="email", projection="all")
bloop/types.py
class Timestamp(Integer):
    """Stores the unix (epoch) time in seconds.  Milliseconds are truncated to 0 on load and save.

    Naive datetimes (``tzinfo is None``) are not supported, and trying to use one will raise ``ValueError``.

    .. code-block:: python

        from datetime import datetime, timedelta, timezone

        class Model(Base):
            id = Column(Integer, hash_key=True)
            date = Column(Timestamp)
        engine.bind()

        obj = Model(id=1, date=datetime.now(timezone.utc))
        engine.save(obj)

        one_day_ago = datetime.now(timezone.utc) - timedelta(days=1)

        query = engine.query(
            Model,
            key=Model.id==1,
            filter=Model.date >= one_day_ago)

        query.first().date

    .. note::

        To use common datetime libraries such as `arrow`_, `delorean`_, or `pendulum`_,
        see :ref:`DateTime and Timestamp Extensions <user-extensions-datetime>` in the user guide.  These
        are drop-in replacements and support non-utc timezones:

        .. code-block:: python

            from bloop import Timestamp  # becomes:
            from bloop.ext.pendulum import Timestamp

    .. _arrow: http://crsmithdev.com/arrow
    .. _delorean: https://delorean.readthedocs.io/en/latest/
    .. _pendulum: https://pendulum.eustace.io
    """
    python_type = datetime.datetime

    def dynamo_load(self, value, *, context, **kwargs):
        if value is None:
            return None
        value = super().dynamo_load(value, context=context, **kwargs)
        # we assume all stored values are utc, so we simply force timezone to utc
        # without changing the day/time values
        return datetime.datetime.fromtimestamp(value, tz=datetime.timezone.utc)

    def dynamo_dump(self, value, *, context, **kwargs):
        if value is None:
            return None
        if value.tzinfo is None:
            raise ValueError(
                "naive datetime instances are not supported.  You can set a timezone with either "
                "your_dt.replace(tzinfo=) or your_dt.astimezone(tz=).  WARNING: calling astimezone on a naive "
                "datetime will assume the naive datetime is in the system's timezone, even though "
                "datetime.utcnow() creates a naive object!  You almost certainly don't want to do that."
            )
        value = value.timestamp()
        return super().dynamo_dump(value, context=context, **kwargs)
bloop/conditions.py
class ConditionRenderer:
    # noinspection PyUnresolvedReferences
    """Renders collections of :class:`~bloop.conditions.BaseCondition` into DynamoDB's wire format for expressions,
    including:

    * ``"ConditionExpression"`` -- used in conditional operations
    * ``"FilterExpression"`` -- used in queries and scans to ignore results that don't match the filter
    * ``"KeyConditionExpressions"`` -- used to describe a query's hash (and range) key(s)
    * ``"ProjectionExpression"`` -- used to include a subset of possible columns in the results of a query or scan
    * ``"UpdateExpression"`` -- used to save objects

    Normally, you will only need to call :func:`~bloop.conditions.ConditionRenderer.render` to handle any combination
    of conditions.  You can also call each individual ``render_*`` function to control how multiple conditions of
    each type are applied.

    You can collect the rendered condition at any time through :attr:`~bloop.conditions.ConditionRenderer.rendered`.

    .. code-block:: python

        >>> renderer.render(obj=user, atomic=True)
        >>> renderer.output
        {'ConditionExpression': '((#n0 = :v1) AND (attribute_not_exists(#n2)) AND (#n4 = :v5))',
         'ExpressionAttributeNames': {'#n0': 'age', '#n2': 'email', '#n4': 'id'},
         'ExpressionAttributeValues': {':v1': {'N': '3'}, ':v5': {'S': 'some-user-id'}}}

    :param engine: Used to dump values in conditions into the appropriate wire format.
    :type engine: :class:`~bloop.engine.Engine`
    """
    def __init__(self, engine):
        self.refs = ReferenceTracker(engine)
        self.engine = engine
        self.expressions = {}

    def render(self, obj=None, condition=None, update=False, filter=None, projection=None, key=None):
        """Main entry point for rendering multiple expressions.  All parameters are optional, except obj when
        atomic or update are True.

        :param obj: *(Optional)* An object to render an atomic condition or update expression for.  Required if
            update or atomic are true.  Default is False.
        :param condition: *(Optional)* Rendered as a "ConditionExpression" for a conditional operation.
            If atomic is True, the two are rendered in an AND condition.  Default is None.
        :type condition: :class:`~bloop.conditions.BaseCondition`
        :param bool update: *(Optional)*  True if an "UpdateExpression" should be rendered for ``obj``.
            Default is False.
        :param filter: *(Optional)* A filter condition for a query or scan, rendered as a "FilterExpression".
            Default is None.
        :type filter: :class:`~bloop.conditions.BaseCondition`
        :param projection: *(Optional)* A set of Columns to include in a query or scan, rendered as a
            "ProjectionExpression".  Default is None.
        :type projection: set :class:`~bloop.models.Column`
        :param key: *(Optional)* A key condition for queries, rendered as a "KeyConditionExpression".  Default is None.
        :type key: :class:`~bloop.conditions.BaseCondition`
        """
        if update and not obj:
            raise InvalidCondition("An object is required to render updates.")

        if filter:
            self.filter_expression(filter)

        if projection:
            self.projection_expression(projection)

        if key:
            self.key_expression(key)

        # Condition requires a bit of work, because either one can be empty/false
        if condition:
            self.condition_expression(condition)

        if update:
            self.update_expression(obj)

    def condition_expression(self, condition):
        self.expressions["ConditionExpression"] = condition.render(self)

    def filter_expression(self, condition):
        self.expressions["FilterExpression"] = condition.render(self)

    def key_expression(self, condition):
        self.expressions["KeyConditionExpression"] = condition.render(self)

    def projection_expression(self, columns):
        included = set()
        ref_names = []
        for column in columns:
            if column in included:
                continue
            included.add(column)
            ref = self.refs.any_ref(column=column)
            ref_names.append(ref.name)
        self.expressions["ProjectionExpression"] = ", ".join(ref_names)

    def update_expression(self, obj):
        updates = {
            ActionType.Add: [],
            ActionType.Delete: [],
            ActionType.Remove: [],
            ActionType.Set: [],
        }
        for column in sorted(
                # Don't include key columns in an UpdateExpression
                filter(lambda c: c not in obj.Meta.keys, global_tracking[obj]),
                key=lambda c: c.dynamo_name):
            name_ref = self.refs.any_ref(column=column)
            value_ref = self.refs.any_ref(column=column, value=getattr(obj, column.name, None))
            update_type = value_ref.action.type
            # Can't set to an empty value, force to a Remove
            if is_empty(value_ref) or update_type is ActionType.Remove:
                self.refs.pop_refs(value_ref)
                update_type = ActionType.Remove
                value_ref = None
            updates[update_type].append((name_ref, value_ref))

        expressions = []
        for update_type, refs in updates.items():
            if not refs:
                continue
            k = update_type.wire_key.upper()
            r = update_type.render
            expressions.append(f"{k} " + ", ".join(r(*ref) for ref in refs))
        if expressions:
            self.expressions["UpdateExpression"] = " ".join(e.strip() for e in expressions)

    @property
    def output(self):
        """The wire format for all conditions that have been rendered.
        A new :class:`~bloop.conditions.ConditionRenderer` should be used for each operation."""
        expressions = {k: v for (k, v) in self.expressions.items() if v is not None}
        if self.refs.attr_names:
            expressions["ExpressionAttributeNames"] = self.refs.attr_names
        if self.refs.attr_values:
            expressions["ExpressionAttributeValues"] = self.refs.attr_values
        return expressions
bloop/types.py
class String(Type):
    python_type = str
    backing_type = STRING

    def dynamo_load(self, value, *, context, **kwargs):
        if not value:
            return ""
        return value

    def dynamo_dump(self, value, *, context, **kwargs):
        if not value:
            return None
        return value

Answer: class Blob(BaseModel):


Input:
package de.fau.cs.mad.yasme.android.ui.fragments;
import android.app.Fragment;
import android.content.Context;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
import de.fau.cs.mad.yasme.android.R;
import de.fau.cs.mad.yasme.android.asyncTasks.server.GetImageWithoutSavingTask;
import de.fau.cs.mad.yasme.android.asyncTasks.server.SearchUserTask;
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.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.UserAdapter;

/**
 * Created by Stefan Ettl <stefan.ettl@fau.de>
 */

public class SearchContactFragment
        extends Fragment
        implements View.OnClickListener,
        AdapterView.OnItemClickListener,
        NotifiableFragment<SearchContactFragment.DataClass> {

    private List<User> users;
    private Spinner searchSpinner;
    private Button searchButton;
    private ListView searchResultView;
    private TextView searchText;

    //private AtomicInteger bgTasksRunning = new AtomicInteger(0);
    private UserAdapter mAdapter;
    private OnSearchFragmentInteractionListener mListener;

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

    @Override
    public void onStart() {
        super.onStart();

Context:
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/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/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/asyncTasks/server/SearchUserTask.java
public class SearchUserTask extends AsyncTask<String, Void, List<User>> {

    private SearchBy searchBy;
    private String searchText;
    private Class classToNotify;

    public SearchUserTask(SearchBy searchBy, String searchText, Class classToNotify) {
        this.searchBy = searchBy;
        this.searchText = searchText;
        this.classToNotify = classToNotify;
    }

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

    @Override
    protected List<User> doInBackground(String... params) {
        SearchTask searchTask = SearchTask.getInstance();
        List<User> uList = new ArrayList<User>();

        try {
            switch (searchBy) {
                case LIKE:
                    uList = searchTask.userByLike(String.valueOf(searchText));
                    return uList;
                case MAIL:
                    uList.add(searchTask.userByMail(String.valueOf(searchText)));
                    return uList;
                case NUMBER:
                    uList.add(searchTask.userByNumber(String.valueOf(searchText)));
                    return uList;
                case ID:
                    uList.add(searchTask.userById(String.valueOf(searchText)));
                    return uList;
                default:
                    return uList;
            }
        } catch (RestServiceException rse) {
            Log.e(this.getClass().getSimpleName(), rse.getMessage());
        }

        return null;
    }

    protected void onPostExecute(List<User> userList) {
        SpinnerObservable.getInstance().removeBackgroundTask(this);
        if (classToNotify == SearchContactFragment.class) {
            ObservableRegistry.getObservable(SearchContactFragment.class)
                    .notifyFragments(new SearchContactFragment.UsersClass(userList));
        }
        if (classToNotify == QRCodeFragment.class) {
            ObservableRegistry.getObservable(QRCodeFragment.class).notifyFragments(userList);
        }
    }

    public enum SearchBy {
        LIKE,
        MAIL,
        NUMBER,
        UNKNOWN,
        ID;

        public static SearchBy getSearchBy(int searchBy) {
            switch (searchBy) {
                case 0:
                    return LIKE;
                case 1:
                    return MAIL;
                case 2:
                    return ID;
            }
            return UNKNOWN;
        }
    }
}
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/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/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/ui/UserAdapter.java
public class UserAdapter extends ArrayAdapter<User> {
    private List<User> users;
    private final Context context;
    private SparseBooleanArray selectedContacts = new SparseBooleanArray();
    private int layout = R.layout.user_item;

    public SparseBooleanArray getSelectedContacts() {
        return selectedContacts;
    }

    /**
     * @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 UserAdapter(Context context, int resource, List<User> users) {
        super(context, resource, users);
        this.users = users;
        this.context = context;
        this.layout = resource;
    }

    @Override
    public void notifyDataSetChanged() {
        selectedContacts = new SparseBooleanArray();
        super.notifyDataSetChanged();
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        User user = DatabaseManager.INSTANCE.getUserDAO().get(users.get(position).getId());
        if (user == null) {
            Log.w(this.getClass().getSimpleName(), "User not found in DB");
            user = users.get(position);
        }

        LayoutInflater inflater = ((Activity) context).getLayoutInflater();

        View rowView = inflater.inflate(layout, parent, false);

        ImageView profileImage = (ImageView) rowView.findViewById(R.id.user_picture);
        TextView initial = (TextView) rowView.findViewById(R.id.user_picture_text);
        TextView profileName = (TextView) rowView.findViewById(R.id.user_name);
        TextView profileId = (TextView) rowView.findViewById(R.id.user_id);
        CheckBox checkBox;
        if (layout == R.layout.user_item_checkbox) {
            checkBox = (CheckBox) rowView.findViewById(R.id.checkBox);
        } else {
            checkBox = null;
        }

        boolean isSelf = (user.getId() == DatabaseManager.INSTANCE.getUserId());
        if (isSelf) {
            SharedPreferences storage = context
                    .getSharedPreferences(AbstractYasmeActivity.STORAGE_PREFS, Context.MODE_PRIVATE);
            user.setProfilePicture(storage.getString(AbstractYasmeActivity.PROFILE_PICTURE, null));
        }

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

        profileName.setText(user.getName());
        profileId.setText("YD " + user.getId());
        if (checkBox != null) {
            checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
                    selectedContacts.append(position, b);
                }
            });
        }

        rowView.requestFocus();
        return rowView;
    }
}
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/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/asyncTasks/server/GetImageWithoutSavingTask.java
public class GetImageWithoutSavingTask extends AsyncTask<Long, Void, Boolean> {

    private BitmapDrawable profilePicture;
    private Class classToNotify;
    private boolean isSelf;
    private User user;

    public GetImageWithoutSavingTask(Class classToNotify, User user) {
        this.classToNotify = classToNotify;
        this.user = user;
    }

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

    /**
     * @return true on success, false on error
     */
    @Override
    protected Boolean doInBackground(Long... params) {
        long userId = user.getId();

        try {
            profilePicture = UserTask.getInstance().getProfilePicture(userId);
        } catch (RestServiceException e) {
            Log.e(this.getClass().getSimpleName(), e.getMessage());
            return false;
        }
        if (profilePicture == null) {
            Log.d(this.getClass().getSimpleName(), "profilePicture was null");
            user.setProfilePicture(null);
            return true;
        }

        isSelf = (userId == DatabaseManager.INSTANCE.getUserId());

        String path;
        try {
            path = PictureManager.INSTANCE.storePicture(user, profilePicture.getBitmap());
        } catch (IOException e) {
            Log.e(this.getClass().getSimpleName(), e.getMessage());
            return false;
        }
        if (path != null && !path.isEmpty()) {
            user.setProfilePicture(path);
        } else {
            return false;
        }

        return true;
    }

    @Override
    protected void onPostExecute(Boolean success) {
        SpinnerObservable.getInstance().removeBackgroundTask(this);
        if (success) {
            if (classToNotify == null) {
                //No one to notify
                return;
            }
            // Notify registered fragments
            FragmentObservable<SearchContactFragment, SearchContactFragment.DataClass> obs =
                    ObservableRegistry.getObservable(classToNotify);
            obs.notifyFragments(new SearchContactFragment.ImageClass(profilePicture, user));
        }
    }
}

Answer: FragmentObservable<SearchContactFragment, DataClass> obs