/* * Copyright (C) 2010 The Android Open Source Project * * 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 com.android.protips; import android.app.PendingIntent; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProvider; import android.content.ComponentName; import android.content.Context; import android.content.SharedPreferences; import android.content.Intent; import android.content.res.Resources; import android.os.Handler; import android.os.HandlerThread; import android.util.Log; import android.view.View; import android.widget.RemoteViews; import java.util.regex.Matcher; import java.util.regex.Pattern; /** Mister Widget appears on your home screen to provide helpful tips. */ public class ProtipWidget extends AppWidgetProvider { public static final String ACTION_NEXT_TIP = "com.android.misterwidget.NEXT_TIP"; public static final String ACTION_POKE = "com.android.misterwidget.HEE_HEE"; public static final String EXTRA_TIMES = "times"; public static final String PREFS_NAME = "Protips"; public static final String PREFS_TIP_NUMBER = "widget_tip"; public static final String PREFS_TIP_SET = "widget_tip_set"; private static final Pattern sNewlineRegex = Pattern.compile(" *\\n *"); private static final Pattern sDrawableRegex = Pattern.compile(" *@(drawable/[a-z0-9_]+) *"); private static Handler mAsyncHandler; static { HandlerThread thr = new HandlerThread("ProtipWidget async"); thr.start(); mAsyncHandler = new Handler(thr.getLooper()); } // initial appearance: eyes closed, no bubble private int mIconRes = R.drawable.droidman_open; private int mMessage = 0; private int mTipSet = 0; private AppWidgetManager mWidgetManager = null; private int[] mWidgetIds; private Context mContext; private CharSequence[] mTips; private void setup(Context context) { mContext = context; mWidgetManager = AppWidgetManager.getInstance(context); mWidgetIds = mWidgetManager.getAppWidgetIds(new ComponentName(context, ProtipWidget.class)); SharedPreferences pref = context.getSharedPreferences(PREFS_NAME, 0); mMessage = pref.getInt(PREFS_TIP_NUMBER, 0); mTipSet = pref.getInt(PREFS_TIP_SET, 0); mTips = context.getResources().getTextArray(mTipSet == 1 ? R.array.tips2 : R.array.tips); if (mTips != null) { if (mMessage >= mTips.length) mMessage = 0; } else { mMessage = -1; } } public void goodmorning() { mMessage = -1; try { setIcon(R.drawable.droidman_down_closed); Thread.sleep(500); setIcon(R.drawable.droidman_down_open); Thread.sleep(200); setIcon(R.drawable.droidman_down_closed); Thread.sleep(100); setIcon(R.drawable.droidman_down_open); Thread.sleep(600); } catch (InterruptedException ex) { } mMessage = 0; mIconRes = R.drawable.droidman_open; refresh(); } @Override public void onReceive(final Context context, final Intent intent) { final PendingResult result = goAsync(); Runnable worker = new Runnable() { @Override public void run() { onReceiveAsync(context, intent); result.finish(); } }; mAsyncHandler.post(worker); } void onReceiveAsync(Context context, Intent intent) { setup(context); Resources res = mContext.getResources(); mTips = res.getTextArray(mTipSet == 1 ? R.array.tips2 : R.array.tips); if (intent.getAction().equals(ACTION_NEXT_TIP)) { mMessage = getNextMessageIndex(); SharedPreferences.Editor pref = context.getSharedPreferences(PREFS_NAME, 0).edit(); pref.putInt(PREFS_TIP_NUMBER, mMessage); pref.apply(); refresh(); } else if (intent.getAction().equals(ACTION_POKE)) { blink(intent.getIntExtra(EXTRA_TIMES, 1)); } else if (intent.getAction().equals(AppWidgetManager.ACTION_APPWIDGET_ENABLED)) { goodmorning(); } else if (intent.getAction().equals("android.provider.Telephony.SECRET_CODE")) { Log.d("Protips", "ACHIEVEMENT UNLOCKED"); mTipSet = 1 - mTipSet; mMessage = 0; SharedPreferences.Editor pref = context.getSharedPreferences(PREFS_NAME, 0).edit(); pref.putInt(PREFS_TIP_NUMBER, mMessage); pref.putInt(PREFS_TIP_SET, mTipSet); pref.apply(); mContext.startActivity( new Intent(Intent.ACTION_MAIN) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TOP) .addCategory(Intent.CATEGORY_HOME)); final Intent bcast = new Intent(context, ProtipWidget.class); bcast.setAction(ACTION_POKE); bcast.putExtra(EXTRA_TIMES, 3); mContext.sendBroadcast(bcast); } else { mIconRes = R.drawable.droidman_open; refresh(); } } private void refresh() { RemoteViews rv = buildUpdate(mContext); for (int i : mWidgetIds) { mWidgetManager.updateAppWidget(i, rv); } } private void setIcon(int resId) { mIconRes = resId; refresh(); } private int getNextMessageIndex() { return (mMessage + 1) % mTips.length; } private void blink(int blinks) { // don't blink if no bubble showing or if goodmorning() is happening if (mMessage < 0) return; setIcon(R.drawable.droidman_closed); try { Thread.sleep(100); while (0<--blinks) { setIcon(R.drawable.droidman_open); Thread.sleep(200); setIcon(R.drawable.droidman_closed); Thread.sleep(100); } } catch (InterruptedException ex) { } setIcon(R.drawable.droidman_open); } public RemoteViews buildUpdate(Context context) { RemoteViews updateViews = new RemoteViews( context.getPackageName(), R.layout.widget); // Action for tap on bubble Intent bcast = new Intent(context, ProtipWidget.class); bcast.setAction(ACTION_NEXT_TIP); PendingIntent pending = PendingIntent.getBroadcast( context, 0, bcast, PendingIntent.FLAG_UPDATE_CURRENT); updateViews.setOnClickPendingIntent(R.id.tip_bubble, pending); // Action for tap on android bcast = new Intent(context, ProtipWidget.class); bcast.setAction(ACTION_POKE); bcast.putExtra(EXTRA_TIMES, 1); pending = PendingIntent.getBroadcast( context, 0, bcast, PendingIntent.FLAG_UPDATE_CURRENT); updateViews.setOnClickPendingIntent(R.id.bugdroid, pending); // Tip bubble text if (mMessage >= 0) { String[] parts = sNewlineRegex.split(mTips[mMessage], 2); String title = parts[0]; String text = parts.length > 1 ? parts[1] : ""; // Look for a callout graphic referenced in the text Matcher m = sDrawableRegex.matcher(text); if (m.find()) { String imageName = m.group(1); int resId = context.getResources().getIdentifier( imageName, null, context.getPackageName()); updateViews.setImageViewResource(R.id.tip_callout, resId); updateViews.setViewVisibility(R.id.tip_callout, View.VISIBLE); text = m.replaceFirst(""); } else { updateViews.setImageViewResource(R.id.tip_callout, 0); updateViews.setViewVisibility(R.id.tip_callout, View.GONE); } updateViews.setTextViewText(R.id.tip_message, text); updateViews.setTextViewText(R.id.tip_header, title); updateViews.setTextViewText(R.id.tip_footer, context.getResources().getString( R.string.pager_footer, (1+mMessage), mTips.length)); updateViews.setViewVisibility(R.id.tip_bubble, View.VISIBLE); } else { updateViews.setViewVisibility(R.id.tip_bubble, View.INVISIBLE); } updateViews.setImageViewResource(R.id.bugdroid, mIconRes); return updateViews; } }