Input:
package com.balch.mocktrade;
import android.Manifest;
import android.app.Activity;
import android.app.Application;
import androidx.lifecycle.ViewModelProviders;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import androidx.annotation.ColorRes;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.snackbar.Snackbar;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.core.graphics.drawable.DrawableCompat;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;
import com.balch.android.app.framework.PresenterActivity;
import com.balch.android.app.framework.core.EditActivity;
import com.balch.android.app.framework.types.Money;
import com.balch.mocktrade.account.Account;
import com.balch.mocktrade.account.AccountEditController;
import com.balch.mocktrade.order.Order;
import com.balch.mocktrade.order.OrderEditController;
import com.balch.mocktrade.order.OrderListActivity;
import com.balch.mocktrade.services.PerformanceItemUpdateBroadcaster;
import com.balch.mocktrade.services.QuoteService;
import com.balch.mocktrade.settings.SettingsActivity;

public class MainActivity extends PresenterActivity<MainPortfolioView, MainPresenter> {
    private static final String TAG = MainActivity.class.getSimpleName();

    private static final int NEW_ACCOUNT_RESULT = 0;
    private static final int NEW_ORDER_RESULT = 1;

    private static final int PERMS_REQUEST_BACKUP = 0;
    private static final int PERMS_REQUEST_RESTORE = 1;

    private MenuItem menuProgressBar;
    private MenuItem menuRefreshButton;
    private MenuItem menuHideExcludeAccounts;
    private MenuItem menuDemoMode;

    private Handler uiHandler = new Handler(Looper.getMainLooper());

    private AccountUpdateReceiver accountUpdateReceiver;

    @Override
    public void onCreateBase(Bundle bundle) {

        ViewProvider viewProvider = (ViewProvider) getApplication();

        if (viewProvider.isTablet(this)) {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        }

        Toolbar toolbar = findViewById(R.id.portfolio_view_toolbar);
        if (toolbar != null) {
            toolbar.setTitle("");
            setSupportActionBar(toolbar);

            // tint the overflow icon
            ColorStateList colorSelector = ContextCompat.getColorStateList(this, R.color.nav_on_color);
            Drawable icon = toolbar.getOverflowIcon();
            if (icon != null) {
                DrawableCompat.setTintList(icon, colorSelector);
                toolbar.setOverflowIcon(icon);
            }
        }

        final AppBarLayout appBarLayout = findViewById(R.id.portfolio_view_app_bar);
        if (appBarLayout != null) {
            if (bundle == null) {
                uiHandler.postDelayed(() -> appBarLayout.setExpanded(false), 500);
            } else {
                appBarLayout.setExpanded(false, false);
            }
        }
    }

    @Override
    public MainPortfolioView createView() {
        return new MainPortfolioView(this,
                (accountId, daysToReturn) ->
                        getPortfolioViewModel().setGraphSelectionCriteria(accountId, daysToReturn));
    }

    @Override
    protected MainPresenter createPresenter(MainPortfolioView view) {
        Application application = getApplication();
        return new MainPresenter((TradeModelProvider) application,
                (ViewProvider) application,
                getPortfolioViewModel(), this, view,
                new MainPresenter.ActivityBridge() {
                    @Override
                    public void showProgress(boolean show) {
                        MainActivity.this.showProgress(show);
                    }

                    @Override
                    public void startNewAccountActivity() {
                        MainActivity.this.showNewAccountActivity();
                    }

                    @Override
                    public void showSnackBar(View parent, String displayMsg, @ColorRes int colorId) {
                        getSnackbar(parent, displayMsg, Snackbar.LENGTH_LONG,
                                R.color.snackbar_background,
                                colorId,
                                com.google.android.material.R.id.snackbar_text)
                                .show();
                    }

                    @Override
                    public void requestStoragePermission(int requestCode) {
                        ActivityCompat.requestPermissions(MainActivity.this,
                                new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE},
                                requestCode);
                    }

                    @Override
                    public void startOrderListActivity(Account account) {
                        startActivity(OrderListActivity.newIntent(MainActivity.this, account.getId()));
                    }

                    @Override

Context:
MockTradeApp/src/main/java/com/balch/mocktrade/order/OrderEditController.java
public class OrderEditController implements ExternalController<Order>, Parcelable {
    public OrderEditController() {
    }

    @Override
    public void onChanged(Context context, ColumnDescriptor descriptor, Object value, ControlMap controlMap) throws ValidatorException {
        if (descriptor.getField().getName().equals(Order.FLD_STRATEGY)) {
            onChangeStrategy((Order.OrderStrategy) value, controlMap);
        } else if (descriptor.getField().getName().equals(Order.FLD_ACTION)) {
            onChangeAction(context, (Order.OrderAction) value, controlMap);
        }
    }

    @Override
    public void validate(Context context, Order order, ControlMap controlMap) throws ValidatorException {
        StockSymbolLayout symbolControl = controlMap.get(Order.FLD_SYMBOL);
        Money price = symbolControl.getPrice();
        if (order.getStrategy() == Order.OrderStrategy.MANUAL) {
            price = order.getLimitPrice();
        }
        Money cost = Money.multiply(price, order.getQuantity());

        boolean hasAvailableFunds = ((order.getAction() == Order.OrderAction.SELL) ||
                (cost.getDollars() <= order.getAccount().getAvailableFunds().getDollars()));

        QuantityPriceLayout quantityControl = controlMap.get(Order.FLD_QUANTITY);
        quantityControl.setCost(cost, hasAvailableFunds);

        if (!hasAvailableFunds) {
            throw new ValidatorException(quantityControl.getContext().getString(R.string.quantity_edit_error_insufficient_funds));
        } else if ((cost.getDollars() == 0.0) && (order.getStrategy() != Order.OrderStrategy.MANUAL)) {
            throw new ValidatorException(quantityControl.getContext().getString(R.string.quantity_edit_error_invalid_amount));
        }
    }

    @Override
    public void initialize(Context context, Order order, ControlMap controlMap) {

        boolean controlEnabled = (order.getAction() == Order.OrderAction.BUY);

        TradeModelProvider modelProvider = ((TradeModelProvider)context.getApplicationContext());
        FinanceModel financeModel = modelProvider.getFinanceModel();

        QuantityPriceLayout quantityControl = controlMap.get(Order.FLD_QUANTITY);
        quantityControl.setOrderInfo(order);
        quantityControl.setMarketIsOpen(financeModel.isMarketOpen());
        quantityControl.setAccountInfo(order.getAccount());
        quantityControl.setEnabled(controlEnabled);

        EditLayout control = controlMap.get(Order.FLD_SYMBOL);
        control.setEnabled(controlEnabled);
    }

    private void showControl(EditLayout control, boolean visible) {
        if (control != null) {
            if (control instanceof ViewGroup) {
                ((ViewGroup) control).setVisibility(visible ? View.VISIBLE : View.GONE);
            }
        }
    }

    private void onChangeStrategy(Order.OrderStrategy strategy, ControlMap controlMap) {
        boolean showLimitPrice = false;
        boolean showStopPrice = false;
        boolean showStopPercent = false;

        switch (strategy) {
            case LIMIT:
            case MANUAL:
                showLimitPrice = true;
                break;
            case STOP_LOSS:
                showStopPrice = true;
                break;
            case TRAILING_STOP_AMOUNT_CHANGE:
                showStopPrice = true;
                break;
            case TRAILING_STOP_PERCENT_CHANGE:
                showStopPercent = true;
                break;
        }

        showControl(controlMap.get(Order.FLD_LIMIT_PRICE), showLimitPrice);
        showControl(controlMap.get(Order.FLD_STOP_PERCENT), showStopPercent);
        showControl(controlMap.get(Order.FLD_STOP_PRICE), showStopPrice);
    }

    private void onChangeAction(Context context, Order.OrderAction action, ControlMap controlMap) {
        EditLayout control = controlMap.get(Order.FLD_STRATEGY);
        if (control instanceof EnumEditLayout) {
            int selectionIndex = 0;
            Order.OrderStrategy strategy = (Order.OrderStrategy) control.getValue();
            List<Object> enumValues = new ArrayList<>();
            List<String> displayValues = new ArrayList<>();

            String [] masterDisplayValues = context.getResources().getStringArray(strategy.getListResId());

            for (int x = 0; x < Order.OrderStrategy.values().length; x++) {
                Order.OrderStrategy s = Order.OrderStrategy.values()[x];
                if ( ((action == Order.OrderAction.BUY) && s.isBuySupported()) ||
                     ((action == Order.OrderAction.SELL) && s.isSellSupported())) {
                    if (s == strategy) {
                        selectionIndex = x;
                    }
                    enumValues.add(s);
                    displayValues.add(masterDisplayValues[x]);
                }
            }

            ((EnumEditLayout)control).setOptions(enumValues, displayValues, selectionIndex);
        }
    }

    private OrderEditController(Parcel in) {
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
    }

    @Override
    public int describeContents() {
        return 0;
    }

    public static final Creator<OrderEditController> CREATOR = new Creator<OrderEditController>() {
        @Override
        public OrderEditController createFromParcel(Parcel in) {
            return new OrderEditController(in);
        }

        @Override
        public OrderEditController[] newArray(int size) {
            return new OrderEditController[size];
        }
    };

}
MockTradeApp/src/main/java/com/balch/mocktrade/services/PerformanceItemUpdateBroadcaster.java
public class PerformanceItemUpdateBroadcaster {
    private static final String TAG = PerformanceItemUpdateBroadcaster.class.getSimpleName();

    private static final String EXTRA_ACCOUNT_ID = "extra_account_id";
    private static final String EXTRA_DAYS_COUNT = "extra_days_count";

    public static final String ACTION = PerformanceItemUpdateBroadcaster.class.getName();

    public static class PerformanceItemUpdateData {
        public final long accountId;
        public final int  days;

        PerformanceItemUpdateData(long accountId, int days) {
            this.accountId = accountId;
            this.days = days;
        }
    }

    static void broadcast(Context context, long accountId, int days) {
        Log.d(TAG, "broadcast sent:" + ACTION);
        Intent intent = new Intent(ACTION);
        intent.putExtra(EXTRA_ACCOUNT_ID, accountId);
        intent.putExtra(EXTRA_DAYS_COUNT, days);
        LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
    }

    public static PerformanceItemUpdateData getData(Intent intent) {
        return new PerformanceItemUpdateData(intent.getLongExtra(EXTRA_ACCOUNT_ID, -1), intent.getIntExtra(EXTRA_DAYS_COUNT, -1));
    }

}
MockTradeApp/src/main/java/com/balch/mocktrade/account/Account.java
public class Account extends DomainObject implements Parcelable {

    static final String FLD_STRATEGY = "strategy";
    static final String FLD_NAME = "name";

    @ColumnEdit(order = 1, labelResId = R.string.account_name_label, hints = {"MAX_CHARS=32","NOT_EMPTY=true"})
    @ColumnNew(order = 1, labelResId = R.string.account_name_label, hints = {"MAX_CHARS=32","NOT_EMPTY=true"})
    private String name;

    @ColumnEdit(order = 2, labelResId = R.string.account_description_label, hints = {"MAX_CHARS=256","DISPLAY_LINES=2"})
    @ColumnNew(order = 2, labelResId = R.string.account_description_label, hints = {"MAX_CHARS=256","DISPLAY_LINES=2"})
    private String description;

    @ColumnEdit(order = 3, labelResId = R.string.account_init_balance_label, state = EditState.READONLY, hints = {"NON_NEGATIVE=true","HIDE_CENTS=true"})
    @ColumnNew(order = 3, labelResId = R.string.account_init_balance_label, hints = {"NON_NEGATIVE=true","HIDE_CENTS=true"})
    private Money initialBalance;

    @ColumnEdit(order = 4, labelResId = R.string.account_strategy_label, state = EditState.READONLY)
    @ColumnNew(order = 4,labelResId = R.string.account_strategy_label)
    private Strategy strategy;

    private Money availableFunds;

    @ColumnEdit(order = 5, labelResId = R.string.account_exclude_from_totals_label, state = EditState.READONLY)
    @ColumnNew(order = 5,labelResId = R.string.account_exclude_from_totals_label)
    private Boolean excludeFromTotals;

    public Account() {
        this("", "", new Money(0), Strategy.NONE, false);
    }

    public Account(String name, String description, Money initialBalance, Strategy strategy, boolean excludeFromTotals) {
        this(name, description, initialBalance, initialBalance.clone(), strategy, excludeFromTotals);
    }

    public Account(String name, String description, Money initialBalance, Money availableFunds,
                   Strategy strategy, boolean excludeFromTotals) {
        this.name = name;
        this.description = description;
        this.initialBalance = initialBalance;
        this.availableFunds = availableFunds;
        this.strategy = strategy;
        this.excludeFromTotals = excludeFromTotals;
    }

    protected Account(Parcel in) {
        super(in);
        name = in.readString();
        description = in.readString();
        initialBalance = in.readParcelable(Money.class.getClassLoader());
        strategy = Strategy.valueOf(in.readString());
        availableFunds = in.readParcelable(Money.class.getClassLoader());
        excludeFromTotals = (in.readByte() == 1) ? Boolean.TRUE : Boolean.FALSE;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        super.writeToParcel(dest, flags);
        dest.writeString(name);
        dest.writeString(description);
        dest.writeParcelable(initialBalance, flags);
        dest.writeString(strategy.name());
        dest.writeParcelable(availableFunds, flags);
        dest.writeByte(((excludeFromTotals != null) && excludeFromTotals.equals(Boolean.TRUE)) ? (byte)1 : 0);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    public static final Creator<Account> CREATOR = new Creator<Account>() {
        @Override
        public Account createFromParcel(Parcel in) {
            return new Account(in);
        }

        @Override
        public Account[] newArray(int size) {
            return new Account[size];
        }
    };

    public void aggregate(Account account) {
        this.initialBalance.add(account.initialBalance);
        this.availableFunds.add(account.availableFunds);
    }

    public String getName() {
        return name;
    }

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

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public Money getInitialBalance() {
        return initialBalance;
    }

    public void setInitialBalance(Money initialBalance) {
        this.initialBalance = initialBalance;
    }

    public Money getAvailableFunds() {
        return availableFunds;
    }

    public void setAvailableFunds(Money availableFunds) {
        this.availableFunds = availableFunds;
    }

    public Strategy getStrategy() {
        return strategy;
    }

    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }

    public Boolean getExcludeFromTotals() {
        return excludeFromTotals;
    }

    public void setExcludeFromTotals(Boolean excludeFromTotals) {
        this.excludeFromTotals = excludeFromTotals;
    }

    public PerformanceItem getPerformanceItem(List<Investment> investments, Date timestamp) {
        Money currentBalance = new Money(this.getAvailableFunds().getMicroCents());
        Money todayChange = new Money(0);

        if (investments != null) {
            for (Investment i : investments) {
                currentBalance.add(i.getValue());

                if (i.isPriceCurrent()) {
                    todayChange.add(Money.subtract(i.getValue(),i.getPrevDayValue()));
                }
            }
        }

        return new PerformanceItem(this.getId(), timestamp, this.initialBalance, currentBalance, todayChange);
    }

    @Override
    public String toString() {
        return "Account{" +
                "name='" + name + '\'' +
                ", description='" + description + '\'' +
                ", initialBalance=" + initialBalance +
                ", availableFunds=" + availableFunds +
                ", strategy=" + strategy +
                ", excludeFromTotals=" + excludeFromTotals +
                '}';
    }

    public enum Strategy implements MetadataUtils.EnumResource {
        NONE(null),
        DOGS_OF_THE_DOW(DogsOfTheDow.class),
        TRIPLE_MOMENTUM(TripleMomentum.class);

        protected final Class<? extends BaseStrategy> strategyClazz;

        Strategy(Class<? extends BaseStrategy> strategyClazz) {
            this.strategyClazz = strategyClazz;
        }

        public Class<? extends BaseStrategy> getStrategyClazz() {
            return strategyClazz;
        }

        @Override
        public int getListResId() {
            return R.array.account_strategy_display_values;
        }
    }

}
MockTradeApp/src/main/java/com/balch/mocktrade/services/QuoteService.java
public class QuoteService extends IntentService {
    private static final String TAG = QuoteService.class.getSimpleName();

    public static final int SNAPSHOT_DAYS_TO_KEEP = 3650;

    public QuoteService() {
        super(QuoteService.class.getName());
    }

    @Override
    protected void onHandleIntent(final Intent intent) {

        try {
            Log.i(TAG, "QuoteService onHandleIntent");

            // get the investment list from the db
            TradeModelProvider modelProvider = ((TradeModelProvider) this.getApplication());
            FinanceModel financeModel = modelProvider.getFinanceModel();
            final PortfolioModel portfolioModel = new PortfolioSqliteModel(modelProvider.getContext(),
                    modelProvider.getSqlConnection(),
                    financeModel,
                    modelProvider.getSettings());
            final List<Investment> investments = portfolioModel.getAllInvestments();
            Settings settings = ((TradeModelProvider) this.getApplication()).getSettings();

            if (investments.size() > 0) {
                final List<Account> accounts = portfolioModel.getAccounts(true);

                final LongSparseArray<List<Investment>> accountIdToInvestmentMap = new LongSparseArray<>(accounts.size());
                List<String> symbols = new ArrayList<>(investments.size());
                for (Investment i : investments) {
                    symbols.add(i.getSymbol());

                    // aggregate investments by account
                    List<Investment> list = accountIdToInvestmentMap.get(i.getAccount().getId());
                    if (list == null) {
                        list = new ArrayList<>();
                        accountIdToInvestmentMap.put(i.getAccount().getId(), list);
                    }
                    list.add(i);
                }

                // get quotes over the wire
                try {
                    Map<String, Quote> quoteMap = financeModel.getQuotes(symbols).blockingFirst();
                    if (quoteMap != null) {
                        boolean newHasQuotes = false;
                        for (Investment i : investments) {
                            try {
                                Quote quote = quoteMap.get(i.getSymbol());
                                if (quote != null) {
                                    if (quote.getLastTradeTime().after(i.getLastTradeTime())) {
                                        newHasQuotes = true;
                                        i.setPrevDayClose(quote.getPreviousClose());
                                        i.setPrice(quote.getPrice(), quote.getLastTradeTime());
                                        portfolioModel.updateInvestment(i);
                                    }
                                }
                            } catch (Exception ex) {
                                Log.e(TAG, "updateInvestment exception", ex);
                            }
                        }

                        boolean isFirstSyncOfDay = !DateUtils.isToday(settings.getLastSyncTime());
                        if (isFirstSyncOfDay) {
                            portfolioModel.purgeSnapshots(SNAPSHOT_DAYS_TO_KEEP);
                        }

                        if (newHasQuotes) {
                            portfolioModel.createSnapshotTotals(accounts, accountIdToInvestmentMap);
                        }

                        processAccountStrategies(accounts, accountIdToInvestmentMap, quoteMap, isFirstSyncOfDay);

                        settings.setLastSyncTime(System.currentTimeMillis());

                        startService(WearSyncService.getIntent(getApplicationContext()));
                    }
                } catch (Exception ex1) {
                    Log.e(TAG, "updateInvestment exception", ex1);

                }
            }

            // if the market is closed reset alarm to next market open time
            if (!financeModel.isInPollTime()) {
                TradeApplication.backupDatabase(getApplicationContext(), true);
                financeModel.setQuoteServiceAlarm();
            }

        } catch (Exception ex) {
            Log.e(TAG, "financeModel.getQuotes(symbols exception", ex);
        } finally {
            PortfolioUpdateBroadcaster.broadcast(QuoteService.this);
            QuoteReceiver.completeWakefulIntent(intent);
        }
    }

    public static Intent getIntent(Context context) {
        return new Intent(context, QuoteService.class);
    }

    protected void processAccountStrategies(List<Account> accounts,
                                            LongSparseArray<List<Investment>> accountIdToInvestmentMap,
                                            Map<String, Quote> quoteMap, boolean doDailyUpdate) {
        for (Account account : accounts) {
            Class<? extends BaseStrategy> strategyClazz = account.getStrategy().getStrategyClazz();
            if (strategyClazz != null) {
                try {
                    TradeModelProvider modelProvider = ((TradeModelProvider)this.getApplication());
                    BaseStrategy strategy = BaseStrategy.createStrategy(strategyClazz,
                            modelProvider.getContext(),
                            modelProvider.getFinanceModel(),
                            modelProvider.getSqlConnection(),
                            modelProvider.getSettings());
                    if (doDailyUpdate) {
                        strategy.dailyUpdate(account, accountIdToInvestmentMap.get(account.getId()), quoteMap);
                    }
                    strategy.pollUpdate(account, accountIdToInvestmentMap.get(account.getId()), quoteMap);
                } catch (Exception ex) {
                    Log.e(TAG, "Error calling strategy.pollUpdate", ex);
                }
            }
        }
    }

}
AppFramework/src/main/java/com/balch/android/app/framework/PresenterActivity.java
public abstract class PresenterActivity<V extends View & BaseView, P extends BasePresenter>
        extends AppCompatActivity  {
    private static final String TAG = PresenterActivity.class.getSimpleName();

    private String className;

    protected P presenter;

    /**
     * Override abstract method to create a view of type V used by the Presenter.
     * The view id will be managed by this class if not specified
     * @return View containing view logic in the MVP pattern
     */
    protected abstract V createView();

    protected abstract P createPresenter(V view);

    // override-able activity functions
    public void onCreateBase(Bundle savedInstanceState) {
    }

    public void onResumeBase() {
    }

    public void onPauseBase() {
    }

    public void onStartBase() {
    }

    public void onStopBase() {
    }

    public void onDestroyBase() {
    }

    public void onSaveInstanceStateBase(Bundle outState) {
    }

    public void onActivityResultBase(int requestCode, int resultCode, Intent data) {
    }

    public boolean onHandleException(String logMsg, Exception ex) {
        return false;
    }

    public PresenterActivity() {
        this.className = this.getClass().getSimpleName();
    }

    private boolean handleException(String logMsg, Exception ex) {
        Log.e(TAG, logMsg, ex);
        return onHandleException(logMsg, ex);
    }

    @SuppressWarnings("unchecked")
    @Override
    final protected void onCreate(Bundle savedInstanceState) {
        StopWatch sw = StopWatch.newInstance();
        Log.d(TAG, this.className + " OnCreate - Begin");
        try {
            super.onCreate(savedInstanceState);
            V view = this.createView();

            this.setContentView(view);

            Application application = getApplication();
            if (!(application instanceof ModelProvider)) {
                throw new IllegalStateException("Android Application object must be derived from Model Provider");
            }

            presenter = this.createPresenter(view);

            this.onCreateBase(savedInstanceState);

        } catch (Exception ex) {
            if (!handleException("OnCreate ", ex)) {
                throw ex;
            }
        }
        Log.i(TAG, this.className + " OnCreate - End (ms):" + sw.stop());
    }

    @Override
    final public void onStart() {
        StopWatch sw = StopWatch.newInstance();
        Log.d(TAG, this.className + " onStart - Begin");
        try {
            super.onStart();
            onStartBase();
        } catch (Exception ex) {
            if (!handleException("onStart ", ex)) {
                throw ex;
            }
        }
        Log.i(TAG, this.className + " onStart - End (ms):" + sw.stop());
    }

    @Override
    final public void onResume() {
        StopWatch sw = StopWatch.newInstance();
        Log.d(TAG, this.className + " onResume - Begin");
        try {
            super.onResume();
            onResumeBase();
        } catch (Exception ex) {
            if (!handleException("onResume ", ex)) {
                throw ex;
            }
        }
        Log.i(TAG, this.className + " onResume - End (ms):" + sw.stop());
    }

    @Override
    final public void onSaveInstanceState(Bundle outState) {
        StopWatch sw = StopWatch.newInstance();
        Log.d(TAG, this.className + " onSaveInstanceState - Begin");
        try {
            super.onSaveInstanceState(outState);
            onSaveInstanceStateBase(outState);
        } catch (Exception ex) {
            if (!handleException("onSaveInstanceState ", ex)) {
                throw ex;
            }
        }
        Log.i(TAG, this.className + " onSaveInstanceState - End (ms):" + sw.stop());
    }

    @Override
    final public void onPause() {
        StopWatch sw = StopWatch.newInstance();
        Log.d(TAG, this.className + " onPause - Begin");
        try {
            onPauseBase();
            super.onPause();
        } catch (Exception ex) {
            if (!handleException("onPause ", ex)) {
                throw ex;
            }
        }
        Log.i(TAG, this.className + " onPause - End (ms):" + sw.stop());
    }

    @Override
    final public void onStop() {
        StopWatch sw = StopWatch.newInstance();
        Log.d(TAG, this.className + " onStop - Begin");

        try {
            onStopBase();
            super.onStop();
        } catch (Exception ex) {
            if (!handleException("onStop", ex)) {
                throw ex;
            }
        }
        Log.i(TAG, this.className + " onStop - End (ms):" + sw.stop());
    }

    @Override
    final public void onDestroy() {
        StopWatch sw = StopWatch.newInstance();
        Log.d(TAG, this.className + " onDestroy - Begin");
        try {
            onDestroyBase();
            presenter.cleanup();
            presenter = null;
        } catch (Exception ex) {
            if (!handleException("onDestroy", ex)) {
                throw ex;
            }
        }
        Log.i(TAG, this.className + " onDestroy - End (ms):" + sw.stop());
        super.onDestroy();
    }

    @Override
    final public void onActivityResult(int requestCode, int resultCode, Intent data) {
        StopWatch sw = StopWatch.newInstance();
        Log.d(TAG, this.className + " onActivityResult - Begin");
        super.onActivityResult(requestCode, resultCode, data);
        try {
            onActivityResultBase(requestCode, resultCode, data);
        } catch (Exception ex) {
            if (!handleException("onActivityResult", ex)) {
                throw ex;
            }
        }
        Log.i(TAG, this.className + " onActivityResult - End (ms):" + sw.stop());
    }

    public Snackbar getSnackbar(View parent, String msg, int length) {
        return Snackbar.make(parent, msg, length);
    }

    public Snackbar getSnackbar(View parent, String msg, int length,
                                @ColorRes int backgroundColorId) {
        Snackbar snackbar = getSnackbar(parent, msg, length);
        ViewGroup group = (ViewGroup) snackbar.getView();
        group.setBackgroundColor(ContextCompat.getColor(this, backgroundColorId));
        return snackbar;
    }

    /**
     * TODO: figure out why android.support.design.R.id.snackbar_text
     * does not resolve in aar
     */
    public Snackbar getSnackbar(View parent, String msg, int length,
                                @ColorRes int backgroundColorId,
                                @ColorRes int textColorId,
                                @IdRes int snackBarTextId) {
        Snackbar snackbar = getSnackbar(parent, msg, length, backgroundColorId);
        ViewGroup group = (ViewGroup) snackbar.getView();

        ((TextView) group.findViewById(snackBarTextId))
                .setTextColor(ContextCompat.getColor(this, textColorId));

        return snackbar;
    }

}
MockTradeApp/src/main/java/com/balch/mocktrade/settings/SettingsActivity.java
public class SettingsActivity extends AppCompatActivity
        implements SharedPreferences.OnSharedPreferenceChangeListener {

    public static Intent newIntent(Context context) {
        return new Intent(context, SettingsActivity.class);
    }

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

        setContentView(R.layout.settings_view);

        Toolbar toolbar = (Toolbar) findViewById(R.id.settings_view_toolbar);
        setSupportActionBar(toolbar);

        ActionBar actionBar = getSupportActionBar();
        if (actionBar != null) {
            actionBar.setDisplayHomeAsUpEnabled(true);
            actionBar.setDisplayShowHomeEnabled(true);
        }

        getFragmentManager().beginTransaction().replace(R.id.settings_view_content_frame, new SettingsPreferenceFragment()).commit();
    }

    @Override
    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
        Settings.Key settingKey = Settings.Key.fromKey(key);
        if (settingKey == Settings.Key.PREF_POLL_INTERVAL) {
            TradeModelProvider modelProvider = ((TradeModelProvider) this.getApplication());
            FinanceModel financeModel = modelProvider.getFinanceModel();
            financeModel.setQuoteServiceAlarm();
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(this);
    }

    @Override
    public void onPause() {
        PreferenceManager.getDefaultSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(this);
        super.onPause();
    }

    public static class SettingsPreferenceFragment extends PreferenceFragment {
        @Override
        public void onCreate(final Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            addPreferencesFromResource(R.xml.settings_pref_screen);

            PreferenceGroup preferenceGroup = (PreferenceGroup) findPreference("settings_version");
            preferenceGroup.setTitle("Version: " + VersionUtils.getVersion(this.getActivity(), BuildConfig.DEBUG));

        }
    }
}
MockTradeApp/src/main/java/com/balch/mocktrade/order/OrderListActivity.java
public class OrderListActivity extends PresenterActivity<OrderListView, OrderPresenter> {

    private static final String EXTRA_ACCOUNT_ID = "EXTRA_ACCOUNT_ID";

    public static Intent newIntent(Context context, long accountId) {
        Intent intent = new Intent(context, OrderListActivity.class);
        intent.putExtra(EXTRA_ACCOUNT_ID, accountId);
        return intent;
    }

    @Override
    public void onCreateBase(Bundle bundle) {

        Toolbar toolbar = findViewById(R.id.order_list_view_toolbar);
        setSupportActionBar(toolbar);

        ActionBar actionBar = getSupportActionBar();
        if (actionBar != null) {
            actionBar.setDisplayHomeAsUpEnabled(true);
            actionBar.setDisplayShowHomeEnabled(true);
        }

        presenter.reload(true);
    }

    @Override
    public OrderListView createView() {
        return new OrderListView(this);
    }

    @Override
    protected OrderPresenter createPresenter(OrderListView view) {
        return new OrderPresenter((TradeModelProvider) getApplication(),
                getOrderViewModel(), getIntent().getLongExtra(EXTRA_ACCOUNT_ID, 0),
                this, view, new OrderPresenter.ActivityBridge() {
            @Override
            public void showProgress(boolean show) {
                OrderListActivity.this.showProgress(show);
            }

            @Override
            public void finish() {
                OrderListActivity.this.finish();
            }
        });
    }

    public void showProgress(boolean show) {
//        this.progressBar.setVisibility(show ? View.VISIBLE: View.GONE);
//        this.refreshImageButton.setVisibility(show ? View.GONE : View.VISIBLE);
    }

    private OrderViewModel getOrderViewModel() {
        return ViewModelProviders.of(this).get(OrderViewModel.class);
    }
}
AppFramework/src/main/java/com/balch/android/app/framework/core/EditActivity.java
public class EditActivity extends PresenterActivity<EditView, EditPresenter> {
    protected static final String EXTRA_ISNEW = "isNew";
    protected static final String EXTRA_ITEM = "item";
    protected static final String EXTRA_VALIDATOR = "validator";
    protected static final String EXTRA_TITLE_RESID = "titleResId";
    protected static final String EXTRA_OK_BUTTON_RESID = "okButtonResId";
    protected static final String EXTRA_CANCEL_BUTTON_RESID = "cancelButtonResId";
    protected static final String EXTRA_RESULT = "EditActivityResult";

    protected static final String STATE_COLUMN_VIEW_IDS = "col_view_ids";

    protected ArrayList<Integer> columnViewIDs = new ArrayList<>();

    protected ExternalController validator;
    protected DomainObject item;
    protected boolean isNew;
    protected int okButtonResId;
    protected int cancelButtonResId;

    @Override
    public void onCreateBase(Bundle savedInstanceState) {
        Intent intent = this.getIntent();
        int titleResId = intent.getIntExtra(EXTRA_TITLE_RESID, 0);
        if (titleResId != 0) {
            setTitle(titleResId);
        }

        Toolbar toolbar = findViewById(R.id.edit_view_toolbar);
        setSupportActionBar(toolbar);

        validator = intent.getParcelableExtra(EXTRA_VALIDATOR);
        item = intent.getParcelableExtra(EXTRA_ITEM);
        isNew = intent.getBooleanExtra(EXTRA_ISNEW, false);
        okButtonResId = intent.getIntExtra(EXTRA_OK_BUTTON_RESID, 0);
        cancelButtonResId = intent.getIntExtra(EXTRA_CANCEL_BUTTON_RESID, 0);

        if (savedInstanceState != null) {
            this.columnViewIDs = savedInstanceState.getIntegerArrayList(STATE_COLUMN_VIEW_IDS);
        }

        presenter.initialize(item, isNew, validator, okButtonResId, cancelButtonResId, columnViewIDs,
                new EditView.EditViewListener() {
                    @Override
                    public void onSave(DomainObject item) {
                        Intent intent = getIntent();
                        intent.putExtra(EXTRA_RESULT, item);
                        setResult(RESULT_OK, intent);
                        finish();
                    }

                    @Override
                    public void onCancel() {
                        Intent intent = getIntent();
                        setResult(RESULT_CANCELED, intent);
                        finish();
                    }
                });
    }

    @Override
    public void onSaveInstanceStateBase(Bundle outState) {
        outState.putIntegerArrayList(STATE_COLUMN_VIEW_IDS, columnViewIDs);
    }

    @Override
    public EditView createView() {
        return new EditView(this);
    }

    @Override
    protected EditPresenter createPresenter(EditView view) {
        return new EditPresenter(view);
    }

    public static Intent getIntent(Context context, int titleResId, DomainObject domainObject, ExternalController externalController,
                                   int okButtonResId, int cancelButtonResId) {
        Intent intent = new Intent(context, EditActivity.class);

        intent.putExtra(EXTRA_ISNEW, (domainObject.getId() == null));
        intent.putExtra(EXTRA_ITEM, domainObject);
        intent.putExtra(EXTRA_VALIDATOR, externalController);
        intent.putExtra(EXTRA_TITLE_RESID, titleResId);
        intent.putExtra(EXTRA_OK_BUTTON_RESID, okButtonResId);
        intent.putExtra(EXTRA_CANCEL_BUTTON_RESID, cancelButtonResId);

        return intent;
    }

    public static <T extends DomainObject> T getResult(Intent intent) {
        return (T) intent.getParcelableExtra(EXTRA_RESULT);
    }
}
AppFramework/src/main/java/com/balch/android/app/framework/types/Money.java
public class Money implements Cloneable, Parcelable, Comparable<Money> {
    private static final String TAG = Money.class.getSimpleName();

    protected static final int DOLLAR_TO_MICRO_CENT = 10000;

    // $1 = 10000mc
    private long microCents;
    private Currency currency = Currency.getInstance("USD");

    public Money() {
        this(0L);
    }

    public Money(long microCents) {
        this.setMicroCents(microCents);
    }

    public Money(double dollars) {
        this.setDollars(dollars);
    }

    public Money(String dollars) {
        this.setDollars(dollars);
    }

    protected Money(Parcel in) {
        microCents = in.readLong();
        currency = Currency.getInstance(in.readString());
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeLong(microCents);
        dest.writeString(currency.getCurrencyCode());
    }

    @Override
    public int describeContents() {
        return 0;
    }

    public static final Creator<Money> CREATOR = new Creator<Money>() {
        @Override
        public Money createFromParcel(Parcel in) {
            return new Money(in);
        }

        @Override
        public Money[] newArray(int size) {
            return new Money[size];
        }
    };

    public long getMicroCents() {
        return microCents;
    }

    public void setMicroCents(long microCents) {
        this.microCents = microCents;
    }

    public double getDollars() {
        return microCents/(double)DOLLAR_TO_MICRO_CENT;
    }

    public void setDollars(double dollars) {
        this.microCents = (long)(dollars * DOLLAR_TO_MICRO_CENT);
    }

    public void setCurrency(Currency currency) {
        this.currency = currency;
    }

    public void setDollars(String dollars) {
        Double val = 0.0;

        if (!TextUtils.isEmpty(dollars)) {
            String symbol = getSymbol();
            if (dollars.startsWith(symbol)) {
                dollars = dollars.substring(symbol.length());
            }

            val = Double.valueOf(dollars.replace(",",""));
        }
        this.setDollars(val);
    }

    public String getFormatted() {
        return getFormatted(2);
    }

    public Spannable getFormattedWithColor() {
        String sign = (microCents >= 0) ? "+" : "-";
        ForegroundColorSpan spanColor = new ForegroundColorSpan((microCents >= 0)? Color.GREEN:Color.RED);

        String val = String.format(Locale.getDefault(), "%s%s%.02f", sign, getSymbol(), Math.abs(microCents)/1000f);
        SpannableStringBuilder spanString = new SpannableStringBuilder(val);
        spanString.setSpan(spanColor, 0, val.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);

        return spanString;
    }

    public String getFormatted(int decimalPlaces) {
        double dollars = getDollars();

        StringBuilder patternBuilder = new StringBuilder(getSymbol());
        patternBuilder.append("#,##0");

        if (decimalPlaces == 1) {
            patternBuilder.append(".0");
        } else if (decimalPlaces >= 2) {
            patternBuilder.append(".00");
            for (int x = 0 ; x < decimalPlaces - 2; x++) {
                patternBuilder.append("#");
            }
        }

        String pattern = patternBuilder.toString();
        DecimalFormat format = new DecimalFormat(pattern + ";-" + pattern);
        return format.format(dollars);
    }

    public String getSymbol() {
        return currency.getSymbol();
    }

    public String getCurrencyNoGroupSep(int decimalPlaces) {
        double dollars = getDollars();
        return String.format("%1$.0"+decimalPlaces+"f", dollars);
    }

    public void multiply(long value) {
        this.microCents *= value;
    }

    public void add(Money money) {
        this.microCents += money.microCents;
    }

    public void subtract(Money money) {
        this.microCents -= money.microCents;
    }

    public static Money multiply(Money money, long quantity) {
        return new Money(money.microCents * quantity);
    }

    public static Money add(Money money1, Money money2) {
        return new Money(money1.microCents + money2.microCents);
    }

    public static Money subtract(Money money1, Money money2) {
        return new Money(money1.microCents - money2.microCents);
    }

    public Money clone() {
        Money clone = null;
        try {
            clone = (Money)super.clone();
        } catch (CloneNotSupportedException e) {
            Log.wtf(TAG, e);
        }
        return clone;
    }

    @Override
    public String toString() {
        return "Money{" +
                "microCents=" + microCents +
                ", currency=" + getFormatted() +
                '}';
    }

    @Override
    public int compareTo(Money another) {
        int compare = 0;
        if (this.microCents != another.microCents) {
            compare = (this.microCents < another.microCents) ? -1 : 1;
        }
        return compare;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Money money = (Money) o;

        if (microCents != money.microCents) return false;
        return !(currency != null ? !currency.equals(money.currency) : money.currency != null);

    }

    @Override
    public int hashCode() {
        int result = (int) (microCents ^ (microCents >>> 32));
        result = 31 * result + (currency != null ? currency.hashCode() : 0);
        return result;
    }
}
MockTradeApp/src/main/java/com/balch/mocktrade/account/AccountEditController.java
public class AccountEditController implements ExternalController<Account>, Parcelable {
    public AccountEditController() {
    }

    @Override
    public void onChanged(Context context, ColumnDescriptor descriptor, Object value, ControlMap controlMap) throws ValidatorException {
        if (descriptor.getField().getName().equals(Account.FLD_STRATEGY)) {
            Account.Strategy strategy = (Account.Strategy)value;
            if (strategy != Account.Strategy.NONE) {
                String [] displayVals = context.getResources().getStringArray(strategy.getListResId());
                EditLayout control = controlMap.get(Account.FLD_NAME);
                control.setValue(displayVals[strategy.ordinal()]);
            }
        }
    }

    @Override
    public void validate(Context context, Account account, ControlMap controlMap) throws ValidatorException {
    }

    @Override
    public void initialize(Context context, Account account, ControlMap controlMap) {
    }

    private AccountEditController(Parcel in) {
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
    }

    @Override
    public int describeContents() {
        return 0;
    }

    public static final Creator<AccountEditController> CREATOR = new Creator<AccountEditController>() {
        @Override
        public AccountEditController createFromParcel(Parcel in) {
            return new AccountEditController(in);
        }

        @Override
        public AccountEditController[] newArray(int size) {
            return new AccountEditController[size];
        }
    };

}
MockTradeApp/src/main/java/com/balch/mocktrade/order/Order.java
public class Order extends DomainObject implements Parcelable {

    static final String FLD_LIMIT_PRICE = "limitPrice";
    static final String FLD_STOP_PRICE = "stopPrice";
    static final String FLD_STOP_PERCENT = "stopPercent";
    static final String FLD_SYMBOL = "symbol";
    static final String FLD_ACTION = "action";
    static final String FLD_STRATEGY = "strategy";
    static final String FLD_QUANTITY = "quantity";

    private Account account;

    @ColumnEdit(order = 1, state = EditState.READONLY, labelResId = R.string.order_symbol_label, hints = {"MAX_CHARS=32","NOT_EMPTY=true"})
    @ColumnNew(order = 1, customControl = StockSymbolLayout.class, labelResId = R.string.order_symbol_label, hints = {"MAX_CHARS=32","NOT_EMPTY=true"})
    private String symbol;

    private OrderStatus status;

    @ColumnEdit(order = 2, state = EditState.READONLY, labelResId = R.string.order_action_label)
    @ColumnNew(order = 2, state = EditState.READONLY, labelResId = R.string.order_action_label)
    private OrderAction action;

    @ColumnEdit(order = 3, state = EditState.READONLY, labelResId = R.string.order_strategy_label)
    @ColumnNew(order = 3, labelResId = R.string.order_strategy_label)
    private OrderStrategy strategy;

    private OrderDuration duration;

    @ColumnEdit(order = 4, labelResId = R.string.order_limit_price_label)
    @ColumnNew(order = 4, labelResId = R.string.order_limit_price_label, hints = {"INIT_EMPTY=true"})
    private Money limitPrice;

    @ColumnEdit(order = 5, labelResId = R.string.order_stop_price_label)
    @ColumnNew(order = 5, labelResId = R.string.order_stop_price_label, hints = {"INIT_EMPTY=true"})
    private Money stopPrice;

    @ColumnEdit(order = 6, labelResId = R.string.order_stop_percent_label, hints = {"PERCENT=true"})
    @ColumnNew(order = 6, labelResId = R.string.order_stop_percent_label, hints = {"PERCENT=true","INIT_EMPTY=true"})
    private Double stopPercent;

    @ColumnEdit(order = 7, labelResId = R.string.order_quantity_label, customControl = QuantityPriceLayout.class)
    @ColumnNew(order = 7, labelResId = R.string.order_quantity_label, customControl = QuantityPriceLayout.class)
    private Long quantity;

    private Money highestPrice;

    public Order() {
        this.symbol = "";
        this.status = OrderStatus.OPEN;
        this.action = OrderAction.BUY;
        this.strategy = OrderStrategy.MARKET;
        this.duration = OrderDuration.GOOD_TIL_CANCELED;
        this.quantity = 0L;
        this.limitPrice = new Money(0);
        this.stopPrice = new Money(0);
        this.stopPercent = 0.0;
        this.highestPrice = new Money(0);
    }

    protected Order(Parcel in) {
        super(in);
        account = in.readParcelable(Account.class.getClassLoader());
        symbol = in.readString();
        status = (OrderStatus)in.readSerializable();
        action = (OrderAction) in.readSerializable();
        strategy = (OrderStrategy) in.readSerializable();
        duration = (OrderDuration) in.readSerializable();
        limitPrice = in.readParcelable(Money.class.getClassLoader());
        stopPrice = in.readParcelable(Money.class.getClassLoader());
        stopPercent = (Double)in.readSerializable();
        quantity = (Long)in.readSerializable();
        highestPrice = in.readParcelable(Money.class.getClassLoader());
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        super.writeToParcel(dest, flags);
        dest.writeParcelable(account, flags);
        dest.writeString(symbol);
        dest.writeSerializable(status);
        dest.writeSerializable(action);
        dest.writeSerializable(strategy);
        dest.writeSerializable(duration);
        dest.writeParcelable(limitPrice, flags);
        dest.writeParcelable(stopPrice, flags);
        dest.writeSerializable(stopPercent);
        dest.writeSerializable(quantity);
        dest.writeParcelable(highestPrice, flags);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    public static final Creator<Order> CREATOR = new Creator<Order>() {
        @Override
        public Order createFromParcel(Parcel in) {
            return new Order(in);
        }

        @Override
        public Order[] newArray(int size) {
            return new Order[size];
        }
    };

    public Account getAccount() {
        return account;
    }

    public void setAccount(Account account) {
        this.account = account;
    }

    public String getSymbol() {
        return symbol;
    }

    public void setSymbol(String symbol) {
        this.symbol = symbol;
    }

    public OrderStatus getStatus() {
        return status;
    }

    public void setStatus(OrderStatus status) {
        this.status = status;
    }

    public OrderAction getAction() {
        return action;
    }

    public void setAction(OrderAction action) {
        this.action = action;
    }

    public OrderStrategy getStrategy() {
        return strategy;
    }

    public void setStrategy(OrderStrategy strategy) {
        this.strategy = strategy;
    }

    public OrderDuration getDuration() {
        return duration;
    }

    public void setDuration(OrderDuration duration) {
        this.duration = duration;
    }

    public Long getQuantity() {
        return quantity;
    }

    public Long getQuantityDelta() {
        return (this.action==OrderAction.BUY) ?
                this.quantity :
                -this.quantity;
    }

    public void setQuantity(Long quantity) {
        this.quantity = quantity;
    }

    public Money getLimitPrice() {
        return limitPrice;
    }

    public void setLimitPrice(Money limitPrice) {
        this.limitPrice = limitPrice;
    }

    public Double getStopPercent() {
        return stopPercent;
    }

    public void setStopPercent(Double stopPercent) {
        this.stopPercent = stopPercent;
    }

    public Money getStopPrice() {
        return stopPrice;
    }

    public void setStopPrice(Money stopPrice) {
        this.stopPrice = stopPrice;
    }

    public Money getHighestPrice() {
        return highestPrice;
    }

    public void setHighestPrice(Money highestPrice) {
        this.highestPrice = highestPrice;
    }

    public Money getCost(Money price) {
        return Money.multiply(price, this.getQuantityDelta());
    }

    public enum OrderStatus implements MetadataUtils.EnumResource {
        OPEN,
        FULFILLED,
        ERROR,
        CANCELED;

        @Override
        public int getListResId() {
            return R.array.order_status_display_values;
        }
    }

    public enum OrderAction implements MetadataUtils.EnumResource {
        BUY,
        SELL;

        @Override
        public int getListResId() {
            return R.array.order_type_display_values;
        }
    }

    private static final int FLAG_BUY = (1);
    private static final int FLAG_SELL = (1<<1);
    public enum OrderStrategy implements MetadataUtils.EnumResource {
        MARKET(FLAG_BUY | FLAG_SELL),
        MANUAL(FLAG_BUY | FLAG_SELL),
        LIMIT(FLAG_BUY | FLAG_SELL),
        STOP_LOSS(FLAG_SELL),
        TRAILING_STOP_AMOUNT_CHANGE(FLAG_SELL),
        TRAILING_STOP_PERCENT_CHANGE(FLAG_SELL);

        private int supportedActions;

        OrderStrategy(int flags) {
            this.supportedActions = flags;
        }

        @Override
        public int getListResId() {
            return R.array.order_strategy_display_values;
        }

        public boolean isBuySupported() {
            return ((this.supportedActions & FLAG_BUY) != 0);
        }

        public boolean isSellSupported() {
            return ((this.supportedActions & FLAG_SELL) != 0);
        }

    }

    enum OrderDuration implements MetadataUtils.EnumResource {
        GOOD_TIL_CANCELED,
        DAY;

        @Override
        public int getListResId() {
            return R.array.order_duration_display_values;
        }
    }

}

Answer: public void startNewOrderActivity(Order order, @StringRes int stringId) {


Input:
import android.util.Log;
import com.sonova.difian.communication.messaging.HiMuteStatus;
import com.sonova.difian.communication.messaging.HiSide;
import com.sonova.difian.communication.messaging.HiStatusMessage;
import com.sonova.difian.communication.messaging.Message;
import com.sonova.difian.communication.messaging.MessageReader;
import com.sonova.difian.communication.messaging.MessageReaderException;
import com.sonova.difian.communication.messaging.MessageType;
import com.sonova.difian.communication.messaging.MessageWriter;
import com.sonova.difian.communication.messaging.SmartMessage;
import com.sonova.difian.communication.messaging.SmartReplyMessage;
import com.sonova.difian.communication.messaging.StreamHelpers;
import com.sonova.difian.communication.messaging.TypingMessage;
import com.sonova.difian.communication.messaging.UserInfoMessage;
import com.sonova.difian.communication.messaging.UserInfoRequestMessage;
import com.sonova.difian.communication.utilities.SocketHelpers;
import com.sonova.difian.utilities.Contract;
import com.sonova.difian.utilities.StringHelpers;
import org.xmlpull.v1.XmlPullParserException;
import org.yaler.AcceptCallback;
import org.yaler.AcceptCallbackState;
import org.yaler.YalerSSLServerSocket;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.AbstractList;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Queue;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
                                    if (id.equals(currentId))
                                    {
                                        handleMessage(message);
                                    }
                                }
                            }

                            byte[] rspBytes = StringHelpers.convertToBytes("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");

                            try
                            {
                                s.getOutputStream().write(rspBytes);
                            }
                            catch (IOException e)
                            {
                                Log.w(TAG, "IOException while writing chat response.", e);
                                ok = false;
                            }

                            synchronized (_lockObject)
                            {
                                currentId = _id;
                            }
                        }

                        synchronized (_lockObject)
                        {
                            try
                            {
                                s.close();
                            }
                            catch (IOException e)
                            {
                                Log.w(TAG, "IOException while closing chat socket.", e);
                            }
                            _rxChatSockets.remove(s);
                        }
                    }
                    catch (Throwable t)
                    {
                        Log.e(TAG, "Uncaught exception in startRx()", t);
                        throw new RuntimeException(t);
                    }
                }
            }).start();
        }
    }

    private void handleMessage(Message message)
    {
        synchronized (_lockObject)
        {
            Contract.check(message != null);

            Log.i(TAG, String.format("Retrieved message: %s", message));

            if (message.getType() == MessageType.USER_INFO)
            {
                _pendingAudiologistInfo = false;
                UserInfoMessage m = (UserInfoMessage)message;

                _audiologistInfo = new AudiologistInfo(m.getName(), m.getImage());
            }
            else if (message.getType() == MessageType.HI_STATUS)
            {
                HiStatusMessage m = (HiStatusMessage)message;

                HiMuteStatus newLeft = m.getMuteStatus(HiSide.LEFT);
                HiMuteStatus newRight = m.getMuteStatus(HiSide.RIGHT);

                if ((_muteStatusLeft != newLeft) && (_muteStatusRight != newRight) && (newLeft == newRight))
                {
                    if (newLeft == HiMuteStatus.MUTED)
                    {
                        addMessage(new ChatMessage(ChatMessageSource.SYSTEM, ChatMessage.TEXT_HI_MUTED_BOTH));
                    }
                    else if (newLeft == HiMuteStatus.UNMUTED)
                    {
                        addMessage(new ChatMessage(ChatMessageSource.SYSTEM, ChatMessage.TEXT_HI_UNMUTED_BOTH));
                    }
                }
                else
                {
                    if (_muteStatusLeft != newLeft)
                    {
                        if (newLeft == HiMuteStatus.MUTED)
                        {
                            addMessage(new ChatMessage(ChatMessageSource.SYSTEM, ChatMessage.TEXT_HI_MUTED_LEFT));
                        }
                        else if (newLeft == HiMuteStatus.UNMUTED)
                        {
                            addMessage(new ChatMessage(ChatMessageSource.SYSTEM, ChatMessage.TEXT_HI_UNMUTED_LEFT));
                        }
                    }
                    if (_muteStatusRight != newRight)
                    {
                        if (newRight == HiMuteStatus.MUTED)
                        {
                            addMessage(new ChatMessage(ChatMessageSource.SYSTEM, ChatMessage.TEXT_HI_MUTED_RIGHT));
                        }
                        else if (newRight == HiMuteStatus.UNMUTED)
                        {
                            addMessage(new ChatMessage(ChatMessageSource.SYSTEM, ChatMessage.TEXT_HI_UNMUTED_RIGHT));
                        }
                    }
                }

                _muteStatusLeft = newLeft;
                _muteStatusRight = newRight;
            }
            else if (message.getType() == MessageType.SMART)
            {
                SmartMessage m = (SmartMessage)message;

                addMessage(new ChatMessage(ChatMessageSource.AUDIOLOGIST, m));

                _isTyping = false;
            }
            else if (message.getType() == MessageType.TYPING)
            {

Context:
Difian/DifianApp/src/main/java/com/sonova/difian/communication/messaging/MessageReaderException.java
@SuppressWarnings("SerializableHasSerializationMethods")
public final class MessageReaderException extends Exception
{
    private static final long serialVersionUID = 1L;

    MessageReaderException()
    {
    }

    MessageReaderException(String message)
    {
        super(message);
    }

    MessageReaderException(String message, Throwable cause)
    {
        super(message, cause);
    }

    MessageReaderException(Throwable cause)
    {
        super(cause);
    }
}
Difian/DifianApp/src/main/java/com/sonova/difian/utilities/StringHelpers.java
public final class StringHelpers
{
    private StringHelpers()
    {
    }

    public static byte[] convertToBytes(String source)
    {
        char[] chars = source.toCharArray();
        byte[] result = new byte[chars.length];
        for (int i = 0; i < chars.length; i++)
        {
            result[i] = (byte)chars[i];
        }
        return result;
    }
}
Difian/DifianApp/src/main/java/com/sonova/difian/communication/messaging/MessageWriter.java
public final class MessageWriter
{
    private static final String TAG = MessageWriter.class.getName();
    private static final MessageWriter _instance = new MessageWriter();

    private MessageWriter()
    {
    }

    public static MessageWriter getInstance()
    {
        return _instance;
    }

    @SuppressWarnings("UnnecessaryCodeBlock")
    public void transmit(OutputStream stream, String host, String id, Message message) throws IOException
    {
        if (stream == null)
        {
            throw new IllegalArgumentException("stream == null in transmit");
        }
        if (host == null)
        {
            throw new IllegalArgumentException("host == null");
        }
        if (id == null)
        {
            throw new IllegalArgumentException("id == null");
        }
        if (message == null)
        {
            throw new IllegalArgumentException("message == null");
        }
        if ((message.getType() != MessageType.USER_INFO_REQUEST) && (message.getType() != MessageType.SMART_REPLY))
        {
            throw new IllegalArgumentException(String.format("Unsupported message type while writing: %s", message.getType()));
        }

        StringWriter messageString = new StringWriter();

        XmlSerializer x = Xml.newSerializer();
        x.setOutput(messageString);

        // Prepare message.
        x.startDocument("ASCII", true);
        {
            x.startTag("", XmlConstants.MESSAGE);
            {
                x.attribute("", "v", "3");
                //noinspection UnnecessaryToStringCall
                x.attribute("", "guid", UUID.randomUUID().toString());
                x.attribute("", "date", new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.US).format(new Date()));
                if (message.getType() == MessageType.USER_INFO_REQUEST)
                {
                    x.attribute("", "type", "user_info_request");
                }
                else if (message.getType() == MessageType.SMART_REPLY)
                {
                    x.attribute("", "type", "smart_reply");

                    SmartReplyMessage m = (SmartReplyMessage)message;

                    if (m.getReMessage() != null)
                    {
                        x.attribute("", XmlConstants.RE_MESSAGE_GUID, m.getReMessage().getGuid());
                    }
                    else
                    {
                        x.attribute("", XmlConstants.RE_MESSAGE_GUID, "00000000-0000-0000-0000-000000000000");
                    }

                    String sel;
                    String text;

                    if (m.getSelectionCount() != 0)
                    {
                        StringBuilder selBuilder = new StringBuilder();
                        StringBuilder textBuilder = new StringBuilder();
                        for (int i = 0; i < m.getSelectionCount(); i++)
                        {
                            if (i != 0)
                            {
                                selBuilder.append(';');
                                textBuilder.append(';');
                            }
                            selBuilder.append(m.getSelection(i).getId());
                            textBuilder.append(m.getSelection(i).getText());
                        }
                        sel = selBuilder.toString();
                        text = textBuilder.toString();
                    }
                    else
                    {
                        text = m.getReText();
                        sel = "";
                    }

                    x.startTag("", "re_text");
                    {
                        x.text(text);
                    }
                    x.endTag("", "re_text");
                    x.startTag("", XmlConstants.SELECTION);
                    {
                        x.attribute("", XmlConstants.OPTION_IDS, sel);
                    }
                    x.endTag("", XmlConstants.SELECTION);
                }
            }
            x.endTag("", XmlConstants.MESSAGE);
        }
        x.endDocument();

        String m = messageString.toString();
        Log.v(MessageWriter.class.getSimpleName(), m);

        // Send HTTP header.
        stream.write(String.format("PUT /%s_-1 HTTP/1.1\r\n", id).getBytes());
        stream.write("Content-Type: text/xml; charset=utf-8\r\n".getBytes());
        stream.write(String.format("Host: %s\r\n", host).getBytes());
        stream.write(String.format("Content-Length: %d\r\n", m.length()).getBytes());
        stream.write("\r\n".getBytes());
        stream.write(m.getBytes());
    }
}
Difian/YalerAndroidLibrary/src/main/java/org/yaler/AcceptCallbackState.java
public enum AcceptCallbackState {
    Undefined,
    Accessible,
    Connected
}
Difian/DifianApp/src/main/java/com/sonova/difian/communication/messaging/Message.java
public abstract class Message
{
    private String _guid;

    public abstract MessageType getType();

    public final void setGuid(String guid)
    {
        _guid = guid;
    }

    public final String getGuid()
    {
        return _guid;
    }
}
Difian/DifianApp/src/main/java/com/sonova/difian/communication/messaging/MessageType.java
public enum MessageType
{
    USER_INFO_REQUEST, USER_INFO, SMART, SMART_REPLY, ALERT, ALERT_REPLY, FD_STATUS, HI_STATUS, TYPING
}
Difian/DifianApp/src/main/java/com/sonova/difian/communication/messaging/UserInfoMessage.java
public final class UserInfoMessage extends Message
{
    private String _name;
    private String _lang;
    private String _text;
    private String _image;

    UserInfoMessage(XmlPullParser parser) throws MessageReaderException, XmlPullParserException, IOException
    {
        parser.nextTag();
        parser.require(XmlPullParser.START_TAG, null, XmlConstants.USER_NAME);
        setName(parser.nextText());
        parser.require(XmlPullParser.END_TAG, null, XmlConstants.USER_NAME);
        while (parser.nextTag() == XmlPullParser.START_TAG)
        {
            if (XmlConstants.USER_LANG.equals(parser.getName()))
            {
                setLang(parser.nextText());
                parser.require(XmlPullParser.END_TAG, null, XmlConstants.USER_LANG);
            }
            else if (XmlConstants.USER_TEXT.equals(parser.getName()))
            {
                setText(parser.nextText());
                parser.require(XmlPullParser.END_TAG, null, XmlConstants.USER_TEXT);
            }
            else if (XmlConstants.IMAGE_BASE64.equals(parser.getName()))
            {
                setImage(parser.nextText());
                parser.require(XmlPullParser.END_TAG, null, XmlConstants.IMAGE_BASE64);
            }
            else
            {
                throw new MessageReaderException("Invalid tag in \"user_info\" <message>.");
            }
        }
    }

    @Override
    public MessageType getType()
    {
        return MessageType.USER_INFO;
    }

    public String getName()
    {
        return _name;
    }

    private void setName(String name)
    {
        _name = name;
    }

    @SuppressWarnings("WeakerAccess")
    public String getLang()
    {
        return _lang;
    }

    private void setLang(String lang)
    {
        _lang = lang;
    }

    @SuppressWarnings("WeakerAccess")
    public String getText()
    {
        return _text;
    }

    private void setText(String text)
    {
        _text = text;
    }

    public String getImage()
    {
        return _image;
    }

    private void setImage(String image)
    {
        _image = image;
    }
}
Difian/YalerAndroidLibrary/src/main/java/org/yaler/AcceptCallback.java
public interface AcceptCallback {
    void statusChanged(AcceptCallbackState state);
}
Difian/DifianApp/src/main/java/com/sonova/difian/communication/messaging/HiStatusMessage.java
public final class HiStatusMessage extends Message
{
    private final HiConnectionStatus[] _status = new HiConnectionStatus[2];
    private final HiMuteStatus[] _muteStatus = new HiMuteStatus[2];

    HiStatusMessage(XmlPullParser parser) throws MessageReaderException, XmlPullParserException, IOException
    {
        for (int i = 0; i < 2; i++)
        {
            parser.nextTag();
            parser.require(XmlPullParser.START_TAG, null, XmlConstants.HI);

            String sideString = parser.getAttributeValue(null, XmlConstants.SIDE);
            HiSide side;
            if (XmlConstants.LEFT.equals(sideString))
            {
                side = HiSide.LEFT;
            }
            else if (XmlConstants.RIGHT.equals(sideString))
            {
                side = HiSide.RIGHT;
            }
            else
            {
                throw new MessageReaderException(String.format("Invalid side in \"hi_status\" <message>: \"%s\".", sideString));
            }

            String statusString = parser.getAttributeValue(null, XmlConstants.STATUS);
            HiConnectionStatus status;
            if (XmlConstants.CONNECTED.equals(statusString))
            {
                status = HiConnectionStatus.CONNECTED;
            }
            else if (XmlConstants.DISCONNECTED.equals(statusString))
            {
                status = HiConnectionStatus.DISCONNECTED;
            }
            else if (XmlConstants.NOT_CONNECTED.equals(statusString))
            {
                status = HiConnectionStatus.NOT_CONNECTED;
            }
            else if (XmlConstants.UNDEFINED.equals(statusString))
            {
                status = HiConnectionStatus.UNDEFINED;
            }
            else
            {
                throw new MessageReaderException("Invalid status in \"hi_status\" <message>.");
            }

            String muteStatusString = parser.getAttributeValue(null, XmlConstants.MUTE_STATUS);
            HiMuteStatus muteStatus;
            if (XmlConstants.MUTED.equals(muteStatusString))
            {
                muteStatus = HiMuteStatus.MUTED;
            }
            else if (XmlConstants.UNMUTED.equals(muteStatusString))
            {
                muteStatus = HiMuteStatus.UNMUTED;
            }
            else if (XmlConstants.UNDEFINED.equals(muteStatusString))
            {
                muteStatus = HiMuteStatus.UNDEFINED;
            }
            else
            {
                throw new MessageReaderException("Invalid mute_status in \"hi_status\" <message>.");
            }

            setStatus(side, status);
            setMuteStatus(side, muteStatus);

            parser.nextTag();
            parser.require(XmlPullParser.END_TAG, null, "hi");
        }
    }

    @Override
    public MessageType getType()
    {
        return MessageType.HI_STATUS;
    }

    public HiConnectionStatus getStatus(HiSide side)
    {
        HiConnectionStatus result = _status[side.ordinal()];
        return (result != null) ? result : HiConnectionStatus.UNDEFINED;
    }

    private void setStatus(HiSide side, HiConnectionStatus status)
    {
        _status[side.ordinal()] = status;
    }

    public HiMuteStatus getMuteStatus(HiSide side)
    {
        HiMuteStatus result = _muteStatus[side.ordinal()];
        return (result != null) ? result : HiMuteStatus.UNDEFINED;
    }

    private void setMuteStatus(HiSide side, HiMuteStatus muteStatus)
    {
        _muteStatus[side.ordinal()] = muteStatus;
    }
}
Difian/DifianApp/src/main/java/com/sonova/difian/communication/messaging/SmartMessage.java
public final class SmartMessage extends Message
{
    private String _text;
    private String _image;
    private SmartMessageOptionsType _optionsType;
    private final List<SmartMessageOption> _options = new ArrayList<SmartMessageOption>();

    SmartMessage(XmlPullParser parser) throws MessageReaderException, XmlPullParserException, IOException
    {
        while (parser.nextTag() == XmlPullParser.START_TAG)
        {
            if ("text".equals(parser.getName()))
            {
                setText(parser.nextText());
                parser.require(XmlPullParser.END_TAG, null, "text");
            }
            else if (XmlConstants.IMAGE_BASE64.equals(parser.getName()))
            {
                setImage(parser.nextText());
                parser.require(XmlPullParser.END_TAG, null, XmlConstants.IMAGE_BASE64);
            }
            else if (XmlConstants.OPTIONS.equals(parser.getName()))
            {
                String optionsType = parser.getAttributeValue(null, "type");
                if ("button".equals(optionsType))
                {
                    setOptionsType(SmartMessageOptionsType.BUTTON);
                }
                else if ("radio".equals(optionsType))
                {
                    setOptionsType(SmartMessageOptionsType.RADIO);
                }
                else if ("checkbox".equals(optionsType))
                {
                    setOptionsType(SmartMessageOptionsType.CHECKBOX);
                }
                else if ("text".equals(optionsType))
                {
                    setOptionsType(SmartMessageOptionsType.TEXT);
                }
                else
                {
                    throw new MessageReaderException("Invalid type in \"smart\" <message> \"options\".");
                }

                while (parser.nextTag() == XmlPullParser.START_TAG)
                {
                    if (XmlConstants.OPTION.equals(parser.getName()))
                    {
                        String optionId = parser.getAttributeValue(null, "id");
                        String optionText = null;
                        String optionImage = null;
                        if ((optionId == null) || !optionId.matches("^[a-z0-9]+$"))
                        {
                            throw new MessageReaderException(String.format("Invalid option id: %s.", optionId));
                        }
                        while (parser.nextTag() == XmlPullParser.START_TAG)
                        {
                            if ("text".equals(parser.getName()))
                            {
                                optionText = parser.nextText();
                                parser.require(XmlPullParser.END_TAG, null, "text");
                            }
                            else if (XmlConstants.IMAGE_BASE64.equals(parser.getName()))
                            {
                                optionImage = parser.nextText();
                                parser.require(XmlPullParser.END_TAG, null, XmlConstants.IMAGE_BASE64);
                            }
                            else
                            {
                                throw new MessageReaderException("Invalid tag in \"smart\" <message> \"option\".");
                            }
                        }
                        if (optionText == null)
                        {
                            throw new MessageReaderException("No option text in \"smart\" <message>.");
                        }
                        addOption(new SmartMessageOption(optionId, optionText, optionImage));
                        parser.require(XmlPullParser.END_TAG, null, XmlConstants.OPTION);
                    }
                    else
                    {
                        throw new MessageReaderException("Invalid tag in \"smart\" <message> \"options\".");
                    }
                }
                parser.require(XmlPullParser.END_TAG, null, XmlConstants.OPTIONS);
            }
            else
            {
                throw new MessageReaderException("Invalid tag in \"smart\" <message>.");
            }
        }
        if (getText() == null)
        {
            throw new MessageReaderException("No text in \"smart\" <message>.");
        }
    }

    @Override
    public MessageType getType()
    {
        return MessageType.SMART;
    }

    @SuppressWarnings("WeakerAccess")
    public String getText()
    {
        return _text;
    }

    private void setText(String text)
    {
        _text = text;
    }

    public String getImage()
    {
        return _image;
    }

    private void setImage(String image)
    {
        _image = image;
    }

    public SmartMessageOptionsType getOptionsType()
    {
        return _optionsType;
    }

    private void setOptionsType(SmartMessageOptionsType optionsType)
    {
        _optionsType = optionsType;
    }

    public int getOptionsCount()
    {
        return _options.size();
    }

    public SmartMessageOption getOption(int id)
    {
        return _options.get(id);
    }

    private void addOption(SmartMessageOption option)
    {
        _options.add(option);
    }
}
Difian/DifianApp/src/main/java/com/sonova/difian/communication/messaging/HiSide.java
public enum HiSide
{
    LEFT, RIGHT
}
Difian/DifianApp/src/main/java/com/sonova/difian/communication/messaging/UserInfoRequestMessage.java
public final class UserInfoRequestMessage extends Message
{
    @Override
    public MessageType getType()
    {
        return MessageType.USER_INFO_REQUEST;
    }
}
Difian/DifianApp/src/main/java/com/sonova/difian/communication/messaging/StreamHelpers.java
public final class StreamHelpers
{
    private static final String TAG = StreamHelpers.class.getName();

    private StreamHelpers()
    {
    }

    public static String readLine(InputStream stream) throws IOException
    {
        if (stream == null)
        {
            throw new IllegalArgumentException("stream == null in readLine");
        }
        StringBuilder line = new StringBuilder();
        int b;
        do
        {
            b = stream.read();
            if (b != -1)
            {
                if (line.length() < 4096)
                {
                    line.append((char)b);
                } else {
                    throw new IOException("line too long", null);
                }
            }
            else
            {
                throw new IOException("unexpected end of stream", null);
            }
        } while (b != '\n');
        String result;
        if ((line.length() >= 2) && (line.charAt(line.length() - 2) == '\r')) {
            result = line.substring(0, line.length() - 2).trim();
        } else {
            result = line.substring(0, line.length() - 1).trim();
        }
        return result;
    }

    public static boolean consumeHttpHeaders (InputStream stream) throws IOException
    {
        boolean continueExpected = false;
        String line = readLine(stream);
        while (!line.equals(""))
        {
            Log.v(TAG, line);
            if (line.equals("Expect: 100-continue"))
            {
                continueExpected = true;
            }
            line = readLine(stream);
        }
        return continueExpected;
    }
}
Difian/DifianApp/src/main/java/com/sonova/difian/utilities/Contract.java
public final class Contract
{
    private Contract()
    {
    }

    @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
    public static void check(boolean condition)
    {
        if (!condition)
        {
            throw new AssertionError();
        }
    }
}
Difian/DifianApp/src/main/java/com/sonova/difian/communication/utilities/SocketHelpers.java
public final class SocketHelpers
{
    private SocketHelpers()
    {
    }

    public static Socket createSocket(SocketFactory factory, int soTimeout)
    {
        Socket result = null;
        try
        {
            result = factory.createSocket();
        }
        catch (IOException e)
        {
            Log.e(SocketHelpers.class.getSimpleName(), "Exception while creating socket.", e);
            throw new RuntimeException(e);
        }

        try
        {
            result.setSoTimeout(soTimeout);
        }
        catch (SocketException e)
        {
            Log.e(SocketHelpers.class.getSimpleName(), "Exception while setting socket timeout.", e);
            throw new RuntimeException(e);
        }

        try
        {
            result.setTcpNoDelay(true);
        }
        catch (SocketException e)
        {
            Log.e(SocketHelpers.class.getSimpleName(), "Exception while setting TcpNoDelay option.", e);
            throw new RuntimeException(e);
        }

        return result;
    }
}
Difian/DifianApp/src/main/java/com/sonova/difian/communication/messaging/SmartReplyMessage.java
public final class SmartReplyMessage extends Message
{
    private final String _reText;

    private final SmartMessage _reMessage;
    private final List<SmartMessageOption> _selection = new ArrayList<SmartMessageOption>();

    private final ChatMessage _message;

    public SmartReplyMessage(ChatMessage message)
    {
        Contract.check(message != null);
        _message = message;

        if (message.getMessage() == null)
        {
            _reText = message.getText();
            _reMessage = null;
        }
        else
        {
            _reText = null;
            _reMessage = message.getMessage();
            for (SmartMessageOption o : message.getSelection())
            {
                addSelection(o);
            }
        }
    }

    @Override
    public MessageType getType()
    {
        return MessageType.SMART_REPLY;
    }

    @SuppressWarnings("WeakerAccess")
    public void addSelection(SmartMessageOption selection)
    {
        Contract.check(_reMessage != null);

        _selection.add(selection);
    }

    public String getReText()
    {
        return _reText;
    }

    public SmartMessage getReMessage()
    {
        return _reMessage;
    }

    public int getSelectionCount()
    {
        return _selection.size();
    }

    public SmartMessageOption getSelection(int id)
    {
        return _selection.get(id);
    }

    public void ack()
    {
        _message.ack();
    }

    public void retry()
    {
        _message.retry();
    }

    public boolean isFailed()
    {
        return _message.isFailed();
    }
}
Difian/DifianApp/src/main/java/com/sonova/difian/communication/messaging/MessageReader.java
public final class MessageReader
{
    private static final String TAG = MessageReader.class.getName();

    private static final MessageReader _instance = new MessageReader();

    public static MessageReader getInstance()
    {
        return _instance;
    }

    private MessageReader()
    {
    }

    public Message parse(InputStream stream) throws XmlPullParserException, IOException, MessageReaderException
    {
        if (stream == null)
        {
            throw new IllegalArgumentException("stream == null in parse");
        }

        Message result;

        XmlPullParser parser = Xml.newPullParser();
        parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
        parser.setInput(stream, null);
        parser.nextTag();

        parser.require(XmlPullParser.START_TAG, null, XmlConstants.MESSAGE);
        String v = parser.getAttributeValue(null, "v");
        if ((v == null) || !"3".equals(v))
        {
            if ((v != null) && "0".equals(v))
            {
                // ignore, treat like v = 3
                Log.w(TAG, "Received <message> with \"v\" = \"0\".");
            }
            else
            {
                throw new MessageReaderException(String.format("Invalid format at <message> attribute \"v\" : %s.", v));
            }
        }
        String guid = parser.getAttributeValue(null, "guid");
        String type = parser.getAttributeValue(null, "type");
        if (type == null)
        {
            throw new MessageReaderException("\"type\" attribute does not exist in <message>.");
        }
        if ("user_info".equals(type))
        {
            result = new UserInfoMessage(parser);
        }
        else if ("hi_status".equals(type))
        {
            result = new HiStatusMessage(parser);
        }
        else if ("smart".equals(type))
        {
            result = new SmartMessage(parser);
        }
        else if ("typing".equals(type))
        {
            result = new TypingMessage(parser);
        }
        else
        {
            Log.w(TAG, String.format("Skipped message of type %s", type));
            throw new MessageReaderException(String.format("Unsupported message type while reading: %s", type));
        }
        result.setGuid(guid);

        return result;
    }
}
Difian/YalerAndroidLibrary/src/main/java/org/yaler/YalerSSLServerSocket.java
public final class YalerSSLServerSocket extends YalerServerSocket {
	public YalerSSLServerSocket(String host, int port, String id) {
		super(host, port, id);
	}

	public YalerSSLServerSocket(String host, int port, String id, Proxy proxy) {
		super(host, port, id, proxy);
	}

	@Override
	public YalerSocketFactory getYalerSocketFactory() {
		return YalerSSLSocketFactory.getInstance();
	}

	@Override
	public InetSocketAddress findLocation(InputStream stream) throws IOException {
		return StreamHelper.findLocation(stream, "https", 443);
	}
}
Difian/DifianApp/src/main/java/com/sonova/difian/communication/messaging/HiMuteStatus.java
public enum HiMuteStatus
{
    MUTED, UNMUTED, UNDEFINED
}
Difian/DifianApp/src/main/java/com/sonova/difian/communication/messaging/TypingMessage.java
public final class TypingMessage extends Message
{
    private final boolean _typing;

    TypingMessage(XmlPullParser parser) throws MessageReaderException
    {
        String typingString = parser.getAttributeValue(null, "is_saving");
        if (typingString == null)
        {
            throw new MessageReaderException("is_saving attribute not present in typing message");
        }
        _typing = Boolean.parseBoolean(typingString);
    }

    @Override
    public MessageType getType()
    {
        return MessageType.TYPING;
    }

    public boolean isTyping()
    {
        return _typing;
    }
}

Answer: TypingMessage m = (TypingMessage)message;