| /* |
| * Copyright (C) 2007 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.example.android.home; |
| |
| import android.app.Activity; |
| import android.app.ActivityManager; |
| import android.app.SearchManager; |
| import android.content.BroadcastReceiver; |
| import android.content.ComponentName; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.content.pm.ActivityInfo; |
| import android.content.pm.PackageManager; |
| import android.content.pm.ResolveInfo; |
| import android.graphics.Bitmap; |
| import android.graphics.Canvas; |
| import android.graphics.Paint; |
| import android.graphics.PaintFlagsDrawFilter; |
| import android.graphics.PixelFormat; |
| import android.graphics.Rect; |
| import android.graphics.ColorFilter; |
| import android.graphics.drawable.BitmapDrawable; |
| import android.graphics.drawable.Drawable; |
| import android.graphics.drawable.PaintDrawable; |
| import android.os.Bundle; |
| import android.os.Environment; |
| import android.util.Log; |
| import android.util.Xml; |
| import android.view.KeyEvent; |
| import android.view.LayoutInflater; |
| import android.view.Menu; |
| import android.view.MenuItem; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.view.animation.Animation; |
| import android.view.animation.AnimationUtils; |
| import android.view.animation.LayoutAnimationController; |
| import android.widget.AdapterView; |
| import android.widget.ArrayAdapter; |
| import android.widget.CheckBox; |
| import android.widget.GridView; |
| import android.widget.TextView; |
| |
| import java.io.IOException; |
| import java.io.FileReader; |
| import java.io.File; |
| import java.io.FileNotFoundException; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.LinkedList; |
| import java.util.List; |
| |
| import org.xmlpull.v1.XmlPullParser; |
| import org.xmlpull.v1.XmlPullParserException; |
| |
| public class Home extends Activity { |
| /** |
| * Tag used for logging errors. |
| */ |
| private static final String LOG_TAG = "Home"; |
| |
| /** |
| * Keys during freeze/thaw. |
| */ |
| private static final String KEY_SAVE_GRID_OPENED = "grid.opened"; |
| |
| private static final String DEFAULT_FAVORITES_PATH = "etc/favorites.xml"; |
| |
| private static final String TAG_FAVORITES = "favorites"; |
| private static final String TAG_FAVORITE = "favorite"; |
| private static final String TAG_PACKAGE = "package"; |
| private static final String TAG_CLASS = "class"; |
| |
| // Identifiers for option menu items |
| private static final int MENU_WALLPAPER_SETTINGS = Menu.FIRST + 1; |
| private static final int MENU_SEARCH = MENU_WALLPAPER_SETTINGS + 1; |
| private static final int MENU_SETTINGS = MENU_SEARCH + 1; |
| |
| /** |
| * Maximum number of recent tasks to query. |
| */ |
| private static final int MAX_RECENT_TASKS = 20; |
| |
| private static boolean mWallpaperChecked; |
| private static ArrayList<ApplicationInfo> mApplications; |
| private static LinkedList<ApplicationInfo> mFavorites; |
| |
| private final BroadcastReceiver mWallpaperReceiver = new WallpaperIntentReceiver(); |
| private final BroadcastReceiver mApplicationsReceiver = new ApplicationsIntentReceiver(); |
| |
| private GridView mGrid; |
| |
| private LayoutAnimationController mShowLayoutAnimation; |
| private LayoutAnimationController mHideLayoutAnimation; |
| |
| private boolean mBlockAnimation; |
| |
| private boolean mHomeDown; |
| private boolean mBackDown; |
| |
| private View mShowApplications; |
| private CheckBox mShowApplicationsCheck; |
| |
| private ApplicationsStackLayout mApplicationsStack; |
| |
| private Animation mGridEntry; |
| private Animation mGridExit; |
| |
| @Override |
| public void onCreate(Bundle icicle) { |
| super.onCreate(icicle); |
| |
| setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL); |
| |
| setContentView(R.layout.home); |
| |
| registerIntentReceivers(); |
| |
| setDefaultWallpaper(); |
| |
| loadApplications(true); |
| |
| bindApplications(); |
| bindFavorites(true); |
| bindRecents(); |
| bindButtons(); |
| |
| mGridEntry = AnimationUtils.loadAnimation(this, R.anim.grid_entry); |
| mGridExit = AnimationUtils.loadAnimation(this, R.anim.grid_exit); |
| } |
| |
| @Override |
| protected void onNewIntent(Intent intent) { |
| super.onNewIntent(intent); |
| |
| // Close the menu |
| if (Intent.ACTION_MAIN.equals(intent.getAction())) { |
| getWindow().closeAllPanels(); |
| } |
| } |
| |
| @Override |
| public void onDestroy() { |
| super.onDestroy(); |
| |
| // Remove the callback for the cached drawables or we leak |
| // the previous Home screen on orientation change |
| final int count = mApplications.size(); |
| for (int i = 0; i < count; i++) { |
| mApplications.get(i).icon.setCallback(null); |
| } |
| |
| unregisterReceiver(mWallpaperReceiver); |
| unregisterReceiver(mApplicationsReceiver); |
| } |
| |
| @Override |
| protected void onResume() { |
| super.onResume(); |
| bindRecents(); |
| } |
| |
| @Override |
| protected void onRestoreInstanceState(Bundle state) { |
| super.onRestoreInstanceState(state); |
| final boolean opened = state.getBoolean(KEY_SAVE_GRID_OPENED, false); |
| if (opened) { |
| showApplications(false); |
| } |
| } |
| |
| @Override |
| protected void onSaveInstanceState(Bundle outState) { |
| super.onSaveInstanceState(outState); |
| outState.putBoolean(KEY_SAVE_GRID_OPENED, mGrid.getVisibility() == View.VISIBLE); |
| } |
| |
| /** |
| * Registers various intent receivers. The current implementation registers |
| * only a wallpaper intent receiver to let other applications change the |
| * wallpaper. |
| */ |
| private void registerIntentReceivers() { |
| IntentFilter filter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED); |
| registerReceiver(mWallpaperReceiver, filter); |
| |
| filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); |
| filter.addAction(Intent.ACTION_PACKAGE_REMOVED); |
| filter.addAction(Intent.ACTION_PACKAGE_CHANGED); |
| filter.addDataScheme("package"); |
| registerReceiver(mApplicationsReceiver, filter); |
| } |
| |
| /** |
| * Creates a new appplications adapter for the grid view and registers it. |
| */ |
| private void bindApplications() { |
| if (mGrid == null) { |
| mGrid = (GridView) findViewById(R.id.all_apps); |
| } |
| mGrid.setAdapter(new ApplicationsAdapter(this, mApplications)); |
| mGrid.setSelection(0); |
| |
| if (mApplicationsStack == null) { |
| mApplicationsStack = (ApplicationsStackLayout) findViewById(R.id.faves_and_recents); |
| } |
| } |
| |
| /** |
| * Binds actions to the various buttons. |
| */ |
| private void bindButtons() { |
| mShowApplications = findViewById(R.id.show_all_apps); |
| mShowApplications.setOnClickListener(new ShowApplications()); |
| mShowApplicationsCheck = (CheckBox) findViewById(R.id.show_all_apps_check); |
| |
| mGrid.setOnItemClickListener(new ApplicationLauncher()); |
| } |
| |
| /** |
| * When no wallpaper was manually set, a default wallpaper is used instead. |
| */ |
| private void setDefaultWallpaper() { |
| if (!mWallpaperChecked) { |
| Drawable wallpaper = peekWallpaper(); |
| if (wallpaper == null) { |
| try { |
| clearWallpaper(); |
| } catch (IOException e) { |
| Log.e(LOG_TAG, "Failed to clear wallpaper " + e); |
| } |
| } else { |
| getWindow().setBackgroundDrawable(new ClippedDrawable(wallpaper)); |
| } |
| mWallpaperChecked = true; |
| } |
| } |
| |
| /** |
| * Refreshes the favorite applications stacked over the all apps button. |
| * The number of favorites depends on the user. |
| */ |
| private void bindFavorites(boolean isLaunching) { |
| if (!isLaunching || mFavorites == null) { |
| |
| if (mFavorites == null) { |
| mFavorites = new LinkedList<ApplicationInfo>(); |
| } else { |
| mFavorites.clear(); |
| } |
| mApplicationsStack.setFavorites(mFavorites); |
| |
| FileReader favReader; |
| |
| // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system". |
| final File favFile = new File(Environment.getRootDirectory(), DEFAULT_FAVORITES_PATH); |
| try { |
| favReader = new FileReader(favFile); |
| } catch (FileNotFoundException e) { |
| Log.e(LOG_TAG, "Couldn't find or open favorites file " + favFile); |
| return; |
| } |
| |
| final Intent intent = new Intent(Intent.ACTION_MAIN, null); |
| intent.addCategory(Intent.CATEGORY_LAUNCHER); |
| |
| final PackageManager packageManager = getPackageManager(); |
| |
| try { |
| final XmlPullParser parser = Xml.newPullParser(); |
| parser.setInput(favReader); |
| |
| beginDocument(parser, TAG_FAVORITES); |
| |
| ApplicationInfo info; |
| |
| while (true) { |
| nextElement(parser); |
| |
| String name = parser.getName(); |
| if (!TAG_FAVORITE.equals(name)) { |
| break; |
| } |
| |
| final String favoritePackage = parser.getAttributeValue(null, TAG_PACKAGE); |
| final String favoriteClass = parser.getAttributeValue(null, TAG_CLASS); |
| |
| final ComponentName cn = new ComponentName(favoritePackage, favoriteClass); |
| intent.setComponent(cn); |
| intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
| |
| info = getApplicationInfo(packageManager, intent); |
| if (info != null) { |
| info.intent = intent; |
| mFavorites.addFirst(info); |
| } |
| } |
| } catch (XmlPullParserException e) { |
| Log.w(LOG_TAG, "Got exception parsing favorites.", e); |
| } catch (IOException e) { |
| Log.w(LOG_TAG, "Got exception parsing favorites.", e); |
| } |
| } |
| |
| mApplicationsStack.setFavorites(mFavorites); |
| } |
| |
| private static void beginDocument(XmlPullParser parser, String firstElementName) |
| throws XmlPullParserException, IOException { |
| |
| int type; |
| while ((type = parser.next()) != XmlPullParser.START_TAG && |
| type != XmlPullParser.END_DOCUMENT) { |
| // Empty |
| } |
| |
| if (type != XmlPullParser.START_TAG) { |
| throw new XmlPullParserException("No start tag found"); |
| } |
| |
| if (!parser.getName().equals(firstElementName)) { |
| throw new XmlPullParserException("Unexpected start tag: found " + parser.getName() + |
| ", expected " + firstElementName); |
| } |
| } |
| |
| private static void nextElement(XmlPullParser parser) throws XmlPullParserException, IOException { |
| int type; |
| while ((type = parser.next()) != XmlPullParser.START_TAG && |
| type != XmlPullParser.END_DOCUMENT) { |
| // Empty |
| } |
| } |
| |
| /** |
| * Refreshes the recently launched applications stacked over the favorites. The number |
| * of recents depends on how many favorites are present. |
| */ |
| private void bindRecents() { |
| final PackageManager manager = getPackageManager(); |
| final ActivityManager tasksManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE); |
| final List<ActivityManager.RecentTaskInfo> recentTasks = tasksManager.getRecentTasks( |
| MAX_RECENT_TASKS, 0); |
| |
| final int count = recentTasks.size(); |
| final ArrayList<ApplicationInfo> recents = new ArrayList<ApplicationInfo>(); |
| |
| for (int i = count - 1; i >= 0; i--) { |
| final Intent intent = recentTasks.get(i).baseIntent; |
| |
| if (Intent.ACTION_MAIN.equals(intent.getAction()) && |
| !intent.hasCategory(Intent.CATEGORY_HOME)) { |
| |
| ApplicationInfo info = getApplicationInfo(manager, intent); |
| if (info != null) { |
| info.intent = intent; |
| if (!mFavorites.contains(info)) { |
| recents.add(info); |
| } |
| } |
| } |
| } |
| |
| mApplicationsStack.setRecents(recents); |
| } |
| |
| private static ApplicationInfo getApplicationInfo(PackageManager manager, Intent intent) { |
| final ResolveInfo resolveInfo = manager.resolveActivity(intent, 0); |
| |
| if (resolveInfo == null) { |
| return null; |
| } |
| |
| final ApplicationInfo info = new ApplicationInfo(); |
| final ActivityInfo activityInfo = resolveInfo.activityInfo; |
| info.icon = activityInfo.loadIcon(manager); |
| if (info.title == null || info.title.length() == 0) { |
| info.title = activityInfo.loadLabel(manager); |
| } |
| if (info.title == null) { |
| info.title = ""; |
| } |
| return info; |
| } |
| |
| @Override |
| public void onWindowFocusChanged(boolean hasFocus) { |
| super.onWindowFocusChanged(hasFocus); |
| if (!hasFocus) { |
| mBackDown = mHomeDown = false; |
| } |
| } |
| |
| @Override |
| public boolean dispatchKeyEvent(KeyEvent event) { |
| if (event.getAction() == KeyEvent.ACTION_DOWN) { |
| switch (event.getKeyCode()) { |
| case KeyEvent.KEYCODE_BACK: |
| mBackDown = true; |
| return true; |
| case KeyEvent.KEYCODE_HOME: |
| mHomeDown = true; |
| return true; |
| } |
| } else if (event.getAction() == KeyEvent.ACTION_UP) { |
| switch (event.getKeyCode()) { |
| case KeyEvent.KEYCODE_BACK: |
| if (!event.isCanceled()) { |
| // Do BACK behavior. |
| } |
| mBackDown = true; |
| return true; |
| case KeyEvent.KEYCODE_HOME: |
| if (!event.isCanceled()) { |
| // Do HOME behavior. |
| } |
| mHomeDown = true; |
| return true; |
| } |
| } |
| |
| return super.dispatchKeyEvent(event); |
| } |
| |
| @Override |
| public boolean onCreateOptionsMenu(Menu menu) { |
| super.onCreateOptionsMenu(menu); |
| |
| menu.add(0, MENU_WALLPAPER_SETTINGS, 0, R.string.menu_wallpaper) |
| .setIcon(android.R.drawable.ic_menu_gallery) |
| .setAlphabeticShortcut('W'); |
| menu.add(0, MENU_SEARCH, 0, R.string.menu_search) |
| .setIcon(android.R.drawable.ic_search_category_default) |
| .setAlphabeticShortcut(SearchManager.MENU_KEY); |
| menu.add(0, MENU_SETTINGS, 0, R.string.menu_settings) |
| .setIcon(android.R.drawable.ic_menu_preferences) |
| .setIntent(new Intent(android.provider.Settings.ACTION_SETTINGS)); |
| |
| return true; |
| } |
| |
| @Override |
| public boolean onOptionsItemSelected(MenuItem item) { |
| switch (item.getItemId()) { |
| case MENU_WALLPAPER_SETTINGS: |
| startWallpaper(); |
| return true; |
| case MENU_SEARCH: |
| onSearchRequested(); |
| return true; |
| } |
| |
| return super.onOptionsItemSelected(item); |
| } |
| |
| private void startWallpaper() { |
| final Intent pickWallpaper = new Intent(Intent.ACTION_SET_WALLPAPER); |
| startActivity(Intent.createChooser(pickWallpaper, getString(R.string.menu_wallpaper))); |
| } |
| |
| /** |
| * Loads the list of installed applications in mApplications. |
| */ |
| private void loadApplications(boolean isLaunching) { |
| if (isLaunching && mApplications != null) { |
| return; |
| } |
| |
| PackageManager manager = getPackageManager(); |
| |
| Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); |
| mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); |
| |
| final List<ResolveInfo> apps = manager.queryIntentActivities(mainIntent, 0); |
| Collections.sort(apps, new ResolveInfo.DisplayNameComparator(manager)); |
| |
| if (apps != null) { |
| final int count = apps.size(); |
| |
| if (mApplications == null) { |
| mApplications = new ArrayList<ApplicationInfo>(count); |
| } |
| mApplications.clear(); |
| |
| for (int i = 0; i < count; i++) { |
| ApplicationInfo application = new ApplicationInfo(); |
| ResolveInfo info = apps.get(i); |
| |
| application.title = info.loadLabel(manager); |
| application.setActivity(new ComponentName( |
| info.activityInfo.applicationInfo.packageName, |
| info.activityInfo.name), |
| Intent.FLAG_ACTIVITY_NEW_TASK |
| | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); |
| application.icon = info.activityInfo.loadIcon(manager); |
| |
| mApplications.add(application); |
| } |
| } |
| } |
| |
| /** |
| * Shows all of the applications by playing an animation on the grid. |
| */ |
| private void showApplications(boolean animate) { |
| if (mBlockAnimation) { |
| return; |
| } |
| mBlockAnimation = true; |
| |
| mShowApplicationsCheck.toggle(); |
| |
| if (mShowLayoutAnimation == null) { |
| mShowLayoutAnimation = AnimationUtils.loadLayoutAnimation( |
| this, R.anim.show_applications); |
| } |
| |
| // This enables a layout animation; if you uncomment this code, you need to |
| // comment the line mGrid.startAnimation() below |
| // mGrid.setLayoutAnimationListener(new ShowGrid()); |
| // mGrid.setLayoutAnimation(mShowLayoutAnimation); |
| // mGrid.startLayoutAnimation(); |
| |
| if (animate) { |
| mGridEntry.setAnimationListener(new ShowGrid()); |
| mGrid.startAnimation(mGridEntry); |
| } |
| |
| mGrid.setVisibility(View.VISIBLE); |
| |
| if (!animate) { |
| mBlockAnimation = false; |
| } |
| |
| // ViewDebug.startHierarchyTracing("Home", mGrid); |
| } |
| |
| /** |
| * Hides all of the applications by playing an animation on the grid. |
| */ |
| private void hideApplications() { |
| if (mBlockAnimation) { |
| return; |
| } |
| mBlockAnimation = true; |
| |
| mShowApplicationsCheck.toggle(); |
| |
| if (mHideLayoutAnimation == null) { |
| mHideLayoutAnimation = AnimationUtils.loadLayoutAnimation( |
| this, R.anim.hide_applications); |
| } |
| |
| mGridExit.setAnimationListener(new HideGrid()); |
| mGrid.startAnimation(mGridExit); |
| mGrid.setVisibility(View.INVISIBLE); |
| mShowApplications.requestFocus(); |
| |
| // This enables a layout animation; if you uncomment this code, you need to |
| // comment the line mGrid.startAnimation() above |
| // mGrid.setLayoutAnimationListener(new HideGrid()); |
| // mGrid.setLayoutAnimation(mHideLayoutAnimation); |
| // mGrid.startLayoutAnimation(); |
| } |
| |
| /** |
| * Receives intents from other applications to change the wallpaper. |
| */ |
| private class WallpaperIntentReceiver extends BroadcastReceiver { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| getWindow().setBackgroundDrawable(new ClippedDrawable(getWallpaper())); |
| } |
| } |
| |
| /** |
| * Receives notifications when applications are added/removed. |
| */ |
| private class ApplicationsIntentReceiver extends BroadcastReceiver { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| loadApplications(false); |
| bindApplications(); |
| bindRecents(); |
| bindFavorites(false); |
| } |
| } |
| |
| /** |
| * GridView adapter to show the list of all installed applications. |
| */ |
| private class ApplicationsAdapter extends ArrayAdapter<ApplicationInfo> { |
| private Rect mOldBounds = new Rect(); |
| |
| public ApplicationsAdapter(Context context, ArrayList<ApplicationInfo> apps) { |
| super(context, 0, apps); |
| } |
| |
| @Override |
| public View getView(int position, View convertView, ViewGroup parent) { |
| final ApplicationInfo info = mApplications.get(position); |
| |
| if (convertView == null) { |
| final LayoutInflater inflater = getLayoutInflater(); |
| convertView = inflater.inflate(R.layout.application, parent, false); |
| } |
| |
| Drawable icon = info.icon; |
| |
| if (!info.filtered) { |
| //final Resources resources = getContext().getResources(); |
| int width = 42;//(int) resources.getDimension(android.R.dimen.app_icon_size); |
| int height = 42;//(int) resources.getDimension(android.R.dimen.app_icon_size); |
| |
| final int iconWidth = icon.getIntrinsicWidth(); |
| final int iconHeight = icon.getIntrinsicHeight(); |
| |
| if (icon instanceof PaintDrawable) { |
| PaintDrawable painter = (PaintDrawable) icon; |
| painter.setIntrinsicWidth(width); |
| painter.setIntrinsicHeight(height); |
| } |
| |
| if (width > 0 && height > 0 && (width < iconWidth || height < iconHeight)) { |
| final float ratio = (float) iconWidth / iconHeight; |
| |
| if (iconWidth > iconHeight) { |
| height = (int) (width / ratio); |
| } else if (iconHeight > iconWidth) { |
| width = (int) (height * ratio); |
| } |
| |
| final Bitmap.Config c = |
| icon.getOpacity() != PixelFormat.OPAQUE ? |
| Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565; |
| final Bitmap thumb = Bitmap.createBitmap(width, height, c); |
| final Canvas canvas = new Canvas(thumb); |
| canvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG, 0)); |
| // Copy the old bounds to restore them later |
| // If we were to do oldBounds = icon.getBounds(), |
| // the call to setBounds() that follows would |
| // change the same instance and we would lose the |
| // old bounds |
| mOldBounds.set(icon.getBounds()); |
| icon.setBounds(0, 0, width, height); |
| icon.draw(canvas); |
| icon.setBounds(mOldBounds); |
| icon = info.icon = new BitmapDrawable(thumb); |
| info.filtered = true; |
| } |
| } |
| |
| final TextView textView = (TextView) convertView.findViewById(R.id.label); |
| textView.setCompoundDrawablesWithIntrinsicBounds(null, icon, null, null); |
| textView.setText(info.title); |
| |
| return convertView; |
| } |
| } |
| |
| /** |
| * Shows and hides the applications grid view. |
| */ |
| private class ShowApplications implements View.OnClickListener { |
| public void onClick(View v) { |
| if (mGrid.getVisibility() != View.VISIBLE) { |
| showApplications(true); |
| } else { |
| hideApplications(); |
| } |
| } |
| } |
| |
| /** |
| * Hides the applications grid when the layout animation is over. |
| */ |
| private class HideGrid implements Animation.AnimationListener { |
| public void onAnimationStart(Animation animation) { |
| } |
| |
| public void onAnimationEnd(Animation animation) { |
| mBlockAnimation = false; |
| } |
| |
| public void onAnimationRepeat(Animation animation) { |
| } |
| } |
| |
| /** |
| * Shows the applications grid when the layout animation is over. |
| */ |
| private class ShowGrid implements Animation.AnimationListener { |
| public void onAnimationStart(Animation animation) { |
| } |
| |
| public void onAnimationEnd(Animation animation) { |
| mBlockAnimation = false; |
| // ViewDebug.stopHierarchyTracing(); |
| } |
| |
| public void onAnimationRepeat(Animation animation) { |
| } |
| } |
| |
| /** |
| * Starts the selected activity/application in the grid view. |
| */ |
| private class ApplicationLauncher implements AdapterView.OnItemClickListener { |
| public void onItemClick(AdapterView parent, View v, int position, long id) { |
| ApplicationInfo app = (ApplicationInfo) parent.getItemAtPosition(position); |
| startActivity(app.intent); |
| } |
| } |
| |
| /** |
| * When a drawable is attached to a View, the View gives the Drawable its dimensions |
| * by calling Drawable.setBounds(). In this application, the View that draws the |
| * wallpaper has the same size as the screen. However, the wallpaper might be larger |
| * that the screen which means it will be automatically stretched. Because stretching |
| * a bitmap while drawing it is very expensive, we use a ClippedDrawable instead. |
| * This drawable simply draws another wallpaper but makes sure it is not stretched |
| * by always giving it its intrinsic dimensions. If the wallpaper is larger than the |
| * screen, it will simply get clipped but it won't impact performance. |
| */ |
| private class ClippedDrawable extends Drawable { |
| private final Drawable mWallpaper; |
| |
| public ClippedDrawable(Drawable wallpaper) { |
| mWallpaper = wallpaper; |
| } |
| |
| @Override |
| public void setBounds(int left, int top, int right, int bottom) { |
| super.setBounds(left, top, right, bottom); |
| // Ensure the wallpaper is as large as it really is, to avoid stretching it |
| // at drawing time |
| mWallpaper.setBounds(left, top, left + mWallpaper.getIntrinsicWidth(), |
| top + mWallpaper.getIntrinsicHeight()); |
| } |
| |
| public void draw(Canvas canvas) { |
| mWallpaper.draw(canvas); |
| } |
| |
| public void setAlpha(int alpha) { |
| mWallpaper.setAlpha(alpha); |
| } |
| |
| public void setColorFilter(ColorFilter cf) { |
| mWallpaper.setColorFilter(cf); |
| } |
| |
| public int getOpacity() { |
| return mWallpaper.getOpacity(); |
| } |
| } |
| } |