Java tutorial
/* * Copyright (c) 2010-2011 Gsta Jonasson. All Rights Reserved. * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.openbitcoinwidget; import android.app.PendingIntent; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProvider; import android.appwidget.AppWidgetProviderInfo; import android.content.Context; import android.content.Intent; import android.graphics.Color; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.util.Log; import android.widget.RemoteViews; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.methods.HttpGet; import org.openbitcoinwidget.net.HttpManager; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.math.BigDecimal; import java.text.SimpleDateFormat; import java.util.Date; import static org.openbitcoinwidget.CurrencyConversion.DigitalCurrency.LITECOIN; /** * */ public class WidgetProvider extends AppWidgetProvider { public static final String LOG_TAG = ""; private static final SimpleDateFormat dateFormat = new SimpleDateFormat("E HH:mm"); private static final int DATA_IS_CONSIDERED_OLD_AFTER_MINUTES = 60; private enum WidgetColor { Warning, StartValue, Increase, Decrease, Normal } @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { updateAppWidgetAsync(context, appWidgetManager, appWidgetIds); } public static void updateAppWidgetAsync(final Context context, AppWidgetManager appWidgetManager, int appWidgetId) { updateAppWidgetAsync(context, appWidgetManager, new int[] { appWidgetId }); } public static void updateAppWidgetAsync(final Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { new UpdateAsyncTask(context, appWidgetManager, appWidgetIds).execute(); } private static void updateAppWidget(final Context context, AppWidgetManager appWidgetManager, int appWidgetId) { WidgetPreferences preferences = PreferencesActivity.getWidgetPreferences(context, appWidgetId); if (preferences == null) { // Don't do anything unless the rate service has been chosen. // Show a "please remove this widget and add a new one" appWidgetManager.updateAppWidget(appWidgetId, new RemoteViews(context.getPackageName(), R.layout.appwidget_replace_me)); return; } boolean isOnLockScreen = isWidgetShownOnLockScreen(appWidgetManager, appWidgetId); RemoteViews views; if (isOnLockScreen) { views = new RemoteViews(context.getPackageName(), R.layout.appwidget_provider_lock_screen); } else { views = new RemoteViews(context.getPackageName(), R.layout.appwidget_provider); } Intent clickIntent = new Intent(context, GraphPopupActivity.class); clickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); clickIntent.setAction("dummyAction"); // Needed to get the extra variables included in the call // Note: the appWidgetId needs to be sent in the pendingIntent as request code, otherwise only ONE // cached intent will be used for all widget instances! PendingIntent pendingIntent = PendingIntent.getActivity(context, appWidgetId, clickIntent, 0); views.setOnClickPendingIntent(R.id.appwidget_box, pendingIntent); views.setTextViewText(R.id.appwidget_service_name, preferences.getRateService().getName()); DataOpenHelper dbHelper = new DataOpenHelper(context); TickerData prevData = dbHelper.getLastTickerData(preferences); TickerData newData; String latestQuote = getLatestQuote(preferences); if (latestQuote != null && !latestQuote.equals("")) { newData = preferences.getRateService().parseJSON(latestQuote); newData.setCurrencyConversion(preferences.getCurrencyConversion()); storeLastValueIfNotNull(dbHelper, newData); updateViews(views, prevData, newData, preferences); } else if (prevData != null) { newData = prevData; updateViews(views, prevData, newData, preferences); } else { updateViewsWithError(views, preferences); } appWidgetManager.updateAppWidget(appWidgetId, views); } private static boolean isWidgetShownOnLockScreen(AppWidgetManager appWidgetManager, int appWidgetId) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { // The below functionality was introduced in API version 16 (Jelly Bean) Bundle myOptions = appWidgetManager.getAppWidgetOptions(appWidgetId); // Get the value of OPTION_APPWIDGET_HOST_CATEGORY int category = myOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, -1); // If the value is WIDGET_CATEGORY_KEYGUARD, it's a lockscreen widget return category == AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD; } return false; } private static void updateViews(RemoteViews views, TickerData prevData, TickerData newData, WidgetPreferences preferences) { String updated = "@ " + dateFormat.format(newData.getTimestamp()); String lastRounded = round(newData.getLast(), preferences); String lowRounded = round(newData.getLow(), preferences); String highRounded = round(newData.getHigh(), preferences); views.setTextViewText(R.id.appwidget_last, preferences.getCurrencyConversion().symbol + lastRounded); views.setTextColor(R.id.appwidget_updated, getColor(preferences.getColorMode(), WidgetColor.Normal)); if (newData.getTimestamp().before(getDateMinutesAgo(DATA_IS_CONSIDERED_OLD_AFTER_MINUTES))) { // Data is old, show it by "old" and "warning" colors views.setTextColor(R.id.appwidget_last, Color.GRAY); views.setTextColor(R.id.appwidget_updated, getColor(preferences.getColorMode(), WidgetColor.Warning)); } else if (prevData != null) { // We have previous data, compare to get the color views.setTextColor(R.id.appwidget_last, getColorFromValueChange(prevData.getLast(), newData.getLast(), preferences.getColorMode())); } else { // No previous data, set standard color views.setTextColor(R.id.appwidget_last, getColor(preferences.getColorMode(), WidgetColor.StartValue)); } views.setTextViewText(R.id.appwidget_high, highRounded); views.setTextViewText(R.id.appwidget_low, lowRounded); views.setTextViewText(R.id.appwidget_updated, updated); // Set Litecoin logo if that is the chosen currency. switch (preferences.getCurrencyConversion().digitalCurrency) { case LITECOIN: views.setImageViewResource(R.id.appwidget_logo, R.drawable.logo); break; // The Bitcoin logo is default in the layout. } } private static void updateViewsWithError(RemoteViews views, WidgetPreferences preferences) { views.setTextViewText(R.id.appwidget_last, "N/A"); views.setTextColor(R.id.appwidget_last, getColor(preferences.getColorMode(), WidgetColor.Warning)); views.setTextViewText(R.id.appwidget_updated, "@ " + dateFormat.format(new Date())); views.setTextColor(R.id.appwidget_updated, getColor(preferences.getColorMode(), WidgetColor.Normal)); } private static int getColor(ColorMode colorMode, WidgetColor widgetColor) { if (colorMode.equals(ColorMode.Default)) { switch (widgetColor) { case Warning: return Color.parseColor("#ff3030"); case StartValue: return Color.YELLOW; case Normal: return Color.LTGRAY; case Increase: return Color.GREEN; case Decrease: return Color.parseColor("#ff3030"); default: throw new IllegalArgumentException("No color defined for " + widgetColor); } } else if (colorMode.equals(ColorMode.Grayscale)) { switch (widgetColor) { case Warning: return Color.WHITE; case StartValue: return Color.LTGRAY; case Normal: return Color.LTGRAY; case Increase: return Color.WHITE; case Decrease: return Color.GRAY; default: throw new IllegalArgumentException("No color defined for " + widgetColor); } } else { throw new IllegalArgumentException("No color mode defined for " + colorMode); } } private static Date getDateMinutesAgo(int minutes) { return new Date(System.currentTimeMillis() - (minutes * 60 * 1000)); } private static String round(Double value, WidgetPreferences preferences) { if (value == null) { return "N/A"; } value *= preferences.getCurrencyUnit().scale; int decimals; if (value >= 10) { decimals = 0; } else if (value >= 1) { decimals = 1; } else if (value >= .1) { decimals = 2; } else if (value >= .01) { decimals = 3; } else { decimals = 4; } return BigDecimal.valueOf(value).setScale(decimals, BigDecimal.ROUND_HALF_UP).toString(); } private static int getColorFromValueChange(Double prevValue, Double nowValue, ColorMode colorMode) { if (prevValue == null || nowValue == null || prevValue.equals(nowValue)) { return getColor(colorMode, WidgetColor.StartValue); } else if (prevValue < nowValue) { return getColor(colorMode, WidgetColor.Increase); } else { return getColor(colorMode, WidgetColor.Decrease); } } private static void storeLastValueIfNotNull(DataOpenHelper dbHelper, TickerData data) { if (data != null) { dbHelper.storeTickerData(data); dbHelper.cleanUp(); } } private static String getLatestQuote(WidgetPreferences preferences) { HttpGet httpget = new HttpGet( preferences.getRateService().getTickerUrl(preferences.getCurrencyConversion())); HttpResponse response; try { response = HttpManager.execute(httpget); HttpEntity entity = response.getEntity(); String responseString = ""; if (entity != null) { InputStream inStream = entity.getContent(); responseString = convertStreamToString(inStream); inStream.close(); } return responseString; } catch (ClientProtocolException e) { Log.e(LOG_TAG, "Error when getting JSON", e); } catch (IOException e) { Log.e(LOG_TAG, "Error when getting JSON: " + e.getMessage()); } return null; } private static String convertStreamToString(InputStream inStream) { BufferedReader rd = new BufferedReader(new InputStreamReader(inStream), 4096); String line; StringBuilder sb = new StringBuilder(); try { while ((line = rd.readLine()) != null) { sb.append(line); } rd.close(); } catch (IOException e) { Log.e(LOG_TAG, "Error when converting inputstream to string", e); } return sb.toString(); } @Override public void onDeleted(Context context, int[] appWidgetIds) { // When the user deletes the widget, delete the preference associated with it. for (int appWidgetId : appWidgetIds) { PreferencesActivity.deletePrefs(context, appWidgetId); } } public static void updateAppWidgetWithWaitMessage(Context context, AppWidgetManager appWidgetManager, int appWidgetId) { appWidgetManager.updateAppWidget(appWidgetId, new RemoteViews(context.getPackageName(), R.layout.appwidget_loading)); } /** * Class for updating the widget(s) asynchronously (not on main thread). */ private static class UpdateAsyncTask extends AsyncTask<Void, Void, Void> { private Context context; private AppWidgetManager appWidgetManager; private int[] appWidgetIds; public UpdateAsyncTask(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { this.context = context; this.appWidgetManager = appWidgetManager; this.appWidgetIds = appWidgetIds; } @Override protected Void doInBackground(Void... bla) { for (int appWidgetId : appWidgetIds) { updateAppWidget(context, appWidgetManager, appWidgetId); } return null; } } }