diff options
Diffstat (limited to 'src/com/android/launcher3')
21 files changed, 926 insertions, 563 deletions
diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java new file mode 100644 index 000000000..9fef7f934 --- /dev/null +++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java @@ -0,0 +1,90 @@ +package com.android.launcher3; + +import android.appwidget.AppWidgetHost; +import android.appwidget.AppWidgetManager; +import android.appwidget.AppWidgetProviderInfo; +import android.content.BroadcastReceiver; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.os.AsyncTask; +import android.util.Log; + +import com.android.launcher3.LauncherSettings.Favorites; + +import java.util.ArrayList; +import java.util.List; + +public class AppWidgetsRestoredReceiver extends BroadcastReceiver { + + private static final String TAG = "AppWidgetsRestoredReceiver"; + + @Override + public void onReceive(Context context, Intent intent) { + if (AppWidgetManager.ACTION_APPWIDGET_HOST_RESTORED.equals(intent.getAction())) { + int[] oldIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_OLD_IDS); + int[] newIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS); + if (oldIds.length == newIds.length) { + restoreAppWidgetIds(context, oldIds, newIds); + } else { + Log.e(TAG, "Invalid host restored received"); + } + } + } + + /** + * Updates the app widgets whose id has changed during the restore process. + */ + static void restoreAppWidgetIds(Context context, int[] oldWidgetIds, int[] newWidgetIds) { + final ContentResolver cr = context.getContentResolver(); + final List<Integer> idsToRemove = new ArrayList<>(); + final AppWidgetManager widgets = AppWidgetManager.getInstance(context); + + for (int i = 0; i < oldWidgetIds.length; i++) { + Log.i(TAG, "Widget state restore id " + oldWidgetIds[i] + " => " + newWidgetIds[i]); + + final AppWidgetProviderInfo provider = widgets.getAppWidgetInfo(newWidgetIds[i]); + + ContentValues values = new ContentValues(); + values.put(LauncherSettings.Favorites.APPWIDGET_ID, newWidgetIds[i]); + values.put(LauncherSettings.Favorites.RESTORED, LauncherModel.isValidProvider(provider) + ? LauncherAppWidgetInfo.RESTORE_COMPLETED + : LauncherAppWidgetInfo.RESTORE_PROVIDER_PENDING); + + String[] widgetIdParams = new String[] { Integer.toString(oldWidgetIds[i]) }; + + int result = cr.update(Favorites.CONTENT_URI, values, + "appWidgetId=? and restored=1", widgetIdParams); + if (result == 0) { + Cursor cursor = cr.query(Favorites.CONTENT_URI, + new String[] {Favorites.APPWIDGET_ID}, + "appWidgetId=?", widgetIdParams, null); + try { + if (!cursor.moveToFirst()) { + // The widget no long exists. + idsToRemove.add(newWidgetIds[i]); + } + } finally { + cursor.close(); + } + } + } + // Unregister the widget IDs which are not present on the workspace. This could happen + // when a widget place holder is removed from workspace, before this method is called. + if (!idsToRemove.isEmpty()) { + final AppWidgetHost appWidgetHost = + new AppWidgetHost(context, Launcher.APPWIDGET_HOST_ID); + new AsyncTask<Void, Void, Void>() { + public Void doInBackground(Void ... args) { + for (Integer id : idsToRemove) { + appWidgetHost.deleteAppWidgetId(id); + Log.e(TAG, "Widget no longer present, appWidgetId=" + id); + } + return null; + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null); + } + } +} diff --git a/src/com/android/launcher3/AppsCustomizePagedView.java b/src/com/android/launcher3/AppsCustomizePagedView.java index 2520b8a12..0e9969697 100644 --- a/src/com/android/launcher3/AppsCustomizePagedView.java +++ b/src/com/android/launcher3/AppsCustomizePagedView.java @@ -377,8 +377,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen int heightSpec = MeasureSpec.makeMeasureSpec(mContentHeight, MeasureSpec.AT_MOST); mWidgetSpacingLayout.measure(widthSpec, heightSpec); - AppsCustomizeTabHost host = (AppsCustomizeTabHost) getTabHost(); - final boolean hostIsTransitioning = host.isTransitioning(); + final boolean hostIsTransitioning = getTabHost().isInTransition(); // Restore the page int page = getPageForComponent(mSaveInstanceStateItemIndex); @@ -1617,12 +1616,8 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen // If we have reset, then we should not continue to restore the previous state mSaveInstanceStateItemIndex = -1; - AppsCustomizeTabHost tabHost = getTabHost(); - String tag = tabHost.getCurrentTabTag(); - if (tag != null) { - if (!tag.equals(tabHost.getTabTagForContentType(ContentType.Applications))) { - tabHost.setCurrentTabFromContent(ContentType.Applications); - } + if (mContentType != ContentType.Applications) { + setContentType(ContentType.Applications); } if (mCurrentPage != 0) { diff --git a/src/com/android/launcher3/AppsCustomizeTabHost.java b/src/com/android/launcher3/AppsCustomizeTabHost.java index c6455c2fe..334d8b6f6 100644 --- a/src/com/android/launcher3/AppsCustomizeTabHost.java +++ b/src/com/android/launcher3/AppsCustomizeTabHost.java @@ -38,35 +38,20 @@ import android.widget.TextView; import java.util.ArrayList; -public class AppsCustomizeTabHost extends TabHost implements LauncherTransitionable, - TabHost.OnTabChangeListener, Insettable { +public class AppsCustomizeTabHost extends FrameLayout implements LauncherTransitionable, Insettable { static final String LOG_TAG = "AppsCustomizeTabHost"; private static final String APPS_TAB_TAG = "APPS"; private static final String WIDGETS_TAB_TAG = "WIDGETS"; - private final LayoutInflater mLayoutInflater; - private ViewGroup mTabs; - private ViewGroup mTabsContainer; - private AppsCustomizePagedView mAppsCustomizePane; - private FrameLayout mAnimationBuffer; - private LinearLayout mContent; - - private boolean mInTransition; - private boolean mTransitioningToWorkspace; - private boolean mResetAfterTransition; - private Runnable mRelayoutAndMakeVisible; + private AppsCustomizePagedView mPagedView; + private View mContent; + private boolean mInTransition = false; + private final Rect mInsets = new Rect(); public AppsCustomizeTabHost(Context context, AttributeSet attrs) { super(context, attrs); - mLayoutInflater = LayoutInflater.from(context); - mRelayoutAndMakeVisible = new Runnable() { - public void run() { - mTabs.requestLayout(); - mTabsContainer.setAlpha(1f); - } - }; } /** @@ -76,17 +61,17 @@ public class AppsCustomizeTabHost extends TabHost implements LauncherTransitiona * tabs manually). */ void setContentTypeImmediate(AppsCustomizePagedView.ContentType type) { - setOnTabChangedListener(null); - onTabChangedStart(); - onTabChangedEnd(type); - setCurrentTabByTag(getTabTagForContentType(type)); - setOnTabChangedListener(this); + mPagedView.setContentType(type); + } + + public void setCurrentTabFromContent(AppsCustomizePagedView.ContentType type) { + setContentTypeImmediate(type); } @Override public void setInsets(Rect insets) { mInsets.set(insets); - FrameLayout.LayoutParams flp = (LayoutParams) mContent.getLayoutParams(); + LayoutParams flp = (LayoutParams) mContent.getLayoutParams(); flp.topMargin = insets.top; flp.bottomMargin = insets.bottom; flp.leftMargin = insets.left; @@ -99,212 +84,12 @@ public class AppsCustomizeTabHost extends TabHost implements LauncherTransitiona */ @Override protected void onFinishInflate() { - // Setup the tab host - setup(); - - final ViewGroup tabsContainer = (ViewGroup) findViewById(R.id.tabs_container); - final TabWidget tabs = getTabWidget(); - final AppsCustomizePagedView appsCustomizePane = (AppsCustomizePagedView) - findViewById(R.id.apps_customize_pane_content); - mTabs = tabs; - mTabsContainer = tabsContainer; - mAppsCustomizePane = appsCustomizePane; - mAnimationBuffer = (FrameLayout) findViewById(R.id.animation_buffer); - mContent = (LinearLayout) findViewById(R.id.apps_customize_content); - if (tabs == null || mAppsCustomizePane == null) throw new Resources.NotFoundException(); - - // Configure the tabs content factory to return the same paged view (that we change the - // content filter on) - TabContentFactory contentFactory = new TabContentFactory() { - public View createTabContent(String tag) { - return appsCustomizePane; - } - }; - - // Create the tabs - TextView tabView; - String label; - label = getContext().getString(R.string.all_apps_button_label); - tabView = (TextView) mLayoutInflater.inflate(R.layout.tab_widget_indicator, tabs, false); - tabView.setText(label); - tabView.setContentDescription(label); - addTab(newTabSpec(APPS_TAB_TAG).setIndicator(tabView).setContent(contentFactory)); - label = getContext().getString(R.string.widgets_tab_label); - tabView = (TextView) mLayoutInflater.inflate(R.layout.tab_widget_indicator, tabs, false); - tabView.setText(label); - tabView.setContentDescription(label); - addTab(newTabSpec(WIDGETS_TAB_TAG).setIndicator(tabView).setContent(contentFactory)); - setOnTabChangedListener(this); - - // Setup the key listener to jump between the last tab view and the market icon - AppsCustomizeTabKeyEventListener keyListener = new AppsCustomizeTabKeyEventListener(); - View lastTab = tabs.getChildTabViewAt(tabs.getTabCount() - 1); - lastTab.setOnKeyListener(keyListener); - View shopButton = findViewById(R.id.market_button); - shopButton.setOnKeyListener(keyListener); - - // Hide the tab bar until we measure - mTabsContainer.setAlpha(0f); + mPagedView = (AppsCustomizePagedView) findViewById(R.id.apps_customize_pane_content); + mContent = findViewById(R.id.content); } - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - boolean remeasureTabWidth = (mTabs.getLayoutParams().width <= 0); - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - - // Set the width of the tab list to the content width - if (remeasureTabWidth) { - int contentWidth = mAppsCustomizePane.getPageContentWidth(); - if (contentWidth > 0 && mTabs.getLayoutParams().width != contentWidth) { - // Set the width and show the tab bar - mTabs.getLayoutParams().width = contentWidth; - mRelayoutAndMakeVisible.run(); - } - - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - } - } - - public boolean onInterceptTouchEvent(MotionEvent ev) { - // If we are mid transitioning to the workspace, then intercept touch events here so we - // can ignore them, otherwise we just let all apps handle the touch events. - if (mInTransition && mTransitioningToWorkspace) { - return true; - } - return super.onInterceptTouchEvent(ev); - }; - - @Override - public boolean onTouchEvent(MotionEvent event) { - // Allow touch events to fall through to the workspace if we are transitioning there - if (mInTransition && mTransitioningToWorkspace) { - return super.onTouchEvent(event); - } - - // Intercept all touch events up to the bottom of the AppsCustomizePane so they do not fall - // through to the workspace and trigger showWorkspace() - if (event.getY() < mAppsCustomizePane.getBottom()) { - return true; - } - return super.onTouchEvent(event); - } - - private void onTabChangedStart() { - } - - private void reloadCurrentPage() { - mAppsCustomizePane.loadAssociatedPages(mAppsCustomizePane.getCurrentPage()); - mAppsCustomizePane.requestFocus(); - } - - private void onTabChangedEnd(AppsCustomizePagedView.ContentType type) { - int bgAlpha = (int) (255 * (getResources().getInteger( - R.integer.config_appsCustomizeSpringLoadedBgAlpha) / 100f)); - setBackgroundColor(Color.argb(bgAlpha, 0, 0, 0)); - mAppsCustomizePane.setContentType(type); - } - - @Override - public void onTabChanged(String tabId) { - final AppsCustomizePagedView.ContentType type = getContentTypeForTabTag(tabId); - - // Animate the changing of the tab content by fading pages in and out - final Resources res = getResources(); - final int duration = res.getInteger(R.integer.config_tabTransitionDuration); - - // We post a runnable here because there is a delay while the first page is loading and - // the feedback from having changed the tab almost feels better than having it stick - post(new Runnable() { - @Override - public void run() { - if (mAppsCustomizePane.getMeasuredWidth() <= 0 || - mAppsCustomizePane.getMeasuredHeight() <= 0) { - reloadCurrentPage(); - return; - } - - // Take the visible pages and re-parent them temporarily to mAnimatorBuffer - // and then cross fade to the new pages - int[] visiblePageRange = new int[2]; - mAppsCustomizePane.getVisiblePages(visiblePageRange); - if (visiblePageRange[0] == -1 && visiblePageRange[1] == -1) { - // If we can't get the visible page ranges, then just skip the animation - reloadCurrentPage(); - return; - } - ArrayList<View> visiblePages = new ArrayList<View>(); - for (int i = visiblePageRange[0]; i <= visiblePageRange[1]; i++) { - visiblePages.add(mAppsCustomizePane.getPageAt(i)); - } - - // We want the pages to be rendered in exactly the same way as they were when - // their parent was mAppsCustomizePane -- so set the scroll on mAnimationBuffer - // to be exactly the same as mAppsCustomizePane, and below, set the left/top - // parameters to be correct for each of the pages - mAnimationBuffer.scrollTo(mAppsCustomizePane.getScrollX(), 0); - - // mAppsCustomizePane renders its children in reverse order, so - // add the pages to mAnimationBuffer in reverse order to match that behavior - for (int i = visiblePages.size() - 1; i >= 0; i--) { - View child = visiblePages.get(i); - if (child instanceof AppsCustomizeCellLayout) { - ((AppsCustomizeCellLayout) child).resetChildrenOnKeyListeners(); - } else if (child instanceof PagedViewGridLayout) { - ((PagedViewGridLayout) child).resetChildrenOnKeyListeners(); - } - PagedViewWidget.setDeletePreviewsWhenDetachedFromWindow(false); - mAppsCustomizePane.removeView(child); - PagedViewWidget.setDeletePreviewsWhenDetachedFromWindow(true); - mAnimationBuffer.setAlpha(1f); - mAnimationBuffer.setVisibility(View.VISIBLE); - LayoutParams p = new FrameLayout.LayoutParams(child.getMeasuredWidth(), - child.getMeasuredHeight()); - p.setMargins((int) child.getLeft(), (int) child.getTop(), 0, 0); - mAnimationBuffer.addView(child, p); - } - - // Toggle the new content - onTabChangedStart(); - onTabChangedEnd(type); - - // Animate the transition - ObjectAnimator outAnim = LauncherAnimUtils.ofFloat(mAnimationBuffer, "alpha", 0f); - outAnim.addListener(new AnimatorListenerAdapter() { - private void clearAnimationBuffer() { - mAnimationBuffer.setVisibility(View.GONE); - PagedViewWidget.setRecyclePreviewsWhenDetachedFromWindow(false); - mAnimationBuffer.removeAllViews(); - PagedViewWidget.setRecyclePreviewsWhenDetachedFromWindow(true); - } - @Override - public void onAnimationEnd(Animator animation) { - clearAnimationBuffer(); - } - @Override - public void onAnimationCancel(Animator animation) { - clearAnimationBuffer(); - } - }); - ObjectAnimator inAnim = LauncherAnimUtils.ofFloat(mAppsCustomizePane, "alpha", 1f); - inAnim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - reloadCurrentPage(); - } - }); - - final AnimatorSet animSet = LauncherAnimUtils.createAnimatorSet(); - animSet.playTogether(outAnim, inAnim); - animSet.setDuration(duration); - animSet.start(); - } - }); - } - - public void setCurrentTabFromContent(AppsCustomizePagedView.ContentType type) { - setOnTabChangedListener(null); - setCurrentTabByTag(getTabTagForContentType(type)); - setOnTabChangedListener(this); + public String getContentTag() { + return getTabTagForContentType(mPagedView.getContentType()); } /** @@ -343,44 +128,41 @@ public class AppsCustomizeTabHost extends TabHost implements LauncherTransitiona } void reset() { - if (mInTransition) { - // Defer to after the transition to reset - mResetAfterTransition = true; - } else { - // Reset immediately - mAppsCustomizePane.reset(); - } + // Reset immediately + mPagedView.reset(); } - private void enableAndBuildHardwareLayer() { - // isHardwareAccelerated() checks if we're attached to a window and if that - // window is HW accelerated-- we were sometimes not attached to a window - // and buildLayer was throwing an IllegalStateException - if (isHardwareAccelerated()) { - // Turn on hardware layers for performance - setLayerType(LAYER_TYPE_HARDWARE, null); - - // force building the layer, so you don't get a blip early in an animation - // when the layer is created layer - buildLayer(); + public void onWindowVisible() { + if (getVisibility() == VISIBLE) { + mContent.setVisibility(VISIBLE); + // We unload the widget previews when the UI is hidden, so need to reload pages + // Load the current page synchronously, and the neighboring pages asynchronously + mPagedView.loadAssociatedPages(mPagedView.getCurrentPage(), true); + mPagedView.loadAssociatedPages(mPagedView.getCurrentPage()); } } + public void onTrimMemory() { + mContent.setVisibility(GONE); + // Clear the widget pages of all their subviews - this will trigger the widget previews + // to delete their bitmaps + mPagedView.clearAllWidgetPages(); + } + @Override public View getContent() { - View appsCustomizeContent = mAppsCustomizePane.getContent(); - if (appsCustomizeContent != null) { - return appsCustomizeContent; - } - return mContent; + return mPagedView; + } + + public boolean isInTransition() { + return mInTransition; } /* LauncherTransitionable overrides */ @Override public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) { - mAppsCustomizePane.onLauncherTransitionPrepare(l, animated, toWorkspace); + mPagedView.onLauncherTransitionPrepare(l, animated, toWorkspace); mInTransition = true; - mTransitioningToWorkspace = toWorkspace; if (toWorkspace) { // Going from All Apps -> Workspace @@ -391,52 +173,43 @@ public class AppsCustomizeTabHost extends TabHost implements LauncherTransitiona // Make sure the current page is loaded (we start loading the side pages after the // transition to prevent slowing down the animation) - mAppsCustomizePane.loadAssociatedPages(mAppsCustomizePane.getCurrentPage(), true); - } - - if (mResetAfterTransition) { - mAppsCustomizePane.reset(); - mResetAfterTransition = false; + // TODO: revisit this + mPagedView.loadAssociatedPages(mPagedView.getCurrentPage(), true); } } @Override public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) { - mAppsCustomizePane.onLauncherTransitionStart(l, animated, toWorkspace); - if (animated) { + mPagedView.onLauncherTransitionStart(l, animated, toWorkspace); + if (animated && !Utilities.isLmp()) { enableAndBuildHardwareLayer(); } - - // Dismiss the workspace cling - l.getLauncherClings().dismissWorkspaceCling(null); } @Override public void onLauncherTransitionStep(Launcher l, float t) { - mAppsCustomizePane.onLauncherTransitionStep(l, t); + mPagedView.onLauncherTransitionStep(l, t); } @Override public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) { - mAppsCustomizePane.onLauncherTransitionEnd(l, animated, toWorkspace); + mPagedView.onLauncherTransitionEnd(l, animated, toWorkspace); mInTransition = false; - if (animated) { + if (animated && !Utilities.isLmp()) { setLayerType(LAYER_TYPE_NONE, null); } if (!toWorkspace) { - // Show the all apps cling (if not already shown) - mAppsCustomizePane.showAllAppsCling(); // Make sure adjacent pages are loaded (we wait until after the transition to // prevent slowing down the animation) - mAppsCustomizePane.loadAssociatedPages(mAppsCustomizePane.getCurrentPage()); + mPagedView.loadAssociatedPages(mPagedView.getCurrentPage()); // Opening apps, need to announce what page we are on. AccessibilityManager am = (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); if (am.isEnabled()) { // Notify the user when the page changes - announceForAccessibility(mAppsCustomizePane.getCurrentPageDescription()); + announceForAccessibility(mPagedView.getCurrentPageDescription()); } // Going from Workspace -> All Apps @@ -469,24 +242,19 @@ public class AppsCustomizeTabHost extends TabHost implements LauncherTransitiona } } - public void onWindowVisible() { - if (getVisibility() == VISIBLE) { - mContent.setVisibility(VISIBLE); - // We unload the widget previews when the UI is hidden, so need to reload pages - // Load the current page synchronously, and the neighboring pages asynchronously - mAppsCustomizePane.loadAssociatedPages(mAppsCustomizePane.getCurrentPage(), true); - mAppsCustomizePane.loadAssociatedPages(mAppsCustomizePane.getCurrentPage()); + private void enableAndBuildHardwareLayer() { + // isHardwareAccelerated() checks if we're attached to a window and if that + // window is HW accelerated-- we were sometimes not attached to a window + // and buildLayer was throwing an IllegalStateException + if (isHardwareAccelerated()) { + // Turn on hardware layers for performance + setLayerType(LAYER_TYPE_HARDWARE, null); + + // force building the layer, so you don't get a blip early in an animation + // when the layer is created layer + buildLayer(); } } - public void onTrimMemory() { - mContent.setVisibility(GONE); - // Clear the widget pages of all their subviews - this will trigger the widget previews - // to delete their bitmaps - mAppsCustomizePane.clearAllWidgetPages(); - } - boolean isTransitioning() { - return mInTransition; - } } diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 3f619a812..ffa7ec37e 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -51,8 +51,6 @@ public class BubbleTextView extends TextView { private static final boolean DEBUG = false; - private int mPrevAlpha = -1; - private HolographicOutlineHelper mOutlineHelper; private final Canvas mTempCanvas = new Canvas(); private final Rect mTempRect = new Rect(); @@ -124,14 +122,22 @@ public class BubbleTextView extends TextView { } } - public void applyFromShortcutInfo(ShortcutInfo info, IconCache iconCache) { + public void applyFromShortcutInfo(ShortcutInfo info, IconCache iconCache, + boolean setDefaultPadding) { Bitmap b = info.getIcon(iconCache); LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - Drawable iconDrawable = Utilities.createIconDrawable(b); + FastBitmapDrawable iconDrawable = Utilities.createIconDrawable(b); + if (info.isDisabled) { + iconDrawable.setSaturation(0); + iconDrawable.setBrightness(20); + } + setCompoundDrawables(null, iconDrawable, null, null); - setCompoundDrawablePadding(grid.iconDrawablePaddingPx); + if (setDefaultPadding) { + DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + setCompoundDrawablePadding(grid.iconDrawablePaddingPx); + } if (info.contentDescription != null) { setContentDescription(info.contentDescription); } @@ -417,10 +423,6 @@ public class BubbleTextView extends TextView { @Override protected boolean onSetAlpha(int alpha) { - if (mPrevAlpha != alpha) { - mPrevAlpha = alpha; - super.onSetAlpha(alpha); - } return true; } diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java index 3d45432c2..4c3388e05 100644 --- a/src/com/android/launcher3/DeleteDropTarget.java +++ b/src/com/android/launcher3/DeleteDropTarget.java @@ -326,7 +326,7 @@ public class DeleteDropTarget extends ButtonDropTarget { final LauncherAppWidgetInfo launcherAppWidgetInfo = (LauncherAppWidgetInfo) item; final LauncherAppWidgetHost appWidgetHost = mLauncher.getAppWidgetHost(); - if (appWidgetHost != null) { + if ((appWidgetHost != null) && launcherAppWidgetInfo.isWidgetIdValid()) { // Deleting an app widget ID is a void call but writes to disk before returning // to the caller... new AsyncTask<Void, Void, Void>() { diff --git a/src/com/android/launcher3/DragLayer.java b/src/com/android/launcher3/DragLayer.java index c54db0127..8bcc407d6 100644 --- a/src/com/android/launcher3/DragLayer.java +++ b/src/com/android/launcher3/DragLayer.java @@ -77,6 +77,10 @@ public class DragLayer extends FrameLayout implements ViewGroup.OnHierarchyChang private int mTopViewIndex; private int mChildCountOnLastUpdate = -1; + // Darkening scrim + private Drawable mBackground; + private float mBackgroundAlpha = 0; + /** * Used to create a new DragLayer from XML. * @@ -91,8 +95,10 @@ public class DragLayer extends FrameLayout implements ViewGroup.OnHierarchyChang setChildrenDrawingOrderEnabled(true); setOnHierarchyChangeListener(this); - mLeftHoverDrawable = getResources().getDrawable(R.drawable.page_hover_left_holo); - mRightHoverDrawable = getResources().getDrawable(R.drawable.page_hover_right_holo); + final Resources res = getResources(); + mLeftHoverDrawable = res.getDrawable(R.drawable.page_hover_left_holo); + mRightHoverDrawable = res.getDrawable(R.drawable.page_hover_right_holo); + mBackground = res.getDrawable(R.drawable.apps_customize_bg); } public void setup(Launcher launcher, DragController controller) { @@ -862,8 +868,17 @@ public class DragLayer extends FrameLayout implements ViewGroup.OnHierarchyChang @Override protected void dispatchDraw(Canvas canvas) { + // Draw the background gradient below children. + if (mBackground != null && mBackgroundAlpha > 0.0f) { + int alpha = (int) (mBackgroundAlpha * 255); + mBackground.setAlpha(alpha); + mBackground.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight()); + mBackground.draw(canvas); + } + super.dispatchDraw(canvas); + // Draw screen hover indicators above children. if (mInScrollArea && !LauncherAppState.getInstance().isScreenLarge()) { Workspace workspace = mLauncher.getWorkspace(); int width = getMeasuredWidth(); @@ -887,6 +902,17 @@ public class DragLayer extends FrameLayout implements ViewGroup.OnHierarchyChang } } + public void setBackgroundAlpha(float alpha) { + if (alpha != mBackgroundAlpha) { + mBackgroundAlpha = alpha; + invalidate(); + } + } + + public float getBackgroundAlpha() { + return mBackgroundAlpha; + } + public void setTouchCompleteListener(TouchCompleteListener listener) { mTouchCompleteListener = listener; } diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java index 85e90202b..ef8d0973d 100644 --- a/src/com/android/launcher3/FastBitmapDrawable.java +++ b/src/com/android/launcher3/FastBitmapDrawable.java @@ -19,15 +19,24 @@ package com.android.launcher3; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.ColorFilter; +import android.graphics.ColorMatrix; +import android.graphics.ColorMatrixColorFilter; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.drawable.Drawable; class FastBitmapDrawable extends Drawable { + + private static final ColorMatrix sTempSaturationMatrix = new ColorMatrix(); + private static final ColorMatrix sTempBrightnessMatrix = new ColorMatrix(); + + private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG); private Bitmap mBitmap; private int mAlpha; - private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG); + + private float mSatutation = 1; + private int mBrightness = 0; FastBitmapDrawable(Bitmap b) { mAlpha = 255; @@ -44,7 +53,7 @@ class FastBitmapDrawable extends Drawable { @Override public void setColorFilter(ColorFilter cf) { - mPaint.setColorFilter(cf); + // No op } @Override @@ -58,6 +67,7 @@ class FastBitmapDrawable extends Drawable { mPaint.setAlpha(alpha); } + @Override public void setFilterBitmap(boolean filterBitmap) { mPaint.setFilterBitmap(filterBitmap); mPaint.setAntiAlias(filterBitmap); @@ -90,4 +100,51 @@ class FastBitmapDrawable extends Drawable { public Bitmap getBitmap() { return mBitmap; } + + public float getSaturation() { + return mSatutation; + } + + public void setSaturation(float saturation) { + mSatutation = saturation; + updateFilter(); + } + + public int getBrightness() { + return mBrightness; + } + + public void addBrightness(int amount) { + mBrightness += amount; + updateFilter(); + } + + public void setBrightness(int brightness) { + mBrightness = brightness; + updateFilter(); + } + + private void updateFilter() { + if (mSatutation != 1 || mBrightness != 0) { + sTempSaturationMatrix.setSaturation(mSatutation); + + if (mBrightness != 0) { + // Brightness: C-new = C-old*(1-amount) + amount + float scale = 1 - mBrightness / 255.0f; + sTempBrightnessMatrix.setScale(scale, scale, scale, 1); + float[] array = sTempBrightnessMatrix.getArray(); + + // Add the amount to RGB components of the matrix, as per the above formula. + // Fifth elements in the array correspond to the constant being added to + // red, blue, green, and alpha channel respectively. + array[4] = mBrightness; + array[9] = mBrightness; + array[14] = mBrightness; + sTempSaturationMatrix.preConcat(sTempBrightnessMatrix); + } + mPaint.setColorFilter(new ColorMatrixColorFilter(sTempSaturationMatrix)); + } else { + mPaint.setColorFilter(null); + } + } } diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index 47c8a4a0f..655e5c368 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -566,15 +566,10 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList } protected View createAndAddShortcut(ShortcutInfo item) { - final TextView textView = - (TextView) mInflater.inflate(R.layout.folder_application, this, false); - textView.setCompoundDrawables(null, - Utilities.createIconDrawable(item.getIcon(mIconCache)), null, null); - textView.setText(item.title); - if (item.contentDescription != null) { - textView.setContentDescription(item.contentDescription); - } - textView.setTag(item); + final BubbleTextView textView = + (BubbleTextView) mInflater.inflate(R.layout.folder_application, this, false); + textView.applyFromShortcutInfo(item, mIconCache, false); + textView.setOnClickListener(this); textView.setOnLongClickListener(this); diff --git a/src/com/android/launcher3/FolderIcon.java b/src/com/android/launcher3/FolderIcon.java index 4f674f55a..5b49fb87a 100644 --- a/src/com/android/launcher3/FolderIcon.java +++ b/src/com/android/launcher3/FolderIcon.java @@ -133,7 +133,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { final ViewGroup cellLayoutChildren = (ViewGroup) getParent(); final ViewGroup cellLayout = (ViewGroup) cellLayoutChildren.getParent(); final Workspace workspace = (Workspace) cellLayout.getParent(); - return !workspace.isSmall(); + return !workspace.workspaceInModalState(); } static FolderIcon fromXml(int resId, Launcher launcher, ViewGroup group, @@ -580,10 +580,17 @@ public class FolderIcon extends FrameLayout implements FolderListener { if (d != null) { mOldBounds.set(d.getBounds()); d.setBounds(0, 0, mIntrinsicIconSize, mIntrinsicIconSize); - d.setColorFilter(Color.argb(params.overlayAlpha, 255, 255, 255), - PorterDuff.Mode.SRC_ATOP); - d.draw(canvas); - d.clearColorFilter(); + if (d instanceof FastBitmapDrawable) { + FastBitmapDrawable fd = (FastBitmapDrawable) d; + fd.addBrightness(params.overlayAlpha); + d.draw(canvas); + fd.addBrightness(-params.overlayAlpha); + } else { + d.setColorFilter(Color.argb(params.overlayAlpha, 255, 255, 255), + PorterDuff.Mode.SRC_ATOP); + d.draw(canvas); + d.clearColorFilter(); + } d.setBounds(mOldBounds); } canvas.restore(); diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java index 2d171238f..79cac8f64 100644 --- a/src/com/android/launcher3/Hotseat.java +++ b/src/com/android/launcher3/Hotseat.java @@ -167,7 +167,7 @@ public class Hotseat extends FrameLayout { int y = getCellYFromOrder(mAllAppsButtonRank); CellLayout.LayoutParams lp = new CellLayout.LayoutParams(x,y,1,1); lp.canReorder = false; - mContent.addViewToCellLayout(allAppsButton, -1, 0, lp, true); + mContent.addViewToCellLayout(allAppsButton, -1, allAppsButton.getId(), lp, true); } } @@ -175,7 +175,7 @@ public class Hotseat extends FrameLayout { public boolean onInterceptTouchEvent(MotionEvent ev) { // We don't want any clicks to go through to the hotseat unless the workspace is in // the normal state. - if (mLauncher.getWorkspace().isSmall()) { + if (mLauncher.getWorkspace().workspaceInModalState()) { return true; } return false; diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 951b5d459..65f9cbee8 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -82,28 +82,36 @@ import android.view.Menu; import android.view.MotionEvent; import android.view.Surface; import android.view.View; +import android.view.ViewAnimationUtils; import android.view.Window; import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.view.ViewTreeObserver.OnGlobalLayoutListener; +import android.view.Window; import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; import android.view.animation.AccelerateDecelerateInterpolator; +import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; +import android.view.animation.LinearInterpolator; import android.view.inputmethod.InputMethodManager; import android.widget.Advanceable; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; + +import com.android.launcher3.DropTarget.DragObject; +import com.android.launcher3.PagedView.PageSwitchListener; import com.android.launcher3.compat.LauncherActivityInfoCompat; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.DropTarget.DragObject; import com.android.launcher3.PagedView.PageSwitchListener; + import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; @@ -198,7 +206,6 @@ public class Launcher extends Activity // Type: int[] private static final String RUNTIME_STATE_VIEW_IDS = "launcher.view_ids"; - static final String INTRO_SCREEN_DISMISSED = "launcher.intro_screen_dismissed"; static final String FIRST_RUN_ACTIVITY_DISPLAYED = "launcher.first_run_activity_displayed"; @@ -218,6 +225,8 @@ public class Launcher extends Activity private State mState = State.WORKSPACE; private AnimatorSet mStateAnimation; + private boolean mIsSafeModeEnabled; + static final int APPWIDGET_HOST_ID = 1024; public static final int EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT = 300; private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500; @@ -421,6 +430,7 @@ public class Launcher extends Activity // the LauncherApplication should call this, but in case of Instrumentation it might not be present yet mSharedPrefs = getSharedPreferences(LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE); + mIsSafeModeEnabled = getPackageManager().isSafeMode(); mModel = app.setLauncher(this); mIconCache = app.getIconCache(); mIconCache.flushInvalidIcons(grid); @@ -1395,7 +1405,7 @@ public class Launcher extends Activity */ View createShortcut(int layoutResId, ViewGroup parent, ShortcutInfo info) { BubbleTextView favorite = (BubbleTextView) mInflater.inflate(layoutResId, parent, false); - favorite.applyFromShortcutInfo(info, mIconCache); + favorite.applyFromShortcutInfo(info, mIconCache, true); favorite.setOnClickListener(this); return favorite; } @@ -2775,6 +2785,7 @@ public class Launcher extends Activity // Could be launching some bookkeeping activity startActivity(intent, optsBundle); } else { + // TODO Component can be null when shortcuts are supported for secondary user launcherApps.startActivityForProfile(intent.getComponent(), user, intent.getSourceBounds(), optsBundle); } @@ -2791,6 +2802,10 @@ public class Launcher extends Activity boolean startActivitySafely(View v, Intent intent, Object tag) { boolean success = false; + if (mIsSafeModeEnabled && !Utilities.isSystemApp(this, intent)) { + Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show(); + return false; + } try { success = startActivity(v, intent, tag); } catch (ActivityNotFoundException e) { @@ -3146,6 +3161,7 @@ public class Launcher extends Activity AppsCustomizePagedView.ContentType contentType = mAppsCustomizeContent.getContentType(); showAppsCustomizeHelper(animated, springLoaded, contentType); } + private void showAppsCustomizeHelper(final boolean animated, final boolean springLoaded, final AppsCustomizePagedView.ContentType contentType) { if (mStateAnimation != null) { @@ -3153,10 +3169,15 @@ public class Launcher extends Activity mStateAnimation.cancel(); mStateAnimation = null; } + + boolean material = Utilities.isLmp(); + final Resources res = getResources(); final int duration = res.getInteger(R.integer.config_appsCustomizeZoomInTime); final int fadeDuration = res.getInteger(R.integer.config_appsCustomizeFadeInTime); + final int revealDuration = res.getInteger(R.integer.config_appsCustomizeRevealTime); + final float scale = (float) res.getInteger(R.integer.config_appsCustomizeZoomScaleFactor); final View fromView = mWorkspace; final AppsCustomizeTabHost toView = mAppsCustomizeTabHost; @@ -3165,9 +3186,10 @@ public class Launcher extends Activity setPivotsForZoom(toView, scale); - // Shrink workspaces away if going to AppsCustomize from workspace + Workspace.State workspaceState = contentType == AppsCustomizePagedView.ContentType.Widgets ? + Workspace.State.OVERVIEW_HIDDEN : Workspace.State.NORMAL_HIDDEN; Animator workspaceAnim = - mWorkspace.getChangeStateAnimation(Workspace.State.SMALL, animated); + mWorkspace.getChangeStateAnimation(workspaceState, animated); if (!LauncherAppState.isDisableAllApps() || contentType == AppsCustomizePagedView.ContentType.Widgets) { // Set the content type for the all apps/widgets space @@ -3175,65 +3197,151 @@ public class Launcher extends Activity } if (animated) { - toView.setScaleX(scale); - toView.setScaleY(scale); - final LauncherViewPropertyAnimator scaleAnim = new LauncherViewPropertyAnimator(toView); - scaleAnim. - scaleX(1f).scaleY(1f). - setDuration(duration). - setInterpolator(new Workspace.ZoomOutInterpolator()); + if (!material) { + toView.setScaleX(scale); + toView.setScaleY(scale); + final LauncherViewPropertyAnimator scaleAnim = + new LauncherViewPropertyAnimator(toView); + scaleAnim. + scaleX(1f).scaleY(1f). + setDuration(duration). + setInterpolator(new Workspace.ZoomOutInterpolator()); + + toView.setVisibility(View.VISIBLE); + toView.setAlpha(0f); + final ObjectAnimator alphaAnim = LauncherAnimUtils + .ofFloat(toView, "alpha", 0f, 1f) + .setDuration(fadeDuration); + alphaAnim.setInterpolator(new DecelerateInterpolator(1.5f)); + alphaAnim.addUpdateListener(new AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + if (animation == null) { + throw new RuntimeException("animation is null"); + } + float t = (Float) animation.getAnimatedValue(); + dispatchOnLauncherTransitionStep(fromView, t); + dispatchOnLauncherTransitionStep(toView, t); + } + }); - toView.setVisibility(View.VISIBLE); - toView.setAlpha(0f); - final ObjectAnimator alphaAnim = LauncherAnimUtils - .ofFloat(toView, "alpha", 0f, 1f) - .setDuration(fadeDuration); - alphaAnim.setInterpolator(new DecelerateInterpolator(1.5f)); - alphaAnim.addUpdateListener(new AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - if (animation == null) { - throw new RuntimeException("animation is null"); + // toView should appear right at the end of the workspace shrink + // animation + mStateAnimation = LauncherAnimUtils.createAnimatorSet(); + mStateAnimation.play(scaleAnim).after(startDelay); + mStateAnimation.play(alphaAnim).after(startDelay); + + mStateAnimation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + // Prepare the position + toView.setTranslationX(0.0f); + toView.setTranslationY(0.0f); + toView.setVisibility(View.VISIBLE); + toView.bringToFront(); } - float t = (Float) animation.getAnimatedValue(); - dispatchOnLauncherTransitionStep(fromView, t); - dispatchOnLauncherTransitionStep(toView, t); - } - }); + @Override + public void onAnimationEnd(Animator animation) { + dispatchOnLauncherTransitionEnd(fromView, animated, false); + dispatchOnLauncherTransitionEnd(toView, animated, false); + + // Hide the search bar + if (mSearchDropTargetBar != null) { + mSearchDropTargetBar.hideSearchBar(false); + } + } + }); + } else { + int width = toView.getMeasuredWidth(); + int height = toView.getMeasuredHeight(); + float revealRadius = (float) Math.sqrt((width * width) / 4 + height * height); - // toView should appear right at the end of the workspace shrink - // animation - mStateAnimation = LauncherAnimUtils.createAnimatorSet(); - mStateAnimation.play(scaleAnim).after(startDelay); - mStateAnimation.play(alphaAnim).after(startDelay); + mStateAnimation = LauncherAnimUtils.createAnimatorSet(); - mStateAnimation.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animation) { - // Prepare the position - toView.setTranslationX(0.0f); - toView.setTranslationY(0.0f); - toView.setVisibility(View.VISIBLE); - toView.bringToFront(); + AppsCustomizePagedView content = (AppsCustomizePagedView) + toView.findViewById(R.id.apps_customize_pane_content); + + View page = content.getPageAt(content.getCurrentPage()); + View revealView = content; + + float yDrift = height / 2f - 400; + + LauncherViewPropertyAnimator panelAlphaAndDrift = + new LauncherViewPropertyAnimator(revealView); + revealView.setTranslationY(yDrift); + revealView.setAlpha(0.3f); + panelAlphaAndDrift.alpha(1) + .translationY(0) + .setDuration(revealDuration) + .setInterpolator(new LogDecelerateInterpolator(100, 0)); + + mStateAnimation.play(panelAlphaAndDrift); + + if (page instanceof CellLayout) { + CellLayout cellLayout = (CellLayout) page; + cellLayout.enableHardwareLayer(true); + + View iconsView = cellLayout.getShortcutsAndWidgets(); + iconsView.setAlpha(0f); + + LauncherViewPropertyAnimator iconsAlpha = + new LauncherViewPropertyAnimator(iconsView); + iconsAlpha.alpha(1f) + .setDuration(revealDuration - 100) + .setInterpolator(new LogDecelerateInterpolator(100, 0)); + mStateAnimation.play(iconsAlpha); } - @Override - public void onAnimationEnd(Animator animation) { - dispatchOnLauncherTransitionEnd(fromView, animated, false); - dispatchOnLauncherTransitionEnd(toView, animated, false); - // Hide the search bar - if (mSearchDropTargetBar != null) { - mSearchDropTargetBar.hideSearchBar(false); + View pageIndicators = toView.findViewById(R.id.apps_customize_page_indicator); + pageIndicators.setAlpha(0f); + final LauncherViewPropertyAnimator indicatorsAlpha = + new LauncherViewPropertyAnimator(pageIndicators); + indicatorsAlpha.alpha(1f); + indicatorsAlpha.setDuration(revealDuration); + mStateAnimation.play(indicatorsAlpha); + + width = revealView.getMeasuredWidth(); + + Animator reveal = + ViewAnimationUtils.createCircularReveal(revealView, width / 2, + height / 2 + 100, 0f, revealRadius); + reveal.setDuration(revealDuration); + reveal.setInterpolator(new LogDecelerateInterpolator(100, 0)); + + toView.setTranslationX(0); + toView.setTranslationY(0); + toView.setAlpha(1f); + // toView should appear right at the end of the workspace shrink + // animation + mStateAnimation.play(reveal); + + reveal.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + // Prepare the position + toView.bringToFront(); + toView.setVisibility(View.VISIBLE); } - } - }); + }); - if (workspaceAnim != null) { - mStateAnimation.play(workspaceAnim); + mStateAnimation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + dispatchOnLauncherTransitionEnd(fromView, animated, false); + dispatchOnLauncherTransitionEnd(toView, animated, false); + + // Hide the search bar + if (mSearchDropTargetBar != null) { + mSearchDropTargetBar.hideSearchBar(false); + } + } + }); } boolean delayAnim = false; - + if (workspaceAnim != null) { + mStateAnimation.play(workspaceAnim); + } dispatchOnLauncherTransitionPrepare(fromView, animated, false); dispatchOnLauncherTransitionPrepare(toView, animated, false); @@ -3305,11 +3413,15 @@ public class Launcher extends Activity mStateAnimation.cancel(); mStateAnimation = null; } + + boolean material = Utilities.isLmp(); + Resources res = getResources(); final int duration = res.getInteger(R.integer.config_appsCustomizeZoomOutTime); - final int fadeOutDuration = - res.getInteger(R.integer.config_appsCustomizeFadeOutTime); + final int fadeOutDuration = res.getInteger(R.integer.config_appsCustomizeFadeOutTime); + final int revealDuration = res.getInteger(R.integer.config_appsCustomizeRevealTime); + final float scaleFactor = (float) res.getInteger(R.integer.config_appsCustomizeZoomScaleFactor); final View fromView = mAppsCustomizeTabHost; @@ -3328,31 +3440,116 @@ public class Launcher extends Activity setPivotsForZoom(fromView, scaleFactor); showHotseat(animated); if (animated) { - final LauncherViewPropertyAnimator scaleAnim = - new LauncherViewPropertyAnimator(fromView); - scaleAnim. - scaleX(scaleFactor).scaleY(scaleFactor). - setDuration(duration). - setInterpolator(new Workspace.ZoomInInterpolator()); - - final ObjectAnimator alphaAnim = LauncherAnimUtils - .ofFloat(fromView, "alpha", 1f, 0f) - .setDuration(fadeOutDuration); - alphaAnim.setInterpolator(new AccelerateDecelerateInterpolator()); - alphaAnim.addUpdateListener(new AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - float t = 1f - (Float) animation.getAnimatedValue(); - dispatchOnLauncherTransitionStep(fromView, t); - dispatchOnLauncherTransitionStep(toView, t); + if (!material) { + final LauncherViewPropertyAnimator scaleAnim = + new LauncherViewPropertyAnimator(fromView); + scaleAnim. + scaleX(scaleFactor).scaleY(scaleFactor). + setDuration(duration). + setInterpolator(new Workspace.ZoomInInterpolator()); + + final ObjectAnimator alphaAnim = LauncherAnimUtils + .ofFloat(fromView, "alpha", 1f, 0f) + .setDuration(fadeOutDuration); + alphaAnim.setInterpolator(new AccelerateDecelerateInterpolator()); + alphaAnim.addUpdateListener(new AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + float t = 1f - (Float) animation.getAnimatedValue(); + dispatchOnLauncherTransitionStep(fromView, t); + dispatchOnLauncherTransitionStep(toView, t); + } + }); + + mStateAnimation = LauncherAnimUtils.createAnimatorSet(); + + dispatchOnLauncherTransitionPrepare(fromView, animated, true); + dispatchOnLauncherTransitionPrepare(toView, animated, true); + mAppsCustomizeContent.stopScrolling(); + + mStateAnimation.playTogether(scaleAnim, alphaAnim); + } else { + mStateAnimation = LauncherAnimUtils.createAnimatorSet(); + + int width = fromView.getMeasuredWidth(); + int height = fromView.getMeasuredHeight(); + float revealRadius = (float) Math.sqrt((width * width) / 4 + height * height); + + AppsCustomizePagedView content = (AppsCustomizePagedView) + fromView.findViewById(R.id.apps_customize_pane_content); + + final View page = content.getPageAt(content.getNextPage()); + View revealView = page; + + float yDrift = height / 2f - 400; + + LauncherViewPropertyAnimator panelAlphaAndDrift = + new LauncherViewPropertyAnimator(revealView); + revealView.setTranslationY(0); + revealView.setAlpha(1); + panelAlphaAndDrift.alpha(0) + .translationY(yDrift) + .setDuration(revealDuration) + .setInterpolator(new LogDecelerateInterpolator(100, 0)); + + mStateAnimation.play(panelAlphaAndDrift); + + if (page instanceof CellLayout) { + final CellLayout cellLayout = (CellLayout) page; + cellLayout.enableHardwareLayer(true); + + final View iconsView = cellLayout.getShortcutsAndWidgets(); + + LauncherViewPropertyAnimator iconsAlpha = + new LauncherViewPropertyAnimator(iconsView); + iconsAlpha.alpha(0f) + .setDuration(revealDuration - 100) + .setInterpolator(new LogDecelerateInterpolator(100, 0)); + + mStateAnimation.play(iconsAlpha); + + mStateAnimation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + cellLayout.setTranslationY(0); + cellLayout.setAlpha(1f); + iconsView.setAlpha(1f); + } + }); } - }); - mStateAnimation = LauncherAnimUtils.createAnimatorSet(); + View pageIndicators = fromView.findViewById(R.id.apps_customize_page_indicator); + final LauncherViewPropertyAnimator indicatorsAlpha = + new LauncherViewPropertyAnimator(pageIndicators); + indicatorsAlpha.alpha(0f); + indicatorsAlpha.setDuration(revealDuration); + indicatorsAlpha.setInterpolator(new DecelerateInterpolator(1.5f)); + mStateAnimation.play(indicatorsAlpha); - dispatchOnLauncherTransitionPrepare(fromView, animated, true); - dispatchOnLauncherTransitionPrepare(toView, animated, true); - mAppsCustomizeContent.stopScrolling(); + width = revealView.getMeasuredWidth(); + + Animator reveal = + ViewAnimationUtils.createCircularReveal(revealView, width / 2, + height / 2 + 100, revealRadius, 0f); + reveal.setInterpolator(new LogDecelerateInterpolator(100, 0)); + reveal.setDuration(revealDuration); + + reveal.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + fromView.setVisibility(View.GONE); + } + }); + + dispatchOnLauncherTransitionPrepare(fromView, animated, true); + dispatchOnLauncherTransitionPrepare(toView, animated, true); + mAppsCustomizeContent.stopScrolling(); + + mStateAnimation.play(reveal); + } + if (workspaceAnim != null) { + mStateAnimation.play(workspaceAnim); + } mStateAnimation.addListener(new AnimatorListenerAdapter() { @Override @@ -3367,10 +3564,6 @@ public class Launcher extends Activity } }); - mStateAnimation.playTogether(scaleAnim, alphaAnim); - if (workspaceAnim != null) { - mStateAnimation.play(workspaceAnim); - } dispatchOnLauncherTransitionStart(fromView, animated, true); dispatchOnLauncherTransitionStart(toView, animated, true); LauncherAnimUtils.startAnimationAfterNextDraw(mStateAnimation, toView); @@ -3835,7 +4028,7 @@ public class Launcher extends Activity text.clear(); // Populate event with a fake title based on the current state. if (mState == State.APPS_CUSTOMIZE) { - text.add(mAppsCustomizeTabHost.getCurrentTabView().getContentDescription()); + text.add(mAppsCustomizeTabHost.getContentTag()); } else { text.add(getString(R.string.all_apps_home_button_label)); } @@ -4197,13 +4390,20 @@ public class Launcher extends Activity } final Workspace workspace = mWorkspace; - final int appWidgetId = item.appWidgetId; - final AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId); - if (DEBUG_WIDGETS) { - Log.d(TAG, "bindAppWidget: id=" + item.appWidgetId + " belongs to component " + appWidgetInfo.provider); - } + final AppWidgetProviderInfo appWidgetInfo; + if (item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) { + final int appWidgetId = item.appWidgetId; + appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId); + if (DEBUG_WIDGETS) { + Log.d(TAG, "bindAppWidget: id=" + item.appWidgetId + " belongs to component " + appWidgetInfo.provider); + } - item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo); + item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo); + } else { + appWidgetInfo = null; + item.hostView = new LauncherAppWidgetHostView(this, false); + item.hostView.updateAppWidget(null); + } item.hostView.setTag(item); item.onBindAppWidget(this); @@ -4363,7 +4563,7 @@ public class Launcher extends Activity } if (mWorkspace != null) { - mWorkspace.updateShortcuts(apps); + mWorkspace.updateShortcutsAndWidgets(apps); } if (!LauncherAppState.isDisableAllApps() && diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java index f47fd13ec..7eb005255 100644 --- a/src/com/android/launcher3/LauncherAppWidgetHostView.java +++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java @@ -18,6 +18,7 @@ package com.android.launcher3; import android.appwidget.AppWidgetHostView; import android.content.Context; +import android.os.Bundle; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; @@ -39,12 +40,28 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView implements Touc private float mSlop; + private boolean mWidgetReady; + public LauncherAppWidgetHostView(Context context) { + this(context, true); + } + + public LauncherAppWidgetHostView(Context context, boolean widgetReady) { super(context); mContext = context; mLongPressHelper = new CheckLongPressHelper(this); mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); mDragLayer = ((Launcher) context).getDragLayer(); + mWidgetReady = widgetReady; + } + + @Override + public void updateAppWidgetSize(Bundle newOptions, int minWidth, int minHeight, int maxWidth, + int maxHeight) { + // If the widget is not yet ready, the app widget size cannot be updated. + if (mWidgetReady) { + super.updateAppWidgetSize(newOptions, minWidth, minHeight, maxWidth, maxHeight); + } } @Override @@ -53,6 +70,15 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView implements Touc } @Override + protected View getDefaultView() { + if (mWidgetReady) { + return super.getDefaultView(); + } else { + return mInflater.inflate(R.layout.appwidget_not_ready, this, false); + } + } + + @Override public void updateAppWidget(RemoteViews remoteViews) { // Store the orientation in which the widget was inflated mPreviousOrientation = mContext.getResources().getConfiguration().orientation; diff --git a/src/com/android/launcher3/LauncherAppWidgetInfo.java b/src/com/android/launcher3/LauncherAppWidgetInfo.java index bec1f9eb3..b3ac12b37 100644 --- a/src/com/android/launcher3/LauncherAppWidgetInfo.java +++ b/src/com/android/launcher3/LauncherAppWidgetInfo.java @@ -28,6 +28,18 @@ import com.android.launcher3.compat.UserHandleCompat; */ public class LauncherAppWidgetInfo extends ItemInfo { + public static final int RESTORE_COMPLETED = 0; + + /** + * This is set during the package backup creation. + */ + public static final int RESTORE_REMAP_PENDING = 1; + + /** + * Widget provider is not yet installed. + */ + public static final int RESTORE_PROVIDER_PENDING = 2; + /** * Indicates that the widget hasn't been instantiated yet. */ @@ -45,6 +57,11 @@ public class LauncherAppWidgetInfo extends ItemInfo { int minWidth = -1; int minHeight = -1; + /** + * Indicates the restore status of the widget. + */ + int restoreStatus; + private boolean mHasNotifiedInitialWidgetSizeChanged; /** @@ -64,6 +81,7 @@ public class LauncherAppWidgetInfo extends ItemInfo { spanY = -1; // We only support app widgets on current user. user = UserHandleCompat.myUserHandle(); + restoreStatus = RESTORE_COMPLETED; } @Override @@ -101,4 +119,8 @@ public class LauncherAppWidgetInfo extends ItemInfo { super.unbind(); hostView = null; } + + public final boolean isWidgetIdValid() { + return restoreStatus != RESTORE_REMAP_PENDING; + } } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index b01db7194..673fef981 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -2047,6 +2047,8 @@ public class LauncherModel extends BroadcastReceiver info.spanX = 1; info.spanY = 1; info.intent.putExtra(ItemInfo.EXTRA_PROFILE, serialNumber); + info.isDisabled = isSafeMode + && !Utilities.isSystemApp(context, intent); // check & update map of what's occupied deleteOnInvalidPlacement.set(false); @@ -2123,31 +2125,54 @@ public class LauncherModel extends BroadcastReceiver // Read all Launcher-specific widget details int appWidgetId = c.getInt(appWidgetIdIndex); String savedProvider = c.getString(appWidgetProviderIndex); - id = c.getLong(idIndex); - final AppWidgetProviderInfo provider = - widgets.getAppWidgetInfo(appWidgetId); - - if (!isSafeMode && (provider == null || provider.provider == null || - provider.provider.getPackageName() == null)) { - String log = "Deleting widget that isn't installed anymore: id=" - + id + " appWidgetId=" + appWidgetId; + final int restoreStatus = c.getInt(restoredIndex); + final boolean restorePending = Utilities.isLmp() + && (restoreStatus == + LauncherAppWidgetInfo.RESTORE_REMAP_PENDING); + final boolean providerPending = Utilities.isLmp() + && (restoreStatus == + LauncherAppWidgetInfo.RESTORE_PROVIDER_PENDING); + + // Do not try to get the provider if restore is pending, as the + // widget id is invalid, and it might point to some other provider. + final AppWidgetProviderInfo provider = restorePending ? null + : widgets.getAppWidgetInfo(appWidgetId); + boolean providerValid = isValidProvider(provider); + + // Skip provider check, + // 1. when the widget id re-map is pending + // 2. provider is pending install for a restored widget + if (!isSafeMode && !providerPending && !restorePending + && !providerValid) { + String log = "Deleting widget that isn't installed anymore: " + + "id=" + id + " appWidgetId=" + appWidgetId; Log.e(TAG, log); Launcher.addDumpLog(TAG, log, false); itemsToRemove.add(id); } else { - appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId, - provider.provider); + if (providerValid) { + appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId, + provider.provider); + int[] minSpan = + Launcher.getMinSpanForWidget(context, provider); + appWidgetInfo.minSpanX = minSpan[0]; + appWidgetInfo.minSpanY = minSpan[1]; + } else { + Log.v(TAG, "Widget restore pending id=" + id + + " appWidgetId=" + appWidgetId + + " status =" + restoreStatus); + appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId, + ComponentName.unflattenFromString(savedProvider)); + appWidgetInfo.restoreStatus = restoreStatus; + } appWidgetInfo.id = id; appWidgetInfo.screenId = c.getInt(screenIndex); appWidgetInfo.cellX = c.getInt(cellXIndex); appWidgetInfo.cellY = c.getInt(cellYIndex); appWidgetInfo.spanX = c.getInt(spanXIndex); appWidgetInfo.spanY = c.getInt(spanYIndex); - int[] minSpan = Launcher.getMinSpanForWidget(context, provider); - appWidgetInfo.minSpanX = minSpan[0]; - appWidgetInfo.minSpanY = minSpan[1]; container = c.getInt(containerIndex); if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP && @@ -2167,14 +2192,20 @@ public class LauncherModel extends BroadcastReceiver } break; } - String providerName = provider.provider.flattenToString(); - if (!providerName.equals(savedProvider)) { - ContentValues values = new ContentValues(); - values.put(LauncherSettings.Favorites.APPWIDGET_PROVIDER, - providerName); - String where = BaseColumns._ID + "= ?"; - String[] args = {Integer.toString(c.getInt(idIndex))}; - contentResolver.update(contentUri, values, where, args); + + if (providerValid) { + String providerName = provider.provider.flattenToString(); + + if (!providerName.equals(savedProvider) || providerPending) { + ContentValues values = new ContentValues(); + values.put(LauncherSettings.Favorites.APPWIDGET_PROVIDER, + providerName); + values.put(LauncherSettings.Favorites.RESTORED, + LauncherAppWidgetInfo.RESTORE_COMPLETED); + String where = BaseColumns._ID + "= ?"; + String[] args = {Long.toString(id)}; + contentResolver.update(contentUri, values, where, args); + } } sBgItemsIdMap.put(appWidgetInfo.id, appWidgetInfo); sBgAppWidgets.add(appWidgetInfo); @@ -3552,18 +3583,18 @@ public class LauncherModel extends BroadcastReceiver mCollator = Collator.getInstance(); } public final int compare(LauncherActivityInfoCompat a, LauncherActivityInfoCompat b) { - CharSequence labelA, labelB; + String labelA, labelB; ComponentName keyA = a.getComponentName(); ComponentName keyB = b.getComponentName(); if (mLabelCache.containsKey(keyA)) { - labelA = mLabelCache.get(keyA); + labelA = mLabelCache.get(keyA).toString(); } else { labelA = a.getLabel().toString().trim(); mLabelCache.put(keyA, labelA); } if (mLabelCache.containsKey(keyB)) { - labelB = mLabelCache.get(keyB); + labelB = mLabelCache.get(keyB).toString(); } else { labelB = b.getLabel().toString().trim(); @@ -3603,6 +3634,11 @@ public class LauncherModel extends BroadcastReceiver } }; + static boolean isValidProvider(AppWidgetProviderInfo provider) { + return (provider != null) && (provider.provider != null) + && (provider.provider.getPackageName() != null); + } + public void dumpState() { Log.d(TAG, "mCallbacks=" + mCallbacks); AppInfo.dumpApplicationInfoList(TAG, "mAllAppsList.data", mBgAllAppsList.data); diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index 2256179c3..3ff617631 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -99,6 +99,8 @@ public class LauncherProvider extends ContentProvider { private static final String ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE = "com.android.launcher.action.APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE"; + private static final String URI_PARAM_IS_EXTERNAL_ADD = "isExternalAdd"; + private LauncherProviderChangeListener mListener; /** @@ -175,6 +177,14 @@ public class LauncherProvider extends ContentProvider { public Uri insert(Uri uri, ContentValues initialValues) { SqlArguments args = new SqlArguments(uri); + // In very limited cases, we support system|signature permission apps to add to the db + String externalAdd = uri.getQueryParameter(URI_PARAM_IS_EXTERNAL_ADD); + if (externalAdd != null && "true".equals(externalAdd)) { + if (!mOpenHelper.initializeExternalAdd(initialValues)) { + return null; + } + } + SQLiteDatabase db = mOpenHelper.getWritableDatabase(); addModifiedTime(initialValues); final long rowId = dbInsertAndCheck(mOpenHelper, db, args.table, null, initialValues); @@ -186,6 +196,7 @@ public class LauncherProvider extends ContentProvider { return uri; } + @Override public int bulkInsert(Uri uri, ContentValues[] values) { SqlArguments args = new SqlArguments(uri); @@ -1245,6 +1256,37 @@ public class LauncherProvider extends ContentProvider { if (LOGD) Log.d(TAG, "mMaxItemId: " + mMaxItemId); } + private boolean initializeExternalAdd(ContentValues values) { + // 1. Ensure that externally added items have a valid item id + long id = generateNewItemId(); + values.put(LauncherSettings.Favorites._ID, id); + + // 2. In the case of an app widget, and if no app widget id is specified, we + // attempt allocate and bind the widget. + Integer itemType = values.getAsInteger(LauncherSettings.Favorites.ITEM_TYPE); + if (itemType != null && + itemType.intValue() == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET && + !values.containsKey(LauncherSettings.Favorites.APPWIDGET_ID)) { + + final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext); + ComponentName cn = ComponentName.unflattenFromString( + values.getAsString(Favorites.APPWIDGET_PROVIDER)); + + if (cn != null) { + try { + int appWidgetId = mAppWidgetHost.allocateAppWidgetId(); + if (!appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,cn)) { + return false; + } + } catch (RuntimeException e) { + Log.e(TAG, "Failed to initialize external widget", e); + return false; + } + } + } + return true; + } + private static final void beginDocument(XmlPullParser parser, String firstElementName) throws XmlPullParserException, IOException { int type; @@ -1752,6 +1794,7 @@ public class LauncherProvider extends ContentProvider { return false; } + private boolean addAppWidget(SQLiteDatabase db, ContentValues values, ComponentName cn, Bundle extras) { boolean allocatedAppWidgets = false; diff --git a/src/com/android/launcher3/LogAccelerateInterpolator.java b/src/com/android/launcher3/LogAccelerateInterpolator.java new file mode 100644 index 000000000..c3bbfa536 --- /dev/null +++ b/src/com/android/launcher3/LogAccelerateInterpolator.java @@ -0,0 +1,25 @@ +package com.android.launcher3; + +import android.animation.TimeInterpolator; + +public class LogAccelerateInterpolator implements TimeInterpolator { + + int mBase; + int mDrift; + final float mLogScale; + + public LogAccelerateInterpolator(int base, int drift) { + mBase = base; + mDrift = drift; + mLogScale = 1f / computeLog(1, mBase, mDrift); + } + + static float computeLog(float t, int base, int drift) { + return (float) -Math.pow(base, -t) + 1 + (drift * t); + } + + @Override + public float getInterpolation(float t) { + return 1 - computeLog(1 - t, mBase, mDrift) * mLogScale; + } +} diff --git a/src/com/android/launcher3/LogDecelerateInterpolator.java b/src/com/android/launcher3/LogDecelerateInterpolator.java new file mode 100644 index 000000000..4c5f6f08c --- /dev/null +++ b/src/com/android/launcher3/LogDecelerateInterpolator.java @@ -0,0 +1,26 @@ +package com.android.launcher3; + +import android.animation.TimeInterpolator; + +public class LogDecelerateInterpolator implements TimeInterpolator { + + int mBase; + int mDrift; + final float mLogScale; + + public LogDecelerateInterpolator(int base, int drift) { + mBase = base; + mDrift = drift; + + mLogScale = 1f / computeLog(1, mBase, mDrift); + } + + static float computeLog(float t, int base, int drift) { + return (float) -Math.pow(base, -t) + 1 + (drift * t); + } + + @Override + public float getInterpolation(float t) { + return computeLog(t, mBase, mDrift) * mLogScale; + } +} diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java index 7e1f0d649..266e9e07f 100644 --- a/src/com/android/launcher3/ShortcutInfo.java +++ b/src/com/android/launcher3/ShortcutInfo.java @@ -79,6 +79,12 @@ public class ShortcutInfo extends ItemInfo { private Bitmap mIcon; /** + * Could be disabled, if the the app is installed but unavailable (eg. in safe mode or when + * sd-card is not available). + */ + boolean isDisabled = false; + + /** * The installation state of the package that this shortcut represents. */ protected int mState; diff --git a/src/com/android/launcher3/Stats.java b/src/com/android/launcher3/Stats.java index 882fb04a3..f3977e456 100644 --- a/src/com/android/launcher3/Stats.java +++ b/src/com/android/launcher3/Stats.java @@ -32,7 +32,6 @@ public class Stats { private static final boolean LOCAL_LAUNCH_LOG = true; public static final String ACTION_LAUNCH = "com.android.launcher3.action.LAUNCH"; - public static final String PERM_LAUNCH = "com.android.launcher3.permission.RECEIVE_LAUNCH_BROADCASTS"; public static final String EXTRA_INTENT = "intent"; public static final String EXTRA_CONTAINER = "container"; public static final String EXTRA_SCREEN = "screen"; @@ -53,6 +52,8 @@ public class Stats { private final Launcher mLauncher; + private final String mLaunchBroadcastPermission; + DataOutputStream mLog; ArrayList<String> mIntents; @@ -61,6 +62,9 @@ public class Stats { public Stats(Launcher launcher) { mLauncher = launcher; + mLaunchBroadcastPermission = + launcher.getResources().getString(R.string.receive_launch_broadcasts_permission); + loadStats(); if (LOCAL_LAUNCH_LOG) { @@ -87,7 +91,7 @@ public class Stats { } }, new IntentFilter(ACTION_LAUNCH), - PERM_LAUNCH, + mLaunchBroadcastPermission, null ); } @@ -120,7 +124,7 @@ public class Stats { .putExtra(EXTRA_CELLX, shortcut.cellX) .putExtra(EXTRA_CELLY, shortcut.cellY); } - mLauncher.sendBroadcast(broadcastIntent, PERM_LAUNCH); + mLauncher.sendBroadcast(broadcastIntent, mLaunchBroadcastPermission); incrementLaunch(flat); diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 74b6e47f1..0a711c5dd 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -18,14 +18,17 @@ package com.android.launcher3; import android.app.Activity; import android.content.ActivityNotFoundException; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.graphics.Bitmap; -import android.graphics.BlurMaskFilter; import android.graphics.Canvas; -import android.graphics.ColorMatrix; -import android.graphics.ColorMatrixColorFilter; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PaintFlagsDrawFilter; @@ -52,10 +55,6 @@ public final class Utilities { public static int sIconTextureWidth = -1; public static int sIconTextureHeight = -1; - private static final Paint sBlurPaint = new Paint(); - private static final Paint sGlowColorPressedPaint = new Paint(); - private static final Paint sGlowColorFocusedPaint = new Paint(); - private static final Paint sDisabledPaint = new Paint(); private static final Rect sOldBounds = new Rect(); private static final Canvas sCanvas = new Canvas(); @@ -75,7 +74,7 @@ public final class Utilities { /** * Returns a FastBitmapDrawable with the icon, accurately sized. */ - static Drawable createIconDrawable(Bitmap icon) { + static FastBitmapDrawable createIconDrawable(Bitmap icon) { FastBitmapDrawable d = new FastBitmapDrawable(icon); d.setFilterBitmap(true); resizeIconDrawable(d); @@ -332,15 +331,6 @@ public final class Utilities { sIconWidth = sIconHeight = (int) resources.getDimension(R.dimen.app_icon_size); sIconTextureWidth = sIconTextureHeight = sIconWidth; - - sBlurPaint.setMaskFilter(new BlurMaskFilter(5 * density, BlurMaskFilter.Blur.NORMAL)); - sGlowColorPressedPaint.setColor(0xffffc300); - sGlowColorFocusedPaint.setColor(0xffff8e00); - - ColorMatrix cm = new ColorMatrix(); - cm.setSaturation(0.2f); - sDisabledPaint.setColorFilter(new ColorMatrixColorFilter(cm)); - sDisabledPaint.setAlpha(0x88); } public static void setIconSize(int widthPx) { @@ -378,4 +368,29 @@ public final class Utilities { "or use the exported attribute for this activity.", e); } } + + static boolean isSystemApp(Context context, Intent intent) { + PackageManager pm = context.getPackageManager(); + ComponentName cn = intent.getComponent(); + String packageName = null; + if (cn == null) { + ResolveInfo info = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY); + if ((info != null) && (info.activityInfo != null)) { + packageName = info.activityInfo.packageName; + } + } else { + packageName = cn.getPackageName(); + } + if (packageName != null) { + try { + PackageInfo info = pm.getPackageInfo(packageName, 0); + return (info != null) && (info.applicationInfo != null) && + ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0); + } catch (NameNotFoundException e) { + return false; + } + } else { + return false; + } + } } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index a8e7580c3..1011588c8 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -30,6 +30,8 @@ import android.app.WallpaperManager; import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetProviderInfo; import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.ContentValues; import android.content.Context; import android.content.SharedPreferences; import android.content.res.Resources; @@ -47,6 +49,7 @@ import android.net.Uri; import android.os.AsyncTask; import android.os.IBinder; import android.os.Parcelable; +import android.provider.BaseColumns; import android.support.v4.view.ViewCompat; import android.util.AttributeSet; import android.util.Log; @@ -61,15 +64,16 @@ import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import android.widget.TextView; -import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.FolderIcon.FolderRingAnimator; import com.android.launcher3.Launcher.CustomContentCallbacks; import com.android.launcher3.LauncherSettings.Favorites; +import com.android.launcher3.compat.UserHandleCompat; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.Set; /** * The workspace is a wide area with a wallpaper and a finite number of pages. @@ -109,9 +113,6 @@ public class Workspace extends SmoothPagedView // These properties refer to the background protection gradient used for AllApps and Customize private ValueAnimator mBackgroundFadeInAnimation; private ValueAnimator mBackgroundFadeOutAnimation; - private Drawable mBackground; - boolean mDrawBackground = true; - private float mBackgroundAlpha = 0; private static final long CUSTOM_CONTENT_GESTURE_DELAY = 200; private long mTouchDownTime = -1; @@ -191,7 +192,7 @@ public class Workspace extends SmoothPagedView // State variable that indicates whether the pages are small (ie when you're // in all apps or customize mode) - enum State { NORMAL, SPRING_LOADED, SMALL, OVERVIEW}; + enum State { NORMAL, NORMAL_HIDDEN, SPRING_LOADED, OVERVIEW, OVERVIEW_HIDDEN}; private State mState = State.NORMAL; private boolean mIsSwitchingState = false; @@ -445,13 +446,6 @@ public class Workspace extends SmoothPagedView setMinScale(mOverviewModeShrinkFactor); setupLayoutTransition(); - final Resources res = getResources(); - try { - mBackground = res.getDrawable(R.drawable.apps_customize_bg); - } catch (Resources.NotFoundException e) { - // In this case, we will skip drawing background protection - } - mWallpaperOffset = new WallpaperOffsetInterpolator(); Display display = mLauncher.getWindowManager().getDefaultDisplay(); display.getSize(mDisplaySize); @@ -1068,8 +1062,8 @@ public class Workspace extends SmoothPagedView */ @Override public boolean onTouch(View v, MotionEvent event) { - return (isSmall() || !isFinishedSwitchingState()) - || (!isSmall() && indexOfChild(v) != mCurrentPage); + return (workspaceInModalState() || !isFinishedSwitchingState()) + || (!workspaceInModalState() && indexOfChild(v) != mCurrentPage); } public boolean isSwitchingState() { @@ -1088,7 +1082,7 @@ public class Workspace extends SmoothPagedView @Override public boolean dispatchUnhandledMove(View focused, int direction) { - if (isSmall() || !isFinishedSwitchingState()) { + if (workspaceInModalState() || !isFinishedSwitchingState()) { // when the home screens are shrunken, shouldn't allow side-scrolling return false; } @@ -1226,7 +1220,7 @@ public class Workspace extends SmoothPagedView } if (mDragController.isDragging()) { - if (isSmall()) { + if (workspaceInModalState()) { // If we are in springloaded mode, then force an event to check if the current touch // is under a new page (to scroll to) mDragController.forceTouchMove(); @@ -1500,7 +1494,7 @@ public class Workspace extends SmoothPagedView } void showOutlines() { - if (!isSmall() && !mIsSwitchingState) { + if (!workspaceInModalState() && !mIsSwitchingState) { if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel(); if (mChildrenOutlineFadeInAnimation != null) mChildrenOutlineFadeInAnimation.cancel(); mChildrenOutlineFadeInAnimation = LauncherAnimUtils.ofFloat(this, "childrenOutlineAlpha", 1.0f); @@ -1510,7 +1504,7 @@ public class Workspace extends SmoothPagedView } void hideOutlines() { - if (!isSmall() && !mIsSwitchingState) { + if (!workspaceInModalState() && !mIsSwitchingState) { if (mChildrenOutlineFadeInAnimation != null) mChildrenOutlineFadeInAnimation.cancel(); if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel(); mChildrenOutlineFadeOutAnimation = LauncherAnimUtils.ofFloat(this, "childrenOutlineAlpha", 0.0f); @@ -1538,15 +1532,9 @@ public class Workspace extends SmoothPagedView return mChildrenOutlineAlpha; } - void disableBackground() { - mDrawBackground = false; - } - void enableBackground() { - mDrawBackground = true; - } - private void animateBackgroundGradient(float finalAlpha, boolean animated) { - if (mBackground == null) return; + final DragLayer dragLayer = mLauncher.getDragLayer(); + if (mBackgroundFadeInAnimation != null) { mBackgroundFadeInAnimation.cancel(); mBackgroundFadeInAnimation = null; @@ -1555,36 +1543,26 @@ public class Workspace extends SmoothPagedView mBackgroundFadeOutAnimation.cancel(); mBackgroundFadeOutAnimation = null; } - float startAlpha = getBackgroundAlpha(); + float startAlpha = dragLayer.getBackgroundAlpha(); if (finalAlpha != startAlpha) { if (animated) { mBackgroundFadeOutAnimation = LauncherAnimUtils.ofFloat(this, startAlpha, finalAlpha); mBackgroundFadeOutAnimation.addUpdateListener(new AnimatorUpdateListener() { public void onAnimationUpdate(ValueAnimator animation) { - setBackgroundAlpha(((Float) animation.getAnimatedValue()).floatValue()); + dragLayer.setBackgroundAlpha( + ((Float)animation.getAnimatedValue()).floatValue()); } }); mBackgroundFadeOutAnimation.setInterpolator(new DecelerateInterpolator(1.5f)); mBackgroundFadeOutAnimation.setDuration(BACKGROUND_FADE_OUT_DURATION); mBackgroundFadeOutAnimation.start(); } else { - setBackgroundAlpha(finalAlpha); + dragLayer.setBackgroundAlpha(finalAlpha); } } } - public void setBackgroundAlpha(float alpha) { - if (alpha != mBackgroundAlpha) { - mBackgroundAlpha = alpha; - invalidate(); - } - } - - public float getBackgroundAlpha() { - return mBackgroundAlpha; - } - float backgroundAlphaInterpolator(float r) { float pivotA = 0.1f; float pivotB = 0.4f; @@ -1656,13 +1634,13 @@ public class Workspace extends SmoothPagedView if (Float.compare(progress, mLastCustomContentScrollProgress) == 0) return; CellLayout cc = mWorkspaceScreens.get(CUSTOM_CONTENT_SCREEN_ID); - if (progress > 0 && cc.getVisibility() != VISIBLE && !isSmall()) { + if (progress > 0 && cc.getVisibility() != VISIBLE && !workspaceInModalState()) { cc.setVisibility(VISIBLE); } mLastCustomContentScrollProgress = progress; - setBackgroundAlpha(progress * 0.8f); + mLauncher.getDragLayer().setBackgroundAlpha(progress * 0.8f); if (mLauncher.getHotseat() != null) { mLauncher.getHotseat().setTranslationX(translationX); @@ -1792,25 +1770,12 @@ public class Workspace extends SmoothPagedView @Override protected void onDraw(Canvas canvas) { - // Draw the background gradient if necessary - if (mBackground != null && mBackgroundAlpha > 0.0f && mDrawBackground) { - int alpha = (int) (mBackgroundAlpha * 255); - mBackground.setAlpha(alpha); - mBackground.setBounds(getScrollX(), 0, getScrollX() + getMeasuredWidth(), - getMeasuredHeight()); - mBackground.draw(canvas); - } - super.onDraw(canvas); // Call back to LauncherModel to finish binding after the first draw post(mBindPages); } - boolean isDrawingBackgroundGradient() { - return (mBackground != null && mBackgroundAlpha > 0.0f && mDrawBackground); - } - @Override protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { if (!mLauncher.isAllAppsVisible()) { @@ -1826,7 +1791,7 @@ public class Workspace extends SmoothPagedView @Override public int getDescendantFocusability() { - if (isSmall()) { + if (workspaceInModalState()) { return ViewGroup.FOCUS_BLOCK_DESCENDANTS; } return super.getDescendantFocusability(); @@ -1844,8 +1809,8 @@ public class Workspace extends SmoothPagedView } } - public boolean isSmall() { - return mState == State.SMALL || mState == State.SPRING_LOADED || mState == State.OVERVIEW; + public boolean workspaceInModalState() { + return mState != State.NORMAL; } void enableChildrenCache(int fromPage, int toPage) { @@ -1880,7 +1845,7 @@ public class Workspace extends SmoothPagedView } private void updateChildrenLayersEnabled(boolean force) { - boolean small = mState == State.SMALL || mState == State.OVERVIEW || mIsSwitchingState; + boolean small = mState == State.OVERVIEW || mIsSwitchingState; boolean enableChildrenLayers = force || small || mAnimatingViewIntoPlace || isPageMoving(); if (enableChildrenLayers != mChildrenLayersEnabled) { @@ -2223,6 +2188,8 @@ public class Workspace extends SmoothPagedView setImportantForAccessibility(accessible); } + private static final int HIDE_WORKSPACE_DURATION = 100; + Animator getChangeStateAnimation(final State state, boolean animated, int delay, int snapPage) { if (mState == state) { return null; @@ -2236,21 +2203,25 @@ public class Workspace extends SmoothPagedView final State oldState = mState; final boolean oldStateIsNormal = (oldState == State.NORMAL); final boolean oldStateIsSpringLoaded = (oldState == State.SPRING_LOADED); - final boolean oldStateIsSmall = (oldState == State.SMALL); + final boolean oldStateIsNormalHidden = (oldState == State.NORMAL_HIDDEN); + final boolean oldStateIsOverviewHidden = (oldState == State.OVERVIEW_HIDDEN); final boolean oldStateIsOverview = (oldState == State.OVERVIEW); setState(state); final boolean stateIsNormal = (state == State.NORMAL); final boolean stateIsSpringLoaded = (state == State.SPRING_LOADED); - final boolean stateIsSmall = (state == State.SMALL); + final boolean stateIsNormalHidden = (state == State.NORMAL_HIDDEN); + final boolean stateIsOverviewHidden = (state == State.OVERVIEW_HIDDEN); final boolean stateIsOverview = (state == State.OVERVIEW); float finalBackgroundAlpha = (stateIsSpringLoaded || stateIsOverview) ? 1.0f : 0f; - float finalHotseatAndPageIndicatorAlpha = (stateIsOverview || stateIsSmall) ? 0f : 1f; + float finalHotseatAndPageIndicatorAlpha = (stateIsNormal || stateIsSpringLoaded) ? 1f : 0f; float finalOverviewPanelAlpha = stateIsOverview ? 1f : 0f; float finalSearchBarAlpha = !stateIsNormal ? 0f : 1f; - float finalWorkspaceTranslationY = stateIsOverview ? getOverviewModeTranslationY() : 0; + float finalWorkspaceTranslationY = stateIsOverview || stateIsOverviewHidden ? + getOverviewModeTranslationY() : 0; - boolean workspaceToAllApps = (oldStateIsNormal && stateIsSmall); - boolean allAppsToWorkspace = (oldStateIsSmall && stateIsNormal); + boolean workspaceToAllApps = (oldStateIsNormal && stateIsNormalHidden); + boolean overviewToAllApps = (oldStateIsOverview && stateIsOverviewHidden); + boolean allAppsToWorkspace = (stateIsNormalHidden && stateIsNormal); boolean workspaceToOverview = (oldStateIsNormal && stateIsOverview); boolean overviewToWorkspace = (oldStateIsOverview && stateIsNormal); @@ -2265,10 +2236,8 @@ public class Workspace extends SmoothPagedView if (state != State.NORMAL) { if (stateIsSpringLoaded) { mNewScale = mSpringLoadedShrinkFactor; - } else if (stateIsOverview) { + } else if (stateIsOverview || stateIsOverviewHidden) { mNewScale = mOverviewModeShrinkFactor; - } else if (stateIsSmall){ - mNewScale = mOverviewModeShrinkFactor - 0.3f; } if (workspaceToAllApps) { updateChildrenLayersEnabled(false); @@ -2276,8 +2245,8 @@ public class Workspace extends SmoothPagedView } final int duration; - if (workspaceToAllApps) { - duration = getResources().getInteger(R.integer.config_workspaceUnshrinkTime); + if (workspaceToAllApps || overviewToAllApps) { + duration = HIDE_WORKSPACE_DURATION; //getResources().getInteger(R.integer.config_workspaceUnshrinkTime); } else if (workspaceToOverview || overviewToWorkspace) { duration = getResources().getInteger(R.integer.config_overviewTransitionTime); } else { @@ -2294,7 +2263,7 @@ public class Workspace extends SmoothPagedView boolean isCurrentPage = (i == snapPage); float initialAlpha = cl.getShortcutsAndWidgets().getAlpha(); float finalAlpha; - if (stateIsSmall) { + if (stateIsNormalHidden || stateIsOverviewHidden) { finalAlpha = 0f; } else if (stateIsNormal && mWorkspaceFadeInAdjacentScreens) { finalAlpha = (i == snapPage || i < numCustomPages()) ? 1f : 0f; @@ -2331,11 +2300,11 @@ public class Workspace extends SmoothPagedView final View hotseat = mLauncher.getHotseat(); final View pageIndicator = getPageIndicator(); if (animated) { - anim.setDuration(duration); LauncherViewPropertyAnimator scale = new LauncherViewPropertyAnimator(this); scale.scaleX(mNewScale) .scaleY(mNewScale) .translationY(finalWorkspaceTranslationY) + .setDuration(duration) .setInterpolator(mZoomInInterpolator); anim.play(scale); for (int index = 0; index < getChildCount(); index++) { @@ -2350,6 +2319,7 @@ public class Workspace extends SmoothPagedView LauncherViewPropertyAnimator alphaAnim = new LauncherViewPropertyAnimator(cl.getShortcutsAndWidgets()); alphaAnim.alpha(mNewAlphas[i]) + .setDuration(duration) .setInterpolator(mZoomInInterpolator); anim.play(alphaAnim); } @@ -2358,6 +2328,7 @@ public class Workspace extends SmoothPagedView ValueAnimator bgAnim = LauncherAnimUtils.ofFloat(cl, 0f, 1f); bgAnim.setInterpolator(mZoomInInterpolator); + bgAnim.setDuration(duration); bgAnim.addUpdateListener(new LauncherAnimatorUpdateListener() { public void onAnimationUpdate(float a, float b) { cl.setBackgroundAlpha( @@ -2400,7 +2371,11 @@ public class Workspace extends SmoothPagedView hotseatAlpha.setInterpolator(null); overviewPanelAlpha.setInterpolator(new DecelerateInterpolator(2)); } - searchBarAlpha.setInterpolator(null); + + overviewPanelAlpha.setDuration(duration); + pageIndicatorAlpha.setDuration(duration); + hotseatAlpha.setDuration(duration); + searchBarAlpha.setDuration(duration); anim.play(overviewPanelAlpha); anim.play(hotseatAlpha); @@ -2425,18 +2400,11 @@ public class Workspace extends SmoothPagedView } mLauncher.updateVoiceButtonProxyVisible(false); - if (stateIsSpringLoaded) { - // Right now we're covered by Apps Customize - // Show the background gradient immediately, so the gradient will - // be showing once AppsCustomize disappears - animateBackgroundGradient(getResources().getInteger( - R.integer.config_appsCustomizeSpringLoadedBgAlpha) / 100f, false); - } else if (stateIsOverview) { - animateBackgroundGradient(getResources().getInteger( - R.integer.config_appsCustomizeSpringLoadedBgAlpha) / 100f, true); - } else { - // Fade the background gradient away + if (stateIsNormal) { animateBackgroundGradient(0f, animated); + } else { + animateBackgroundGradient(getResources().getInteger( + R.integer.config_workspaceScrimAlpha) / 100f, animated); } return anim; } @@ -2822,7 +2790,8 @@ public class Workspace extends SmoothPagedView } public boolean transitionStateShouldAllowDrop() { - return ((!isSwitchingState() || mTransitionProgress > 0.5f) && mState != State.SMALL); + return ((!isSwitchingState() || mTransitionProgress > 0.5f) && + (mState == State.NORMAL || mState == State.SPRING_LOADED)); } /** @@ -3137,7 +3106,12 @@ public class Workspace extends SmoothPagedView final ItemInfo info = (ItemInfo) cell.getTag(); if (hasMovedLayouts) { // Reparent the view - getParentCellLayoutForView(cell).removeView(cell); + CellLayout parentCell = getParentCellLayoutForView(cell); + if (parentCell != null) { + parentCell.removeView(cell); + } else if (LauncherAppState.isDogfoodBuild()) { + throw new NullPointerException("mDragInfo.cell has null parent"); + } addInScreen(cell, container, screenId, mTargetCell[0], mTargetCell[1], info.spanX, info.spanY); } @@ -3591,11 +3565,17 @@ public class Workspace extends SmoothPagedView public void onDragOver(DragObject d) { // Skip drag over events while we are dragging over side pages - if (mInScrollArea || mIsSwitchingState || mState == State.SMALL) return; + if (mInScrollArea || !transitionStateShouldAllowDrop()) return; Rect r = new Rect(); CellLayout layout = null; ItemInfo item = (ItemInfo) d.dragInfo; + if (item == null) { + if (LauncherAppState.isDogfoodBuild()) { + throw new NullPointerException("DragObject has null info"); + } + return; + } // Ensure that we have proper spans for the item that we are dropping if (item.spanX < 0 || item.spanY < 0) throw new RuntimeException("Improper spans found"); @@ -3604,7 +3584,7 @@ public class Workspace extends SmoothPagedView final View child = (mDragInfo == null) ? null : mDragInfo.cell; // Identify whether we have dragged over a side page - if (isSmall()) { + if (workspaceInModalState()) { if (mLauncher.getHotseat() != null && !isExternalDragWidget(d)) { if (isPointInSelfOverHotseat(d.x, d.y, r)) { layout = mLauncher.getHotseat().getLayout(); @@ -4223,6 +4203,8 @@ public class Workspace extends SmoothPagedView CellLayout parentCell = getParentCellLayoutForView(mDragInfo.cell); if (parentCell != null) { parentCell.removeView(mDragInfo.cell); + } else if (LauncherAppState.isDogfoodBuild()) { + throw new NullPointerException("mDragInfo.cell has null parent"); } if (mDragInfo.cell instanceof DropTarget) { mDragController.removeDropTarget((DropTarget) mDragInfo.cell); @@ -4482,7 +4464,7 @@ public class Workspace extends SmoothPagedView @Override public void scrollLeft() { - if (!isSmall() && !mIsSwitchingState) { + if (!workspaceInModalState() && !mIsSwitchingState) { super.scrollLeft(); } Folder openFolder = getOpenFolder(); @@ -4493,7 +4475,7 @@ public class Workspace extends SmoothPagedView @Override public void scrollRight() { - if (!isSmall() && !mIsSwitchingState) { + if (!workspaceInModalState() && !mIsSwitchingState) { super.scrollRight(); } Folder openFolder = getOpenFolder(); @@ -4515,7 +4497,7 @@ public class Workspace extends SmoothPagedView } boolean result = false; - if (!isSmall() && !mIsSwitchingState && getOpenFolder() == null) { + if (!workspaceInModalState() && !mIsSwitchingState && getOpenFolder() == null) { mInScrollArea = true; final int page = getNextPage() + @@ -4609,7 +4591,7 @@ public class Workspace extends SmoothPagedView public Folder getFolderForTag(final Object tag) { final Folder[] value = new Folder[1]; - mapOverShortcuts(MAP_NO_RECURSE, new ShortcutOperator() { + mapOverItems(MAP_NO_RECURSE, new ItemOperator() { @Override public boolean evaluate(ItemInfo info, View v, View parent) { if (v instanceof Folder) { @@ -4627,7 +4609,7 @@ public class Workspace extends SmoothPagedView public View getViewForTag(final Object tag) { final View[] value = new View[1]; - mapOverShortcuts(MAP_NO_RECURSE, new ShortcutOperator() { + mapOverItems(MAP_NO_RECURSE, new ItemOperator() { @Override public boolean evaluate(ItemInfo info, View v, View parent) { if (v.getTag() == tag) { @@ -4641,7 +4623,7 @@ public class Workspace extends SmoothPagedView } void clearDropTargets() { - mapOverShortcuts(MAP_NO_RECURSE, new ShortcutOperator() { + mapOverItems(MAP_NO_RECURSE, new ItemOperator() { @Override public boolean evaluate(ItemInfo info, View v, View parent) { if (v instanceof DropTarget) { @@ -4790,14 +4772,14 @@ public class Workspace extends SmoothPagedView shortcutInfo.updateIcon(mIconCache); shortcutInfo.title = appInfo.title.toString(); shortcutInfo.contentDescription = appInfo.contentDescription; - shortcut.applyFromShortcutInfo(shortcutInfo, mIconCache); + shortcut.applyFromShortcutInfo(shortcutInfo, mIconCache, true); } } } - interface ShortcutOperator { + interface ItemOperator { /** - * Process the next shortcut, possibly with side-effect on {@link ShortcutOperator#value}. + * Process the next itemInfo, possibly with side-effect on {@link ItemOperator#value}. * * @param info info for the shortcut * @param view view for the shortcut @@ -4808,12 +4790,12 @@ public class Workspace extends SmoothPagedView } /** - * Map the operator over the shortcuts, return the first-non-null value. + * Map the operator over the shortcuts and widgets, return the first-non-null value. * * @param recurse true: iterate over folder children. false: op get the folders themselves. * @param op the operator to map over the shortcuts */ - void mapOverShortcuts(boolean recurse, ShortcutOperator op) { + void mapOverItems(boolean recurse, ItemOperator op) { ArrayList<ShortcutAndWidgetContainer> containers = getAllShortcutAndWidgetContainers(); final int containerCount = containers.size(); for (int containerIdx = 0; containerIdx < containerCount; containerIdx++) { @@ -4844,14 +4826,16 @@ public class Workspace extends SmoothPagedView } } - void updateShortcuts(ArrayList<AppInfo> apps) { + void updateShortcutsAndWidgets(ArrayList<AppInfo> apps) { // Create a map of the apps to test against final HashMap<ComponentName, AppInfo> appsMap = new HashMap<ComponentName, AppInfo>(); + final HashSet<String> pkgNames = new HashSet<>(); for (AppInfo ai : apps) { appsMap.put(ai.componentName, ai); + pkgNames.add(ai.componentName.getPackageName()); } - mapOverShortcuts(MAP_RECURSE, new ShortcutOperator() { + mapOverItems(MAP_RECURSE, new ItemOperator() { @Override public boolean evaluate(ItemInfo info, View v, View parent) { if (info instanceof ShortcutInfo) { @@ -4864,6 +4848,8 @@ public class Workspace extends SmoothPagedView return false; } }); + + restorePendingWidgets(pkgNames); } public void removeAbandonedPromise(BubbleTextView abandonedIcon, UserHandleCompat user) { @@ -4879,7 +4865,7 @@ public class Workspace extends SmoothPagedView } public void updatePackageState(final String pkgName, final int state) { - mapOverShortcuts(MAP_RECURSE, new ShortcutOperator() { + mapOverItems(MAP_RECURSE, new ItemOperator() { @Override public boolean evaluate(ItemInfo info, View v, View parent) { if (info instanceof ShortcutInfo @@ -4892,10 +4878,44 @@ public class Workspace extends SmoothPagedView return false; } }); + + if (state == ShortcutInfo.PACKAGE_STATE_DEFAULT) { + // Update any pending widget + HashSet<String> packages = new HashSet<>(); + packages.add(pkgName); + restorePendingWidgets(packages); + } + } + + private void restorePendingWidgets(final Set<String> installedPackaged) { + final ContentResolver contentResolver = getContext().getContentResolver(); + // Iterate non recursively as widgets can't be inside a folder. + mapOverItems(MAP_NO_RECURSE, new ItemOperator() { + + @Override + public boolean evaluate(ItemInfo info, View v, View parent) { + if (info instanceof LauncherAppWidgetInfo) { + LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) info; + if (widgetInfo.restoreStatus == LauncherAppWidgetInfo.RESTORE_PROVIDER_PENDING + && installedPackaged.contains(widgetInfo.providerName.getPackageName())) { + // Package installed. Update pending widgets. + ContentValues values = new ContentValues(); + values.put(LauncherSettings.Favorites.RESTORED, + LauncherAppWidgetInfo.RESTORE_COMPLETED); + String where = BaseColumns._ID + "= ?"; + String[] args = {Long.toString(widgetInfo.id)}; + contentResolver.update(LauncherSettings.Favorites.CONTENT_URI, + values, where, args); + } + } + // process all the widget + return false; + } + }); } private void moveToScreen(int page, boolean animate) { - if (!isSmall()) { + if (!workspaceInModalState()) { if (animate) { snapToPage(page); } else { |