/*
* Author: Balch
* Created: 7/28/16 11:25 PM
*
* This file is part of MockTrade.
*
* MockTrade is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MockTrade is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with MockTrade. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright (C) 2016
*
*/
package com.balch.mocktrade.services;
import android.app.IntentService;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Bundle;
import android.util.Log;
import com.balch.android.app.framework.types.Money;
import com.balch.mocktrade.TradeModelProvider;
import com.balch.mocktrade.R;
import com.balch.mocktrade.account.Account;
import com.balch.mocktrade.investment.Investment;
import com.balch.mocktrade.portfolio.PortfolioModel;
import com.balch.mocktrade.portfolio.PortfolioSqliteModel;
import com.balch.mocktrade.settings.Settings;
import com.balch.mocktrade.shared.HighlightItem;
import com.balch.mocktrade.shared.PerformanceItem;
import com.balch.mocktrade.shared.WatchConfigItem;
import com.balch.mocktrade.shared.WearDataSync;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.PendingResult;
import com.google.android.gms.wearable.DataApi;
import com.google.android.gms.wearable.DataItem;
import com.google.android.gms.wearable.DataItemBuffer;
import com.google.android.gms.wearable.DataMap;
import com.google.android.gms.wearable.DataMapItem;
import com.google.android.gms.wearable.PutDataMapRequest;
import com.google.android.gms.wearable.Wearable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
public class WearSyncService extends IntentService implements
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener {
private static final String TAG = WearSyncService.class.getSimpleName();
private static final String EXTRA_SEND_PERF_ITEMS = "extra_send_perf_items";
private static final String EXTRA_SEND_CONFIG_ITEMS = "extra_send_config_items";
private static final String EXTRA_SEND_HIGHLIGHTS = "extra_send_highlights";
private static final String EXTRA_BROADCAST_ACCOUNT_ID = "extra_broadcast_account_id";
private static final long CONNECTION_TIME_OUT_MS = 1000;
private GoogleApiClient mGoogleApiClient;
public static Intent getIntent(Context context, boolean sendPerformanceItems, boolean sendHighlights,
boolean sendConfigItems, boolean broadcastAccountId) {
Intent intent = new Intent(context, WearSyncService.class);
intent.putExtra(EXTRA_SEND_PERF_ITEMS, sendPerformanceItems);
intent.putExtra(EXTRA_SEND_HIGHLIGHTS, sendHighlights);
intent.putExtra(EXTRA_SEND_CONFIG_ITEMS, sendConfigItems);
intent.putExtra(EXTRA_BROADCAST_ACCOUNT_ID, broadcastAccountId);
return intent;
}
public static Intent getIntent(Context context) {
return getIntent(context, true, true, false, false);
}
public WearSyncService() {
super(WearSyncService.class.getName());
}
@Override
public void onCreate() {
super.onCreate();
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(Wearable.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
}
@Override
protected void onHandleIntent(final Intent intent) {
mGoogleApiClient.blockingConnect(CONNECTION_TIME_OUT_MS, TimeUnit.MILLISECONDS);
if (mGoogleApiClient.isConnected()) {
try {
boolean sendPerformanceItems = intent.getBooleanExtra(EXTRA_SEND_PERF_ITEMS, true);
boolean sendHighlights = intent.getBooleanExtra(EXTRA_SEND_HIGHLIGHTS, true);
boolean sendConfigItems = intent.getBooleanExtra(EXTRA_SEND_CONFIG_ITEMS, false);
boolean broadcastAccountID = intent.getBooleanExtra(EXTRA_BROADCAST_ACCOUNT_ID, false);
Log.i(TAG, "WearSyncService onHandleIntent");
TradeModelProvider modelProvider = (TradeModelProvider) this.getApplication();
PortfolioModel portfolioModel = new PortfolioSqliteModel(modelProvider.getContext(),
modelProvider.getSqlConnection(), modelProvider.getNetworkRequestProvider(),
modelProvider.getSettings());
DataItemBuffer dataItems = Wearable.DataApi.getDataItems(mGoogleApiClient).await();
long accountId = getAccountIdFromDataBuffer(dataItems);
dataItems.release();
List<PerformanceItem> performanceItems = portfolioModel.getCurrentSnapshot(accountId);
if (broadcastAccountID) {
PerformanceItemUpdateBroadcaster.broadcast(getApplicationContext(), accountId, -1);
}
if (sendConfigItems) {
PutDataMapRequest putDataMapRequest = PutDataMapRequest.create(WearDataSync.PATH_WATCH_CONFIG_SYNC);
putDataMapRequest.getDataMap().putDataMapArrayList(WearDataSync.DATA_WATCH_CONFIG_DATA_ITEMS, getConfigDataMap(modelProvider.getSettings() ));
putDataMapRequest.setUrgent();
PendingResult<DataApi.DataItemResult> pendingResult = Wearable.DataApi.putDataItem(mGoogleApiClient, putDataMapRequest.asPutDataRequest());
pendingResult.await();
}
if (sendPerformanceItems && (performanceItems != null)) {
ArrayList<DataMap> dataMapList = new ArrayList<>(performanceItems.size());
for (PerformanceItem item : performanceItems) {
dataMapList.add(item.toDataMap());
}
PutDataMapRequest putDataMapRequest = PutDataMapRequest.create(WearDataSync.PATH_SNAPSHOT_SYNC);
putDataMapRequest.getDataMap().putDataMapArrayList(WearDataSync.DATA_SNAPSHOT_DAILY, dataMapList);
putDataMapRequest.setUrgent();
PendingResult<DataApi.DataItemResult> pendingResult = Wearable.DataApi.putDataItem(mGoogleApiClient, putDataMapRequest.asPutDataRequest());
pendingResult.await();
}
if (sendHighlights) {
boolean allAccounts = !modelProvider.getSettings().getBoolean(Settings.Key.PREF_HIDE_EXCLUDE_ACCOUNTS);
boolean demoMode = modelProvider.getSettings().getBoolean(Settings.Key.PREF_DEMO_MODE);
List<Account> accounts = portfolioModel.getAccounts(allAccounts);
if ((accounts != null) && (accounts.size() > 0)) {
PerformanceItem totalsPerformanceItem = new PerformanceItem(-1, new Date(), new Money(), new Money(), new Money());
ArrayList<DataMap> accountsDataMapList = new ArrayList<>();
Investment bestTotalPerformer = null;
Investment worstTotalPerformer = null;
Investment bestDayPerformer = null;
Investment worstDayPerformer = null;
Resources resources = getResources();
Date timestamp = new Date();
for (Account account : accounts) {
PerformanceItem performanceItem = new PerformanceItem(-1, new Date(), new Money(), new Money(), new Money());
List<Investment> investments = portfolioModel.getInvestments(account.getId());
if (allAccounts || !account.getExcludeFromTotals()) {
for (Investment investment : investments) {
if (bestDayPerformer != null) {
if (investment.getTodayChangePercent() > bestDayPerformer.getTodayChangePercent()) {
bestDayPerformer = investment;
}
} else {
bestDayPerformer = investment;
}
if (worstDayPerformer != null) {
if (investment.getTodayChangePercent() < worstDayPerformer.getTodayChangePercent()) {
worstDayPerformer = investment;
}
} else {
worstDayPerformer = investment;
}
if (bestTotalPerformer != null) {
if (investment.getTotalChangePercent() > bestTotalPerformer.getTotalChangePercent()) {
bestTotalPerformer = investment;
}
} else {
bestTotalPerformer = investment;
}
if (worstTotalPerformer != null) {
if (investment.getTotalChangePercent() < worstTotalPerformer.getTotalChangePercent()) {
worstTotalPerformer = investment;
}
} else {
worstTotalPerformer = investment;
}
}
}
performanceItem.aggregate(account.getPerformanceItem(investments, timestamp));
HighlightItem item = new HighlightItem(HighlightItem.HighlightType.TOTAL_ACCOUNT,
resources.getString(R.string.highlight_total_account), account.getName(),
performanceItem.getCostBasis(), performanceItem.getValue(),
performanceItem.getTodayChange(), performanceItem.getTotalChangePercent(),
account.getId());
accountsDataMapList.add(item.toDataMap());
if (demoMode || !account.getExcludeFromTotals()) {
totalsPerformanceItem.aggregate(account.getPerformanceItem(investments, timestamp));
}
}
ArrayList<DataMap> dataMapList = new ArrayList<>();
HighlightItem item = new HighlightItem(HighlightItem.HighlightType.TOTAL_OVERALL,
resources.getString(R.string.highlight_total_overall), "",
totalsPerformanceItem.getCostBasis(), totalsPerformanceItem.getValue(),
totalsPerformanceItem.getTodayChange(), -1, -1);
dataMapList.add(item.toDataMap());
dataMapList.addAll(accountsDataMapList);
if (bestTotalPerformer != null) {
dataMapList.add(getDataMapFromInvestment(HighlightItem.HighlightType.PERFORMER_BEST_TOTAL,
resources.getString(R.string.highlight_best_total), bestTotalPerformer));
}
if (bestDayPerformer != null) {
dataMapList.add(getDataMapFromInvestment(HighlightItem.HighlightType.PERFORMER_BEST_DAY,
resources.getString(R.string.highlight_best_day), bestDayPerformer));
}
if (worstTotalPerformer != null) {
dataMapList.add(getDataMapFromInvestment(HighlightItem.HighlightType.PERFORMER_WORST_TOTAL,
resources.getString(R.string.highlight_worst_total), worstTotalPerformer));
}
if (worstDayPerformer != null) {
dataMapList.add(getDataMapFromInvestment(HighlightItem.HighlightType.PERFORMER_WORST_DAY,
resources.getString(R.string.highlight_worst_day), worstDayPerformer));
}
PutDataMapRequest putDataMapRequest = PutDataMapRequest.create(WearDataSync.PATH_HIGHLIGHTS_SYNC);
putDataMapRequest.setUrgent();
putDataMapRequest.getDataMap().putDataMapArrayList(WearDataSync.DATA_HIGHLIGHTS, dataMapList);
PendingResult<DataApi.DataItemResult> pendingResult =
Wearable.DataApi.putDataItem(mGoogleApiClient, putDataMapRequest.asPutDataRequest());
pendingResult.await();
}
}
} catch (Exception ex) {
Log.e(TAG, "onHandleIntent exception", ex);
} finally {
mGoogleApiClient.disconnect();
}
} else {
Log.e(TAG, "Failed to connect to GoogleApiClient");
}
}
static long getAccountIdFromDataBuffer(DataItemBuffer dataItems) {
long accountId = -1;
for (int x = 0; x < dataItems.getCount(); x++) {
DataItem dataItem = dataItems.get(x);
if (dataItem.getUri().getPath().equals(WearDataSync.PATH_WATCH_FACE_ACCOUNT_ID)) {
accountId = getAccountIdFromDataBuffer(dataItem);
}
}
return accountId;
}
static long getAccountIdFromDataBuffer(DataItem dataItem) {
long accountId = -1;
if (dataItem.getUri().getPath().equals(WearDataSync.PATH_WATCH_FACE_ACCOUNT_ID)) {
DataMapItem dataMapItem = DataMapItem.fromDataItem(dataItem);
DataMap dataMap = dataMapItem.getDataMap();
accountId = dataMap.getLong(WearDataSync.DATA_WATCH_FACE_ACCOUNT_ID, -1);
}
return accountId;
}
private DataMap getDataMapFromInvestment(HighlightItem.HighlightType highlightType,
String description, Investment investment) {
long accountId = (investment.getAccount() != null) ? investment.getAccount().getId() : -1;
HighlightItem item = new HighlightItem(highlightType, description,
investment.getSymbol(), investment.getCostBasis(), investment.getValue(),
investment.getTodayChange(), investment.getTodayChangePercent(), accountId);
return item.toDataMap();
}
private ArrayList<DataMap> getConfigDataMap(Settings settings) {
Resources resources = getResources();
ArrayList<DataMap> dataMaps = new ArrayList<>();
dataMaps.add(newConfigItem(Settings.Key.PREF_TWENTY_FOUR_HOUR_DISPLAY, resources.getString(R.string.watch_config_item_twenty_four_hour_time), settings).toDataMap());
dataMaps.add(newConfigItem(Settings.Key.PREF_HIDE_EXCLUDE_ACCOUNTS, resources.getString(R.string.watch_config_item_hide_exclude_account), settings).toDataMap());
dataMaps.add(newConfigItem(Settings.Key.PREF_DEMO_MODE, resources.getString(R.string.watch_config_item_obfuscate_total), settings).toDataMap());
return dataMaps;
}
private WatchConfigItem newConfigItem(Settings.Key key, String description, Settings settings) {
return new WatchConfigItem(key.key(), description, settings.getBoolean(key));
}
@Override
public void onConnected(Bundle connectionHint) {
}
@Override
public void onConnectionSuspended(int cause) {
}
@Override
public void onConnectionFailed(ConnectionResult result) {
Log.e(TAG, "onConnectionFailed: "+result.toString());
}
}