/******************************************************************************* * Copyright (c) 2010 Denis Solonenko. * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Public License v2.0 * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * * Contributors: * Denis Solonenko - initial API and implementation ******************************************************************************/ package ru.orangesoftware.financisto2.activity; import android.app.PendingIntent; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProvider; import android.appwidget.AppWidgetProviderInfo; import android.content.ComponentName; import android.content.ContentUris; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.database.Cursor; import android.graphics.Color; import android.net.Uri; import android.util.Log; import android.widget.RemoteViews; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import ru.orangesoftware.financisto2.R; import ru.orangesoftware.financisto2.db.DatabaseAdapter; import ru.orangesoftware.financisto2.db.DatabaseAdapter_; import ru.orangesoftware.financisto2.model.Account; import ru.orangesoftware.financisto2.model.AccountType; import ru.orangesoftware.financisto2.model.CardIssuer; import ru.orangesoftware.financisto2.model.ElectronicPaymentType; import ru.orangesoftware.financisto2.utils.MyPreferences; import ru.orangesoftware.financisto2.utils.Utils; import ru.orangesoftware.financisto2.utils.Utils_; import ru.orangesoftware.orb.EntityManager; import static android.appwidget.AppWidgetManager.INVALID_APPWIDGET_ID; import static ru.orangesoftware.financisto2.utils.EnumUtils.selectEnum; public class AccountWidget extends AppWidgetProvider { private static final Uri CONTENT_URI = Uri.parse("content://ru.orangesoftware.financisto/accountwidget"); private static final String WIDGET_UPDATE_ACTION = "ru.orangesoftware.financisto2.UPDATE_WIDGET"; private static final String PREFS_NAME = "ru.orangesoftware.financisto2.activity.AccountWidget"; private static final String PREF_PREFIX_KEY = "prefix_"; public static final String WIDGET_ID = "widgetId"; public static void updateWidgets(Context context) { Class[] allWidgetProviders = new Class[]{AccountWidget.class, AccountWidget3x1.class, AccountWidget4x1.class}; List<Integer> allWidgetIds = new ArrayList<Integer>(); AppWidgetManager manager = AppWidgetManager.getInstance(context); for (Class widgetProvider : allWidgetProviders) { ComponentName thisWidget = new ComponentName(context, widgetProvider); int[] widgetIds = manager.getAppWidgetIds(thisWidget); for (int widgetId : widgetIds) { allWidgetIds.add(widgetId); } } int[] ids = new int[allWidgetIds.size()]; for (int i=0; i<ids.length; i++) { ids[i] = allWidgetIds.get(i); } updateWidgets(context, manager, ids, false); } @Override public void onReceive(Context context, Intent intent) { Log.d("FinancistoWidget", "onReceive intent "+intent); String action = intent.getAction(); if (WIDGET_UPDATE_ACTION.equals(action)) { int widgetId = intent.getIntExtra(WIDGET_ID, INVALID_APPWIDGET_ID); if (widgetId != INVALID_APPWIDGET_ID) { AppWidgetManager manager = AppWidgetManager.getInstance(context); updateWidgets(context, manager, new int[]{widgetId}, true); } } else { super.onReceive(context, intent); } } @Override public void onUpdate(Context context, AppWidgetManager manager, int[] appWidgetIds) { updateWidgets(context, manager, appWidgetIds, false); } private static void updateWidgets(Context context, AppWidgetManager manager, int[] appWidgetIds, boolean nextAccount) { Log.d("FinancistoWidget", "updateWidgets " + Arrays.toString(appWidgetIds) + " -> " + nextAccount); for (int id : appWidgetIds) { AppWidgetProviderInfo appWidgetInfo = manager.getAppWidgetInfo(id); if (appWidgetInfo != null) { int layoutId = appWidgetInfo.initialLayout; if (MyPreferences.isWidgetEnabled(context)) { long accountId = loadAccountForWidget(context, id); Class providerClass = getProviderClass(appWidgetInfo); Log.d("FinancistoWidget", "using provider " + providerClass); RemoteViews remoteViews = nextAccount || accountId == -1 ? buildUpdateForNextAccount(context, id, layoutId, providerClass, accountId) : buildUpdateForCurrentAccount(context, id, layoutId, providerClass, accountId); manager.updateAppWidget(id, remoteViews); } else { manager.updateAppWidget(id, noDataUpdate(context, layoutId)); } } } } private static Class getProviderClass(AppWidgetProviderInfo appWidgetInfo) { Class widgetProviderClass = AccountWidget.class; try { widgetProviderClass = Class.forName(appWidgetInfo.provider.getClassName()); } catch (ClassNotFoundException e) { } return widgetProviderClass; } private static long loadAccountForWidget(Context context, int widgetId) { SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, 0); return prefs.getLong(PREF_PREFIX_KEY + widgetId, -1); } private static void saveAccountForWidget(Context context, int widgetId, long accountId) { SharedPreferences.Editor prefs = context.getSharedPreferences(PREFS_NAME, 0).edit(); prefs.putLong(PREF_PREFIX_KEY + widgetId, accountId); prefs.apply(); } private static RemoteViews updateWidgetFromAccount(Context context, int widgetId, int layoutId, Class providerClass, Account a) { RemoteViews updateViews = new RemoteViews(context.getPackageName(), layoutId); updateViews.setTextViewText(R.id.line1, a.title); AccountType type = AccountType.valueOf(a.type); if (type.isCard && a.cardIssuer != null) { CardIssuer cardIssuer = selectEnum(CardIssuer.class, a.cardIssuer, CardIssuer.VISA); updateViews.setImageViewResource(R.id.account_icon, cardIssuer.iconId); } else if (type.isElectronic && a.cardIssuer != null) { ElectronicPaymentType paymentType = selectEnum(ElectronicPaymentType.class, a.cardIssuer, ElectronicPaymentType.PAYPAL); updateViews.setImageViewResource(R.id.account_icon, paymentType.iconId); } else { updateViews.setImageViewResource(R.id.account_icon, type.iconId); } long amount = a.totalAmount; updateViews.setTextViewText(R.id.note, Utils.amountToString(a.currency, amount)); Utils u = Utils_.getInstance_(context); int amountColor = u.getAmountColor(amount); updateViews.setTextColor(R.id.note, amountColor); addScrollOnClick(context, updateViews, widgetId, providerClass); addTapOnClick(context, updateViews); addButtonsClick(context, updateViews); saveAccountForWidget(context, widgetId, a.id); return updateViews; } private static void addScrollOnClick(Context context, RemoteViews updateViews, int widgetId, Class providerClass) { Uri widgetUri = ContentUris.withAppendedId(CONTENT_URI, widgetId); Intent intent = new Intent(WIDGET_UPDATE_ACTION, widgetUri, context, providerClass); intent.putExtra(WIDGET_ID, widgetId); intent.putExtra("ts", System.currentTimeMillis()); PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); updateViews.setOnClickPendingIntent(R.id.account_icon, pendingIntent); } private static void addTapOnClick(Context context, RemoteViews updateViews) { Intent intent = MainActivity2_.intent(context).get(); PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0); updateViews.setOnClickPendingIntent(R.id.layout, pendingIntent); } private static void addButtonsClick(Context context, RemoteViews updateViews) { Intent intent = TransactionActivity_.intent(context).get(); PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0); updateViews.setOnClickPendingIntent(R.id.add_transaction, pendingIntent); intent = TransferActivity_.intent(context).get(); pendingIntent = PendingIntent.getActivity(context, 0, intent, 0); updateViews.setOnClickPendingIntent(R.id.add_transfer, pendingIntent); } private static RemoteViews buildUpdateForCurrentAccount(Context context, int widgetId, int layoutId, Class providerClass, long accountId) { DatabaseAdapter db = DatabaseAdapter_.getInstance_(context); try { Account a = db.getAccount(accountId); if (a != null) { Log.d("FinancistoWidget", "buildUpdateForCurrentAccount building update for "+widgetId+" -> "+accountId); return updateWidgetFromAccount(context, widgetId, layoutId, providerClass, a); } else { Log.d("FinancistoWidget", "buildUpdateForCurrentAccount not found "+widgetId+" -> "+accountId); return buildUpdateForNextAccount(context, widgetId, layoutId, providerClass, -1); } } catch (Exception ex) { return errorUpdate(context); } } private static RemoteViews buildUpdateForNextAccount(Context context, int widgetId, int layoutId, Class providerClass, long accountId) { DatabaseAdapter db = DatabaseAdapter_.getInstance_(context); try { Cursor c = db.getAllActiveAccounts(); try { int count = c.getCount(); if (count > 0) { Log.d("FinancistoWidget", "buildUpdateForNextAccount "+widgetId+" -> "+accountId); if (count == 1 || accountId == -1) { if (c.moveToNext()) { Account a = EntityManager.loadFromCursor(c, Account.class); return updateWidgetFromAccount(context, widgetId, layoutId, providerClass, a); } } else { boolean found = false; while (c.moveToNext()) { Account a = EntityManager.loadFromCursor(c, Account.class); if (a.id == accountId) { found = true; Log.d("FinancistoWidget", "buildUpdateForNextAccount found -> "+accountId); } else { if (found) { Log.d("FinancistoWidget", "buildUpdateForNextAccount building update for -> "+a.id); return updateWidgetFromAccount(context, widgetId, layoutId, providerClass, a); } } } c.moveToFirst(); Account a = EntityManager.loadFromCursor(c, Account.class); Log.d("FinancistoWidget", "buildUpdateForNextAccount not found, taking the first one -> "+a.id); return updateWidgetFromAccount(context, widgetId, layoutId, providerClass, a); } } return noDataUpdate(context, layoutId); } finally { c.close(); } } catch (Exception ex) { return errorUpdate(context); } } private static RemoteViews errorUpdate(Context context) { RemoteViews updateViews = new RemoteViews(context.getPackageName(), R.layout.widget_2x1_no_data); updateViews.setTextViewText(R.id.line1, "Error!"); updateViews.setTextColor(R.id.line1, Color.RED); return updateViews; } private static RemoteViews noDataUpdate(Context context, int layoutId) { int noDataLayoutId = getNoDataLayout(layoutId); return new RemoteViews(context.getPackageName(), noDataLayoutId); } private static int getNoDataLayout(int layoutId) { switch (layoutId) { case R.layout.widget_3x1: return R.layout.widget_3x1_no_data; case R.layout.widget_4x1: return R.layout.widget_4x1_no_data; default: return R.layout.widget_2x1_no_data; } } }