diff options
18 files changed, 1070 insertions, 326 deletions
diff --git a/res/anim/paged_view_click_feedback.xml b/res/anim/paged_view_click_feedback.xml new file mode 100644 index 0000000..786d974 --- /dev/null +++ b/res/anim/paged_view_click_feedback.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<alpha xmlns:android="http://schemas.android.com/apk/res/android" + android:fromAlpha="1.0" + android:toAlpha="0.65" + android:duration="100" + android:fillAfter="true" + android:repeatCount="1" + android:repeatMode="reverse" /> diff --git a/res/drawable/default_widget_preview.9.png b/res/drawable/default_widget_preview.9.png Binary files differindex e966b1b..b3ddada 100644 --- a/res/drawable/default_widget_preview.9.png +++ b/res/drawable/default_widget_preview.9.png diff --git a/res/layout-xlarge/all_apps_paged_view_application.xml b/res/layout-xlarge/all_apps_paged_view_application.xml index 98c2737..7448765 100644 --- a/res/layout-xlarge/all_apps_paged_view_application.xml +++ b/res/layout-xlarge/all_apps_paged_view_application.xml @@ -18,12 +18,13 @@ android:id="@+id/name" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_gravity="center_horizontal" + android:gravity="center_horizontal" android:textColor="#FFFFFFFF" android:shadowColor="#FF000000" android:shadowDx="0.0" android:shadowDy="1.0" + android:shadowRadius="1.0" android:maxLines="2" android:fadingEdge="horizontal" /> diff --git a/res/layout-xlarge/customize_paged_view_item.xml b/res/layout-xlarge/customize_paged_view_item.xml new file mode 100644 index 0000000..7448765 --- /dev/null +++ b/res/layout-xlarge/customize_paged_view_item.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/name" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:gravity="center_horizontal" + + android:textColor="#FFFFFFFF" + android:shadowColor="#FF000000" + android:shadowDx="0.0" + android:shadowDy="1.0" + android:shadowRadius="1.0" + + android:maxLines="2" + android:fadingEdge="horizontal" /> diff --git a/res/layout-xlarge/customize_paged_view_wallpaper_placeholder.xml b/res/layout-xlarge/customize_paged_view_wallpaper_placeholder.xml new file mode 100644 index 0000000..df73bcd --- /dev/null +++ b/res/layout-xlarge/customize_paged_view_wallpaper_placeholder.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/name" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:gravity="center_vertical" + + android:textColor="#FFFFFFFF" + android:shadowColor="#FF000000" + android:shadowDx="0.0" + android:shadowDy="1.0" + android:shadowRadius="1.0" + android:drawableLeft="@drawable/ic_launcher_wallpaper" + android:drawablePadding="10dip" + + android:maxLines="2" + android:fadingEdge="horizontal" /> diff --git a/res/layout-xlarge/customize_paged_view_widget.xml b/res/layout-xlarge/customize_paged_view_widget.xml new file mode 100644 index 0000000..7448765 --- /dev/null +++ b/res/layout-xlarge/customize_paged_view_widget.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/name" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:gravity="center_horizontal" + + android:textColor="#FFFFFFFF" + android:shadowColor="#FF000000" + android:shadowDx="0.0" + android:shadowDy="1.0" + android:shadowRadius="1.0" + + android:maxLines="2" + android:fadingEdge="horizontal" /> diff --git a/res/layout-xlarge/launcher.xml b/res/layout-xlarge/launcher.xml index a1879ef..3e00381 100644 --- a/res/layout-xlarge/launcher.xml +++ b/res/layout-xlarge/launcher.xml @@ -26,7 +26,7 @@ layout="@layout/all_apps_tabbed" android:id="@+id/all_apps_view" android:layout_width="match_parent" - android:layout_height="500dip" + android:layout_height="550dip" android:layout_gravity="top"/> <!-- The workspace contains 5 screens of cells --> @@ -112,38 +112,24 @@ <TabHost android:id="@android:id/tabhost" android:layout_width="match_parent" - android:layout_height="500dip" + android:layout_height="550dip" android:layout_gravity="bottom"> <LinearLayout android:orientation="vertical" + android:background="#40000000" android:layout_width="match_parent" android:layout_height="match_parent"> <TabWidget android:id="@android:id/tabs" - android:layout_width="match_parent" - android:layout_height="wrap_content" /> + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:tabStripEnabled="false" + android:paddingBottom="10dp" /> <FrameLayout android:id="@android:id/tabcontent" - android:background="#ff000000" android:layout_width="match_parent" android:layout_height="match_parent"> - <com.android.launcher2.WidgetChooser - android:id="@+id/widget_chooser" - android:layout_width="match_parent" - android:layout_height="match_parent" /> - <com.android.launcher2.FolderChooser - android:id="@+id/folder_chooser" - android:layout_width="match_parent" - android:layout_height="match_parent" /> - <com.android.launcher2.ShortcutChooser - android:id="@+id/shortcut_chooser" - android:layout_width="match_parent" - android:layout_height="match_parent" /> - <TextView - android:id="@+id/wallpaperstab" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:text="@string/wallpapers_temp_tab_text" /> </FrameLayout> </LinearLayout> </TabHost> diff --git a/src/com/android/launcher2/AllAppsPagedView.java b/src/com/android/launcher2/AllAppsPagedView.java index e0d248e..4e81937 100644 --- a/src/com/android/launcher2/AllAppsPagedView.java +++ b/src/com/android/launcher2/AllAppsPagedView.java @@ -20,6 +20,10 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import android.animation.Animatable; +import android.animation.AnimatableListenerAdapter; +import android.animation.Animator; +import android.animation.PropertyAnimator; import android.content.ComponentName; import android.content.Context; import android.content.res.TypedArray; @@ -98,9 +102,11 @@ public class AllAppsPagedView extends PagedView public void setAppFilter(int filterType) { mAppFilter = filterType; - mFilteredApps = rebuildFilteredApps(mApps); - setCurrentScreen(0); - invalidatePageData(); + if (mApps != null) { + mFilteredApps = rebuildFilteredApps(mApps); + setCurrentScreen(0); + invalidatePageData(); + } } @Override @@ -156,22 +162,13 @@ public class AllAppsPagedView extends PagedView if (childIndex == getCurrentScreen()) { final ApplicationInfo app = (ApplicationInfo) v.getTag(); - AlphaAnimation anim = new AlphaAnimation(1.0f, 0.65f); - anim.setDuration(100); - anim.setFillAfter(true); - anim.setRepeatMode(AlphaAnimation.REVERSE); - anim.setRepeatCount(1); - anim.setAnimationListener(new AnimationListener() { - @Override - public void onAnimationStart(Animation animation) {} + // animate some feedback to the click + animateClickFeedback(v, new Runnable() { @Override - public void onAnimationRepeat(Animation animation) { + public void run() { mLauncher.startActivitySafely(app.intent, app); } - @Override - public void onAnimationEnd(Animation animation) {} }); - v.startAnimation(anim); } } @@ -223,26 +220,30 @@ public class AllAppsPagedView extends PagedView @Override public void setApps(ArrayList<ApplicationInfo> list) { mApps = list; - Collections.sort(mApps, new Comparator<ApplicationInfo>() { - @Override - public int compare(ApplicationInfo object1, ApplicationInfo object2) { - return object1.title.toString().compareTo(object2.title.toString()); - } - }); + Collections.sort(mApps, LauncherModel.APP_NAME_COMPARATOR); mFilteredApps = rebuildFilteredApps(mApps); invalidatePageData(); } + private void addAppsWithoutInvalidate(ArrayList<ApplicationInfo> list) { + // we add it in place, in alphabetical order + final int count = list.size(); + for (int i = 0; i < count; ++i) { + final ApplicationInfo info = list.get(i); + final int index = Collections.binarySearch(mApps, info, LauncherModel.APP_NAME_COMPARATOR); + if (index < 0) { + mApps.add(-(index + 1), info); + } + } + mFilteredApps = rebuildFilteredApps(mApps); + } @Override public void addApps(ArrayList<ApplicationInfo> list) { - // TODO: we need to add it in place, in alphabetical order - mApps.addAll(list); - mFilteredApps.addAll(rebuildFilteredApps(list)); + addAppsWithoutInvalidate(list); invalidatePageData(); } - @Override - public void removeApps(ArrayList<ApplicationInfo> list) { + private void removeAppsWithoutInvalidate(ArrayList<ApplicationInfo> list) { // loop through all the apps and remove apps that have the same component final int length = list.size(); for (int i = 0; i < length; ++i) { @@ -251,14 +252,19 @@ public class AllAppsPagedView extends PagedView mApps.remove(removeIndex); } } - mFilteredApps = rebuildFilteredApps(list); + mFilteredApps = rebuildFilteredApps(mApps); + } + @Override + public void removeApps(ArrayList<ApplicationInfo> list) { + removeAppsWithoutInvalidate(list); invalidatePageData(); } @Override public void updateApps(ArrayList<ApplicationInfo> list) { - removeApps(list); - addApps(list); + removeAppsWithoutInvalidate(list); + addAppsWithoutInvalidate(list); + invalidatePageData(); } private int findAppByComponent(ArrayList<ApplicationInfo> list, ApplicationInfo item) { @@ -308,25 +314,43 @@ public class AllAppsPagedView extends PagedView @Override public void syncPageItems(int page) { // ensure that we have the right number of items on the pages - int numCells = mCellCountX * mCellCountY; - int startIndex = page * numCells; - int endIndex = Math.min(startIndex + numCells, mFilteredApps.size()); + final int cellsPerPage = mCellCountX * mCellCountY; + final int startIndex = page * cellsPerPage; + final int endIndex = Math.min(startIndex + cellsPerPage, mFilteredApps.size()); PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(page); - // TODO: we can optimize by just re-applying to existing views - layout.removeAllViews(); + + final int curNumPageItems = layout.getChildCount(); + final int numPageItems = endIndex - startIndex; + + // remove any extra items + int extraPageItemsDiff = curNumPageItems - numPageItems; + for (int i = 0; i < extraPageItemsDiff; ++i) { + layout.removeViewAt(numPageItems); + } + // add any necessary items + for (int i = curNumPageItems; i < numPageItems; ++i) { + TextView text = (TextView) mInflater.inflate(R.layout.all_apps_paged_view_application, layout, false); + text.setOnClickListener(this); + text.setOnLongClickListener(this); + + layout.addViewToCellLayout(text, -1, i, + new PagedViewCellLayout.LayoutParams(0, 0, 1, 1)); + } + + // actually reapply to the existing text views for (int i = startIndex; i < endIndex; ++i) { + int index = i - startIndex; ApplicationInfo info = mFilteredApps.get(i); - TextView text = (TextView) mInflater.inflate(R.layout.all_apps_paged_view_application, layout, false); + TextView text = (TextView) layout.getChildAt(index); text.setCompoundDrawablesWithIntrinsicBounds(null, new BitmapDrawable(info.iconBitmap), null, null); text.setText(info.title); text.setTag(info); - text.setOnClickListener(this); - text.setOnLongClickListener(this); - int index = i - startIndex; - layout.addViewToCellLayout(text, index, i, - new PagedViewCellLayout.LayoutParams(index % mCellCountX, index / mCellCountX, 1, 1)); + PagedViewCellLayout.LayoutParams params = + (PagedViewCellLayout.LayoutParams) text.getLayoutParams(); + params.cellX = index % mCellCountX; + params.cellY = index / mCellCountX; } } diff --git a/src/com/android/launcher2/AllAppsTabbed.java b/src/com/android/launcher2/AllAppsTabbed.java index 0470cee..114da4e 100644 --- a/src/com/android/launcher2/AllAppsTabbed.java +++ b/src/com/android/launcher2/AllAppsTabbed.java @@ -18,6 +18,10 @@ package com.android.launcher2; import java.util.ArrayList; +import android.animation.Animatable; +import android.animation.AnimatableListenerAdapter; +import android.animation.Animator; +import android.animation.PropertyAnimator; import android.content.Context; import android.content.res.Resources; import android.graphics.Color; @@ -96,16 +100,30 @@ public class AllAppsTabbed extends TabHost implements AllAppsView { setOnTabChangedListener(new OnTabChangeListener() { public void onTabChanged(String tabId) { - String tag = getCurrentTabTag(); - if (tag == TAG_ALL) { - mAllApps.setAppFilter(AllAppsPagedView.ALL_APPS_FLAG); - } else if (tag == TAG_APPS) { - mAllApps.setAppFilter(ApplicationInfo.APP_FLAG); - } else if (tag == TAG_GAMES) { - mAllApps.setAppFilter(ApplicationInfo.GAME_FLAG); - } else if (tag == TAG_DOWNLOADED) { - mAllApps.setAppFilter(ApplicationInfo.DOWNLOADED_FLAG); - } + // animate the changing of the tab content by fading pages in and out + final int duration = 150; + final float alpha = mAllApps.getAlpha(); + Animator alphaAnim = new PropertyAnimator(duration, mAllApps, "alpha", alpha, 0.0f); + alphaAnim.addListener(new AnimatableListenerAdapter() { + public void onAnimationEnd(Animatable animation) { + String tag = getCurrentTabTag(); + if (tag == TAG_ALL) { + mAllApps.setAppFilter(AllAppsPagedView.ALL_APPS_FLAG); + } else if (tag == TAG_APPS) { + mAllApps.setAppFilter(ApplicationInfo.APP_FLAG); + } else if (tag == TAG_GAMES) { + mAllApps.setAppFilter(ApplicationInfo.GAME_FLAG); + } else if (tag == TAG_DOWNLOADED) { + mAllApps.setAppFilter(ApplicationInfo.DOWNLOADED_FLAG); + } + + final float alpha = mAllApps.getAlpha(); + Animator alphaAnim = + new PropertyAnimator(duration, mAllApps, "alpha", alpha, 1.0f); + alphaAnim.start(); + } + }); + alphaAnim.start(); } }); @@ -135,9 +153,12 @@ public class AllAppsTabbed extends TabHost implements AllAppsView { @Override public void setVisibility(int visibility) { + final boolean isVisible = (visibility == View.VISIBLE); super.setVisibility(visibility); - float zoom = visibility == View.VISIBLE ? 1.0f : 0.0f; + float zoom = (isVisible ? 1.0f : 0.0f); mAllApps.zoom(zoom, false); + if (!isVisible) + mAllApps.cleanup(); } @Override diff --git a/src/com/android/launcher2/CustomizePagedView.java b/src/com/android/launcher2/CustomizePagedView.java new file mode 100644 index 0000000..7679e39 --- /dev/null +++ b/src/com/android/launcher2/CustomizePagedView.java @@ -0,0 +1,535 @@ +/* + * 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.launcher2; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import android.appwidget.AppWidgetManager; +import android.appwidget.AppWidgetProviderInfo; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Rect; +import android.graphics.Bitmap.Config; +import android.graphics.Region.Op; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.provider.LiveFolders; +import android.util.AttributeSet; +import android.util.Log; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.TextView; + +import com.android.launcher.R; + +public class CustomizePagedView extends PagedView + implements View.OnLongClickListener, + DragSource { + + public enum CustomizationType { + WidgetCustomization, + FolderCustomization, + ShortcutCustomization, + WallpaperCustomization + } + + private static final String TAG = "CustomizeWorkspace"; + private static final boolean DEBUG = false; + + private Launcher mLauncher; + private DragController mDragController; + private PackageManager mPackageManager; + + private CustomizationType mCustomizationType; + + private PagedViewCellLayout mTmpWidgetLayout; + private ArrayList<ArrayList<PagedViewCellLayout.LayoutParams>> mWidgetPages; + private List<AppWidgetProviderInfo> mWidgetList; + private List<ResolveInfo> mFolderList; + private List<ResolveInfo> mShortcutList; + + private int mCellCountX; + private int mCellCountY; + + private final Canvas mCanvas = new Canvas(); + private final LayoutInflater mInflater; + + public CustomizePagedView(Context context) { + this(context, null); + } + + public CustomizePagedView(Context context, AttributeSet attrs) { + super(context, attrs); + mCellCountX = 8; + mCellCountY = 4; + mCustomizationType = CustomizationType.WidgetCustomization; + mWidgetPages = new ArrayList<ArrayList<PagedViewCellLayout.LayoutParams>>(); + mTmpWidgetLayout = new PagedViewCellLayout(context); + mInflater = LayoutInflater.from(context); + setupPage(mTmpWidgetLayout); + setVisibility(View.GONE); + setSoundEffectsEnabled(false); + } + + public void setLauncher(Launcher launcher) { + Context context = getContext(); + mLauncher = launcher; + mPackageManager = context.getPackageManager(); + } + + public void update() { + Context context = getContext(); + + // get the list of widgets + mWidgetList = AppWidgetManager.getInstance(mLauncher).getInstalledProviders(); + Collections.sort(mWidgetList, new Comparator<AppWidgetProviderInfo>() { + @Override + public int compare(AppWidgetProviderInfo object1, AppWidgetProviderInfo object2) { + return object1.label.compareTo(object2.label); + } + }); + + Comparator<ResolveInfo> resolveInfoComparator = new Comparator<ResolveInfo>() { + @Override + public int compare(ResolveInfo object1, ResolveInfo object2) { + return object1.loadLabel(mPackageManager).toString().compareTo( + object2.loadLabel(mPackageManager).toString()); + } + }; + + // get the list of live folder intents + Intent liveFolderIntent = new Intent(LiveFolders.ACTION_CREATE_LIVE_FOLDER); + mFolderList = mPackageManager.queryIntentActivities(liveFolderIntent, 0); + + // manually create a separate entry for creating a folder in Launcher + ResolveInfo folder = new ResolveInfo(); + folder.icon = R.drawable.ic_launcher_folder; + folder.labelRes = R.string.group_folder; + folder.resolvePackageName = context.getPackageName(); + mFolderList.add(0, folder); + Collections.sort(mFolderList, resolveInfoComparator); + + // get the list of shortcuts + Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT); + mShortcutList = mPackageManager.queryIntentActivities(shortcutsIntent, 0); + Collections.sort(mShortcutList, resolveInfoComparator); + + invalidatePageData(); + } + + public void setDragController(DragController dragger) { + mDragController = dragger; + } + + public void setCustomizationFilter(CustomizationType filterType) { + mCustomizationType = filterType; + setCurrentScreen(0); + invalidatePageData(); + } + + @Override + public void onDropCompleted(View target, boolean success) { + // do nothing + } + + @Override + public boolean onLongClick(View v) { + if (!v.isInTouchMode()) { + return false; + } + + final View animView = v; + switch (mCustomizationType) { + case WidgetCustomization: + AppWidgetProviderInfo appWidgetInfo = (AppWidgetProviderInfo) v.getTag(); + LauncherAppWidgetInfo dragInfo = new LauncherAppWidgetInfo(appWidgetInfo.provider); + dragInfo.minWidth = appWidgetInfo.minWidth; + dragInfo.minHeight = appWidgetInfo.minHeight; + mDragController.startDrag(v, this, dragInfo, DragController.DRAG_ACTION_COPY); + mLauncher.hideCustomizationDrawer(); + return true; + case FolderCustomization: + // animate some feedback to the long press + animateClickFeedback(v, new Runnable() { + @Override + public void run() { + // add the folder + ResolveInfo resolveInfo = (ResolveInfo) animView.getTag(); + Intent createFolderIntent = new Intent(LiveFolders.ACTION_CREATE_LIVE_FOLDER); + if (resolveInfo.labelRes == R.string.group_folder) { + // Create app shortcuts is a special built-in case of shortcuts + createFolderIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, + getContext().getString(R.string.group_folder)); + } else { + ComponentName name = new ComponentName(resolveInfo.activityInfo.packageName, + resolveInfo.activityInfo.name); + createFolderIntent.setComponent(name); + } + mLauncher.prepareAddItemFromHomeCustomizationDrawer(); + mLauncher.addLiveFolder(createFolderIntent); + } + }); + return true; + case ShortcutCustomization: + // animate some feedback to the long press + animateClickFeedback(v, new Runnable() { + @Override + public void run() { + // add the shortcut + ResolveInfo info = (ResolveInfo) animView.getTag(); + Intent createShortcutIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT); + if (info.labelRes == R.string.group_applications) { + // Create app shortcuts is a special built-in case of shortcuts + createShortcutIntent.putExtra( + Intent.EXTRA_SHORTCUT_NAME,getContext().getString( + R.string.group_applications)); + } else { + ComponentName name = new ComponentName(info.activityInfo.packageName, + info.activityInfo.name); + createShortcutIntent.setComponent(name); + } + mLauncher.prepareAddItemFromHomeCustomizationDrawer(); + mLauncher.processShortcut(createShortcutIntent); + } + }); + return true; + } + return false; + } + + private int relayoutWidgets() { + final int widgetCount = mWidgetList.size(); + if (widgetCount == 0) return 0; + + mWidgetPages.clear(); + ArrayList<PagedViewCellLayout.LayoutParams> page = + new ArrayList<PagedViewCellLayout.LayoutParams>(); + mWidgetPages.add(page); + int rowOffsetX = 0; + int rowOffsetY = 0; + int curRowHeight = 0; + // we only get the cell dims this way for the layout calculations because + // we know that we aren't going to change the dims when we construct it + // afterwards + for (int i = 0; i < widgetCount; ++i) { + AppWidgetProviderInfo info = mWidgetList.get(i); + PagedViewCellLayout.LayoutParams params; + + final int cellSpanX = mTmpWidgetLayout.estimateCellHSpan(info.minWidth); + final int cellSpanY = mTmpWidgetLayout.estimateCellVSpan(info.minHeight); + + if (((rowOffsetX + cellSpanX) <= mCellCountX) && + ((rowOffsetY + cellSpanY) <= mCellCountY)) { + // just add to end of current row + params = new PagedViewCellLayout.LayoutParams(rowOffsetX, rowOffsetY, + cellSpanX, cellSpanY); + + rowOffsetX += cellSpanX; + curRowHeight = Math.max(curRowHeight, cellSpanY); + } else { + /* + // fix all the items in this last row to be bottom aligned + int prevRowOffsetX = rowOffsetX; + for (int j = page.size() - 1; j >= 0; --j) { + PagedViewCellLayout.LayoutParams params = page.get(j); + // skip once we get to the previous row + if (params.cellX > prevRowOffsetX) + break; + + params.cellY += curRowHeight - params.cellVSpan; + prevRowOffsetX = params.cellX; + } + */ + + // doesn't fit on current row, see if we can start a new row on + // this page + if ((rowOffsetY + curRowHeight + cellSpanY) > mCellCountY) { + // start a new page and add this item to it + page = new ArrayList<PagedViewCellLayout.LayoutParams>(); + mWidgetPages.add(page); + + params = new PagedViewCellLayout.LayoutParams(0, 0, cellSpanX, cellSpanY); + rowOffsetX = cellSpanX; + rowOffsetY = 0; + curRowHeight = cellSpanY; + } else { + // add it to the current page on this new row + params = new PagedViewCellLayout.LayoutParams(0, rowOffsetY + curRowHeight, + cellSpanX, cellSpanY); + + rowOffsetX = cellSpanX; + rowOffsetY += curRowHeight; + curRowHeight = cellSpanY; + } + } + + params.setTag(info); + page.add(params); + } + return mWidgetPages.size(); + } + + private Drawable getWidgetIcon(PagedViewCellLayout.LayoutParams params, + AppWidgetProviderInfo info) { + PackageManager packageManager = mLauncher.getPackageManager(); + String packageName = info.provider.getPackageName(); + Drawable drawable = null; + if (info.previewImage != 0) { + drawable = packageManager.getDrawable(packageName, info.previewImage, null); + if (drawable == null) { + Log.w(TAG, "Can't load icon drawable 0x" + Integer.toHexString(info.icon) + + " for provider: " + info.provider); + } else { + return drawable; + } + } + + // If we don't have a preview image, create a default one + if (drawable == null) { + Resources resources = mLauncher.getResources(); + + // Determine the size the widget will take in the layout + // Create a new bitmap to hold the widget preview + int[] dims = mTmpWidgetLayout.estimateCellDimensions(getMeasuredWidth(), + getMeasuredHeight(), params.cellHSpan, params.cellVSpan); + final int width = dims[0]; + final int height = dims[1] - 35; + // TEMP + // TEMP: HACK ABOVE TO GET TEXT TO SHOW + // TEMP + Bitmap bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888); + mCanvas.setBitmap(bitmap); + // For some reason, we must re-set the clip rect here, otherwise it will be wrong + mCanvas.clipRect(0, 0, width, height, Op.REPLACE); + + Drawable background = resources.getDrawable(R.drawable.default_widget_preview); + background.setBounds(0, 0, width, height); + background.draw(mCanvas); + + // Draw the icon vertically centered, flush left + try { + Rect tmpRect = new Rect(); + Drawable icon = null; + if (info.icon != 0) { + icon = packageManager.getDrawable(packageName, info.icon, null); + } else { + icon = resources.getDrawable(R.drawable.ic_launcher_application); + } + background.getPadding(tmpRect); + + final int iconSize = Math.min( + Math.min(icon.getIntrinsicWidth(), width - tmpRect.left - tmpRect.right), + Math.min(icon.getIntrinsicHeight(), height - tmpRect.top - tmpRect.bottom)); + final int left = (width / 2) - (iconSize / 2); + final int top = (height / 2) - (iconSize / 2); + icon.setBounds(new Rect(left, top, left + iconSize, top + iconSize)); + icon.draw(mCanvas); + } catch (Resources.NotFoundException e) { + // if we can't find the icon, then just don't draw it + } + + drawable = new BitmapDrawable(resources, bitmap); + } + drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); + return drawable; + } + + private void setupPage(PagedViewCellLayout layout) { + layout.setCellCount(mCellCountX, mCellCountY); + layout.setPadding(20, 10, 20, 0); + } + + private void syncWidgetPages() { + if (mWidgetList == null) return; + + // calculate the layout for all the widget pages first and ensure that + // we have the right number of pages + int numPages = relayoutWidgets(); + int curNumPages = getChildCount(); + // remove any extra pages after the "last" page + int extraPageDiff = curNumPages - numPages; + for (int i = 0; i < extraPageDiff; ++i) { + removeViewAt(numPages); + } + // add any necessary pages + for (int i = curNumPages; i < numPages; ++i) { + PagedViewCellLayout layout = new PagedViewCellLayout(getContext()); + setupPage(layout); + addView(layout); + } + } + + private void syncWidgetPageItems(int page) { + // ensure that we have the right number of items on the pages + PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(page); + final ArrayList<PagedViewCellLayout.LayoutParams> list = mWidgetPages.get(page); + final int count = list.size(); + layout.removeAllViews(); + for (int i = 0; i < count; ++i) { + PagedViewCellLayout.LayoutParams params = list.get(i); + AppWidgetProviderInfo info = (AppWidgetProviderInfo) params.getTag(); + final Drawable icon = getWidgetIcon(params, info); + TextView text = (TextView) mInflater.inflate(R.layout.customize_paged_view_widget, + layout, false); + text.setCompoundDrawablesWithIntrinsicBounds(null, icon, null, null); + text.setText(info.label); + text.setTag(info); + text.setOnLongClickListener(this); + + layout.addViewToCellLayout(text, -1, mWidgetList.indexOf(info), params); + } + } + + private void syncListPages(List<ResolveInfo> list) { + // ensure that we have the right number of pages + int numPages = (int) Math.ceil((float) list.size() / (mCellCountX * mCellCountY)); + int curNumPages = getChildCount(); + // remove any extra pages after the "last" page + int extraPageDiff = curNumPages - numPages; + for (int i = 0; i < extraPageDiff; ++i) { + removeViewAt(numPages); + } + // add any necessary pages + for (int i = curNumPages; i < numPages; ++i) { + PagedViewCellLayout layout = new PagedViewCellLayout(getContext()); + setupPage(layout); + addView(layout); + } + } + + private void syncListPageItems(int page, List<ResolveInfo> list) { + // ensure that we have the right number of items on the pages + int numCells = mCellCountX * mCellCountY; + int startIndex = page * numCells; + int endIndex = Math.min(startIndex + numCells, list.size()); + PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(page); + // TODO: we can optimize by just re-applying to existing views + layout.removeAllViews(); + for (int i = startIndex; i < endIndex; ++i) { + ResolveInfo info = list.get(i); + Drawable image = info.loadIcon(mPackageManager); + TextView text = (TextView) mInflater.inflate(R.layout.customize_paged_view_item, + layout, false); + image.setBounds(0, 0, image.getIntrinsicWidth(), image.getIntrinsicHeight()); + text.setCompoundDrawablesWithIntrinsicBounds(null, image, null, null); + text.setText(info.loadLabel(mPackageManager)); + text.setTag(info); + text.setOnLongClickListener(this); + + final int index = i - startIndex; + final int x = index % mCellCountX; + final int y = index / mCellCountX; + layout.addViewToCellLayout(text, -1, i, new PagedViewCellLayout.LayoutParams(x,y, 1,1)); + } + } + + private void syncWallpaperPages() { + // NOT CURRENTLY IMPLEMENTED + // ensure that we have the right number of pages + int numPages = 1; + int curNumPages = getChildCount(); + // remove any extra pages after the "last" page + int extraPageDiff = curNumPages - numPages; + for (int i = 0; i < extraPageDiff; ++i) { + removeViewAt(numPages); + } + // add any necessary pages + for (int i = curNumPages; i < numPages; ++i) { + PagedViewCellLayout layout = new PagedViewCellLayout(getContext()); + setupPage(layout); + addView(layout); + } + } + + private void syncWallpaperPageItems(int page) { + PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(page); + layout.removeAllViews(); + + TextView text = (TextView) mInflater.inflate( + R.layout.customize_paged_view_wallpaper_placeholder, layout, false); + // NOTE: this is just place holder text until MikeJurka implements wallpaper picker + text.setText("Wallpaper customization coming soon!"); + + layout.addViewToCellLayout(text, -1, 0, new PagedViewCellLayout.LayoutParams(0, 0, 3, 1)); + } + + @Override + public void syncPages() { + switch (mCustomizationType) { + case WidgetCustomization: + syncWidgetPages(); + break; + case FolderCustomization: + syncListPages(mFolderList); + break; + case ShortcutCustomization: + syncListPages(mShortcutList); + break; + case WallpaperCustomization: + syncWallpaperPages(); + break; + default: + removeAllViews(); + setCurrentScreen(0); + break; + } + + // only try and center the page if there is one page + final int childCount = getChildCount(); + if (childCount == 1) { + PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(0); + layout.enableCenteredContent(true); + } else { + for (int i = 0; i < childCount; ++i) { + PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(i); + layout.enableCenteredContent(false); + } + } + + // bound the current page + setCurrentScreen(Math.max(0, Math.min(childCount - 1, getCurrentScreen()))); + } + + @Override + public void syncPageItems(int page) { + switch (mCustomizationType) { + case WidgetCustomization: + syncWidgetPageItems(page); + break; + case FolderCustomization: + syncListPageItems(page, mFolderList); + break; + case ShortcutCustomization: + syncListPageItems(page, mShortcutList); + break; + case WallpaperCustomization: + syncWallpaperPageItems(page); + break; + } + } +} diff --git a/src/com/android/launcher2/FolderChooser.java b/src/com/android/launcher2/FolderChooser.java deleted file mode 100644 index b152ad5..0000000 --- a/src/com/android/launcher2/FolderChooser.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.android.launcher2; - -import com.android.launcher.R; - -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.pm.ResolveInfo; -import android.provider.LiveFolders; -import android.util.AttributeSet; -import android.view.View; -import android.widget.AdapterView; - -public class FolderChooser extends HomeCustomizationItemGallery { - - public FolderChooser(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { - // todo: this code sorta overlaps with other places - ResolveInfo info = (ResolveInfo)getAdapter().getItem(position); - mLauncher.prepareAddItemFromHomeCustomizationDrawer(); - - Intent createFolderIntent = new Intent(LiveFolders.ACTION_CREATE_LIVE_FOLDER); - if (info.labelRes == R.string.group_folder) { - // Create app shortcuts is a special built-in case of shortcuts - createFolderIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, getContext().getString(R.string.group_folder)); - } else { - ComponentName name = new ComponentName(info.activityInfo.packageName, info.activityInfo.name); - createFolderIntent.setComponent(name); - } - mLauncher.addLiveFolder(createFolderIntent); - - return true; - } -} diff --git a/src/com/android/launcher2/HomeCustomizationItemGallery.java b/src/com/android/launcher2/HomeCustomizationItemGallery.java deleted file mode 100644 index 2eba49f..0000000 --- a/src/com/android/launcher2/HomeCustomizationItemGallery.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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.launcher2; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.MotionEvent; -import android.widget.Gallery; - -public abstract class HomeCustomizationItemGallery extends Gallery - implements Gallery.OnItemLongClickListener { - - protected Context mContext; - - protected Launcher mLauncher; - - protected int mMotionDownRawX; - protected int mMotionDownRawY; - - public HomeCustomizationItemGallery(Context context, AttributeSet attrs) { - super(context, attrs); - setLongClickable(true); - setOnItemLongClickListener(this); - mContext = context; - - setCallbackDuringFling(false); - } - - public void setLauncher(Launcher launcher) { - mLauncher = launcher; - } - - @Override - public boolean onTouchEvent(MotionEvent ev) { - if (ev.getAction() == MotionEvent.ACTION_DOWN && mLauncher.isAllAppsVisible()) { - return false; - } - - super.onTouchEvent(ev); - - int x = (int) ev.getX(); - int y = (int) ev.getY(); - - switch (ev.getAction()) { - case MotionEvent.ACTION_DOWN: - mMotionDownRawX = (int) ev.getRawX(); - mMotionDownRawY = (int) ev.getRawY(); - } - return true; - } -} - diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java index ccd6f65..41f9e7d 100644 --- a/src/com/android/launcher2/Launcher.java +++ b/src/com/android/launcher2/Launcher.java @@ -16,8 +16,13 @@ package com.android.launcher2; -import com.android.common.Search; -import com.android.launcher.R; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; import android.animation.Animatable; import android.animation.AnimatableListenerAdapter; @@ -39,8 +44,8 @@ import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; -import android.content.Intent.ShortcutIconResource; import android.content.IntentFilter; +import android.content.Intent.ShortcutIconResource; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; @@ -50,6 +55,7 @@ import android.content.res.TypedArray; import android.database.ContentObserver; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; @@ -73,10 +79,12 @@ import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; -import android.view.View.OnLongClickListener; import android.view.ViewGroup; import android.view.WindowManager; import android.view.animation.AccelerateInterpolator; +import android.view.View.OnLongClickListener; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import android.view.inputmethod.InputMethodManager; @@ -84,17 +92,16 @@ import android.widget.EditText; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.PopupWindow; +import android.widget.RelativeLayout; import android.widget.TabHost; +import android.widget.TabWidget; import android.widget.TextView; import android.widget.Toast; +import android.widget.TabHost.OnTabChangeListener; +import android.widget.TabHost.TabContentFactory; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; +import com.android.common.Search; +import com.android.launcher.R; /** * Default launcher application. @@ -166,6 +173,12 @@ public final class Launcher extends Activity // Type: long private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME_ID = "launcher.rename_folder_id"; + // tags for the customization tabs + private static final String WIDGETS_TAG = "widgets"; + private static final String FOLDERS_TAG = "folders"; + private static final String SHORTCUTS_TAG = "shortcuts"; + private static final String WALLPAPERS_TAG = "wallpapers"; + static final int APPWIDGET_HOST_ID = 1024; private static final Object sLock = new Object(); @@ -192,6 +205,7 @@ public final class Launcher extends Activity private HandleView mHandleView; private AllAppsView mAllAppsGrid; private TabHost mHomeCustomizationDrawer; + private CustomizePagedView mCustomizePagedView; private Bundle mSavedState; @@ -252,18 +266,72 @@ public final class Launcher extends Activity if (mHomeCustomizationDrawer != null) { mHomeCustomizationDrawer.setup(); + // share the same customization workspace across all the tabs + mCustomizePagedView = new CustomizePagedView(this); + TabContentFactory contentFactory = new TabContentFactory() { + public View createTabContent(String tag) { + return mCustomizePagedView; + } + }; + String widgetsLabel = getString(R.string.widgets_tab_label); - mHomeCustomizationDrawer.addTab(mHomeCustomizationDrawer.newTabSpec("widgets") - .setIndicator(widgetsLabel).setContent(R.id.widget_chooser)); + mHomeCustomizationDrawer.addTab(mHomeCustomizationDrawer.newTabSpec(WIDGETS_TAG) + .setIndicator(widgetsLabel).setContent(contentFactory)); String foldersLabel = getString(R.string.folders_tab_label); - mHomeCustomizationDrawer.addTab(mHomeCustomizationDrawer.newTabSpec("folders") - .setIndicator(foldersLabel).setContent(R.id.folder_chooser)); + mHomeCustomizationDrawer.addTab(mHomeCustomizationDrawer.newTabSpec(FOLDERS_TAG) + .setIndicator(foldersLabel).setContent(contentFactory)); String shortcutsLabel = getString(R.string.shortcuts_tab_label); - mHomeCustomizationDrawer.addTab(mHomeCustomizationDrawer.newTabSpec("shortcuts") - .setIndicator(shortcutsLabel).setContent(R.id.shortcut_chooser)); + mHomeCustomizationDrawer.addTab(mHomeCustomizationDrawer.newTabSpec(SHORTCUTS_TAG) + .setIndicator(shortcutsLabel).setContent(contentFactory)); String wallpapersLabel = getString(R.string.wallpapers_tab_label); - mHomeCustomizationDrawer.addTab(mHomeCustomizationDrawer.newTabSpec("wallpapers") - .setIndicator(wallpapersLabel).setContent(R.id.wallpaperstab)); + mHomeCustomizationDrawer.addTab(mHomeCustomizationDrawer.newTabSpec(WALLPAPERS_TAG) + .setIndicator(wallpapersLabel).setContent(contentFactory)); + + // TEMP: just styling the tab widget to be a bit nicer until we get the actual + // new assets + TabWidget tabWidget = mHomeCustomizationDrawer.getTabWidget(); + for (int i = 0; i < tabWidget.getChildCount(); ++i) { + RelativeLayout tab = (RelativeLayout) tabWidget.getChildTabViewAt(i); + TextView text = (TextView) tab.getChildAt(1); + text.setTextSize(20.0f); + text.setPadding(20, 0, 20, 0); + text.setShadowLayer(1.0f, 0.0f, 1.0f, Color.BLACK); + tab.setBackgroundDrawable(null); + } + + mHomeCustomizationDrawer.setOnTabChangedListener(new OnTabChangeListener() { + public void onTabChanged(String tabId) { + // animate the changing of the tab content by fading pages in and out + final int duration = 150; + final float alpha = mCustomizePagedView.getAlpha(); + Animator alphaAnim = new PropertyAnimator(duration, mCustomizePagedView, + "alpha", alpha, 0.0f); + alphaAnim.addListener(new AnimatableListenerAdapter() { + public void onAnimationEnd(Animatable animation) { + String tag = mHomeCustomizationDrawer.getCurrentTabTag(); + if (tag == WIDGETS_TAG) { + mCustomizePagedView.setCustomizationFilter( + CustomizePagedView.CustomizationType.WidgetCustomization); + } else if (tag == FOLDERS_TAG) { + mCustomizePagedView.setCustomizationFilter( + CustomizePagedView.CustomizationType.FolderCustomization); + } else if (tag == SHORTCUTS_TAG) { + mCustomizePagedView.setCustomizationFilter( + CustomizePagedView.CustomizationType.ShortcutCustomization); + } else if (tag == WALLPAPERS_TAG) { + mCustomizePagedView.setCustomizationFilter( + CustomizePagedView.CustomizationType.WallpaperCustomization); + } + + final float alpha = mCustomizePagedView.getAlpha(); + Animator alphaAnim = new PropertyAnimator(duration, mCustomizePagedView, + "alpha", alpha, 1.0f); + alphaAnim.start(); + } + }); + alphaAnim.start(); + } + }); mHomeCustomizationDrawer.setCurrentTab(0); } @@ -776,24 +844,10 @@ public final class Launcher extends Activity mHandleView.setOnLongClickListener(this); } - WidgetChooser widgetChooser = (WidgetChooser) findViewById(R.id.widget_chooser); - if (widgetChooser != null) { - WidgetListAdapter widgetGalleryAdapter = new WidgetListAdapter(this); - widgetChooser.setAdapter(widgetGalleryAdapter); - widgetChooser.setDragController(dragController); - widgetChooser.setLauncher(this); - - FolderChooser folderChooser = (FolderChooser) findViewById(R.id.folder_chooser); - IntentListAdapter folderTypes = new FolderListAdapter( - this, LiveFolders.ACTION_CREATE_LIVE_FOLDER); - folderChooser.setAdapter(folderTypes); - folderChooser.setLauncher(this); - - ShortcutChooser shortcutChooser = (ShortcutChooser) findViewById(R.id.shortcut_chooser); - IntentListAdapter shortcutTypes = new IntentListAdapter( - this, Intent.ACTION_CREATE_SHORTCUT); - shortcutChooser.setAdapter(shortcutTypes); - shortcutChooser.setLauncher(this); + if (mCustomizePagedView != null) { + mCustomizePagedView.setLauncher(this); + mCustomizePagedView.setDragController(dragController); + mCustomizePagedView.update(); } else { ImageView hotseatLeft = (ImageView) findViewById(R.id.hotseat_left); hotseatLeft.setContentDescription(mHotseatLabels[0]); @@ -2192,6 +2246,9 @@ public final class Launcher extends Activity } void showAllApps(boolean animated) { + if (mAllAppsGrid.isVisible()) + return; + if (LauncherApplication.isScreenXLarge()) { mWorkspace.shrinkToBottom(animated); } @@ -2199,6 +2256,7 @@ public final class Launcher extends Activity if (LauncherApplication.isScreenXLarge() && animated) { if (isCustomizationDrawerVisible()) { cameraPan(mHomeCustomizationDrawer, (View) mAllAppsGrid); + mCustomizePagedView.cleanup(); } else { cameraZoomOut((View) mAllAppsGrid, true); } @@ -2299,6 +2357,7 @@ public final class Launcher extends Activity mWorkspace.unshrink(); } cameraZoomIn(mHomeCustomizationDrawer); + mCustomizePagedView.cleanup(); } } @@ -2648,6 +2707,14 @@ public final class Launcher extends Activity } /** + * A number of packages were updated. + */ + public void bindPackagesUpdated() { + // update the customization drawer contents + mCustomizePagedView.update(); + } + + /** * Prints out out state for debugging. */ public void dumpState() { diff --git a/src/com/android/launcher2/LauncherModel.java b/src/com/android/launcher2/LauncherModel.java index 6c3ddd2..797bf68 100644 --- a/src/com/android/launcher2/LauncherModel.java +++ b/src/com/android/launcher2/LauncherModel.java @@ -104,6 +104,7 @@ public class LauncherModel extends BroadcastReceiver { public void bindAppsAdded(ArrayList<ApplicationInfo> apps); public void bindAppsUpdated(ArrayList<ApplicationInfo> apps); public void bindAppsRemoved(ArrayList<ApplicationInfo> apps, boolean permanent); + public void bindPackagesUpdated(); public boolean isAllAppsVisible(); } @@ -1320,6 +1321,15 @@ public class LauncherModel extends BroadcastReceiver { } }); } + + mHandler.post(new Runnable() { + @Override + public void run() { + if (callbacks == mCallbacks.get()) { + callbacks.bindPackagesUpdated(); + } + } + }); } } diff --git a/src/com/android/launcher2/PagedView.java b/src/com/android/launcher2/PagedView.java index 26805e0..0e8ffa0 100644 --- a/src/com/android/launcher2/PagedView.java +++ b/src/com/android/launcher2/PagedView.java @@ -22,6 +22,7 @@ import android.content.Context; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.os.Handler; import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; @@ -32,8 +33,14 @@ import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.ViewParent; +import android.view.animation.AlphaAnimation; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.view.animation.Animation.AnimationListener; import android.widget.Scroller; +import com.android.launcher.R; + /** * An abstraction of the original Workspace which supports browsing through a * sequential list of "pages" (or PagedViewCellLayouts). @@ -81,16 +88,12 @@ public abstract class PagedView extends ViewGroup { private ScreenSwitchListener mScreenSwitchListener; private boolean mDimmedPagesDirty; + private final Handler mHandler = new Handler(); public interface ScreenSwitchListener { void onScreenSwitch(View newScreen, int newScreenIndex); } - /** - * Constructor - * - * @param context The application's context. - */ public PagedView(Context context) { this(context, null); } @@ -158,6 +161,7 @@ public abstract class PagedView extends ViewGroup { mCurrentScreen = Math.max(0, Math.min(currentScreen, getScreenCount() - 1)); scrollTo(getChildOffset(mCurrentScreen) - getRelativeChildOffset(mCurrentScreen), 0); + invalidate(); notifyScreenSwitchListener(); } @@ -457,6 +461,23 @@ public abstract class PagedView extends ViewGroup { return mTouchState != TOUCH_STATE_REST; } + protected void animateClickFeedback(View v, final Runnable r) { + // animate the view slightly to show click feedback running some logic after it is "pressed" + Animation anim = AnimationUtils.loadAnimation(getContext(), + R.anim.paged_view_click_feedback); + anim.setAnimationListener(new AnimationListener() { + @Override + public void onAnimationStart(Animation animation) {} + @Override + public void onAnimationRepeat(Animation animation) { + r.run(); + } + @Override + public void onAnimationEnd(Animation animation) {} + }); + v.startAnimation(anim); + } + /* * Determines if we should change the touch state to start scrolling after the * user moves their touch point too far. @@ -689,6 +710,10 @@ public abstract class PagedView extends ViewGroup { if (!mScroller.isFinished()) mScroller.abortAnimation(); mScroller.startScroll(sX, 0, delta, 0, duration); + + // only load some associated pages + loadAssociatedPages(mNextScreen); + invalidate(); } @@ -775,13 +800,86 @@ public abstract class PagedView extends ViewGroup { }; } + private void clearDimmedBitmaps(boolean skipCurrentScreens) { + final int count = getChildCount(); + if (mCurrentScreen < count) { + if (skipCurrentScreens) { + int lowerScreenBound = Math.max(0, mCurrentScreen - 1); + int upperScreenBound = Math.min(mCurrentScreen + 1, count - 1); + for (int i = 0; i < count; ++i) { + if (i < lowerScreenBound || i > upperScreenBound) { + PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(i); + layout.clearDimmedBitmap(); + } + } + } else { + for (int i = 0; i < count; ++i) { + PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(i); + layout.clearDimmedBitmap(); + } + } + } + } + Runnable clearLayoutOtherDimmedBitmapsRunnable = new Runnable() { + @Override + public void run() { + if (mScroller.isFinished()) { + clearDimmedBitmaps(true); + mHandler.removeMessages(0); + } else { + mHandler.postDelayed(clearLayoutOtherDimmedBitmapsRunnable, 50); + } + } + }; + Runnable clearLayoutDimmedBitmapsRunnable = new Runnable() { + @Override + public void run() { + if (mScroller.isFinished()) { + clearDimmedBitmaps(false); + mHandler.removeMessages(0); + } else { + mHandler.postDelayed(clearLayoutOtherDimmedBitmapsRunnable, 50); + } + } + }; + + // called when this paged view is no longer visible + public void cleanup() { + // clear all the layout dimmed bitmaps + mHandler.removeMessages(0); + mHandler.postDelayed(clearLayoutDimmedBitmapsRunnable, 500); + } + + public void loadAssociatedPages(int screen) { + final int count = getChildCount(); + if (screen < count) { + int lowerScreenBound = Math.max(0, screen - 1); + int upperScreenBound = Math.min(screen + 1, count - 1); + boolean hasDimmedBitmap = false; + for (int i = 0; i < count; ++i) { + if (lowerScreenBound <= i && i <= upperScreenBound) { + syncPageItems(i); + } else { + PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(i); + if (layout.getChildCount() > 0) { + layout.removeAllViews(); + } + hasDimmedBitmap |= layout.getDimmedBitmapAlpha() > 0.0f; + } + } + + if (hasDimmedBitmap) { + mHandler.removeMessages(0); + mHandler.postDelayed(clearLayoutOtherDimmedBitmapsRunnable, 500); + } + } + } + public abstract void syncPages(); public abstract void syncPageItems(int page); public void invalidatePageData() { syncPages(); - for (int i = 0; i < getChildCount(); ++i) { - syncPageItems(i); - } + loadAssociatedPages(mCurrentScreen); invalidateDimmedPages(); requestLayout(); } diff --git a/src/com/android/launcher2/PagedViewCellLayout.java b/src/com/android/launcher2/PagedViewCellLayout.java index 6c9ff6d..16df2a4 100644 --- a/src/com/android/launcher2/PagedViewCellLayout.java +++ b/src/com/android/launcher2/PagedViewCellLayout.java @@ -55,6 +55,8 @@ public class PagedViewCellLayout extends ViewGroup { private final Rect mLayoutRect = new Rect(); private final Rect mDimmedBitmapRect = new Rect(); + private boolean mCenterContent; + private int mCellCountX; private int mCellCountY; private int mCellWidth; @@ -236,13 +238,28 @@ public class PagedViewCellLayout extends ViewGroup { protected void onLayout(boolean changed, int l, int t, int r, int b) { int count = getChildCount(); + int offsetX = 0; + if (mCenterContent) { + // determine the max width of all the rows and center accordingly + int maxRowWidth = 0; + for (int i = 0; i < count; i++) { + View child = getChildAt(i); + if (child.getVisibility() != GONE) { + PagedViewCellLayout.LayoutParams lp = + (PagedViewCellLayout.LayoutParams) child.getLayoutParams(); + maxRowWidth = Math.max(maxRowWidth, lp.x + lp.width); + } + } + offsetX = (getMeasuredWidth() / 2) - (maxRowWidth / 2); + } + for (int i = 0; i < count; i++) { View child = getChildAt(i); if (child.getVisibility() != GONE) { PagedViewCellLayout.LayoutParams lp = (PagedViewCellLayout.LayoutParams) child.getLayoutParams(); - int childLeft = lp.x; + int childLeft = offsetX + lp.x; int childTop = lp.y; child.layout(childLeft, childTop, childLeft + lp.width, childTop + lp.height); } @@ -261,6 +278,10 @@ public class PagedViewCellLayout extends ViewGroup { return super.onTouchEvent(event) || true; } + public void enableCenteredContent(boolean enabled) { + mCenterContent = enabled; + } + @Override protected void setChildrenDrawingCacheEnabled(boolean enabled) { final int count = getChildCount(); @@ -327,12 +348,32 @@ public class PagedViewCellLayout extends ViewGroup { } } + public void clearDimmedBitmap() { + setDimmedBitmapAlpha(0.0f); + if (mDimmedBitmap != null) { + mDimmedBitmap.recycle(); + mDimmedBitmap = null; + } + } + private void setChildrenAlpha(float alpha) { for (int i = 0; i < getChildCount(); i++) { getChildAt(i).setAlpha(alpha); } } + public int[] getCellCountForDimensions(int width, int height) { + // Always assume we're working with the smallest span to make sure we + // reserve enough space in both orientations + int smallerSize = Math.min(mCellWidth, mCellHeight); + + // Always round up to next largest cell + int spanX = (width + smallerSize) / smallerSize; + int spanY = (height + smallerSize) / smallerSize; + + return new int[] { spanX, spanY }; + } + /** * Start dragging the specified child * @@ -343,6 +384,39 @@ public class PagedViewCellLayout extends ViewGroup { lp.isDragging = true; } + public int estimateCellHSpan(int width) { + return (width + mCellWidth) / mCellWidth; + } + public int estimateCellVSpan(int height) { + return (height + mCellHeight) / mCellHeight; + } + public int[] estimateCellDimensions(int approxWidth, int approxHeight, + int cellHSpan, int cellVSpan) { + // NOTE: we are disabling this until we find a good way to approximate this without fully + // measuring + /* + // we may want to use this before any measuring/layout happens, so we pass in an approximate + // size for the layout + int numWidthGaps = mCellCountX - 1; + int numHeightGaps = mCellCountY - 1; + int hSpaceLeft = approxWidth - mPaddingLeft + - mPaddingRight - (mCellWidth * mCellCountX); + int vSpaceLeft = approxHeight - mPaddingTop + - mPaddingBottom - (mCellHeight * mCellCountY); + int widthGap = hSpaceLeft / numWidthGaps; + int heightGap = vSpaceLeft / numHeightGaps; + int minGap = Math.min(widthGap, heightGap); + return new int[] { + (cellHSpan * mCellWidth) + ((cellHSpan - 1) * minGap), + (cellVSpan * mCellHeight) + ((cellVSpan - 1) * minGap) + }; + */ + return new int[] { + (cellHSpan * mCellWidth), + (cellVSpan * mCellHeight) + }; + } + @Override public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { return new PagedViewCellLayout.LayoutParams(getContext(), attrs); @@ -388,6 +462,9 @@ public class PagedViewCellLayout extends ViewGroup { */ public boolean isDragging; + // a data object that you can bind to this layout params + private Object mTag; + // X coordinate of the view in the layout. @ViewDebug.ExportedProperty int x; @@ -395,6 +472,12 @@ public class PagedViewCellLayout extends ViewGroup { @ViewDebug.ExportedProperty int y; + public LayoutParams() { + super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); + cellHSpan = 1; + cellVSpan = 1; + } + public LayoutParams(Context c, AttributeSet attrs) { super(c, attrs); cellHSpan = 1; @@ -440,8 +523,17 @@ public class PagedViewCellLayout extends ViewGroup { y = vStartPadding + myCellY * (cellHeight + heightGap) + topMargin; } + public Object getTag() { + return mTag; + } + + public void setTag(Object tag) { + mTag = tag; + } + public String toString() { - return "(" + this.cellX + ", " + this.cellY + ")"; + return "(" + this.cellX + ", " + this.cellY + ", " + + this.cellHSpan + ", " + this.cellVSpan + ")"; } } } diff --git a/src/com/android/launcher2/ShortcutChooser.java b/src/com/android/launcher2/ShortcutChooser.java deleted file mode 100644 index 1e3e5d0..0000000 --- a/src/com/android/launcher2/ShortcutChooser.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.android.launcher2; - -import com.android.launcher.R; - -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.pm.ResolveInfo; -import android.util.AttributeSet; -import android.view.View; -import android.widget.AdapterView; - -public class ShortcutChooser extends HomeCustomizationItemGallery { - - public ShortcutChooser(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { - // todo: this code sorta overlaps with other places - ResolveInfo info = (ResolveInfo)getAdapter().getItem(position); - mLauncher.prepareAddItemFromHomeCustomizationDrawer(); - - Intent createShortcutIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT); - if (info.labelRes == R.string.group_applications) { - // Create app shortcuts is a special built-in case of shortcuts - createShortcutIntent.putExtra( - Intent.EXTRA_SHORTCUT_NAME,getContext().getString(R.string.group_applications)); - } else { - ComponentName name = new ComponentName(info.activityInfo.packageName, info.activityInfo.name); - createShortcutIntent.setComponent(name); - } - mLauncher.processShortcut(createShortcutIntent); - - return true; - } -} diff --git a/src/com/android/launcher2/WidgetChooser.java b/src/com/android/launcher2/WidgetChooser.java deleted file mode 100644 index 2218e6d..0000000 --- a/src/com/android/launcher2/WidgetChooser.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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.launcher2; - -import android.appwidget.AppWidgetProviderInfo; -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.util.AttributeSet; -import android.view.View; -import android.widget.AdapterView; -import android.widget.TextView; - -public class WidgetChooser extends HomeCustomizationItemGallery implements DragSource { - private DragController mDragController; - - public WidgetChooser(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public void setDragController(DragController dragger) { - mDragController = dragger; - } - - public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { - Drawable[] drawables = ((TextView)view).getCompoundDrawables(); - Bitmap bmp = ((BitmapDrawable)drawables[1]).getBitmap(); - final int w = bmp.getWidth(); - final int h = bmp.getHeight(); - - // We don't really have an accurate location to use. This will do. - int screenX = mMotionDownRawX - (w / 2); - int screenY = mMotionDownRawY - h; - - AppWidgetProviderInfo info = (AppWidgetProviderInfo)getAdapter().getItem(position); - LauncherAppWidgetInfo dragInfo = new LauncherAppWidgetInfo(info.provider); - // TODO: Is this really the best place to do this? - dragInfo.minWidth = info.minWidth; - dragInfo.minHeight = info.minHeight; - mDragController.startDrag(bmp, screenX, screenY, - 0, 0, w, h, this, dragInfo, DragController.DRAG_ACTION_COPY); - mLauncher.hideCustomizationDrawer(); - return true; - } - - public void onDropCompleted(View target, boolean success) { - } -} - |