diff options
Diffstat (limited to 'src')
41 files changed, 602 insertions, 225 deletions
diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java index 8d418f984..a207d9a12 100644 --- a/src/com/android/launcher3/BaseRecyclerView.java +++ b/src/com/android/launcher3/BaseRecyclerView.java @@ -86,10 +86,8 @@ public class BaseRecyclerView extends RecyclerView private int mDownX; private int mDownY; - private int mLastX; private int mLastY; private int mScrollbarWidth; - private int mScrollbarMinHeight; private int mScrollbarInset; private Rect mBackgroundPadding = new Rect(); @@ -121,8 +119,6 @@ public class BaseRecyclerView extends RecyclerView mFastScrollTextPaint.setTextSize(res.getDimensionPixelSize( R.dimen.all_apps_fast_scroll_text_size)); mScrollbarWidth = res.getDimensionPixelSize(R.dimen.all_apps_fast_scroll_bar_width); - mScrollbarMinHeight = - res.getDimensionPixelSize(R.dimen.all_apps_fast_scroll_bar_min_height); mScrollbarInset = res.getDimensionPixelSize(R.dimen.all_apps_fast_scroll_scrubber_touch_inset); setFastScrollerAlpha(mFastScrollAlpha); @@ -173,7 +169,7 @@ public class BaseRecyclerView extends RecyclerView switch (action) { case MotionEvent.ACTION_DOWN: // Keep track of the down positions - mDownX = mLastX = x; + mDownX = x; mDownY = mLastY = y; if (shouldStopScroll(ev)) { stopScroll(); @@ -188,7 +184,6 @@ public class BaseRecyclerView extends RecyclerView animateFastScrollerVisibility(true); } if (mDraggingFastScroller) { - mLastX = x; mLastY = y; // Scroll to the right position, and update the section name diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index edf502112..f4e306af3 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -28,7 +28,6 @@ import android.graphics.Region; import android.graphics.drawable.Drawable; import android.os.Build; import android.util.AttributeSet; -import android.util.Log; import android.util.SparseArray; import android.util.TypedValue; import android.view.KeyEvent; @@ -36,7 +35,6 @@ import android.view.MotionEvent; import android.view.ViewConfiguration; import android.view.ViewParent; import android.widget.TextView; - import com.android.launcher3.IconCache.IconLoadRequest; import com.android.launcher3.model.PackageItemInfo; @@ -59,10 +57,12 @@ public class BubbleTextView extends TextView { private static final int DISPLAY_WORKSPACE = 0; private static final int DISPLAY_ALL_APPS = 1; + private final Launcher mLauncher; private Drawable mIcon; private final Drawable mBackground; private final CheckLongPressHelper mLongPressHelper; private final HolographicOutlineHelper mOutlineHelper; + private final StylusEventHelper mStylusEventHelper; private boolean mBackgroundSizeChanged; @@ -92,8 +92,8 @@ public class BubbleTextView extends TextView { public BubbleTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = ((Launcher) context).getDeviceProfile(); + mLauncher = (Launcher) context; + DeviceProfile grid = mLauncher.getDeviceProfile(); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.BubbleTextView, defStyle, 0); @@ -125,6 +125,7 @@ public class BubbleTextView extends TextView { } mLongPressHelper = new CheckLongPressHelper(this); + mStylusEventHelper = new StylusEventHelper(this); mOutlineHelper = HolographicOutlineHelper.obtain(getContext()); if (mCustomShadowsEnabled) { @@ -142,7 +143,7 @@ public class BubbleTextView extends TextView { boolean promiseStateChanged) { Bitmap b = info.getIcon(iconCache); - FastBitmapDrawable iconDrawable = Utilities.createIconDrawable(b); + FastBitmapDrawable iconDrawable = mLauncher.createIconDrawable(b); iconDrawable.setGhostModeEnabled(info.isDisabled != 0); setIcon(iconDrawable, mIconSize); @@ -158,7 +159,7 @@ public class BubbleTextView extends TextView { } public void applyFromApplicationInfo(AppInfo info) { - setIcon(Utilities.createIconDrawable(info.iconBitmap), mIconSize); + setIcon(mLauncher.createIconDrawable(info.iconBitmap), mIconSize); setText(info.title); if (info.contentDescription != null) { setContentDescription(info.contentDescription); @@ -171,7 +172,7 @@ public class BubbleTextView extends TextView { } public void applyFromPackageItemInfo(PackageItemInfo info) { - setIcon(Utilities.createIconDrawable(info.iconBitmap), mIconSize); + setIcon(mLauncher.createIconDrawable(info.iconBitmap), mIconSize); setText(info.title); if (info.contentDescription != null) { setContentDescription(info.contentDescription); @@ -236,6 +237,12 @@ public class BubbleTextView extends TextView { // isPressed() on an ACTION_UP boolean result = super.onTouchEvent(event); + // Check for a stylus button press, if it occurs cancel any long press checks. + if (mStylusEventHelper.checkAndPerformStylusEvent(event)) { + mLongPressHelper.cancelLongPress(); + result = true; + } + switch (event.getAction()) { case MotionEvent.ACTION_DOWN: // So that the pressed outline is visible immediately on setStayPressed(), @@ -245,7 +252,10 @@ public class BubbleTextView extends TextView { mPressedBackground = mOutlineHelper.createMediumDropShadow(this); } - mLongPressHelper.postCheckForLongPress(); + // If we're in a stylus button press, don't check for long press. + if (!mStylusEventHelper.inStylusButtonPressed()) { + mLongPressHelper.postCheckForLongPress(); + } break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java index 4cd28c034..09a71b0cc 100644 --- a/src/com/android/launcher3/ButtonDropTarget.java +++ b/src/com/android/launcher3/ButtonDropTarget.java @@ -64,7 +64,7 @@ public abstract class ButtonDropTarget extends TextView protected Drawable mDrawable; private AnimatorSet mCurrentColorAnim; - private ColorMatrix mSrcFilter, mDstFilter, mCurrentFilter; + @Thunk ColorMatrix mSrcFilter, mDstFilter, mCurrentFilter; public ButtonDropTarget(Context context, AttributeSet attrs) { diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index 61567ac00..b5d0dca24 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -95,6 +95,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { boolean[][] mTmpOccupied; private OnTouchListener mInterceptTouchListener; + private StylusEventHelper mStylusEventHelper; private ArrayList<FolderRingAnimator> mFolderOuterRings = new ArrayList<FolderRingAnimator>(); private int[] mFolderLeaveBehindCell = {-1, -1}; @@ -130,10 +131,8 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { private final ClickShadowView mTouchFeedbackView; - @Thunk HashMap<CellLayout.LayoutParams, Animator> mReorderAnimators = new - HashMap<CellLayout.LayoutParams, Animator>(); - private HashMap<View, ReorderPreviewAnimation> - mShakeAnimators = new HashMap<View, ReorderPreviewAnimation>(); + @Thunk HashMap<CellLayout.LayoutParams, Animator> mReorderAnimators = new HashMap<>(); + @Thunk HashMap<View, ReorderPreviewAnimation> mShakeAnimators = new HashMap<>(); private boolean mItemPlacementDirty = false; @@ -286,6 +285,8 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mWidthGap, mHeightGap, mCountX, mCountY); + mStylusEventHelper = new StylusEventHelper(this); + mTouchFeedbackView = new ClickShadowView(context); addView(mTouchFeedbackView); addView(mShortcutsAndWidgets); @@ -338,6 +339,20 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { return false; } + @Override + public boolean onTouchEvent(MotionEvent ev) { + boolean handled = super.onTouchEvent(ev); + // Stylus button press on a home screen should not switch between overview mode and + // the home screen mode, however, once in overview mode stylus button press should be + // enabled to allow rearranging the different home screens. So check what mode + // the workspace is in, and only perform stylus button presses while in overview mode. + if (mLauncher.mWorkspace.isInOverviewMode() + && mStylusEventHelper.checkAndPerformStylusEvent(ev)) { + return true; + } + return handled; + } + public void enableHardwareLayer(boolean hasLayer) { mShortcutsAndWidgets.setLayerType(hasLayer ? LAYER_TYPE_HARDWARE : LAYER_TYPE_NONE, sPaint); } diff --git a/src/com/android/launcher3/CommonAppTypeParser.java b/src/com/android/launcher3/CommonAppTypeParser.java index 31641799d..5314ecff1 100644 --- a/src/com/android/launcher3/CommonAppTypeParser.java +++ b/src/com/android/launcher3/CommonAppTypeParser.java @@ -26,6 +26,7 @@ import android.util.Log; import com.android.launcher3.AutoInstallsLayout.LayoutParserCallback; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.backup.BackupProtos.Favorite; +import com.android.launcher3.util.Thunk; import org.xmlpull.v1.XmlPullParserException; @@ -44,8 +45,8 @@ public class CommonAppTypeParser implements LayoutParserCallback { private final long mItemId; - private final int mResId; - private final Context mContext; + @Thunk final int mResId; + @Thunk final Context mContext; ContentValues parsedValues; Intent parsedIntent; diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index bee6cb093..82be409dd 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -405,7 +405,8 @@ public class DeviceProfile { View searchBar = launcher.getSearchBar(); lp = (FrameLayout.LayoutParams) searchBar.getLayoutParams(); if (hasVerticalBarLayout) { - // Vertical search bar space + // Vertical search bar space -- The search bar is fixed in the layout to be on the left + // of the screen regardless of RTL lp.gravity = Gravity.LEFT; lp.width = searchBarSpaceHeightPx; @@ -438,8 +439,9 @@ public class DeviceProfile { View hotseat = launcher.findViewById(R.id.hotseat); lp = (FrameLayout.LayoutParams) hotseat.getLayoutParams(); if (hasVerticalBarLayout) { - // Vertical hotseat - lp.gravity = Gravity.END; + // Vertical hotseat -- The hotseat is fixed in the layout to be on the right of the + // screen regardless of RTL + lp.gravity = Gravity.RIGHT; lp.width = hotseatBarHeightPx; lp.height = LayoutParams.MATCH_PARENT; hotseat.findViewById(R.id.layout).setPadding(0, 2 * edgeMarginPx, 0, 2 * edgeMarginPx); diff --git a/src/com/android/launcher3/DragView.java b/src/com/android/launcher3/DragView.java index b3323384d..dfa8202a7 100644 --- a/src/com/android/launcher3/DragView.java +++ b/src/com/android/launcher3/DragView.java @@ -44,7 +44,7 @@ public class DragView extends View { private Bitmap mBitmap; private Bitmap mCrossFadeBitmap; - private Paint mPaint; + @Thunk Paint mPaint; private int mRegistrationX; private int mRegistrationY; @@ -62,7 +62,7 @@ public class DragView extends View { // size. This is ignored for non-icons. private float mIntrinsicIconScale = 1f; - private float[] mCurrentFilter; + @Thunk float[] mCurrentFilter; private ValueAnimator mFilterAnimator; /** diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java index 46e4902f9..70bb01af0 100644 --- a/src/com/android/launcher3/FocusHelper.java +++ b/src/com/android/launcher3/FocusHelper.java @@ -426,7 +426,7 @@ public class FocusHelper { /** * Private helper method to get the CellLayoutChildren given a CellLayout index. */ - private static ShortcutAndWidgetContainer getCellLayoutChildrenForIndex( + @Thunk static ShortcutAndWidgetContainer getCellLayoutChildrenForIndex( ViewGroup container, int i) { CellLayout parent = (CellLayout) container.getChildAt(i); return parent.getShortcutsAndWidgets(); diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index fcfb717ab..94f8fc875 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -29,6 +29,7 @@ import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.os.Build; +import android.os.Bundle; import android.text.InputType; import android.text.Selection; import android.text.Spannable; @@ -66,7 +67,8 @@ import java.util.Collections; */ public class Folder extends LinearLayout implements DragSource, View.OnClickListener, View.OnLongClickListener, DropTarget, FolderListener, TextView.OnEditorActionListener, - View.OnFocusChangeListener, DragListener, UninstallSource, AccessibilityDragSource { + View.OnFocusChangeListener, DragListener, UninstallSource, AccessibilityDragSource, + Stats.LaunchSourceProvider { private static final String TAG = "Launcher.Folder"; /** @@ -923,7 +925,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList View v = list.get(i); ItemInfo info = (ItemInfo) v.getTag(); LauncherModel.addItemToDatabase(mLauncher, info, mInfo.id, 0, - info.cellX, info.cellY); + info.cellX, info.cellY); } } @@ -1338,6 +1340,14 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList outRect.right += mScrollAreaOffset; } + @Override + public void fillInLaunchSourceData(Bundle sourceData) { + // Fill in from the folder icon's launch source provider first + Stats.LaunchSourceUtils.populateSourceDataFromAncestorProvider(mFolderIcon, sourceData); + sourceData.putString(Stats.SOURCE_EXTRA_SUB_CONTAINER, Stats.SUB_CONTAINER_FOLDER); + sourceData.putInt(Stats.SOURCE_EXTRA_SUB_CONTAINER_PAGE, mContent.getCurrentPage()); + } + private class OnScrollHintListener implements OnAlarmListener { private final DragObject mDragObject; diff --git a/src/com/android/launcher3/FolderIcon.java b/src/com/android/launcher3/FolderIcon.java index 8652eef40..f1e9dc83b 100644 --- a/src/com/android/launcher3/FolderIcon.java +++ b/src/com/android/launcher3/FolderIcon.java @@ -57,6 +57,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { @Thunk static boolean sStaticValuesDirty = true; private CheckLongPressHelper mLongPressHelper; + private StylusEventHelper mStylusEventHelper; // The number of icons to display in the public static final int NUM_ITEMS_IN_PREVIEW = 3; @@ -128,6 +129,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { private void init() { mLongPressHelper = new CheckLongPressHelper(this); + mStylusEventHelper = new StylusEventHelper(this); setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate()); } @@ -643,9 +645,10 @@ public class FolderIcon extends FrameLayout implements FolderListener { final Runnable onCompleteRunnable) { final PreviewItemDrawingParams finalParams = computePreviewItemDrawingParams(0, null); - final float scale0 = 1.0f; - final float transX0 = (mAvailableSpaceInPreview - d.getIntrinsicWidth()) / 2; - final float transY0 = (mAvailableSpaceInPreview - d.getIntrinsicHeight()) / 2 + getPaddingTop(); + float iconSize = mLauncher.getDeviceProfile().iconSizePx; + final float scale0 = iconSize / d.getIntrinsicWidth() ; + final float transX0 = (mAvailableSpaceInPreview - iconSize) / 2; + final float transY0 = (mAvailableSpaceInPreview - iconSize) / 2 + getPaddingTop(); mAnimParams.drawable = d; ValueAnimator va = LauncherAnimUtils.ofFloat(this, 0f, 1.0f); @@ -719,6 +722,12 @@ public class FolderIcon extends FrameLayout implements FolderListener { // isPressed() on an ACTION_UP boolean result = super.onTouchEvent(event); + // Check for a stylus button press, if it occurs cancel any long press checks. + if (mStylusEventHelper.checkAndPerformStylusEvent(event)) { + mLongPressHelper.cancelLongPress(); + return true; + } + switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mLongPressHelper.postCheckForLongPress(); diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java index ce33164fa..6e33d1014 100644 --- a/src/com/android/launcher3/Hotseat.java +++ b/src/com/android/launcher3/Hotseat.java @@ -19,15 +19,16 @@ package com.android.launcher3; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; -import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.os.Bundle; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.MotionEvent; import android.widget.FrameLayout; import android.widget.TextView; -public class Hotseat extends FrameLayout { +public class Hotseat extends FrameLayout + implements Stats.LaunchSourceProvider{ private CellLayout mContent; @@ -130,7 +131,7 @@ public class Hotseat extends FrameLayout { inflater.inflate(R.layout.all_apps_button, mContent, false); Drawable d = context.getResources().getDrawable(R.drawable.all_apps_button_icon); - Utilities.resizeIconDrawable(d); + mLauncher.resizeIconDrawable(d); allAppsButton.setCompoundDrawables(null, d, null, null); allAppsButton.setContentDescription(context.getString(R.string.all_apps_button_label)); @@ -160,4 +161,9 @@ public class Hotseat extends FrameLayout { } return false; } + + @Override + public void fillInLaunchSourceData(Bundle sourceData) { + sourceData.putString(Stats.SOURCE_EXTRA_CONTAINER, Stats.CONTAINER_HOTSEAT); + } } diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index 6dfca9ef3..3165337c2 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -16,7 +16,6 @@ package com.android.launcher3; -import android.app.ActivityManager; import android.content.ComponentName; import android.content.ContentValues; import android.content.Context; @@ -69,7 +68,7 @@ public class IconCache { private static final int LOW_RES_SCALE_FACTOR = 8; - private static final Object ICON_UPDATE_TOKEN = new Object(); + @Thunk static final Object ICON_UPDATE_TOKEN = new Object(); @Thunk static class CacheEntry { public Bitmap icon; @@ -79,28 +78,25 @@ public class IconCache { } private final HashMap<UserHandleCompat, Bitmap> mDefaultIcons = new HashMap<>(); - private final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor(); + @Thunk final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor(); private final Context mContext; private final PackageManager mPackageManager; - private final UserManagerCompat mUserManager; + @Thunk final UserManagerCompat mUserManager; private final LauncherAppsCompat mLauncherApps; private final HashMap<ComponentKey, CacheEntry> mCache = new HashMap<ComponentKey, CacheEntry>(INITIAL_ICON_CACHE_CAPACITY); private final int mIconDpi; - private final IconDB mIconDb; + @Thunk final IconDB mIconDb; - private final Handler mWorkerHandler; + @Thunk final Handler mWorkerHandler; public IconCache(Context context, InvariantDeviceProfile inv) { - ActivityManager activityManager = - (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); - mContext = context; mPackageManager = context.getPackageManager(); mUserManager = UserManagerCompat.getInstance(mContext); mLauncherApps = LauncherAppsCompat.getInstance(mContext); - mIconDpi = activityManager.getLauncherLargeIconDensity(); + mIconDpi = inv.fillResIconDpi; mIconDb = new IconDB(context); mWorkerHandler = new Handler(LauncherModel.getWorkerLooper()); @@ -136,10 +132,6 @@ public class IconCache { return getFullResDefaultActivityIcon(); } - public int getFullResIconDpi() { - return mIconDpi; - } - public Drawable getFullResIcon(ActivityInfo info) { Resources resources; try { @@ -283,6 +275,7 @@ public class IconCache { ComponentName component = ComponentName.unflattenFromString(cn); PackageInfo info = pkgInfoMap.get(component.getPackageName()); if (info == null) { + remove(component, user); itemsToRemove.add(c.getInt(rowIndex)); continue; } @@ -299,6 +292,7 @@ public class IconCache { continue; } if (app == null) { + remove(component, user); itemsToRemove.add(c.getInt(rowIndex)); } else { appsToUpdate.add(app); @@ -320,7 +314,7 @@ public class IconCache { } } - private void addIconToDBAndMemCache(LauncherActivityInfoCompat app, PackageInfo info, + @Thunk void addIconToDBAndMemCache(LauncherActivityInfoCompat app, PackageInfo info, long userSerial) { // Reuse the existing entry if it already exists in the DB. This ensures that we do not // create bitmap if it was already created during loader. @@ -342,7 +336,7 @@ public class IconCache { SQLiteDatabase.CONFLICT_REPLACE); } - private ContentValues updateCacheAndGetContentValues(LauncherActivityInfoCompat app, + @Thunk ContentValues updateCacheAndGetContentValues(LauncherActivityInfoCompat app, boolean replaceExisting) { final ComponentKey key = new ComponentKey(app.getComponentName(), app.getUser()); CacheEntry entry = null; @@ -570,9 +564,10 @@ public class IconCache { */ private CacheEntry getEntryForPackageLocked(String packageName, UserHandleCompat user, boolean useLowResIcon) { - ComponentName cn = new ComponentName(packageName, EMPTY_CLASS_NAME); + ComponentName cn = new ComponentName(packageName, packageName + EMPTY_CLASS_NAME); ComponentKey cacheKey = new ComponentKey(cn, user); CacheEntry entry = mCache.get(cacheKey); + if (entry == null || (entry.isLowResIcon && !useLowResIcon)) { entry = new CacheEntry(); boolean entryUpdated = true; @@ -688,14 +683,14 @@ public class IconCache { * LauncherActivityInfoCompat list. Items are updated/added one at a time, so that the * worker thread doesn't get blocked. */ - private class SerializedIconUpdateTask implements Runnable { + @Thunk class SerializedIconUpdateTask implements Runnable { private final long mUserSerial; private final HashMap<String, PackageInfo> mPkgInfoMap; private final Stack<LauncherActivityInfoCompat> mAppsToAdd; private final Stack<LauncherActivityInfoCompat> mAppsToUpdate; private final HashSet<String> mUpdatedPackages = new HashSet<String>(); - private SerializedIconUpdateTask(long userSerial, HashMap<String, PackageInfo> pkgInfoMap, + @Thunk SerializedIconUpdateTask(long userSerial, HashMap<String, PackageInfo> pkgInfoMap, Stack<LauncherActivityInfoCompat> appsToAdd, Stack<LauncherActivityInfoCompat> appsToUpdate) { mUserSerial = userSerial; @@ -744,7 +739,7 @@ public class IconCache { } private static final class IconDB extends SQLiteOpenHelper { - private final static int DB_VERSION = 4; + private final static int DB_VERSION = 5; private final static String TABLE_NAME = "icons"; private final static String COLUMN_ROWID = "rowid"; diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java index 7f34593a9..fcaf834a2 100644 --- a/src/com/android/launcher3/InvariantDeviceProfile.java +++ b/src/com/android/launcher3/InvariantDeviceProfile.java @@ -35,6 +35,8 @@ public class InvariantDeviceProfile { // This is a static that we use for the default icon size on a 4/5-inch phone private static float DEFAULT_ICON_SIZE_DP = 60; + private static final float ICON_SIZE_DEFINED_IN_APP_DP = 48; + // Constants that affects the interpolation curve between statically defined device profile // buckets. private static float KNEARESTNEIGHBOR = 3; @@ -60,6 +62,8 @@ public class InvariantDeviceProfile { public int numFolderRows; public int numFolderColumns; float iconSize; + int iconBitmapSize; + int fillResIconDpi; float iconTextSize; /** @@ -135,8 +139,10 @@ public class InvariantDeviceProfile { numFolderColumns = closestProfile.numFolderColumns; iconSize = interpolatedDeviceProfileOut.iconSize; + iconBitmapSize = Utilities.pxFromDp(iconSize, dm); iconTextSize = interpolatedDeviceProfileOut.iconTextSize; hotseatIconSize = interpolatedDeviceProfileOut.hotseatIconSize; + fillResIconDpi = getLauncherIconDensity(iconBitmapSize); // If the partner customization apk contains any grid overrides, apply them // Supported overrides: numRows, numColumns, iconSize @@ -187,6 +193,29 @@ public class InvariantDeviceProfile { return predefinedDeviceProfiles; } + private int getLauncherIconDensity(int requiredSize) { + // Densities typically defined by an app. + int[] densityBuckets = new int[] { + DisplayMetrics.DENSITY_LOW, + DisplayMetrics.DENSITY_MEDIUM, + DisplayMetrics.DENSITY_TV, + DisplayMetrics.DENSITY_HIGH, + DisplayMetrics.DENSITY_XHIGH, + DisplayMetrics.DENSITY_XXHIGH, + DisplayMetrics.DENSITY_XXXHIGH + }; + + int density = DisplayMetrics.DENSITY_XXXHIGH; + for (int i = densityBuckets.length - 1; i >= 0; i--) { + float expectedSize = ICON_SIZE_DEFINED_IN_APP_DP * densityBuckets[i] + / DisplayMetrics.DENSITY_DEFAULT; + if (expectedSize >= requiredSize) { + density = densityBuckets[i]; + } + } + + return density; + } /** * Apply any Partner customization grid overrides. diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index ef34660df..6dfe0ebea 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -265,7 +265,7 @@ public class Launcher extends Activity private int[] mTmpAddItemCellCoordinates = new int[2]; - private Hotseat mHotseat; + @Thunk Hotseat mHotseat; private ViewGroup mOverviewPanel; private View mAllAppsButton; @@ -402,23 +402,35 @@ public class Launcher extends Activity FocusIndicatorView mFocusHandler; - private boolean mRotationEnabled = false; - private boolean mPreferenceObserverRegistered = false; + @Thunk boolean mRotationEnabled = false; + private boolean mScreenOrientationSettingReceiverRegistered = false; - final private SharedPreferences.OnSharedPreferenceChangeListener mSettingsObserver = - new SharedPreferences.OnSharedPreferenceChangeListener() { - @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - if (Utilities.ALLOW_ROTATION_PREFERENCE_KEY.equals(key)) { - if (mRotationEnabled = sharedPreferences.getBoolean( - Utilities.ALLOW_ROTATION_PREFERENCE_KEY, false)) { - unlockScreenOrientation(true); - } else { - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR); + final private BroadcastReceiver mScreenOrientationSettingReceiver = + new BroadcastReceiver() { + @Thunk Runnable mUpdateOrientationRunnable = new Runnable() { + public void run() { + setOrientation(); + } + }; + + @Override + public void onReceive(Context context, Intent intent) { + mRotationEnabled = intent.getBooleanExtra( + Utilities.SCREEN_ROTATION_SETTING_EXTRA, false); + if (!waitUntilResume(mUpdateOrientationRunnable, true)) { + setOrientation(); + } } - } + }; + + @Thunk void setOrientation() { + if (mRotationEnabled) { + unlockScreenOrientation(true); + } else { + setRequestedOrientation( + ActivityInfo.SCREEN_ORIENTATION_NOSENSOR); } - }; + } @Override protected void onCreate(Bundle savedInstanceState) { @@ -453,9 +465,6 @@ public class Launcher extends Activity app.getInvariantDeviceProfile().landscapeProfile : app.getInvariantDeviceProfile().portraitProfile; - // TODO: Move this to icon cache. - Utilities.setIconSize(mDeviceProfile.iconSizePx); - // the LauncherApplication should call this, but in case of Instrumentation it might not be present yet mSharedPrefs = getSharedPreferences(LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE); @@ -523,16 +532,18 @@ public class Launcher extends Activity // In case we are on a device with locked rotation, we should look at preferences to check // if the user has specifically allowed rotation. if (!mRotationEnabled) { - getSharedPreferences(LauncherFiles.ROTATION_PREF_FILE, - Context.MODE_MULTI_PROCESS).registerOnSharedPreferenceChangeListener( - mSettingsObserver); - mPreferenceObserverRegistered = true; + String updateOrientationBroadcastPermission = getResources().getString( + R.string.receive_update_orientation_broadcasts_permission); + registerReceiver(mScreenOrientationSettingReceiver, + new IntentFilter(Utilities.SCREEN_ROTATION_SETTING_INTENT), + updateOrientationBroadcastPermission, null); + mScreenOrientationSettingReceiverRegistered = true; mRotationEnabled = Utilities.isAllowRotationPrefEnabled(getApplicationContext()); } // On large interfaces, or on devices that a user has specifically enabled screen rotation, // we want the screen to auto-rotate based on the current orientation - unlockScreenOrientation(true); + setOrientation(); if (mLauncherCallbacks != null) { mLauncherCallbacks.onCreate(savedInstanceState); @@ -1531,7 +1542,6 @@ public class Launcher extends Activity * Add a shortcut to the workspace. * * @param data The intent describing the shortcut. - * @param cellInfo The position on screen where to create the shortcut. */ private void completeAddShortcut(Intent data, long container, long screenId, int cellX, int cellY) { @@ -2020,11 +2030,9 @@ public class Launcher extends Activity public void onDestroy() { super.onDestroy(); - if (mPreferenceObserverRegistered) { - getSharedPreferences(LauncherFiles.ROTATION_PREF_FILE, - Context.MODE_MULTI_PROCESS).unregisterOnSharedPreferenceChangeListener( - mSettingsObserver); - mPreferenceObserverRegistered = false; + if (mScreenOrientationSettingReceiverRegistered) { + unregisterReceiver(mScreenOrientationSettingReceiver); + mScreenOrientationSettingReceiverRegistered = false; } // Remove all pending runnables @@ -2549,13 +2557,6 @@ public class Launcher extends Activity } } - public void onClickPagedViewIcon(View v) { - startAppShortcutOrInfoActivity(v); - if (mLauncherCallbacks != null) { - mLauncherCallbacks.onClickPagedViewIcon(v); - } - } - @SuppressLint("ClickableViewAccessibility") public boolean onTouch(View v, MotionEvent event) { return false; @@ -2714,7 +2715,7 @@ public class Launcher extends Activity } boolean success = startActivitySafely(v, intent, tag); - mStats.recordLaunch(intent, shortcut); + mStats.recordLaunch(v, intent, shortcut); if (success && v instanceof BubbleTextView) { mWaitingForResume = (BubbleTextView) v; @@ -2936,7 +2937,7 @@ public class Launcher extends Activity } } - boolean startActivity(View v, Intent intent, Object tag) { + private boolean startActivity(View v, Intent intent, Object tag) { intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); try { // Only launch using the new animation if the shortcut has not opted out (this is a @@ -3007,7 +3008,7 @@ public class Launcher extends Activity return false; } - boolean startActivitySafely(View v, Intent intent, Object tag) { + private 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(); @@ -3674,7 +3675,7 @@ public class Launcher extends Activity * * @return {@code true} if we are currently paused. The caller might be able to skip some work */ - private boolean waitUntilResume(Runnable run, boolean deletePreviousRunnables) { + @Thunk boolean waitUntilResume(Runnable run, boolean deletePreviousRunnables) { if (mPaused) { if (LOGD) Log.d(TAG, "Deferring update until onResume"); if (deletePreviousRunnables) { @@ -4682,6 +4683,23 @@ public class Launcher extends Activity } /** + * Returns a FastBitmapDrawable with the icon, accurately sized. + */ + public FastBitmapDrawable createIconDrawable(Bitmap icon) { + FastBitmapDrawable d = new FastBitmapDrawable(icon); + d.setFilterBitmap(true); + resizeIconDrawable(d); + return d; + } + + /** + * Resizes an icon drawable to the correct icon size. + */ + public void resizeIconDrawable(Drawable icon) { + icon.setBounds(0, 0, mDeviceProfile.iconSizePx, mDeviceProfile.iconSizePx); + } + + /** * Prints out out state for debugging. */ public void dumpState() { diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java index 71fb2d295..cf461a5b8 100644 --- a/src/com/android/launcher3/LauncherAppWidgetHostView.java +++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java @@ -36,6 +36,7 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView implements Touc LayoutInflater mInflater; private CheckLongPressHelper mLongPressHelper; + private StylusEventHelper mStylusEventHelper; private Context mContext; private int mPreviousOrientation; private DragLayer mDragLayer; @@ -46,6 +47,7 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView implements Touc super(context); mContext = context; mLongPressHelper = new CheckLongPressHelper(this); + mStylusEventHelper = new StylusEventHelper(this); mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); mDragLayer = ((Launcher) context).getDragLayer(); setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate()); @@ -89,11 +91,17 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView implements Touc return true; } - // Watch for longpress events at this level to make sure - // users can always pick up this widget + // Watch for longpress or stylus button press events at this level to + // make sure users can always pick up this widget + if (mStylusEventHelper.checkAndPerformStylusEvent(ev)) { + mLongPressHelper.cancelLongPress(); + return true; + } switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: { - mLongPressHelper.postCheckForLongPress(); + if (!mStylusEventHelper.inStylusButtonPressed()) { + mLongPressHelper.postCheckForLongPress(); + } mDragLayer.setTouchCompleteListener(this); break; } diff --git a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java index 7ca4fe325..85af92f30 100644 --- a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java +++ b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java @@ -67,7 +67,8 @@ public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo { if (isCustomWidget) { return cache.getFullResIcon(provider.getPackageName(), icon); } - return super.loadIcon(context, cache.getFullResIconDpi()); + return super.loadIcon(context, + LauncherAppState.getInstance().getInvariantDeviceProfile().fillResIconDpi); } public String toString(PackageManager pm) { diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java index af4101221..b40ace3fb 100644 --- a/src/com/android/launcher3/LauncherBackupHelper.java +++ b/src/com/android/launcher3/LauncherBackupHelper.java @@ -51,13 +51,13 @@ import com.android.launcher3.backup.BackupProtos.Screen; import com.android.launcher3.backup.BackupProtos.Widget; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; +import com.android.launcher3.util.Thunk; import com.google.protobuf.nano.InvalidProtocolBufferNanoException; import com.google.protobuf.nano.MessageNano; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; @@ -135,7 +135,7 @@ public class LauncherBackupHelper implements BackupHelper { private static final int SCREEN_RANK_INDEX = 2; - private final Context mContext; + @Thunk final Context mContext; private final HashSet<String> mExistingKeys; private final ArrayList<Key> mKeys; private final ItemTypeMatcher[] mItemTypeMatchers; @@ -1157,15 +1157,15 @@ public class LauncherBackupHelper implements BackupHelper { .getSerialNumberForUser(UserHandleCompat.myUserHandle()); } - private class InvalidBackupException extends IOException { + @Thunk class InvalidBackupException extends IOException { private static final long serialVersionUID = 8931456637211665082L; - private InvalidBackupException(Throwable cause) { + @Thunk InvalidBackupException(Throwable cause) { super(cause); } - public InvalidBackupException(String reason) { + @Thunk InvalidBackupException(String reason) { super(reason); } } diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java index a5f36ba93..70e400bca 100644 --- a/src/com/android/launcher3/LauncherCallbacks.java +++ b/src/com/android/launcher3/LauncherCallbacks.java @@ -56,6 +56,8 @@ public interface LauncherCallbacks { public void bindAllApplications(ArrayList<AppInfo> apps); public void onClickFolderIcon(View v); public void onClickAppShortcut(View v); + + @Deprecated public void onClickPagedViewIcon(View v); public void onClickWallpaperPicker(View v); public void onClickSettingsButton(View v); diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 224ebbf89..53966a58f 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -275,7 +275,7 @@ public class LauncherModel extends BroadcastReceiver /** * Runs the specified runnable after the loader is complete */ - private void runAfterBindCompletes(Runnable r) { + @Thunk void runAfterBindCompletes(Runnable r) { if (isLoadingWorkspace() || !mHasLoaderCompletedOnce) { synchronized (mBindCompleteRunnables) { mBindCompleteRunnables.add(r); @@ -2932,6 +2932,9 @@ public class LauncherModel extends BroadcastReceiver } }); } + + // Reload widget list. No need to refresh, as we only want to update the icons and labels. + loadAndBindWidgetsAndShortcuts(mApp.getContext(), callbacks, false); } void enqueuePackageUpdated(PackageUpdatedTask task) { @@ -3114,8 +3117,9 @@ public class LauncherModel extends BroadcastReceiver // Update shortcuts which use iconResource. if ((si.iconResource != null) && packageSet.contains(si.iconResource.packageName)) { - Bitmap icon = Utilities.createIconBitmap(si.iconResource.packageName, - si.iconResource.resourceName, mIconCache, context); + Bitmap icon = Utilities.createIconBitmap( + si.iconResource.packageName, + si.iconResource.resourceName, context); if (icon != null) { si.setIcon(icon); si.usingFallbackIcon = false; @@ -3350,7 +3354,7 @@ public class LauncherModel extends BroadcastReceiver * * @see #loadAndBindWidgetsAndShortcuts */ - private WidgetsModel createWidgetsModel(Context context, boolean refresh) { + @Thunk WidgetsModel createWidgetsModel(Context context, boolean refresh) { PackageManager packageManager = context.getPackageManager(); final ArrayList<Object> widgetsAndShortcuts = new ArrayList<Object>(); widgetsAndShortcuts.addAll(getWidgetProviders(context, refresh)); @@ -3558,7 +3562,7 @@ public class LauncherModel extends BroadcastReceiver String resourceName = c.getString(iconResourceIndex); info.customIcon = false; // the resource - icon = Utilities.createIconBitmap(packageName, resourceName, mIconCache, context); + icon = Utilities.createIconBitmap(packageName, resourceName, context); // the db if (icon == null) { icon = Utilities.createIconBitmap(c, iconIndex, context); @@ -3612,7 +3616,7 @@ public class LauncherModel extends BroadcastReceiver if (extra instanceof ShortcutIconResource) { iconResource = (ShortcutIconResource) extra; icon = Utilities.createIconBitmap(iconResource.packageName, - iconResource.resourceName, mIconCache, context); + iconResource.resourceName, context); } } diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index 27511527d..45070d190 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -88,7 +88,7 @@ public class LauncherProvider extends ContentProvider { static final Uri CONTENT_APPWIDGET_RESET_URI = Uri.parse("content://" + AUTHORITY + "/appWidgetReset"); - private DatabaseHelper mOpenHelper; + @Thunk DatabaseHelper mOpenHelper; @Override public boolean onCreate() { @@ -665,7 +665,7 @@ public class LauncherProvider extends ContentProvider { * Replaces all shortcuts of type {@link Favorites#ITEM_TYPE_SHORTCUT} which have a valid * launcher activity target with {@link Favorites#ITEM_TYPE_APPLICATION}. */ - private void convertShortcutsToLauncherActivities(SQLiteDatabase db) { + @Thunk void convertShortcutsToLauncherActivities(SQLiteDatabase db) { db.beginTransaction(); Cursor c = null; SQLiteStatement updateStmt = null; diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index 9271e8b15..18832c680 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -22,11 +22,13 @@ import android.animation.AnimatorSet; import android.animation.LayoutTransition; import android.animation.ObjectAnimator; import android.animation.TimeInterpolator; +import android.annotation.TargetApi; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Rect; +import android.os.Build; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -45,9 +47,7 @@ import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; import android.view.animation.Interpolator; - import com.android.launcher3.util.Thunk; - import java.util.ArrayList; /** @@ -186,7 +186,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc // We use the min scale to determine how much to expand the actually PagedView measured // dimensions such that when we are zoomed out, the view is not clipped private static int REORDERING_DROP_REPOSITION_DURATION = 200; - private static int REORDERING_REORDER_REPOSITION_DURATION = 300; + @Thunk static int REORDERING_REORDER_REPOSITION_DURATION = 300; private static int REORDERING_SIDE_PAGE_HOVER_TIMEOUT = 80; private float mMinScale = 1f; @@ -956,7 +956,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc return 0; } - private void updateMaxScrollX() { + @Thunk void updateMaxScrollX() { int childCount = getChildCount(); if (childCount > 0) { final int index = mIsRtl ? 0 : childCount - 1; @@ -2322,6 +2322,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc private static final int ANIM_TAG_KEY = 100; /* Accessibility */ + @TargetApi(Build.VERSION_CODES.LOLLIPOP) @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); diff --git a/src/com/android/launcher3/PendingAppWidgetHostView.java b/src/com/android/launcher3/PendingAppWidgetHostView.java index c8b27efd3..08f8e5601 100644 --- a/src/com/android/launcher3/PendingAppWidgetHostView.java +++ b/src/com/android/launcher3/PendingAppWidgetHostView.java @@ -121,7 +121,7 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implemen // 2) Preload icon in the center // 3) Setup icon in the center and app icon in the top right corner. if (mDisabledForSafeMode) { - FastBitmapDrawable disabledIcon = Utilities.createIconDrawable(mIcon); + FastBitmapDrawable disabledIcon = mLauncher.createIconDrawable(mIcon); disabledIcon.setGhostModeEnabled(true); mCenterDrawable = disabledIcon; mTopCornerDrawable = null; @@ -134,7 +134,7 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implemen sPreloaderTheme.applyStyle(R.style.PreloadIcon, true); } - FastBitmapDrawable drawable = Utilities.createIconDrawable(mIcon); + FastBitmapDrawable drawable = mLauncher.createIconDrawable(mIcon); mCenterDrawable = new PreloadIconDrawable(drawable, sPreloaderTheme); mCenterDrawable.setCallback(this); mTopCornerDrawable = null; diff --git a/src/com/android/launcher3/SettingsActivity.java b/src/com/android/launcher3/SettingsActivity.java index a1da1b6fd..27763f545 100644 --- a/src/com/android/launcher3/SettingsActivity.java +++ b/src/com/android/launcher3/SettingsActivity.java @@ -18,12 +18,14 @@ package com.android.launcher3; import android.app.Activity; import android.content.Context; +import android.content.Intent; import android.os.Bundle; +import android.preference.Preference; import android.preference.PreferenceFragment; +import android.preference.PreferenceScreen; /** - * Settings activity for Launcher. Currently implements the following setting: - * LockToPortrait + * Settings activity for Launcher. Currently implements the following setting: Allow rotation */ public class SettingsActivity extends Activity { @Override @@ -41,13 +43,25 @@ public class SettingsActivity extends Activity { */ @SuppressWarnings("WeakerAccess") public static class LauncherSettingsFragment extends PreferenceFragment { - @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - getPreferenceManager().setSharedPreferencesMode(Context.MODE_PRIVATE); + getPreferenceManager().setSharedPreferencesMode(Context.MODE_MULTI_PROCESS); getPreferenceManager().setSharedPreferencesName(LauncherFiles.ROTATION_PREF_FILE); addPreferencesFromResource(R.xml.launcher_preferences); } + + @Override + public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, + Preference preference) { + boolean allowRotation = getPreferenceManager().getSharedPreferences().getBoolean( + Utilities.ALLOW_ROTATION_PREFERENCE_KEY, false); + Intent rotationSetting = new Intent(Utilities.SCREEN_ROTATION_SETTING_INTENT); + String launchBroadcastPermission = getResources().getString( + R.string.receive_update_orientation_broadcasts_permission); + rotationSetting.putExtra(Utilities.SCREEN_ROTATION_SETTING_EXTRA, allowRotation); + getActivity().sendBroadcast(rotationSetting, launchBroadcastPermission); + return true; + } } } diff --git a/src/com/android/launcher3/Stats.java b/src/com/android/launcher3/Stats.java index 9d06f755f..cb0e252b2 100644 --- a/src/com/android/launcher3/Stats.java +++ b/src/com/android/launcher3/Stats.java @@ -20,9 +20,63 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.os.Bundle; import android.util.Log; +import android.view.View; +import android.view.ViewParent; public class Stats { + + /** + * Implemented by containers to provide a launch source for a given child. + */ + public interface LaunchSourceProvider { + void fillInLaunchSourceData(Bundle sourceData); + } + + /** + * Helpers to add the source to a launch intent. + */ + public static class LaunchSourceUtils { + /** + * Create a default bundle for LaunchSourceProviders to fill in their data. + */ + public static Bundle createSourceData() { + Bundle sourceData = new Bundle(); + sourceData.putString(SOURCE_EXTRA_CONTAINER, CONTAINER_HOMESCREEN); + // Have default container/sub container pages + sourceData.putInt(SOURCE_EXTRA_CONTAINER_PAGE, 0); + sourceData.putInt(SOURCE_EXTRA_SUB_CONTAINER_PAGE, 0); + return sourceData; + } + + /** + * Finds the next launch source provider in the parents of the view hierarchy and populates + * the source data from that provider. + */ + public static void populateSourceDataFromAncestorProvider(View v, Bundle sourceData) { + if (v == null) { + return; + } + + Stats.LaunchSourceProvider provider = null; + ViewParent parent = v.getParent(); + while (parent != null && parent instanceof View) { + if (parent instanceof Stats.LaunchSourceProvider) { + provider = (Stats.LaunchSourceProvider) parent; + break; + } + parent = parent.getParent(); + } + + if (provider != null) { + provider.fillInLaunchSourceData(sourceData); + } else if (LauncherAppState.isDogfoodBuild()) { + throw new RuntimeException("Expected LaunchSourceProvider"); + } + } + } + private static final boolean DEBUG_BROADCASTS = false; public static final String ACTION_LAUNCH = "com.android.launcher3.action.LAUNCH"; @@ -31,6 +85,22 @@ public class Stats { public static final String EXTRA_SCREEN = "screen"; public static final String EXTRA_CELLX = "cellX"; public static final String EXTRA_CELLY = "cellY"; + public static final String EXTRA_SOURCE = "source"; + + public static final String SOURCE_EXTRA_CONTAINER = "container"; + public static final String SOURCE_EXTRA_CONTAINER_PAGE = "container_page"; + public static final String SOURCE_EXTRA_SUB_CONTAINER = "sub_container"; + public static final String SOURCE_EXTRA_SUB_CONTAINER_PAGE = "sub_container_page"; + + public static final String CONTAINER_SEARCH_BOX = "search_box"; + public static final String CONTAINER_ALL_APPS = "all_apps"; + public static final String CONTAINER_HOMESCREEN = "homescreen"; // aka. Workspace + public static final String CONTAINER_HOTSEAT = "hotseat"; + + public static final String SUB_CONTAINER_FOLDER = "folder"; + public static final String SUB_CONTAINER_ALL_APPS_A_Z = "a-z"; + public static final String SUB_CONTAINER_ALL_APPS_PREDICTION = "prediction"; + public static final String SUB_CONTAINER_ALL_APPS_SEARCH = "search"; private final Launcher mLauncher; private final String mLaunchBroadcastPermission; @@ -56,11 +126,7 @@ public class Stats { } } - public void recordLaunch(Intent intent) { - recordLaunch(intent, null); - } - - public void recordLaunch(Intent intent, ShortcutInfo shortcut) { + public void recordLaunch(View v, Intent intent, ShortcutInfo shortcut) { intent = new Intent(intent); intent.setSourceBounds(null); @@ -72,6 +138,10 @@ public class Stats { .putExtra(EXTRA_CELLX, shortcut.cellX) .putExtra(EXTRA_CELLY, shortcut.cellY); } + + Bundle sourceExtras = LaunchSourceUtils.createSourceData(); + LaunchSourceUtils.populateSourceDataFromAncestorProvider(v, sourceExtras); + broadcastIntent.putExtra(EXTRA_SOURCE, sourceExtras); mLauncher.sendBroadcast(broadcastIntent, mLaunchBroadcastPermission); } } diff --git a/src/com/android/launcher3/StylusEventHelper.java b/src/com/android/launcher3/StylusEventHelper.java new file mode 100644 index 000000000..da46e6a54 --- /dev/null +++ b/src/com/android/launcher3/StylusEventHelper.java @@ -0,0 +1,84 @@ +package com.android.launcher3; + +import com.android.launcher3.Utilities; + +import android.util.Log; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; + +/** + * Helper for identifying when a stylus touches a view while the primary stylus button is pressed. + * This can occur in {@value MotionEvent#ACTION_DOWN} or {@value MotionEvent#ACTION_MOVE}. On a + * stylus button press this performs the view's {@link View#performLongClick()} method, if the view + * is long clickable. + */ +public class StylusEventHelper { + private boolean mIsButtonPressed; + private View mView; + + public StylusEventHelper(View view) { + mView = view; + } + + /** + * Call this in onTouchEvent method of a view to identify a stylus button press and perform a + * long click (if the view is long clickable). + * + * @param event The event to check for a stylus button press. + * @return Whether a stylus event occurred and was handled. + */ + public boolean checkAndPerformStylusEvent(MotionEvent event) { + final float slop = ViewConfiguration.get(mView.getContext()).getScaledTouchSlop(); + + if (!mView.isLongClickable()) { + // We don't do anything unless the view is long clickable. + return false; + } + + final boolean stylusButtonPressed = isStylusButtonPressed(event); + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + mIsButtonPressed = false; + if (stylusButtonPressed && mView.performLongClick()) { + mIsButtonPressed = true; + return true; + } + break; + case MotionEvent.ACTION_MOVE: + if (Utilities.pointInView(mView, event.getX(), event.getY(), slop)) { + if (!mIsButtonPressed && stylusButtonPressed && mView.performLongClick()) { + mIsButtonPressed = true; + return true; + } else if (mIsButtonPressed && !stylusButtonPressed) { + mIsButtonPressed = false; + } + } + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + mIsButtonPressed = false; + break; + } + return false; + } + + /** + * Whether a stylus button press is occurring. + */ + public boolean inStylusButtonPressed() { + return mIsButtonPressed; + } + + /** + * Identifies if the provided {@link MotionEvent} is a stylus with the primary stylus button + * pressed. + * + * @param event The event to check. + * @return Whether a stylus button press occurred. + */ + public static boolean isStylusButtonPressed(MotionEvent event) { + return event.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS + && event.isButtonPressed(MotionEvent.BUTTON_SECONDARY); + } +}
\ No newline at end of file diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index a9cbf6970..b267f759d 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -71,9 +71,6 @@ public final class Utilities { private static final String TAG = "Launcher.Utilities"; - private static int sIconWidth = -1; - private static int sIconHeight = -1; - private static final Rect sOldBounds = new Rect(); private static final Canvas sCanvas = new Canvas(); @@ -87,32 +84,18 @@ public final class Utilities { static int sColors[] = { 0xffff0000, 0xff00ff00, 0xff0000ff }; static int sColorIndex = 0; - static int[] sLoc0 = new int[2]; - static int[] sLoc1 = new int[2]; + private static final int[] sLoc0 = new int[2]; + private static final int[] sLoc1 = new int[2]; // To turn on these properties, type // adb shell setprop log.tag.PROPERTY_NAME [VERBOSE | SUPPRESS] - static final String FORCE_ENABLE_ROTATION_PROPERTY = "launcher_force_rotate"; - public static boolean sForceEnableRotation = isPropertyEnabled(FORCE_ENABLE_ROTATION_PROPERTY); + private static final String FORCE_ENABLE_ROTATION_PROPERTY = "launcher_force_rotate"; + private static boolean sForceEnableRotation = isPropertyEnabled(FORCE_ENABLE_ROTATION_PROPERTY); public static final String ALLOW_ROTATION_PREFERENCE_KEY = "pref_allowRotation"; - - /** - * Returns a FastBitmapDrawable with the icon, accurately sized. - */ - public static FastBitmapDrawable createIconDrawable(Bitmap icon) { - FastBitmapDrawable d = new FastBitmapDrawable(icon); - d.setFilterBitmap(true); - resizeIconDrawable(d); - return d; - } - - /** - * Resizes an icon drawable to the correct icon size. - */ - static void resizeIconDrawable(Drawable icon) { - icon.setBounds(0, 0, sIconWidth, sIconHeight); - } + public static final String SCREEN_ROTATION_SETTING_INTENT = + "come.android.launcher3.SCREEN_ORIENTATION_PREF_CHANGED"; + public static final String SCREEN_ROTATION_SETTING_EXTRA = "screenRotationPref"; public static boolean isPropertyEnabled(String propertyName) { return Log.isLoggable(propertyName, Log.VERBOSE); @@ -141,7 +124,7 @@ public final class Utilities { return Build.VERSION.SDK_INT >= 22; } - static Bitmap createIconBitmap(Cursor c, int iconIndex, Context context) { + public static Bitmap createIconBitmap(Cursor c, int iconIndex, Context context) { byte[] data = c.getBlob(iconIndex); try { return createIconBitmap(BitmapFactory.decodeByteArray(data, 0, data.length), context); @@ -154,7 +137,7 @@ public final class Utilities { * Returns a bitmap suitable for the all apps view. If the package or the resource do not * exist, it returns null. */ - static Bitmap createIconBitmap(String packageName, String resourceName, IconCache cache, + public static Bitmap createIconBitmap(String packageName, String resourceName, Context context) { PackageManager packageManager = context.getPackageManager(); // the resource @@ -163,7 +146,8 @@ public final class Utilities { if (resources != null) { final int id = resources.getIdentifier(resourceName, null, null); return createIconBitmap( - resources.getDrawableForDensity(id, cache.getFullResIconDpi()), context); + resources.getDrawableForDensity(id, LauncherAppState.getInstance() + .getInvariantDeviceProfile().fillResIconDpi), context); } } catch (Exception e) { // Icon not found. @@ -171,16 +155,16 @@ public final class Utilities { return null; } + private static int getIconBitmapSize() { + return LauncherAppState.getInstance().getInvariantDeviceProfile().iconBitmapSize; + } + /** * Returns a bitmap which is of the appropriate size to be displayed as an icon */ - static Bitmap createIconBitmap(Bitmap icon, Context context) { - synchronized (sCanvas) { // we share the statics :-( - if (sIconWidth == -1) { - initStatics(context); - } - } - if (sIconWidth == icon.getWidth() && sIconHeight == icon.getHeight()) { + public static Bitmap createIconBitmap(Bitmap icon, Context context) { + final int iconBitmapSize = getIconBitmapSize(); + if (iconBitmapSize == icon.getWidth() && iconBitmapSize == icon.getHeight()) { return icon; } return createIconBitmap(new BitmapDrawable(context.getResources(), icon), context); @@ -190,13 +174,11 @@ public final class Utilities { * Returns a bitmap suitable for the all apps view. */ public static Bitmap createIconBitmap(Drawable icon, Context context) { - synchronized (sCanvas) { // we share the statics :-( - if (sIconWidth == -1) { - initStatics(context); - } + synchronized (sCanvas) { + final int iconBitmapSize = getIconBitmapSize(); - int width = sIconWidth; - int height = sIconHeight; + int width = iconBitmapSize; + int height = iconBitmapSize; if (icon instanceof PaintDrawable) { PaintDrawable painter = (PaintDrawable) icon; @@ -223,8 +205,8 @@ public final class Utilities { } // no intrinsic size --> use default size - int textureWidth = sIconWidth; - int textureHeight = sIconHeight; + int textureWidth = iconBitmapSize; + int textureHeight = iconBitmapSize; final Bitmap bitmap = Bitmap.createBitmap(textureWidth, textureHeight, Bitmap.Config.ARGB_8888); @@ -354,15 +336,6 @@ public final class Utilities { localY < (v.getHeight() + slop); } - private static void initStatics(Context context) { - final Resources resources = context.getResources(); - sIconWidth = sIconHeight = (int) resources.getDimension(R.dimen.app_icon_size); - } - - public static void setIconSize(int widthPx) { - sIconWidth = sIconHeight = widthPx; - } - public static void scaleRect(Rect r, float scale) { if (scale != 1.0f) { r.left = (int) (r.left * scale + 0.5f); diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java index e8cc48685..a62177142 100644 --- a/src/com/android/launcher3/WidgetPreviewLoader.java +++ b/src/com/android/launcher3/WidgetPreviewLoader.java @@ -56,7 +56,7 @@ public class WidgetPreviewLoader { * Weak reference objects, do not prevent their referents from being made finalizable, * finalized, and then reclaimed. */ - private Set<Bitmap> mUnusedBitmaps = + @Thunk Set<Bitmap> mUnusedBitmaps = Collections.newSetFromMap(new WeakHashMap<Bitmap, Boolean>()); private final Context mContext; @@ -67,7 +67,7 @@ public class WidgetPreviewLoader { private final InvariantDeviceProfile mDeviceProfile; private final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor(); - private final Handler mWorkerHandler; + @Thunk final Handler mWorkerHandler; public WidgetPreviewLoader(Context context, InvariantDeviceProfile inv, IconCache iconCache) { mContext = context; @@ -290,7 +290,7 @@ public class WidgetPreviewLoader { /** * Reads the preview bitmap from the DB or null if the preview is not in the DB. */ - private Bitmap readFromDb(WidgetCacheKey key, Bitmap recycle, PreviewLoadTask loadTask) { + @Thunk Bitmap readFromDb(WidgetCacheKey key, Bitmap recycle, PreviewLoadTask loadTask) { Cursor cursor = null; try { cursor = mDb.getReadableDatabase().query( @@ -329,7 +329,7 @@ public class WidgetPreviewLoader { return null; } - private Bitmap generatePreview(Launcher launcher, Object info, Bitmap recycle, + @Thunk Bitmap generatePreview(Launcher launcher, Object info, Bitmap recycle, int previewWidth, int previewHeight) { if (info instanceof LauncherAppWidgetProviderInfo) { return generateWidgetPreview(launcher, (LauncherAppWidgetProviderInfo) info, @@ -512,7 +512,7 @@ public class WidgetPreviewLoader { /** * @return an array of containing versionCode and lastUpdatedTime for the package. */ - private long[] getPackageVersion(String packageName) { + @Thunk long[] getPackageVersion(String packageName) { synchronized (mPackageVersions) { long[] versions = mPackageVersions.get(packageName); if (versions == null) { @@ -561,14 +561,13 @@ public class WidgetPreviewLoader { } public class PreviewLoadTask extends AsyncTask<Void, Void, Bitmap> { - - private final WidgetCacheKey mKey; + @Thunk final WidgetCacheKey mKey; private final Object mInfo; private final int mPreviewHeight; private final int mPreviewWidth; private final WidgetCell mCaller; - private long[] mVersions; - private Bitmap mBitmapToRecycle; + @Thunk long[] mVersions; + @Thunk Bitmap mBitmapToRecycle; PreviewLoadTask(WidgetCacheKey key, Object info, int previewWidth, int previewHeight, WidgetCell caller) { @@ -674,7 +673,7 @@ public class WidgetPreviewLoader { private static final class WidgetCacheKey extends ComponentKey { // TODO: remove dependency on size - private final String size; + @Thunk final String size; public WidgetCacheKey(ComponentName componentName, UserHandleCompat user, String size) { super(componentName, user); diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 6d5affb59..d0b9a2206 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -28,6 +28,7 @@ import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetProviderInfo; import android.content.ComponentName; import android.content.Context; +import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Resources; import android.content.res.TypedArray; @@ -42,6 +43,7 @@ import android.graphics.Region.Op; import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.os.Build; +import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Parcelable; @@ -86,7 +88,7 @@ import java.util.concurrent.atomic.AtomicInteger; public class Workspace extends PagedView implements DropTarget, DragSource, DragScroller, View.OnTouchListener, DragController.DragListener, LauncherTransitionable, ViewGroup.OnHierarchyChangeListener, - Insettable, UninstallSource, AccessibilityDragSource { + Insettable, UninstallSource, AccessibilityDragSource, Stats.LaunchSourceProvider { private static final String TAG = "Launcher.Workspace"; private static boolean ENFORCE_DRAG_EVENT_ORDER = false; @@ -1618,7 +1620,7 @@ public class Workspace extends PagedView // We should only update the drag layer background alpha if we are not in all apps or the // widgets tray if (mState == State.NORMAL) { - mLauncher.getDragLayer().setBackgroundAlpha(progress * 0.8f); + mLauncher.getDragLayer().setBackgroundAlpha(progress == 1 ? 0 : progress * 0.8f); } if (mLauncher.getHotseat() != null) { @@ -4461,6 +4463,12 @@ public class Workspace extends PagedView mLauncher.getDragLayer().getLocationInDragLayer(this, loc); } + @Override + public void fillInLaunchSourceData(Bundle sourceData) { + sourceData.putString(Stats.SOURCE_EXTRA_CONTAINER, Stats.CONTAINER_HOMESCREEN); + sourceData.putInt(Stats.SOURCE_EXTRA_CONTAINER_PAGE, getCurrentPage()); + } + /** * Used as a workaround to ensure that the AppWidgetService receives the * PACKAGE_ADDED broadcast before updating widgets. diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java index 93cf8d050..3c49ccc41 100644 --- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java +++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java @@ -251,7 +251,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate { return actions; } - private void performResizeAction(int action, View host, LauncherAppWidgetInfo info) { + @Thunk void performResizeAction(int action, View host, LauncherAppWidgetInfo info) { CellLayout.LayoutParams lp = (CellLayout.LayoutParams) host.getLayoutParams(); CellLayout layout = (CellLayout) host.getParent().getParent(); layout.markCellsAsUnoccupiedForView(host); diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index c05f7c0b9..9386500be 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -26,6 +26,7 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.graphics.drawable.InsetDrawable; import android.os.Build; +import android.os.Bundle; import android.support.v7.widget.RecyclerView; import android.text.Editable; import android.text.TextWatcher; @@ -57,6 +58,7 @@ import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherTransitionable; import com.android.launcher3.R; +import com.android.launcher3.Stats; import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; import com.android.launcher3.allapps.AppSearchManager.AppSearchResultCallback; @@ -172,7 +174,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc TextWatcher, TextView.OnEditorActionListener, LauncherTransitionable, AlphabeticalAppsList.AdapterChangedCallback, AllAppsGridAdapter.PredictionBarSpacerCallbacks, View.OnTouchListener, View.OnClickListener, View.OnLongClickListener, - ViewTreeObserver.OnPreDrawListener, AppSearchResultCallback { + ViewTreeObserver.OnPreDrawListener, AppSearchResultCallback, Stats.LaunchSourceProvider { public static final boolean GRID_MERGE_SECTIONS = true; @@ -191,14 +193,14 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc private RecyclerView.LayoutManager mLayoutManager; private RecyclerView.ItemDecoration mItemDecoration; - private FrameLayout mContentView; + @Thunk FrameLayout mContentView; @Thunk AllAppsRecyclerView mAppsRecyclerView; - private ViewGroup mPredictionBarView; + @Thunk ViewGroup mPredictionBarView; private View mHeaderView; - private View mSearchBarContainerView; + @Thunk View mSearchBarContainerView; private View mSearchButtonView; private View mDismissSearchButtonView; - private AllAppsSearchEditView mSearchBarEditView; + @Thunk AllAppsSearchEditView mSearchBarEditView; private HeaderElevationController mElevationController; @@ -215,7 +217,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc private int mContainerInset; private int mPredictionBarHeight; private int mLastRecyclerViewScrollPos = -1; - private boolean mFocusPredictionBarOnFirstBind; + @Thunk boolean mFocusPredictionBarOnFirstBind; private CheckLongPressHelper mPredictionIconCheckForLongPress; private View mPredictionIconUnderTouch; @@ -870,6 +872,15 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc return false; } + @Override + public void fillInLaunchSourceData(Bundle sourceData) { + // Since the other cases are caught by the AllAppsRecyclerView LaunchSourceProvider, we just + // handle the prediction bar icons here + sourceData.putString(Stats.SOURCE_EXTRA_CONTAINER, Stats.CONTAINER_ALL_APPS); + sourceData.putString(Stats.SOURCE_EXTRA_SUB_CONTAINER, + Stats.SUB_CONTAINER_ALL_APPS_PREDICTION); + } + /** * Returns the predicted app in the prediction bar given a set of local coordinates. */ @@ -936,7 +947,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc /** * Hides the search field. */ - private void hideSearchField(boolean animated, final boolean returnFocusToRecyclerView) { + @Thunk void hideSearchField(boolean animated, final boolean returnFocusToRecyclerView) { mSearchManager.cancel(true); final boolean resetTextField = mSearchBarEditView.getText().toString().length() > 0; @@ -1001,7 +1012,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc /** * Returns an input method manager. */ - private InputMethodManager getInputMethodManager() { + @Thunk InputMethodManager getInputMethodManager() { return (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); } } diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java index e010270ce..307d9403d 100644 --- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java +++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java @@ -288,7 +288,7 @@ class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.ViewHol private GridLayoutManager mGridLayoutMgr; private GridSpanSizer mGridSizer; private GridItemDecoration mItemDecoration; - private PredictionBarSpacerCallbacks mPredictionBarCb; + @Thunk PredictionBarSpacerCallbacks mPredictionBarCb; private View.OnTouchListener mTouchListener; private View.OnClickListener mIconClickListener; private View.OnLongClickListener mIconLongClickListener; diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java index cc5add3b2..e1b5d918e 100644 --- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java +++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java @@ -18,14 +18,15 @@ package com.android.launcher3.allapps; import android.content.Context; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.os.Bundle; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import android.view.View; - import com.android.launcher3.BaseRecyclerView; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; +import com.android.launcher3.Stats; import com.android.launcher3.Utilities; import java.util.List; @@ -33,7 +34,8 @@ import java.util.List; /** * A RecyclerView with custom fast scroll support for the all apps view. */ -public class AllAppsRecyclerView extends BaseRecyclerView { +public class AllAppsRecyclerView extends BaseRecyclerView + implements Stats.LaunchSourceProvider { private AlphabeticalAppsList mApps; private int mNumAppsPerRow; @@ -125,6 +127,18 @@ public class AllAppsRecyclerView extends BaseRecyclerView { addOnItemTouchListener(this); } + @Override + public void fillInLaunchSourceData(Bundle sourceData) { + sourceData.putString(Stats.SOURCE_EXTRA_CONTAINER, Stats.CONTAINER_ALL_APPS); + if (mApps.hasFilter()) { + sourceData.putString(Stats.SOURCE_EXTRA_SUB_CONTAINER, + Stats.SUB_CONTAINER_ALL_APPS_SEARCH); + } else { + sourceData.putString(Stats.SOURCE_EXTRA_SUB_CONTAINER, + Stats.SUB_CONTAINER_ALL_APPS_A_Z); + } + } + /** * Maps the touch (from 0..1) to the adapter position that should be visible. */ diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java index 0dc2d1e63..e284f77c4 100644 --- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java @@ -27,9 +27,10 @@ import com.android.launcher3.LauncherAppState; import com.android.launcher3.compat.AlphabeticIndexCompat; import com.android.launcher3.model.AbstractUserComparator; import com.android.launcher3.model.AppNameComparator; +import com.android.launcher3.util.Thunk; +import java.nio.charset.Charset; import java.nio.charset.CharsetEncoder; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -45,6 +46,7 @@ public class AlphabeticalAppsList { public static final String TAG = "AlphabeticalAppsList"; private static final boolean DEBUG = false; + private static final boolean DEBUG_PREDICTIONS = false; /** * Info about a section in the alphabetic list @@ -151,7 +153,7 @@ public class AlphabeticalAppsList { * The logic we use to merge sections on tablets. Currently, we don't show section names on * tablet layouts, so just merge all the sections indiscriminately. */ - private static class TabletMergeAlgorithm implements MergeAlgorithm { + @Thunk static class TabletMergeAlgorithm implements MergeAlgorithm { @Override public boolean continueMerging(SectionInfo section, SectionInfo withSection, @@ -177,7 +179,7 @@ public class AlphabeticalAppsList { mMinAppsPerRow = minAppsPerRow; mMinRowsInMergedSection = minRowsInMergedSection; mMaxAllowableMerges = maxNumMerges; - mAsciiEncoder = StandardCharsets.US_ASCII.newEncoder(); + mAsciiEncoder = Charset.forName("US-ASCII").newEncoder(); } @Override @@ -476,6 +478,15 @@ public class AlphabeticalAppsList { mAdapterItems.clear(); mSections.clear(); + if (DEBUG_PREDICTIONS) { + if (mPredictedAppComponents.isEmpty() && !mApps.isEmpty()) { + mPredictedAppComponents.add(mApps.get(0).componentName); + mPredictedAppComponents.add(mApps.get(0).componentName); + mPredictedAppComponents.add(mApps.get(0).componentName); + mPredictedAppComponents.add(mApps.get(0).componentName); + } + } + // Process the predicted app components mPredictedApps.clear(); if (mPredictedAppComponents != null && !mPredictedAppComponents.isEmpty() && !hasFilter()) { diff --git a/src/com/android/launcher3/model/AppNameComparator.java b/src/com/android/launcher3/model/AppNameComparator.java index cdac40ac0..cd45d2c94 100644 --- a/src/com/android/launcher3/model/AppNameComparator.java +++ b/src/com/android/launcher3/model/AppNameComparator.java @@ -19,6 +19,7 @@ import android.content.Context; import com.android.launcher3.AppInfo; import com.android.launcher3.ItemInfo; +import com.android.launcher3.util.Thunk; import java.text.Collator; import java.util.Comparator; @@ -82,7 +83,7 @@ public class AppNameComparator { /** * Compares two titles with the same return value semantics as Comparator. */ - private int compareTitles(String titleA, String titleB) { + @Thunk int compareTitles(String titleA, String titleB) { // Ensure that we de-prioritize any titles that don't start with a linguistic letter or digit boolean aStartsWithLetter = Character.isLetterOrDigit(titleA.codePointAt(0)); boolean bStartsWithLetter = Character.isLetterOrDigit(titleB.codePointAt(0)); diff --git a/src/com/android/launcher3/model/PackageItemInfo.java b/src/com/android/launcher3/model/PackageItemInfo.java index 0f0134ae3..30f228c68 100644 --- a/src/com/android/launcher3/model/PackageItemInfo.java +++ b/src/com/android/launcher3/model/PackageItemInfo.java @@ -16,7 +16,6 @@ package com.android.launcher3.model; -import android.content.ComponentName; import android.graphics.Bitmap; import com.android.launcher3.ItemInfo; @@ -27,7 +26,6 @@ import java.util.Arrays; * Represents a {@link Package} in the widget tray section. */ public class PackageItemInfo extends ItemInfo { - private static final String TAG = "PackageInfo"; /** * A bitmap version of the application icon. @@ -35,12 +33,21 @@ public class PackageItemInfo extends ItemInfo { public Bitmap iconBitmap; /** - * Indicates whether we're using a low res icon + * Indicates whether we're using a low res icon. */ public boolean usingLowResIcon; + /** + * Package name of the {@link ItemInfo}. + */ public String packageName; + /** + * Character that is used as a section name for the {@link ItemInfo#title}. + * (e.g., "G" will be stored if title is "Google") + */ + public String titleSectionName; + int flags = 0; PackageItemInfo(String packageName) { diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java index fdb9795d8..625d4d696 100644 --- a/src/com/android/launcher3/model/WidgetsModel.java +++ b/src/com/android/launcher3/model/WidgetsModel.java @@ -8,8 +8,8 @@ import android.util.Log; import com.android.launcher3.IconCache; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppWidgetProviderInfo; - import com.android.launcher3.Utilities; +import com.android.launcher3.compat.AlphabeticIndexCompat; import com.android.launcher3.compat.UserHandleCompat; import java.util.ArrayList; @@ -39,11 +39,13 @@ public class WidgetsModel { private final Comparator mWidgetAndShortcutNameComparator; private final Comparator mAppNameComparator; private final IconCache mIconCache; + private AlphabeticIndexCompat mIndexer; public WidgetsModel(Context context) { mWidgetAndShortcutNameComparator = new WidgetsAndShortcutNameComparator(context); mAppNameComparator = (new AppNameComparator(context)).getAppInfoComparator(); mIconCache = LauncherAppState.getInstance().getIconCache(); + mIndexer = new AlphabeticIndexCompat(context); } private WidgetsModel(WidgetsModel model) { @@ -62,6 +64,9 @@ public class WidgetsModel { // Access methods that may be deleted if the private fields are made package-private. public PackageItemInfo getPackageItemInfo(int pos) { + if (pos >= mPackageItemInfos.size() || pos < 0) { + return null; + } return mPackageItemInfos.get(pos); } @@ -112,6 +117,7 @@ public class WidgetsModel { pInfo = new PackageItemInfo(packageName); mIconCache.getTitleAndIconForApp(packageName, UserHandleCompat.myUserHandle(), true /* userLowResIcon */, pInfo); + pInfo.titleSectionName = mIndexer.computeSectionName(pInfo.title); mWidgetsList.put(pInfo, widgetsShortcutsList); tmpPackageItemInfos.put(packageName, pInfo); mPackageItemInfos.add(pInfo); diff --git a/src/com/android/launcher3/util/LongArrayMap.java b/src/com/android/launcher3/util/LongArrayMap.java index e3c96cd42..a337e85bd 100644 --- a/src/com/android/launcher3/util/LongArrayMap.java +++ b/src/com/android/launcher3/util/LongArrayMap.java @@ -43,7 +43,7 @@ public class LongArrayMap<E> extends LongSparseArray<E> implements Iterable<E> { return new ValueIterator(); } - private class ValueIterator implements Iterator<E> { + @Thunk class ValueIterator implements Iterator<E> { private int mNextIndex = 0; diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java index 3ec164506..2714f5182 100644 --- a/src/com/android/launcher3/widget/WidgetCell.java +++ b/src/com/android/launcher3/widget/WidgetCell.java @@ -23,6 +23,7 @@ import android.content.res.Resources; import android.graphics.Bitmap; import android.util.AttributeSet; import android.util.Log; +import android.view.MotionEvent; import android.view.View; import android.view.View.OnLayoutChangeListener; import android.widget.LinearLayout; @@ -35,6 +36,7 @@ import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.R; +import com.android.launcher3.StylusEventHelper; import com.android.launcher3.WidgetPreviewLoader; import com.android.launcher3.WidgetPreviewLoader.PreviewLoadRequest; import com.android.launcher3.compat.AppWidgetManagerCompat; @@ -73,6 +75,7 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { private WidgetPreviewLoader mWidgetPreviewLoader; private PreviewLoadRequest mActiveRequest; + private StylusEventHelper mStylusEventHelper; private Launcher mLauncher; @@ -89,6 +92,7 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { final Resources r = context.getResources(); mLauncher = (Launcher) context; + mStylusEventHelper = new StylusEventHelper(this); mDimensionsFormatString = r.getString(R.string.widget_dims_format); setContainerWidth(); @@ -202,6 +206,15 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { return Math.min(size[0], info.spanX * cellWidth); } + @Override + public boolean onTouchEvent(MotionEvent ev) { + boolean handled = super.onTouchEvent(ev); + if (mStylusEventHelper.checkAndPerformStylusEvent(ev)) { + return true; + } + return handled; + } + /** * Helper method to get the string info of the tag. */ diff --git a/src/com/android/launcher3/widget/WidgetHostViewLoader.java b/src/com/android/launcher3/widget/WidgetHostViewLoader.java index d65455053..887587905 100644 --- a/src/com/android/launcher3/widget/WidgetHostViewLoader.java +++ b/src/com/android/launcher3/widget/WidgetHostViewLoader.java @@ -15,6 +15,7 @@ import com.android.launcher3.DragLayer; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.compat.AppWidgetManagerCompat; +import com.android.launcher3.util.Thunk; public class WidgetHostViewLoader { @@ -38,7 +39,7 @@ public class WidgetHostViewLoader { PendingAddWidgetInfo mCreateWidgetInfo = null; // TODO: technically, this class should not have to know the existence of the launcher. - private Launcher mLauncher; + @Thunk Launcher mLauncher; private Handler mHandler; public WidgetHostViewLoader(Launcher launcher) { @@ -188,7 +189,7 @@ public class WidgetHostViewLoader { return options; } - private void setState(int state) { + @Thunk void setState(int state) { if (DEBUG) { Log.d(TAG, String.format(" state [%d -> %d]", mState, state)); } diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java index 11c2107f2..1184394f7 100644 --- a/src/com/android/launcher3/widget/WidgetsContainerView.java +++ b/src/com/android/launcher3/widget/WidgetsContainerView.java @@ -20,8 +20,8 @@ import android.content.Context; import android.graphics.Bitmap; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.graphics.drawable.InsetDrawable; import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView.State; import android.util.AttributeSet; import android.util.Log; @@ -46,6 +46,7 @@ import com.android.launcher3.Utilities; import com.android.launcher3.WidgetPreviewLoader; import com.android.launcher3.Workspace; import com.android.launcher3.model.WidgetsModel; +import com.android.launcher3.util.Thunk; /** * The widgets list view container. @@ -60,7 +61,7 @@ public class WidgetsContainerView extends BaseContainerView private static final int PRELOAD_SCREEN_HEIGHT_MULTIPLE = 1; /* Global instances that are used inside this container. */ - private Launcher mLauncher; + @Thunk Launcher mLauncher; private DragController mDragController; private IconCache mIconCache; @@ -92,7 +93,6 @@ public class WidgetsContainerView extends BaseContainerView mWidgetHostViewLoader = new WidgetHostViewLoader(mLauncher); mAdapter = new WidgetsListAdapter(context, this, this, mLauncher); mIconCache = (LauncherAppState.getInstance()).getIconCache(); - if (DEBUG) { Log.d(TAG, "WidgetsContainerView constructor"); } @@ -240,6 +240,7 @@ public class WidgetsContainerView extends BaseContainerView Drawable icon = mIconCache.getFullResIcon(createShortcutInfo.activityInfo); preview = Utilities.createIconBitmap(icon, mLauncher); createItemInfo.spanX = createItemInfo.spanY = 1; + scale = ((float) mLauncher.getDeviceProfile().iconSizePx) / preview.getWidth(); } // Don't clip alpha values for the drag outline if we're using the default widget preview @@ -345,6 +346,23 @@ public class WidgetsContainerView extends BaseContainerView setPadding(mFixedBounds.left, mFixedBounds.top, getMeasuredWidth() - mFixedBounds.right, mFixedBounds.bottom); } + + int inset = mFixedBounds.isEmpty() ? mView.getScrollbarWidth() : mFixedBoundsContainerInset; + mView.setPadding(inset + mView.getScrollbarWidth(), inset, + inset, inset); + } + + @Override + protected void onUpdateBackgrounds() { + InsetDrawable background; + // Update the background of the reveal view and list to be inset with the fixed bound + // insets instead of the default insets + // TODO: Use quantum_panel instead of quantum_panel_shape. + int inset = mFixedBounds.isEmpty() ? mView.getScrollbarWidth() : mFixedBoundsContainerInset; + background = new InsetDrawable( + getContext().getResources().getDrawable(R.drawable.quantum_panel_shape), + inset, 0, inset, 0); + mView.updateBackgroundPadding(background); } /** diff --git a/src/com/android/launcher3/widget/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/WidgetsRecyclerView.java index bef255908..4aa332380 100644 --- a/src/com/android/launcher3/widget/WidgetsRecyclerView.java +++ b/src/com/android/launcher3/widget/WidgetsRecyclerView.java @@ -19,20 +19,25 @@ package com.android.launcher3.widget; import android.content.Context; import android.graphics.Rect; import android.graphics.drawable.Drawable; -import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.LinearLayoutManager; import android.util.AttributeSet; -import android.view.MotionEvent; +import android.view.View; import com.android.launcher3.BaseRecyclerView; +import com.android.launcher3.Utilities; +import com.android.launcher3.compat.AlphabeticIndexCompat; import com.android.launcher3.model.WidgetsModel; +import com.android.launcher3.model.PackageItemInfo; /** * The widgets recycler view. */ public class WidgetsRecyclerView extends BaseRecyclerView { + private static final String TAG = "WidgetsRecyclerView"; private WidgetsModel mWidgets; private Rect mBackgroundPadding = new Rect(); + private PackageItemInfo mLastPackageItemInfo; public WidgetsRecyclerView(Context context) { this(context, null); @@ -68,8 +73,17 @@ public class WidgetsRecyclerView extends BaseRecyclerView { */ @Override public String scrollToPositionAtProgress(float touchFraction) { - // Ensure that we have any sections - return ""; + float pos = mWidgets.getPackageSize() * touchFraction; + + int posInt = (int) pos; + LinearLayoutManager layoutManager = ((LinearLayoutManager) getLayoutManager()); + getCurScrollState(scrollPosState); + layoutManager.scrollToPositionWithOffset((int) pos, + (int) (scrollPosState.rowHeight * ((float) posInt - pos))); + + posInt = (int) ((touchFraction == 1)? pos -1 : pos); + PackageItemInfo p = mWidgets.getPackageItemInfo(posInt); + return p.titleSectionName; } /** @@ -78,19 +92,41 @@ public class WidgetsRecyclerView extends BaseRecyclerView { @Override public void updateVerticalScrollbarBounds() { int rowCount = mWidgets.getPackageSize(); + verticalScrollbarBounds.setEmpty(); - // Skip early if there are no items. + // Skip early if, there are no items. if (rowCount == 0) { - verticalScrollbarBounds.setEmpty(); return; } - int x, y; + // Skip early if, there no child laid out in the container. getCurScrollState(scrollPosState); if (scrollPosState.rowIndex < 0) { + return; + } + + int actualHeight = getHeight() - getPaddingTop() - getPaddingBottom(); + int totalScrollHeight = rowCount * scrollPosState.rowHeight; + // Skip early if the height of all the rows are actually less than the container height. + if (totalScrollHeight < actualHeight) { verticalScrollbarBounds.setEmpty(); + return; } - // TODO + + int scrollbarHeight = (int) (actualHeight / ((float) totalScrollHeight / actualHeight)); + int availableY = totalScrollHeight - actualHeight; + int availableScrollY = actualHeight - scrollbarHeight; + int y = (scrollPosState.rowIndex * scrollPosState.rowHeight) + - scrollPosState.rowTopOffset; + y = getPaddingTop() + + (int) (((float) (getPaddingTop() + y) / availableY) * availableScrollY); + + // Calculate the position and size of the scroll bar. + int x = getWidth() - getScrollbarWidth() - mBackgroundPadding.right; + if (Utilities.isRtl(getResources())) { + x = mBackgroundPadding.left; + } + verticalScrollbarBounds.set(x, y, x + getScrollbarWidth(), y + scrollbarHeight); } /** @@ -107,6 +143,11 @@ public class WidgetsRecyclerView extends BaseRecyclerView { if (rowCount == 0) { return; } - // TODO + View child = getChildAt(0); + int position = getChildPosition(child); + + stateOut.rowIndex = position; + stateOut.rowTopOffset = getLayoutManager().getDecoratedTop(child); + stateOut.rowHeight = child.getHeight(); } }
\ No newline at end of file |