Home | History | Annotate | Download | only in home
      1 /*
      2  * Copyright (C) 2007 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.example.android.home;
     18 
     19 import android.app.Activity;
     20 import android.app.ActivityManager;
     21 import android.app.SearchManager;
     22 import android.content.BroadcastReceiver;
     23 import android.content.ComponentName;
     24 import android.content.Context;
     25 import android.content.Intent;
     26 import android.content.IntentFilter;
     27 import android.content.pm.ActivityInfo;
     28 import android.content.pm.PackageManager;
     29 import android.content.pm.ResolveInfo;
     30 import android.graphics.Bitmap;
     31 import android.graphics.Canvas;
     32 import android.graphics.Paint;
     33 import android.graphics.PaintFlagsDrawFilter;
     34 import android.graphics.PixelFormat;
     35 import android.graphics.Rect;
     36 import android.graphics.ColorFilter;
     37 import android.graphics.drawable.BitmapDrawable;
     38 import android.graphics.drawable.Drawable;
     39 import android.graphics.drawable.PaintDrawable;
     40 import android.os.Bundle;
     41 import android.os.Environment;
     42 import android.util.Log;
     43 import android.util.Xml;
     44 import android.view.KeyEvent;
     45 import android.view.LayoutInflater;
     46 import android.view.Menu;
     47 import android.view.MenuItem;
     48 import android.view.View;
     49 import android.view.ViewGroup;
     50 import android.view.animation.Animation;
     51 import android.view.animation.AnimationUtils;
     52 import android.view.animation.LayoutAnimationController;
     53 import android.widget.AdapterView;
     54 import android.widget.ArrayAdapter;
     55 import android.widget.CheckBox;
     56 import android.widget.GridView;
     57 import android.widget.TextView;
     58 
     59 import java.io.IOException;
     60 import java.io.FileReader;
     61 import java.io.File;
     62 import java.io.FileNotFoundException;
     63 import java.util.ArrayList;
     64 import java.util.Collections;
     65 import java.util.LinkedList;
     66 import java.util.List;
     67 
     68 import org.xmlpull.v1.XmlPullParser;
     69 import org.xmlpull.v1.XmlPullParserException;
     70 
     71 public class Home extends Activity {
     72     /**
     73      * Tag used for logging errors.
     74      */
     75     private static final String LOG_TAG = "Home";
     76 
     77     /**
     78      * Keys during freeze/thaw.
     79      */
     80     private static final String KEY_SAVE_GRID_OPENED = "grid.opened";
     81 
     82     private static final String DEFAULT_FAVORITES_PATH = "etc/favorites.xml";
     83 
     84     private static final String TAG_FAVORITES = "favorites";
     85     private static final String TAG_FAVORITE = "favorite";
     86     private static final String TAG_PACKAGE = "package";
     87     private static final String TAG_CLASS = "class";
     88 
     89     // Identifiers for option menu items
     90     private static final int MENU_WALLPAPER_SETTINGS = Menu.FIRST + 1;
     91     private static final int MENU_SEARCH = MENU_WALLPAPER_SETTINGS + 1;
     92     private static final int MENU_SETTINGS = MENU_SEARCH + 1;
     93 
     94     /**
     95      * Maximum number of recent tasks to query.
     96      */
     97     private static final int MAX_RECENT_TASKS = 20;
     98 
     99     private static boolean mWallpaperChecked;
    100     private static ArrayList<ApplicationInfo> mApplications;
    101     private static LinkedList<ApplicationInfo> mFavorites;
    102 
    103     private final BroadcastReceiver mWallpaperReceiver = new WallpaperIntentReceiver();
    104     private final BroadcastReceiver mApplicationsReceiver = new ApplicationsIntentReceiver();
    105 
    106     private GridView mGrid;
    107 
    108     private LayoutAnimationController mShowLayoutAnimation;
    109     private LayoutAnimationController mHideLayoutAnimation;
    110 
    111     private boolean mBlockAnimation;
    112 
    113     private boolean mHomeDown;
    114     private boolean mBackDown;
    115 
    116     private View mShowApplications;
    117     private CheckBox mShowApplicationsCheck;
    118 
    119     private ApplicationsStackLayout mApplicationsStack;
    120 
    121     private Animation mGridEntry;
    122     private Animation mGridExit;
    123 
    124     @Override
    125     public void onCreate(Bundle icicle) {
    126         super.onCreate(icicle);
    127 
    128         setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
    129 
    130         setContentView(R.layout.home);
    131 
    132         registerIntentReceivers();
    133 
    134         setDefaultWallpaper();
    135 
    136         loadApplications(true);
    137 
    138         bindApplications();
    139         bindFavorites(true);
    140         bindRecents();
    141         bindButtons();
    142 
    143         mGridEntry = AnimationUtils.loadAnimation(this, R.anim.grid_entry);
    144         mGridExit = AnimationUtils.loadAnimation(this, R.anim.grid_exit);
    145     }
    146 
    147     @Override
    148     protected void onNewIntent(Intent intent) {
    149         super.onNewIntent(intent);
    150 
    151         // Close the menu
    152         if (Intent.ACTION_MAIN.equals(intent.getAction())) {
    153             getWindow().closeAllPanels();
    154         }
    155     }
    156 
    157     @Override
    158     public void onDestroy() {
    159         super.onDestroy();
    160 
    161         // Remove the callback for the cached drawables or we leak
    162         // the previous Home screen on orientation change
    163         final int count = mApplications.size();
    164         for (int i = 0; i < count; i++) {
    165             mApplications.get(i).icon.setCallback(null);
    166         }
    167 
    168         unregisterReceiver(mWallpaperReceiver);
    169         unregisterReceiver(mApplicationsReceiver);
    170     }
    171 
    172     @Override
    173     protected void onResume() {
    174         super.onResume();
    175         bindRecents();
    176     }
    177 
    178     @Override
    179     protected void onRestoreInstanceState(Bundle state) {
    180         super.onRestoreInstanceState(state);
    181         final boolean opened = state.getBoolean(KEY_SAVE_GRID_OPENED, false);
    182         if (opened) {
    183             showApplications(false);
    184         }
    185     }
    186 
    187     @Override
    188     protected void onSaveInstanceState(Bundle outState) {
    189         super.onSaveInstanceState(outState);
    190         outState.putBoolean(KEY_SAVE_GRID_OPENED, mGrid.getVisibility() == View.VISIBLE);
    191     }
    192 
    193     /**
    194      * Registers various intent receivers. The current implementation registers
    195      * only a wallpaper intent receiver to let other applications change the
    196      * wallpaper.
    197      */
    198     private void registerIntentReceivers() {
    199         IntentFilter filter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);
    200         registerReceiver(mWallpaperReceiver, filter);
    201 
    202         filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
    203         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
    204         filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
    205         filter.addDataScheme("package");
    206         registerReceiver(mApplicationsReceiver, filter);
    207     }
    208 
    209     /**
    210      * Creates a new appplications adapter for the grid view and registers it.
    211      */
    212     private void bindApplications() {
    213         if (mGrid == null) {
    214             mGrid = (GridView) findViewById(R.id.all_apps);
    215         }
    216         mGrid.setAdapter(new ApplicationsAdapter(this, mApplications));
    217         mGrid.setSelection(0);
    218 
    219         if (mApplicationsStack == null) {
    220             mApplicationsStack = (ApplicationsStackLayout) findViewById(R.id.faves_and_recents);
    221         }
    222     }
    223 
    224     /**
    225      * Binds actions to the various buttons.
    226      */
    227     private void bindButtons() {
    228         mShowApplications = findViewById(R.id.show_all_apps);
    229         mShowApplications.setOnClickListener(new ShowApplications());
    230         mShowApplicationsCheck = (CheckBox) findViewById(R.id.show_all_apps_check);
    231 
    232         mGrid.setOnItemClickListener(new ApplicationLauncher());
    233     }
    234 
    235     /**
    236      * When no wallpaper was manually set, a default wallpaper is used instead.
    237      */
    238     private void setDefaultWallpaper() {
    239         if (!mWallpaperChecked) {
    240             Drawable wallpaper = peekWallpaper();
    241             if (wallpaper == null) {
    242                 try {
    243                     clearWallpaper();
    244                 } catch (IOException e) {
    245                     Log.e(LOG_TAG, "Failed to clear wallpaper " + e);
    246                 }
    247             } else {
    248                 getWindow().setBackgroundDrawable(new ClippedDrawable(wallpaper));
    249             }
    250             mWallpaperChecked = true;
    251         }
    252     }
    253 
    254     /**
    255      * Refreshes the favorite applications stacked over the all apps button.
    256      * The number of favorites depends on the user.
    257      */
    258     private void bindFavorites(boolean isLaunching) {
    259         if (!isLaunching || mFavorites == null) {
    260 
    261             if (mFavorites == null) {
    262                 mFavorites = new LinkedList<ApplicationInfo>();
    263             } else {
    264                 mFavorites.clear();
    265             }
    266             mApplicationsStack.setFavorites(mFavorites);
    267 
    268             FileReader favReader;
    269 
    270             // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system".
    271             final File favFile = new File(Environment.getRootDirectory(), DEFAULT_FAVORITES_PATH);
    272             try {
    273                 favReader = new FileReader(favFile);
    274             } catch (FileNotFoundException e) {
    275                 Log.e(LOG_TAG, "Couldn't find or open favorites file " + favFile);
    276                 return;
    277             }
    278 
    279             final Intent intent = new Intent(Intent.ACTION_MAIN, null);
    280             intent.addCategory(Intent.CATEGORY_LAUNCHER);
    281 
    282             final PackageManager packageManager = getPackageManager();
    283 
    284             try {
    285                 final XmlPullParser parser = Xml.newPullParser();
    286                 parser.setInput(favReader);
    287 
    288                 beginDocument(parser, TAG_FAVORITES);
    289 
    290                 ApplicationInfo info;
    291 
    292                 while (true) {
    293                     nextElement(parser);
    294 
    295                     String name = parser.getName();
    296                     if (!TAG_FAVORITE.equals(name)) {
    297                         break;
    298                     }
    299 
    300                     final String favoritePackage = parser.getAttributeValue(null, TAG_PACKAGE);
    301                     final String favoriteClass = parser.getAttributeValue(null, TAG_CLASS);
    302 
    303                     final ComponentName cn = new ComponentName(favoritePackage, favoriteClass);
    304                     intent.setComponent(cn);
    305                     intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    306 
    307                     info = getApplicationInfo(packageManager, intent);
    308                     if (info != null) {
    309                         info.intent = intent;
    310                         mFavorites.addFirst(info);
    311                     }
    312                 }
    313             } catch (XmlPullParserException e) {
    314                 Log.w(LOG_TAG, "Got exception parsing favorites.", e);
    315             } catch (IOException e) {
    316                 Log.w(LOG_TAG, "Got exception parsing favorites.", e);
    317             }
    318         }
    319 
    320         mApplicationsStack.setFavorites(mFavorites);
    321     }
    322 
    323     private static void beginDocument(XmlPullParser parser, String firstElementName)
    324             throws XmlPullParserException, IOException {
    325 
    326         int type;
    327         while ((type = parser.next()) != XmlPullParser.START_TAG &&
    328                 type != XmlPullParser.END_DOCUMENT) {
    329             // Empty
    330         }
    331 
    332         if (type != XmlPullParser.START_TAG) {
    333             throw new XmlPullParserException("No start tag found");
    334         }
    335 
    336         if (!parser.getName().equals(firstElementName)) {
    337             throw new XmlPullParserException("Unexpected start tag: found " + parser.getName() +
    338                     ", expected " + firstElementName);
    339         }
    340     }
    341 
    342     private static void nextElement(XmlPullParser parser) throws XmlPullParserException, IOException {
    343         int type;
    344         while ((type = parser.next()) != XmlPullParser.START_TAG &&
    345                 type != XmlPullParser.END_DOCUMENT) {
    346             // Empty
    347         }
    348     }
    349 
    350     /**
    351      * Refreshes the recently launched applications stacked over the favorites. The number
    352      * of recents depends on how many favorites are present.
    353      */
    354     private void bindRecents() {
    355         final PackageManager manager = getPackageManager();
    356         final ActivityManager tasksManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
    357         final List<ActivityManager.RecentTaskInfo> recentTasks = tasksManager.getRecentTasks(
    358                 MAX_RECENT_TASKS, 0);
    359 
    360         final int count = recentTasks.size();
    361         final ArrayList<ApplicationInfo> recents = new ArrayList<ApplicationInfo>();
    362 
    363         for (int i = count - 1; i >= 0; i--) {
    364             final Intent intent = recentTasks.get(i).baseIntent;
    365 
    366             if (Intent.ACTION_MAIN.equals(intent.getAction()) &&
    367                     !intent.hasCategory(Intent.CATEGORY_HOME)) {
    368 
    369                 ApplicationInfo info = getApplicationInfo(manager, intent);
    370                 if (info != null) {
    371                     info.intent = intent;
    372                     if (!mFavorites.contains(info)) {
    373                         recents.add(info);
    374                     }
    375                 }
    376             }
    377         }
    378 
    379         mApplicationsStack.setRecents(recents);
    380     }
    381 
    382     private static ApplicationInfo getApplicationInfo(PackageManager manager, Intent intent) {
    383         final ResolveInfo resolveInfo = manager.resolveActivity(intent, 0);
    384 
    385         if (resolveInfo == null) {
    386             return null;
    387         }
    388 
    389         final ApplicationInfo info = new ApplicationInfo();
    390         final ActivityInfo activityInfo = resolveInfo.activityInfo;
    391         info.icon = activityInfo.loadIcon(manager);
    392         if (info.title == null || info.title.length() == 0) {
    393             info.title = activityInfo.loadLabel(manager);
    394         }
    395         if (info.title == null) {
    396             info.title = "";
    397         }
    398         return info;
    399     }
    400 
    401     @Override
    402     public void onWindowFocusChanged(boolean hasFocus) {
    403         super.onWindowFocusChanged(hasFocus);
    404         if (!hasFocus) {
    405             mBackDown = mHomeDown = false;
    406         }
    407     }
    408 
    409     @Override
    410     public boolean dispatchKeyEvent(KeyEvent event) {
    411         if (event.getAction() == KeyEvent.ACTION_DOWN) {
    412             switch (event.getKeyCode()) {
    413                 case KeyEvent.KEYCODE_BACK:
    414                     mBackDown = true;
    415                     return true;
    416                 case KeyEvent.KEYCODE_HOME:
    417                     mHomeDown = true;
    418                     return true;
    419             }
    420         } else if (event.getAction() == KeyEvent.ACTION_UP) {
    421             switch (event.getKeyCode()) {
    422                 case KeyEvent.KEYCODE_BACK:
    423                     if (!event.isCanceled()) {
    424                         // Do BACK behavior.
    425                     }
    426                     mBackDown = true;
    427                     return true;
    428                 case KeyEvent.KEYCODE_HOME:
    429                     if (!event.isCanceled()) {
    430                         // Do HOME behavior.
    431                     }
    432                     mHomeDown = true;
    433                     return true;
    434             }
    435         }
    436 
    437         return super.dispatchKeyEvent(event);
    438     }
    439 
    440     @Override
    441     public boolean onCreateOptionsMenu(Menu menu) {
    442         super.onCreateOptionsMenu(menu);
    443 
    444         menu.add(0, MENU_WALLPAPER_SETTINGS, 0, R.string.menu_wallpaper)
    445                  .setIcon(android.R.drawable.ic_menu_gallery)
    446                  .setAlphabeticShortcut('W');
    447         menu.add(0, MENU_SEARCH, 0, R.string.menu_search)
    448                 .setIcon(android.R.drawable.ic_search_category_default)
    449                 .setAlphabeticShortcut(SearchManager.MENU_KEY);
    450         menu.add(0, MENU_SETTINGS, 0, R.string.menu_settings)
    451                 .setIcon(android.R.drawable.ic_menu_preferences)
    452                 .setIntent(new Intent(android.provider.Settings.ACTION_SETTINGS));
    453 
    454         return true;
    455     }
    456 
    457     @Override
    458     public boolean onOptionsItemSelected(MenuItem item) {
    459         switch (item.getItemId()) {
    460             case MENU_WALLPAPER_SETTINGS:
    461                 startWallpaper();
    462                 return true;
    463             case MENU_SEARCH:
    464                 onSearchRequested();
    465                 return true;
    466         }
    467 
    468         return super.onOptionsItemSelected(item);
    469     }
    470 
    471     private void startWallpaper() {
    472         final Intent pickWallpaper = new Intent(Intent.ACTION_SET_WALLPAPER);
    473         startActivity(Intent.createChooser(pickWallpaper, getString(R.string.menu_wallpaper)));
    474     }
    475 
    476     /**
    477      * Loads the list of installed applications in mApplications.
    478      */
    479     private void loadApplications(boolean isLaunching) {
    480         if (isLaunching && mApplications != null) {
    481             return;
    482         }
    483 
    484         PackageManager manager = getPackageManager();
    485 
    486         Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
    487         mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
    488 
    489         final List<ResolveInfo> apps = manager.queryIntentActivities(mainIntent, 0);
    490         Collections.sort(apps, new ResolveInfo.DisplayNameComparator(manager));
    491 
    492         if (apps != null) {
    493             final int count = apps.size();
    494 
    495             if (mApplications == null) {
    496                 mApplications = new ArrayList<ApplicationInfo>(count);
    497             }
    498             mApplications.clear();
    499 
    500             for (int i = 0; i < count; i++) {
    501                 ApplicationInfo application = new ApplicationInfo();
    502                 ResolveInfo info = apps.get(i);
    503 
    504                 application.title = info.loadLabel(manager);
    505                 application.setActivity(new ComponentName(
    506                         info.activityInfo.applicationInfo.packageName,
    507                         info.activityInfo.name),
    508                         Intent.FLAG_ACTIVITY_NEW_TASK
    509                         | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
    510                 application.icon = info.activityInfo.loadIcon(manager);
    511 
    512                 mApplications.add(application);
    513             }
    514         }
    515     }
    516 
    517     /**
    518      * Shows all of the applications by playing an animation on the grid.
    519      */
    520     private void showApplications(boolean animate) {
    521         if (mBlockAnimation) {
    522             return;
    523         }
    524         mBlockAnimation = true;
    525 
    526         mShowApplicationsCheck.toggle();
    527 
    528         if (mShowLayoutAnimation == null) {
    529             mShowLayoutAnimation = AnimationUtils.loadLayoutAnimation(
    530                     this, R.anim.show_applications);
    531         }
    532 
    533         // This enables a layout animation; if you uncomment this code, you need to
    534         // comment the line mGrid.startAnimation() below
    535 //        mGrid.setLayoutAnimationListener(new ShowGrid());
    536 //        mGrid.setLayoutAnimation(mShowLayoutAnimation);
    537 //        mGrid.startLayoutAnimation();
    538 
    539         if (animate) {
    540             mGridEntry.setAnimationListener(new ShowGrid());
    541             mGrid.startAnimation(mGridEntry);
    542         }
    543 
    544         mGrid.setVisibility(View.VISIBLE);
    545 
    546         if (!animate) {
    547             mBlockAnimation = false;
    548         }
    549 
    550         // ViewDebug.startHierarchyTracing("Home", mGrid);
    551     }
    552 
    553     /**
    554      * Hides all of the applications by playing an animation on the grid.
    555      */
    556     private void hideApplications() {
    557         if (mBlockAnimation) {
    558             return;
    559         }
    560         mBlockAnimation = true;
    561 
    562         mShowApplicationsCheck.toggle();
    563 
    564         if (mHideLayoutAnimation == null) {
    565             mHideLayoutAnimation = AnimationUtils.loadLayoutAnimation(
    566                     this, R.anim.hide_applications);
    567         }
    568 
    569         mGridExit.setAnimationListener(new HideGrid());
    570         mGrid.startAnimation(mGridExit);
    571         mGrid.setVisibility(View.INVISIBLE);
    572         mShowApplications.requestFocus();
    573 
    574         // This enables a layout animation; if you uncomment this code, you need to
    575         // comment the line mGrid.startAnimation() above
    576 //        mGrid.setLayoutAnimationListener(new HideGrid());
    577 //        mGrid.setLayoutAnimation(mHideLayoutAnimation);
    578 //        mGrid.startLayoutAnimation();
    579     }
    580 
    581     /**
    582      * Receives intents from other applications to change the wallpaper.
    583      */
    584     private class WallpaperIntentReceiver extends BroadcastReceiver {
    585         @Override
    586         public void onReceive(Context context, Intent intent) {
    587             getWindow().setBackgroundDrawable(new ClippedDrawable(getWallpaper()));
    588         }
    589     }
    590 
    591     /**
    592      * Receives notifications when applications are added/removed.
    593      */
    594     private class ApplicationsIntentReceiver extends BroadcastReceiver {
    595         @Override
    596         public void onReceive(Context context, Intent intent) {
    597             loadApplications(false);
    598             bindApplications();
    599             bindRecents();
    600             bindFavorites(false);
    601         }
    602     }
    603 
    604     /**
    605      * GridView adapter to show the list of all installed applications.
    606      */
    607     private class ApplicationsAdapter extends ArrayAdapter<ApplicationInfo> {
    608         private Rect mOldBounds = new Rect();
    609 
    610         public ApplicationsAdapter(Context context, ArrayList<ApplicationInfo> apps) {
    611             super(context, 0, apps);
    612         }
    613 
    614         @Override
    615         public View getView(int position, View convertView, ViewGroup parent) {
    616             final ApplicationInfo info = mApplications.get(position);
    617 
    618             if (convertView == null) {
    619                 final LayoutInflater inflater = getLayoutInflater();
    620                 convertView = inflater.inflate(R.layout.application, parent, false);
    621             }
    622 
    623             Drawable icon = info.icon;
    624 
    625             if (!info.filtered) {
    626                 //final Resources resources = getContext().getResources();
    627                 int width = 42;//(int) resources.getDimension(android.R.dimen.app_icon_size);
    628                 int height = 42;//(int) resources.getDimension(android.R.dimen.app_icon_size);
    629 
    630                 final int iconWidth = icon.getIntrinsicWidth();
    631                 final int iconHeight = icon.getIntrinsicHeight();
    632 
    633                 if (icon instanceof PaintDrawable) {
    634                     PaintDrawable painter = (PaintDrawable) icon;
    635                     painter.setIntrinsicWidth(width);
    636                     painter.setIntrinsicHeight(height);
    637                 }
    638 
    639                 if (width > 0 && height > 0 && (width < iconWidth || height < iconHeight)) {
    640                     final float ratio = (float) iconWidth / iconHeight;
    641 
    642                     if (iconWidth > iconHeight) {
    643                         height = (int) (width / ratio);
    644                     } else if (iconHeight > iconWidth) {
    645                         width = (int) (height * ratio);
    646                     }
    647 
    648                     final Bitmap.Config c =
    649                             icon.getOpacity() != PixelFormat.OPAQUE ?
    650                                 Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
    651                     final Bitmap thumb = Bitmap.createBitmap(width, height, c);
    652                     final Canvas canvas = new Canvas(thumb);
    653                     canvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG, 0));
    654                     // Copy the old bounds to restore them later
    655                     // If we were to do oldBounds = icon.getBounds(),
    656                     // the call to setBounds() that follows would
    657                     // change the same instance and we would lose the
    658                     // old bounds
    659                     mOldBounds.set(icon.getBounds());
    660                     icon.setBounds(0, 0, width, height);
    661                     icon.draw(canvas);
    662                     icon.setBounds(mOldBounds);
    663                     icon = info.icon = new BitmapDrawable(thumb);
    664                     info.filtered = true;
    665                 }
    666             }
    667 
    668             final TextView textView = (TextView) convertView.findViewById(R.id.label);
    669             textView.setCompoundDrawablesWithIntrinsicBounds(null, icon, null, null);
    670             textView.setText(info.title);
    671 
    672             return convertView;
    673         }
    674     }
    675 
    676     /**
    677      * Shows and hides the applications grid view.
    678      */
    679     private class ShowApplications implements View.OnClickListener {
    680         public void onClick(View v) {
    681             if (mGrid.getVisibility() != View.VISIBLE) {
    682                 showApplications(true);
    683             } else {
    684                 hideApplications();
    685             }
    686         }
    687     }
    688 
    689     /**
    690      * Hides the applications grid when the layout animation is over.
    691      */
    692     private class HideGrid implements Animation.AnimationListener {
    693         public void onAnimationStart(Animation animation) {
    694         }
    695 
    696         public void onAnimationEnd(Animation animation) {
    697             mBlockAnimation = false;
    698         }
    699 
    700         public void onAnimationRepeat(Animation animation) {
    701         }
    702     }
    703 
    704     /**
    705      * Shows the applications grid when the layout animation is over.
    706      */
    707     private class ShowGrid implements Animation.AnimationListener {
    708         public void onAnimationStart(Animation animation) {
    709         }
    710 
    711         public void onAnimationEnd(Animation animation) {
    712             mBlockAnimation = false;
    713             // ViewDebug.stopHierarchyTracing();
    714         }
    715 
    716         public void onAnimationRepeat(Animation animation) {
    717         }
    718     }
    719 
    720     /**
    721      * Starts the selected activity/application in the grid view.
    722      */
    723     private class ApplicationLauncher implements AdapterView.OnItemClickListener {
    724         public void onItemClick(AdapterView parent, View v, int position, long id) {
    725             ApplicationInfo app = (ApplicationInfo) parent.getItemAtPosition(position);
    726             startActivity(app.intent);
    727         }
    728     }
    729 
    730     /**
    731      * When a drawable is attached to a View, the View gives the Drawable its dimensions
    732      * by calling Drawable.setBounds(). In this application, the View that draws the
    733      * wallpaper has the same size as the screen. However, the wallpaper might be larger
    734      * that the screen which means it will be automatically stretched. Because stretching
    735      * a bitmap while drawing it is very expensive, we use a ClippedDrawable instead.
    736      * This drawable simply draws another wallpaper but makes sure it is not stretched
    737      * by always giving it its intrinsic dimensions. If the wallpaper is larger than the
    738      * screen, it will simply get clipped but it won't impact performance.
    739      */
    740     private class ClippedDrawable extends Drawable {
    741         private final Drawable mWallpaper;
    742 
    743         public ClippedDrawable(Drawable wallpaper) {
    744             mWallpaper = wallpaper;
    745         }
    746 
    747         @Override
    748         public void setBounds(int left, int top, int right, int bottom) {
    749             super.setBounds(left, top, right, bottom);
    750             // Ensure the wallpaper is as large as it really is, to avoid stretching it
    751             // at drawing time
    752             mWallpaper.setBounds(left, top, left + mWallpaper.getIntrinsicWidth(),
    753                     top + mWallpaper.getIntrinsicHeight());
    754         }
    755 
    756         public void draw(Canvas canvas) {
    757             mWallpaper.draw(canvas);
    758         }
    759 
    760         public void setAlpha(int alpha) {
    761             mWallpaper.setAlpha(alpha);
    762         }
    763 
    764         public void setColorFilter(ColorFilter cf) {
    765             mWallpaper.setColorFilter(cf);
    766         }
    767 
    768         public int getOpacity() {
    769             return mWallpaper.getOpacity();
    770         }
    771     }
    772 }
    773