1807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns/* 2807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns * Copyright (C) 2009 The Android Open Source Project 3807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns * 4807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns * Licensed under the Apache License, Version 2.0 (the "License"); 5807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns * you may not use this file except in compliance with the License. 6807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns * You may obtain a copy of the License at 7807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns * 8807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns * http://www.apache.org/licenses/LICENSE-2.0 9807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns * 10807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns * Unless required by applicable law or agreed to in writing, software 11807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns * distributed under the License is distributed on an "AS IS" BASIS, 12807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns * See the License for the specific language governing permissions and 14807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns * limitations under the License. 15807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns */ 16807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns 17807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johnspackage com.example.android.wiktionary; 18807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns 19807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johnsimport com.example.android.wiktionary.SimpleWikiHelper.ApiException; 20807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johnsimport com.example.android.wiktionary.SimpleWikiHelper.ParseException; 21807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns 22807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johnsimport android.app.PendingIntent; 23807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johnsimport android.app.Service; 24807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johnsimport android.appwidget.AppWidgetManager; 25807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johnsimport android.appwidget.AppWidgetProvider; 26807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johnsimport android.content.ComponentName; 27807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johnsimport android.content.Context; 28807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johnsimport android.content.Intent; 29807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johnsimport android.content.res.Resources; 30807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johnsimport android.net.Uri; 31807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johnsimport android.os.IBinder; 32807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johnsimport android.text.format.Time; 33807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johnsimport android.util.Log; 34807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johnsimport android.widget.RemoteViews; 35807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns 36807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johnsimport java.util.regex.Matcher; 37807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johnsimport java.util.regex.Pattern; 38807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns 39807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns/** 40807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns * Define a simple widget that shows the Wiktionary "Word of the day." To build 41807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns * an update we spawn a background {@link Service} to perform the API queries. 42807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns */ 43807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johnspublic class WordWidget extends AppWidgetProvider { 44807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns @Override 45807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { 46807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns // To prevent any ANR timeouts, we perform the update in a service 47807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns context.startService(new Intent(context, UpdateService.class)); 48807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns } 49807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns 50807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns public static class UpdateService extends Service { 51807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns @Override 52807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns public void onStart(Intent intent, int startId) { 53807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns // Build the widget update for today 54807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns RemoteViews updateViews = buildUpdate(this); 55807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns 56807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns // Push update for this widget to the home screen 57807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns ComponentName thisWidget = new ComponentName(this, WordWidget.class); 58807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns AppWidgetManager manager = AppWidgetManager.getInstance(this); 59807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns manager.updateAppWidget(thisWidget, updateViews); 60807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns } 61807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns 62807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns @Override 63807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns public IBinder onBind(Intent intent) { 64807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns return null; 65807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns } 66807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns 67807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns /** 68807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns * Regular expression that splits "Word of the day" entry into word 69807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns * name, word type, and the first description bullet point. 70807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns */ 71807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns private static final String WOTD_PATTERN = 72807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns "(?s)\\{\\{wotd\\|(.+?)\\|(.+?)\\|([^#\\|]+).*?\\}\\}"; 73807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns 74807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns /** 75807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns * Build a widget update to show the current Wiktionary 76807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns * "Word of the day." Will block until the online API returns. 77807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns */ 78807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns public RemoteViews buildUpdate(Context context) { 79807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns // Pick out month names from resources 80807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns Resources res = context.getResources(); 81807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns String[] monthNames = res.getStringArray(R.array.month_names); 82807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns 83807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns // Find current month and day 84807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns Time today = new Time(); 85807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns today.setToNow(); 86807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns 87807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns // Build the page title for today, such as "March 21" 88807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns String pageName = res.getString(R.string.template_wotd_title, 89807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns monthNames[today.month], today.monthDay); 90807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns String pageContent = null; 91807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns 92807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns try { 93807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns // Try querying the Wiktionary API for today's word 94807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns SimpleWikiHelper.prepareUserAgent(context); 95807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns pageContent = SimpleWikiHelper.getPageContent(pageName, false); 96807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns } catch (ApiException e) { 97807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns Log.e("WordWidget", "Couldn't contact API", e); 98807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns } catch (ParseException e) { 99807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns Log.e("WordWidget", "Couldn't parse API response", e); 100807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns } 101807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns 102807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns RemoteViews views = null; 103807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns Matcher matcher = Pattern.compile(WOTD_PATTERN).matcher(pageContent); 104807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns if (matcher.find()) { 105807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns // Build an update that holds the updated widget contents 106807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns views = new RemoteViews(context.getPackageName(), R.layout.widget_word); 107807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns 108807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns String wordTitle = matcher.group(1); 109807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns views.setTextViewText(R.id.word_title, wordTitle); 110807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns views.setTextViewText(R.id.word_type, matcher.group(2)); 111807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns views.setTextViewText(R.id.definition, matcher.group(3).trim()); 112807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns 113807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns // When user clicks on widget, launch to Wiktionary definition page 114807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns String definePage = String.format("%s://%s/%s", ExtendedWikiHelper.WIKI_AUTHORITY, 115807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns ExtendedWikiHelper.WIKI_LOOKUP_HOST, wordTitle); 116807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns Intent defineIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(definePage)); 117807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns PendingIntent pendingIntent = PendingIntent.getActivity(context, 118807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns 0 /* no requestCode */, defineIntent, 0 /* no flags */); 119807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns views.setOnClickPendingIntent(R.id.widget, pendingIntent); 120807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns 121807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns } else { 122807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns // Didn't find word of day, so show error message 123807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns views = new RemoteViews(context.getPackageName(), R.layout.widget_message); 124807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns views.setTextViewText(R.id.message, context.getString(R.string.widget_error)); 125807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns } 126807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns return views; 127807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns } 128807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns } 129807dc15b095139d806eab5c9d4de4fbf692ed447Trevor Johns} 130