Home | History | Annotate | Download | only in protips
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.protips;
     18 
     19 import android.app.PendingIntent;
     20 import android.appwidget.AppWidgetManager;
     21 import android.appwidget.AppWidgetProvider;
     22 import android.content.ComponentName;
     23 import android.content.Context;
     24 import android.content.SharedPreferences;
     25 import android.content.Intent;
     26 import android.content.res.Resources;
     27 import android.os.Handler;
     28 import android.os.HandlerThread;
     29 import android.util.Log;
     30 import android.view.View;
     31 import android.widget.RemoteViews;
     32 
     33 import java.util.regex.Matcher;
     34 import java.util.regex.Pattern;
     35 
     36 /** Mister Widget appears on your home screen to provide helpful tips. */
     37 public class ProtipWidget extends AppWidgetProvider {
     38     public static final String ACTION_NEXT_TIP = "com.android.misterwidget.NEXT_TIP";
     39     public static final String ACTION_POKE = "com.android.misterwidget.HEE_HEE";
     40 
     41     public static final String EXTRA_TIMES = "times";
     42 
     43     public static final String PREFS_NAME = "Protips";
     44     public static final String PREFS_TIP_NUMBER = "widget_tip";
     45     public static final String PREFS_TIP_SET = "widget_tip_set";
     46 
     47     private static final Pattern sNewlineRegex = Pattern.compile(" *\\n *");
     48     private static final Pattern sDrawableRegex = Pattern.compile(" *@(drawable/[a-z0-9_]+) *");
     49 
     50     private static Handler mAsyncHandler;
     51     static {
     52         HandlerThread thr = new HandlerThread("ProtipWidget async");
     53         thr.start();
     54         mAsyncHandler = new Handler(thr.getLooper());
     55     }
     56 
     57     // initial appearance: eyes closed, no bubble
     58     private int mIconRes = R.drawable.droidman_open;
     59     private int mMessage = 0;
     60     private int mTipSet = 0;
     61 
     62     private AppWidgetManager mWidgetManager = null;
     63     private int[] mWidgetIds;
     64     private Context mContext;
     65 
     66     private CharSequence[] mTips;
     67 
     68     private void setup(Context context) {
     69         mContext = context;
     70         mWidgetManager = AppWidgetManager.getInstance(context);
     71         mWidgetIds = mWidgetManager.getAppWidgetIds(new ComponentName(context, ProtipWidget.class));
     72 
     73         SharedPreferences pref = context.getSharedPreferences(PREFS_NAME, 0);
     74         mMessage = pref.getInt(PREFS_TIP_NUMBER, 0);
     75         mTipSet = pref.getInt(PREFS_TIP_SET, 0);
     76 
     77         mTips = context.getResources().getTextArray(mTipSet == 1 ? R.array.tips2 : R.array.tips);
     78 
     79         if (mTips != null) {
     80             if (mMessage >= mTips.length) mMessage = 0;
     81         } else {
     82             mMessage = -1;
     83         }
     84     }
     85 
     86     public void goodmorning() {
     87         mMessage = -1;
     88         try {
     89             setIcon(R.drawable.droidman_down_closed);
     90             Thread.sleep(500);
     91             setIcon(R.drawable.droidman_down_open);
     92             Thread.sleep(200);
     93             setIcon(R.drawable.droidman_down_closed);
     94             Thread.sleep(100);
     95             setIcon(R.drawable.droidman_down_open);
     96             Thread.sleep(600);
     97         } catch (InterruptedException ex) {
     98         }
     99         mMessage = 0;
    100         mIconRes = R.drawable.droidman_open;
    101         refresh();
    102     }
    103 
    104     @Override
    105     public void onReceive(final Context context, final Intent intent) {
    106         final PendingResult result = goAsync();
    107         Runnable worker = new Runnable() {
    108             @Override
    109             public void run() {
    110                 onReceiveAsync(context, intent);
    111                 result.finish();
    112             }
    113         };
    114         mAsyncHandler.post(worker);
    115     }
    116 
    117     void onReceiveAsync(Context context, Intent intent) {
    118         setup(context);
    119 
    120         Resources res = mContext.getResources();
    121         mTips = res.getTextArray(mTipSet == 1 ? R.array.tips2 : R.array.tips);
    122 
    123         if (intent.getAction().equals(ACTION_NEXT_TIP)) {
    124             mMessage = getNextMessageIndex();
    125             SharedPreferences.Editor pref = context.getSharedPreferences(PREFS_NAME, 0).edit();
    126             pref.putInt(PREFS_TIP_NUMBER, mMessage);
    127             pref.apply();
    128             refresh();
    129         } else if (intent.getAction().equals(ACTION_POKE)) {
    130             blink(intent.getIntExtra(EXTRA_TIMES, 1));
    131         } else if (intent.getAction().equals(AppWidgetManager.ACTION_APPWIDGET_ENABLED)) {
    132             goodmorning();
    133         } else if (intent.getAction().equals("android.provider.Telephony.SECRET_CODE")) {
    134             Log.d("Protips", "ACHIEVEMENT UNLOCKED");
    135             mTipSet = 1 - mTipSet;
    136             mMessage = 0;
    137 
    138             SharedPreferences.Editor pref = context.getSharedPreferences(PREFS_NAME, 0).edit();
    139             pref.putInt(PREFS_TIP_NUMBER, mMessage);
    140             pref.putInt(PREFS_TIP_SET, mTipSet);
    141             pref.apply();
    142 
    143             mContext.startActivity(
    144                 new Intent(Intent.ACTION_MAIN)
    145                     .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TOP)
    146                     .addCategory(Intent.CATEGORY_HOME));
    147 
    148             final Intent bcast = new Intent(context, ProtipWidget.class);
    149             bcast.setAction(ACTION_POKE);
    150             bcast.putExtra(EXTRA_TIMES, 3);
    151             mContext.sendBroadcast(bcast);
    152         } else {
    153             mIconRes = R.drawable.droidman_open;
    154             refresh();
    155         }
    156     }
    157 
    158     private void refresh() {
    159         RemoteViews rv = buildUpdate(mContext);
    160         for (int i : mWidgetIds) {
    161             mWidgetManager.updateAppWidget(i, rv);
    162         }
    163     }
    164 
    165     private void setIcon(int resId) {
    166         mIconRes = resId;
    167         refresh();
    168     }
    169 
    170     private int getNextMessageIndex() {
    171         return (mMessage + 1) % mTips.length;
    172     }
    173 
    174     private void blink(int blinks) {
    175         // don't blink if no bubble showing or if goodmorning() is happening
    176         if (mMessage < 0) return;
    177 
    178         setIcon(R.drawable.droidman_closed);
    179         try {
    180             Thread.sleep(100);
    181             while (0<--blinks) {
    182                 setIcon(R.drawable.droidman_open);
    183                 Thread.sleep(200);
    184                 setIcon(R.drawable.droidman_closed);
    185                 Thread.sleep(100);
    186             }
    187         } catch (InterruptedException ex) { }
    188         setIcon(R.drawable.droidman_open);
    189     }
    190 
    191     public RemoteViews buildUpdate(Context context) {
    192         RemoteViews updateViews = new RemoteViews(
    193             context.getPackageName(), R.layout.widget);
    194 
    195         // Action for tap on bubble
    196         Intent bcast = new Intent(context, ProtipWidget.class);
    197         bcast.setAction(ACTION_NEXT_TIP);
    198         PendingIntent pending = PendingIntent.getBroadcast(
    199             context, 0, bcast, PendingIntent.FLAG_UPDATE_CURRENT);
    200         updateViews.setOnClickPendingIntent(R.id.tip_bubble, pending);
    201 
    202         // Action for tap on android
    203         bcast = new Intent(context, ProtipWidget.class);
    204         bcast.setAction(ACTION_POKE);
    205         bcast.putExtra(EXTRA_TIMES, 1);
    206         pending = PendingIntent.getBroadcast(
    207             context, 0, bcast, PendingIntent.FLAG_UPDATE_CURRENT);
    208         updateViews.setOnClickPendingIntent(R.id.bugdroid, pending);
    209 
    210         // Tip bubble text
    211         if (mMessage >= 0) {
    212             String[] parts = sNewlineRegex.split(mTips[mMessage], 2);
    213             String title = parts[0];
    214             String text = parts.length > 1 ? parts[1] : "";
    215 
    216             // Look for a callout graphic referenced in the text
    217             Matcher m = sDrawableRegex.matcher(text);
    218             if (m.find()) {
    219                 String imageName = m.group(1);
    220                 int resId = context.getResources().getIdentifier(
    221 
    222                     imageName, null, context.getPackageName());
    223                 updateViews.setImageViewResource(R.id.tip_callout, resId);
    224                 updateViews.setViewVisibility(R.id.tip_callout, View.VISIBLE);
    225                 text = m.replaceFirst("");
    226             } else {
    227                 updateViews.setImageViewResource(R.id.tip_callout, 0);
    228                 updateViews.setViewVisibility(R.id.tip_callout, View.GONE);
    229             }
    230 
    231             updateViews.setTextViewText(R.id.tip_message,
    232                 text);
    233             updateViews.setTextViewText(R.id.tip_header,
    234                 title);
    235             updateViews.setTextViewText(R.id.tip_footer,
    236                 context.getResources().getString(
    237                     R.string.pager_footer,
    238                     (1+mMessage), mTips.length));
    239             updateViews.setViewVisibility(R.id.tip_bubble, View.VISIBLE);
    240         } else {
    241             updateViews.setViewVisibility(R.id.tip_bubble, View.INVISIBLE);
    242         }
    243 
    244         updateViews.setImageViewResource(R.id.bugdroid, mIconRes);
    245 
    246         return updateViews;
    247     }
    248 }
    249