Merge "Disallow scrolling if workspace is in transition." into ub-launcher3-burnaby-polish
diff --git a/WallpaperPicker/res/values-sw720dp-v19/styles.xml b/WallpaperPicker/res/values-sw720dp-v19/styles.xml
deleted file mode 100644
index d8dab22..0000000
--- a/WallpaperPicker/res/values-sw720dp-v19/styles.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
--->
-
-<resources>
- <style name="Theme" parent="@android:style/Theme.DeviceDefault.Wallpaper.NoTitleBar">
- <item name="android:windowActionModeOverlay">true</item>
- <item name="android:windowTranslucentStatus">true</item>
- <item name="android:windowTranslucentNavigation">true</item>
- </style>
-</resources>
diff --git a/WallpaperPicker/res/values-sw720dp/styles.xml b/WallpaperPicker/res/values-sw720dp/styles.xml
index 12f8884..0058f7e 100644
--- a/WallpaperPicker/res/values-sw720dp/styles.xml
+++ b/WallpaperPicker/res/values-sw720dp/styles.xml
@@ -18,7 +18,11 @@
-->
<resources>
- <style name="Theme" parent="@android:style/Theme.DeviceDefault.Wallpaper.NoTitleBar">
+ <style name="BaseWallpaperTheme" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar">
+ <item name="android:windowBackground">@android:color/transparent</item>
+ <item name="android:colorBackgroundCacheHint">@null</item>
+ <item name="android:windowShowWallpaper">true</item>
+ <item name="android:windowNoTitle">true</item>
<item name="android:windowActionModeOverlay">true</item>
</style>
</resources>
diff --git a/WallpaperPicker/res/values-v21/styles.xml b/WallpaperPicker/res/values-v21/styles.xml
index 70220ed..de4b2f2 100644
--- a/WallpaperPicker/res/values-v21/styles.xml
+++ b/WallpaperPicker/res/values-v21/styles.xml
@@ -34,8 +34,8 @@
</style>
<style name="Theme" parent="@style/BaseWallpaperTheme">
- <item name="android:windowTranslucentStatus">true</item>
- <item name="android:windowTranslucentNavigation">true</item>
+ <item name="android:statusBarColor">#00000000</item>
+ <item name="android:navigationBarColor">#00000000</item>
<item name="android:colorControlActivated">@color/launcher_accent_color</item>
<item name="android:colorAccent">@color/launcher_accent_color</item>
<item name="android:colorPrimary">@color/launcher_accent_color</item>
diff --git a/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java b/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java
index 2d496a5..6baac6a 100644
--- a/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java
+++ b/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java
@@ -159,6 +159,7 @@
public enum State { NOT_LOADED, LOADED, ERROR_LOADING };
private State mState = State.NOT_LOADED;
+ /** Returns whether loading was successful. */
public boolean loadInBackground(InBitmapProvider bitmapProvider) {
ExifInterface ei = new ExifInterface();
if (readExif(ei)) {
@@ -193,7 +194,7 @@
try {
mPreview = loadPreviewBitmap(opts);
} catch (IllegalArgumentException e) {
- Log.d(TAG, "Unable to reusage bitmap", e);
+ Log.d(TAG, "Unable to reuse bitmap", e);
opts.inBitmap = null;
mPreview = null;
}
@@ -202,6 +203,10 @@
if (mPreview == null) {
mPreview = loadPreviewBitmap(opts);
}
+ if (mPreview == null) {
+ mState = State.ERROR_LOADING;
+ return false;
+ }
// Verify that the bitmap can be used on GL surface
try {
@@ -212,7 +217,7 @@
Log.d(TAG, "Image cannot be rendered on a GL surface", e);
mState = State.ERROR_LOADING;
}
- return true;
+ return mState == State.LOADED;
}
}
@@ -310,7 +315,7 @@
Bitmap b = BitmapFactory.decodeStream(is, null, options);
Utils.closeSilently(is);
return b;
- } catch (FileNotFoundException e) {
+ } catch (FileNotFoundException | OutOfMemoryError e) {
Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e);
return null;
}
@@ -412,7 +417,8 @@
"Failed to create preview of apropriate size! "
+ " in: %dx%d, out: %dx%d",
mWidth, mHeight,
- preview.getWidth(), preview.getHeight()));
+ preview == null ? -1 : preview.getWidth(),
+ preview == null ? -1 : preview.getHeight()));
}
}
}
diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java
index c95d558..ede6c71 100644
--- a/src/com/android/launcher3/AppInfo.java
+++ b/src/com/android/launcher3/AppInfo.java
@@ -51,11 +51,6 @@
*/
boolean usingLowResIcon;
- /**
- * The time at which the app was first installed.
- */
- long firstInstallTime;
-
public ComponentName componentName;
static final int DOWNLOADED_FLAG = 1;
@@ -84,7 +79,6 @@
this.container = ItemInfo.NO_ID;
flags = initFlags(info);
- firstInstallTime = info.getFirstInstallTime();
iconCache.getTitleAndIcon(this, info, true /* useLowResIcon */);
intent = makeLaunchIntent(context, info, user);
this.user = user;
@@ -109,7 +103,6 @@
title = Utilities.trim(info.title);
intent = new Intent(info.intent);
flags = info.flags;
- firstInstallTime = info.firstInstallTime;
iconBitmap = info.iconBitmap;
}
@@ -129,7 +122,6 @@
Log.d(tag, label + " size=" + list.size());
for (AppInfo info: list) {
Log.d(tag, " title=\"" + info.title + "\" iconBitmap=" + info.iconBitmap
- + " firstInstallTime=" + info.firstInstallTime
+ " componentName=" + info.componentName.getPackageName());
}
}
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 5f64a82..0a2a017 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -36,7 +36,6 @@
import android.graphics.drawable.TransitionDrawable;
import android.os.Build;
import android.os.Parcelable;
-import android.os.PowerManager;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.util.Log;
@@ -2183,10 +2182,7 @@
// Animations are disabled in power save mode, causing the repeated animation to jump
// spastically between beginning and end states. Since this looks bad, we don't repeat
// the animation in power save mode.
- PowerManager powerManager = (PowerManager) getContext()
- .getSystemService(Context.POWER_SERVICE);
- boolean powerSaverOn = Utilities.ATLEAST_LOLLIPOP && powerManager.isPowerSaveMode();
- if (!powerSaverOn) {
+ if (!Utilities.isPowerSaverOn(getContext())) {
va.setRepeatMode(ValueAnimator.REVERSE);
va.setRepeatCount(ValueAnimator.INFINITE);
}
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index c0ad516..ccbfba1 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -245,7 +245,9 @@
hotseatCellHeightPx = iconSizePx;
// Folder
- folderCellWidthPx = Math.min(cellWidthPx + 6 * edgeMarginPx,
+ int folderCellPadding = isTablet || isLandscape ? 6 * edgeMarginPx : 3 * edgeMarginPx;
+ // Don't let the folder get too close to the edges of the screen.
+ folderCellWidthPx = Math.min(cellWidthPx + folderCellPadding,
(availableWidthPx - 4 * edgeMarginPx) / inv.numFolderColumns);
folderCellHeightPx = cellHeightPx + edgeMarginPx;
folderBackgroundOffset = -edgeMarginPx;
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index 9377bad..da895c6 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -318,9 +318,10 @@
sendCustomAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
String.format(getContext().getString(R.string.folder_renamed), newTitle));
}
- // In order to clear the focus from the text field, we set the focus on ourself. This
- // ensures that every time the field is clicked, focus is gained, giving reliable behavior.
- requestFocus();
+
+ // This ensures that focus is gained every time the field is clicked, which selects all
+ // the text and brings up the soft keyboard if necessary.
+ mFolderName.clearFocus();
Selection.setSelection((Spannable) mFolderName.getText(), 0, 0);
mIsEditingName = false;
@@ -451,6 +452,11 @@
mContent.snapToPageImmediately(0);
}
+ // This is set to true in close(), but isn't reset to false until onDropCompleted(). This
+ // leads to an consistent state if you drag out of the folder and drag back in without
+ // dropping. One resulting issue is that replaceFolderWithFinalItem() can be called twice.
+ mDeleteFolderOnDropCompleted = false;
+
Animator openFolderAnim = null;
final Runnable onCompleteRunnable;
if (!Utilities.ATLEAST_LOLLIPOP) {
@@ -640,7 +646,7 @@
@Override
public void onAnimationEnd(Animator animation) {
setLayerType(LAYER_TYPE_NONE, null);
- close();
+ close(true);
}
@Override
public void onAnimationStart(Animator animation) {
@@ -654,7 +660,7 @@
oa.start();
}
- public void close() {
+ public void close(boolean wasAnimated) {
// TODO: Clear all active animations.
DragLayer parent = (DragLayer) getParent();
if (parent != null) {
@@ -662,7 +668,9 @@
}
mDragController.removeDropTarget(this);
clearFocus();
- mFolderIcon.requestFocus();
+ if (wasAnimated) {
+ mFolderIcon.requestFocus();
+ }
if (mRearrangeOnClose) {
rearrangeChildren();
@@ -1144,10 +1152,10 @@
// addInScreenFromBind() to ensure that hotseat items are placed correctly.
mLauncher.getWorkspace().addInScreenFromBind(newIcon, mInfo.container,
mInfo.screenId, mInfo.cellX, mInfo.cellY, mInfo.spanX, mInfo.spanY);
- }
- // Focus the newly created child
- newIcon.requestFocus();
+ // Focus the newly created child
+ newIcon.requestFocus();
+ }
}
}
};
@@ -1164,15 +1172,37 @@
return mDestroyed;
}
- // This method keeps track of the last item in the folder for the purposes
+ // This method keeps track of the first and last item in the folder for the purposes
// of keyboard focus
public void updateTextViewFocus() {
- View lastChild = mContent.getLastItem();
- if (lastChild != null) {
+ final View firstChild = mContent.getFirstItem();
+ final View lastChild = mContent.getLastItem();
+ if (firstChild != null && lastChild != null) {
mFolderName.setNextFocusDownId(lastChild.getId());
mFolderName.setNextFocusRightId(lastChild.getId());
mFolderName.setNextFocusLeftId(lastChild.getId());
mFolderName.setNextFocusUpId(lastChild.getId());
+ // Hitting TAB from the folder name wraps around to the first item on the current
+ // folder page, and hitting SHIFT+TAB from that item wraps back to the folder name.
+ mFolderName.setNextFocusForwardId(firstChild.getId());
+ // When clicking off the folder when editing the name, this Folder gains focus. When
+ // pressing an arrow key from that state, give the focus to the first item.
+ this.setNextFocusDownId(firstChild.getId());
+ this.setNextFocusRightId(firstChild.getId());
+ this.setNextFocusLeftId(firstChild.getId());
+ this.setNextFocusUpId(firstChild.getId());
+ // When pressing shift+tab in the above state, give the focus to the last item.
+ setOnKeyListener(new OnKeyListener() {
+ @Override
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ boolean isShiftPlusTab = keyCode == KeyEvent.KEYCODE_TAB &&
+ event.hasModifiers(KeyEvent.META_SHIFT_ON);
+ if (isShiftPlusTab && Folder.this.isFocused()) {
+ return lastChild.requestFocus();
+ }
+ return false;
+ }
+ });
}
}
@@ -1293,7 +1323,11 @@
rearrangeChildren();
}
if (getItemCount() <= 1) {
- replaceFolderWithFinalItem();
+ if (mInfo.opened) {
+ mLauncher.closeFolder(this, true);
+ } else {
+ replaceFolderWithFinalItem();
+ }
}
}
@@ -1337,6 +1371,8 @@
public void onFocusChange(View v, boolean hasFocus) {
if (v == mFolderName && hasFocus) {
startEditingFolderName();
+ } else if (v == mFolderName && !hasFocus) {
+ dismissEditingName();
}
}
diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java
index cc9c573..d503d2c 100644
--- a/src/com/android/launcher3/FolderPagedView.java
+++ b/src/com/android/launcher3/FolderPagedView.java
@@ -402,16 +402,28 @@
return !ALLOW_FOLDER_SCROLL && getItemCount() >= mMaxItemsPerPage;
}
+ public View getFirstItem() {
+ if (getChildCount() < 1) {
+ return null;
+ }
+ ShortcutAndWidgetContainer currContainer = getCurrentCellLayout().getShortcutsAndWidgets();
+ if (mGridCountX > 0) {
+ return currContainer.getChildAt(0, 0);
+ } else {
+ return currContainer.getChildAt(0);
+ }
+ }
+
public View getLastItem() {
if (getChildCount() < 1) {
return null;
}
- ShortcutAndWidgetContainer lastContainer = getCurrentCellLayout().getShortcutsAndWidgets();
- int lastRank = lastContainer.getChildCount() - 1;
+ ShortcutAndWidgetContainer currContainer = getCurrentCellLayout().getShortcutsAndWidgets();
+ int lastRank = currContainer.getChildCount() - 1;
if (mGridCountX > 0) {
- return lastContainer.getChildAt(lastRank % mGridCountX, lastRank / mGridCountX);
+ return currContainer.getChildAt(lastRank % mGridCountX, lastRank / mGridCountX);
} else {
- return lastContainer.getChildAt(lastRank);
+ return currContainer.getChildAt(lastRank);
}
}
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index 9a75193..571d99a 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -171,7 +171,6 @@
private static void queuePendingShortcutInfo(PendingInstallShortcutInfo info, Context context) {
// Queue the item up for adding if launcher has not loaded properly yet
- LauncherAppState.setApplicationContext(context.getApplicationContext());
LauncherAppState app = LauncherAppState.getInstance();
boolean launcherNotLoaded = app.getModel().getCallback() == null;
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index e983eb1..0d183db 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -17,7 +17,6 @@
package com.android.launcher3;
import android.annotation.TargetApi;
-import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Point;
import android.util.DisplayMetrics;
@@ -84,9 +83,6 @@
DeviceProfile landscapeProfile;
DeviceProfile portraitProfile;
- // On Marshmallow the status bar is no longer opaque, when drawn on the right.
- public boolean isRightInsetOpaque;
-
InvariantDeviceProfile() {
}
@@ -170,9 +166,6 @@
largeSide, smallSide, true /* isLandscape */);
portraitProfile = new DeviceProfile(context, this, smallestSize, largestSize,
smallSide, largeSide, false /* isLandscape */);
-
- isRightInsetOpaque = !Utilities.ATLEAST_MARSHMALLOW ||
- context.getSystemService(ActivityManager.class).isLowRamDevice();
}
ArrayList<InvariantDeviceProfile> getPredefinedDeviceProfiles() {
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 9824e3e..a379acc 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -85,7 +85,6 @@
import android.view.ViewGroup;
import android.view.ViewStub;
import android.view.ViewTreeObserver;
-import android.view.Window;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.OvershootInterpolator;
@@ -418,7 +417,6 @@
super.onCreate(savedInstanceState);
- LauncherAppState.setApplicationContext(getApplicationContext());
LauncherAppState app = LauncherAppState.getInstance();
// Load configuration-specific DeviceProfile
@@ -1623,18 +1621,28 @@
// The AppWidgetHostView has already been inflated and instantiated
launcherInfo.hostView = hostView;
}
- launcherInfo.hostView.setTag(launcherInfo);
launcherInfo.hostView.setVisibility(View.VISIBLE);
- launcherInfo.notifyWidgetSizeChanged(this);
-
- mWorkspace.addInScreen(launcherInfo.hostView, container, screenId, info.cellX,
- info.cellY, launcherInfo.spanX, launcherInfo.spanY, isWorkspaceLocked());
-
- addWidgetToAutoAdvanceIfNeeded(launcherInfo.hostView, appWidgetInfo);
+ addAppWidgetToWorkspace(launcherInfo, appWidgetInfo, isWorkspaceLocked());
}
resetAddInfo();
}
+ private void addAppWidgetToWorkspace(LauncherAppWidgetInfo item,
+ LauncherAppWidgetProviderInfo appWidgetInfo, boolean insert) {
+ item.hostView.setTag(item);
+ item.onBindAppWidget(this);
+
+ item.hostView.setFocusable(true);
+ item.hostView.setOnFocusChangeListener(mFocusHandler);
+
+ mWorkspace.addInScreen(item.hostView, item.container, item.screenId,
+ item.cellX, item.cellY, item.spanX, item.spanY, insert);
+
+ if (!item.isCustomWidget()) {
+ addWidgetToAutoAdvanceIfNeeded(item.hostView, appWidgetInfo);
+ }
+ }
+
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -1684,31 +1692,10 @@
}
registerReceiver(mReceiver, filter);
FirstFrameAnimatorHelper.initializeDrawListener(getWindow().getDecorView());
- setupTransparentSystemBarsForLollipop();
mAttached = true;
mVisible = true;
}
- /**
- * Sets up transparent navigation and status bars in Lollipop.
- * This method is a no-op for other platform versions.
- */
- @TargetApi(Build.VERSION_CODES.LOLLIPOP)
- private void setupTransparentSystemBarsForLollipop() {
- if (Utilities.ATLEAST_LOLLIPOP) {
- Window window = getWindow();
- window.getAttributes().systemUiVisibility |=
- (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
- | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
- | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
- window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
- | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
- window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
- window.setStatusBarColor(Color.TRANSPARENT);
- window.setNavigationBarColor(Color.TRANSPARENT);
- }
- }
-
@Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
@@ -3125,6 +3112,10 @@
}
oa.setDuration(getResources().getInteger(R.integer.config_folderExpandDuration));
oa.start();
+ if (Utilities.isPowerSaverOn(this)) {
+ // Animations are disabled in battery saver mode, so just skip to the end state.
+ oa.end();
+ }
}
private void shrinkAndFadeInFolderIcon(final FolderIcon fi, boolean animate) {
@@ -3226,7 +3217,7 @@
if (animate) {
folder.animateClosed();
} else {
- folder.close();
+ folder.close(false);
}
// Notify the accessibility manager that this folder "window" has disappeared and no
@@ -4124,15 +4115,7 @@
item.hostView.setOnClickListener(this);
}
- item.hostView.setTag(item);
- item.onBindAppWidget(this);
-
- workspace.addInScreen(item.hostView, item.container, item.screenId, item.cellX,
- item.cellY, item.spanX, item.spanY, false);
- if (!item.isCustomWidget()) {
- addWidgetToAutoAdvanceIfNeeded(item.hostView, appWidgetInfo);
- }
-
+ addAppWidgetToWorkspace(item, appWidgetInfo, false);
workspace.requestLayout();
if (DEBUG_WIDGETS) {
diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java
index cf461a5..c49d43f 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java
@@ -19,6 +19,8 @@
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
+import android.graphics.Rect;
+import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -28,6 +30,8 @@
import com.android.launcher3.DragLayer.TouchCompleteListener;
+import java.util.ArrayList;
+
/**
* {@inheritDoc}
*/
@@ -43,6 +47,8 @@
private float mSlop;
+ private boolean mChildrenFocused;
+
public LauncherAppWidgetHostView(Context context) {
super(context);
mContext = context;
@@ -175,6 +181,90 @@
@Override
public int getDescendantFocusability() {
- return ViewGroup.FOCUS_BLOCK_DESCENDANTS;
+ return mChildrenFocused ? ViewGroup.FOCUS_BEFORE_DESCENDANTS
+ : ViewGroup.FOCUS_BLOCK_DESCENDANTS;
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ if (mChildrenFocused && event.getKeyCode() == KeyEvent.KEYCODE_ESCAPE
+ && event.getAction() == KeyEvent.ACTION_UP) {
+ mChildrenFocused = false;
+ requestFocus();
+ return true;
+ }
+ return super.dispatchKeyEvent(event);
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (!mChildrenFocused && keyCode == KeyEvent.KEYCODE_ENTER) {
+ event.startTracking();
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ if (event.isTracking()) {
+ if (!mChildrenFocused && keyCode == KeyEvent.KEYCODE_ENTER) {
+ mChildrenFocused = true;
+ ArrayList<View> focusableChildren = getFocusables(FOCUS_FORWARD);
+ focusableChildren.remove(this);
+ int childrenCount = focusableChildren.size();
+ switch (childrenCount) {
+ case 0:
+ mChildrenFocused = false;
+ break;
+ case 1: {
+ if (getTag() instanceof ItemInfo) {
+ ItemInfo item = (ItemInfo) getTag();
+ if (item.spanX == 1 && item.spanY == 1) {
+ focusableChildren.get(0).performClick();
+ mChildrenFocused = false;
+ return true;
+ }
+ }
+ // continue;
+ }
+ default:
+ focusableChildren.get(0).requestFocus();
+ return true;
+ }
+ }
+ }
+ return super.onKeyUp(keyCode, event);
+ }
+
+ @Override
+ protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
+ if (gainFocus) {
+ mChildrenFocused = false;
+ }
+ super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
+ }
+
+ @Override
+ public void requestChildFocus(View child, View focused) {
+ super.requestChildFocus(child, focused);
+ dispatchChildFocus(focused != null);
+ }
+
+ @Override
+ public void clearChildFocus(View child) {
+ super.clearChildFocus(child);
+ dispatchChildFocus(false);
+ }
+
+ @Override
+ public boolean dispatchUnhandledMove(View focused, int direction) {
+ return mChildrenFocused;
+ }
+
+ private void dispatchChildFocus(boolean focused) {
+ if (getOnFocusChangeListener() != null) {
+ getOnFocusChangeListener().onFocusChange(this, focused || isFocused());
+ }
}
}
diff --git a/src/com/android/launcher3/LauncherAppWidgetInfo.java b/src/com/android/launcher3/LauncherAppWidgetInfo.java
index 882f7e2..55edf45 100644
--- a/src/com/android/launcher3/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetInfo.java
@@ -123,17 +123,11 @@
*/
void onBindAppWidget(Launcher launcher) {
if (!mHasNotifiedInitialWidgetSizeChanged) {
- notifyWidgetSizeChanged(launcher);
+ AppWidgetResizeFrame.updateWidgetSizeRanges(hostView, launcher, spanX, spanY);
+ mHasNotifiedInitialWidgetSizeChanged = true;
}
}
- /**
- * Trigger an update callback to the widget to notify it that its size has changed.
- */
- void notifyWidgetSizeChanged(Launcher launcher) {
- AppWidgetResizeFrame.updateWidgetSizeRanges(hostView, launcher, spanX, spanY);
- mHasNotifiedInitialWidgetSizeChanged = true;
- }
@Override
public String toString() {
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index ef79cf8..f095a05 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -84,6 +84,10 @@
@Override
public boolean onCreate() {
final Context context = getContext();
+ // The content provider exists for the entire duration of the launcher main process and
+ // is the first component to get created. Initializing application context here ensures
+ // that LauncherAppState always exists in the main process.
+ LauncherAppState.setApplicationContext(context.getApplicationContext());
StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
mOpenHelper = new DatabaseHelper(context);
StrictMode.setThreadPolicy(oldPolicy);
diff --git a/src/com/android/launcher3/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java
index 1c6ca87..71ccd85 100644
--- a/src/com/android/launcher3/LauncherRootView.java
+++ b/src/com/android/launcher3/LauncherRootView.java
@@ -1,17 +1,22 @@
package com.android.launcher3;
+import android.annotation.TargetApi;
+import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
+import android.view.View;
public class LauncherRootView extends InsettableFrameLayout {
private final Paint mOpaquePaint;
private boolean mDrawRightInsetBar;
+ private View mAlignedView;
+
public LauncherRootView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -21,10 +26,32 @@
}
@Override
+ protected void onFinishInflate() {
+ if (getChildCount() > 0) {
+ // LauncherRootView contains only one child, which should be aligned
+ // based on the horizontal insets.
+ mAlignedView = getChildAt(0);
+ }
+ super.onFinishInflate();
+ }
+
+ @TargetApi(23)
+ @Override
protected boolean fitSystemWindows(Rect insets) {
- setInsets(insets);
- mDrawRightInsetBar = mInsets.right > 0 && LauncherAppState
- .getInstance().getInvariantDeviceProfile().isRightInsetOpaque;
+ mDrawRightInsetBar = insets.right > 0 &&
+ (!Utilities.ATLEAST_MARSHMALLOW ||
+ getContext().getSystemService(ActivityManager.class).isLowRamDevice());
+ setInsets(mDrawRightInsetBar ? new Rect(0, insets.top, 0, insets.bottom) : insets);
+
+ if (mAlignedView != null) {
+ // Apply margins on aligned view to handle left/right insets.
+ MarginLayoutParams lp = (MarginLayoutParams) mAlignedView.getLayoutParams();
+ if (lp.leftMargin != insets.left || lp.rightMargin != insets.right) {
+ lp.leftMargin = insets.left;
+ lp.rightMargin = insets.right;
+ mAlignedView.setLayoutParams(lp);
+ }
+ }
return true; // I'll take it from here
}
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 9258360..b6d3cc4 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -202,9 +202,6 @@
protected final Rect mInsets = new Rect();
protected final boolean mIsRtl;
- // When set to true, full screen content and overscroll effect is shited inside by right inset.
- protected boolean mIgnoreRightInset;
-
// Edge effect
private final LauncherEdgeEffect mEdgeGlowLeft = new LauncherEdgeEffect();
private final LauncherEdgeEffect mEdgeGlowRight = new LauncherEdgeEffect();
@@ -822,8 +819,7 @@
childWidthMode = MeasureSpec.EXACTLY;
childHeightMode = MeasureSpec.EXACTLY;
- childWidth = getViewportWidth() - mInsets.left
- - (mIgnoreRightInset ? mInsets.right : 0);
+ childWidth = getViewportWidth() - mInsets.left - mInsets.right;
childHeight = getViewportHeight();
}
if (referenceChildWidth == 0) {
@@ -1182,9 +1178,8 @@
getEdgeVerticalPostion(sTmpIntPoint);
- int width = mIgnoreRightInset ? (display.width() - mInsets.right) : display.width();
- canvas.translate(sTmpIntPoint[0] - display.top, -width);
- mEdgeGlowRight.setSize(sTmpIntPoint[1] - sTmpIntPoint[0], width);
+ canvas.translate(sTmpIntPoint[0] - display.top, -display.width());
+ mEdgeGlowRight.setSize(sTmpIntPoint[1] - sTmpIntPoint[0], display.width());
if (mEdgeGlowRight.draw(canvas)) {
postInvalidateOnAnimation();
}
@@ -1225,7 +1220,17 @@
@Override
public boolean dispatchUnhandledMove(View focused, int direction) {
- // XXX-RTL: This will be fixed in a future CL
+ if (super.dispatchUnhandledMove(focused, direction)) {
+ return true;
+ }
+
+ if (mIsRtl) {
+ if (direction == View.FOCUS_LEFT) {
+ direction = View.FOCUS_RIGHT;
+ } else if (direction == View.FOCUS_RIGHT) {
+ direction = View.FOCUS_LEFT;
+ }
+ }
if (direction == View.FOCUS_LEFT) {
if (getCurrentPage() > 0) {
snapToPage(getCurrentPage() - 1);
@@ -1237,7 +1242,7 @@
return true;
}
}
- return super.dispatchUnhandledMove(focused, direction);
+ return false;
}
@Override
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index 5766cf2..6bdcb4b 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -52,7 +52,7 @@
public static final int FLAG_AUTOINTALL_ICON = 2; //0B10;
/**
- * The icon is being installed. If {@link FLAG_RESTORED_ICON} or {@link FLAG_AUTOINTALL_ICON}
+ * The icon is being installed. If {@link #FLAG_RESTORED_ICON} or {@link #FLAG_AUTOINTALL_ICON}
* is set, then the icon is either being installed or is in a broken state.
*/
public static final int FLAG_INSTALL_SESSION_ACTIVE = 4; // 0B100;
@@ -126,19 +126,14 @@
private int mInstallProgress;
/**
- * Refer {@link AppInfo#firstInstallTime}.
- */
- public long firstInstallTime;
-
- /**
- * TODO move this to {@link status}
+ * TODO move this to {@link #status}
*/
int flags = 0;
/**
* If this shortcut is a placeholder, then intent will be a market intent for the package, and
* this will hold the original intent from the database. Otherwise, null.
- * Refer {@link #FLAG_RESTORE_PENDING}, {@link #FLAG_INSTALL_PENDING}
+ * Refer {@link #FLAG_RESTORED_ICON}, {@link #FLAG_AUTOINTALL_ICON}
*/
Intent promisedIntent;
@@ -172,7 +167,6 @@
mIcon = info.mIcon; // TODO: should make a copy here. maybe we don't need this ctor at all
customIcon = info.customIcon;
flags = info.flags;
- firstInstallTime = info.firstInstallTime;
user = info.user;
status = info.status;
}
@@ -184,7 +178,6 @@
intent = new Intent(info.intent);
customIcon = false;
flags = info.flags;
- firstInstallTime = info.firstInstallTime;
}
public void setIcon(Bitmap b) {
@@ -293,7 +286,6 @@
shortcut.intent = AppInfo.makeLaunchIntent(context, info, info.getUser());
shortcut.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
shortcut.flags = AppInfo.initFlags(info);
- shortcut.firstInstallTime = info.getFirstInstallTime();
return shortcut;
}
}
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index d14d056..735cbeb 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -46,6 +46,7 @@
import android.graphics.drawable.PaintDrawable;
import android.os.Build;
import android.os.Bundle;
+import android.os.PowerManager;
import android.os.Process;
import android.text.Spannable;
import android.text.SpannableString;
@@ -759,4 +760,10 @@
return context.getSharedPreferences(
LauncherFiles.SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE);
}
+
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ public static boolean isPowerSaverOn(Context context) {
+ PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ return ATLEAST_LOLLIPOP && powerManager.isPowerSaveMode();
+ }
}
diff --git a/src/com/android/launcher3/WallpaperChangedReceiver.java b/src/com/android/launcher3/WallpaperChangedReceiver.java
index 2d5612f..c24fbff 100644
--- a/src/com/android/launcher3/WallpaperChangedReceiver.java
+++ b/src/com/android/launcher3/WallpaperChangedReceiver.java
@@ -22,8 +22,6 @@
public class WallpaperChangedReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent data) {
- LauncherAppState.setApplicationContext(context.getApplicationContext());
- LauncherAppState appState = LauncherAppState.getInstance();
- appState.onWallpaperChanged();
+ LauncherAppState.getInstance().onWallpaperChanged();
}
}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 88a4c19..5073902 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -455,7 +455,6 @@
setWallpaperDimension();
setEdgeGlowColor(getResources().getColor(R.color.workspace_edge_effect_color));
- mIgnoreRightInset = app.getInvariantDeviceProfile().isRightInsetOpaque;
}
private void setupLayoutTransition() {
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
index 6424e03..4aa667e 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
@@ -43,7 +43,6 @@
PackageInstallerCompatVL(Context context) {
mInstaller = context.getPackageManager().getPackageInstaller();
- LauncherAppState.setApplicationContext(context.getApplicationContext());
mCache = LauncherAppState.getInstance().getIconCache();
mWorker = new Handler(LauncherModel.getWorkerLooper());
diff --git a/src/com/android/launcher3/util/ManagedProfileHeuristic.java b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
index 849b05c..fb9bbb2 100644
--- a/src/com/android/launcher3/util/ManagedProfileHeuristic.java
+++ b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
@@ -42,6 +42,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -85,6 +86,7 @@
private ArrayList<ShortcutInfo> mHomescreenApps;
private ArrayList<ShortcutInfo> mWorkFolderApps;
+ private HashMap<ShortcutInfo, Long> mShortcutToInstallTimeMap;
private ManagedProfileHeuristic(Context context, UserHandleCompat user) {
mContext = context;
@@ -100,32 +102,29 @@
Context.MODE_PRIVATE);
}
+ private void initVars() {
+ mHomescreenApps = new ArrayList<>();
+ mWorkFolderApps = new ArrayList<>();
+ mShortcutToInstallTimeMap = new HashMap<>();
+ }
+
/**
* Checks the list of user apps and adds icons for newly installed apps on the homescreen or
* workfolder.
*/
public void processUserApps(List<LauncherActivityInfoCompat> apps) {
- mHomescreenApps = new ArrayList<>();
- mWorkFolderApps = new ArrayList<>();
+ initVars();
HashSet<String> packageSet = new HashSet<>();
final boolean userAppsExisted = getUserApps(packageSet);
boolean newPackageAdded = false;
-
for (LauncherActivityInfoCompat info : apps) {
String packageName = info.getComponentName().getPackageName();
if (!packageSet.contains(packageName)) {
packageSet.add(packageName);
newPackageAdded = true;
-
- try {
- PackageInfo pkgInfo = mContext.getPackageManager()
- .getPackageInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES);
- markForAddition(info, pkgInfo.firstInstallTime);
- } catch (NameNotFoundException e) {
- Log.e(TAG, "Unknown package " + packageName, e);
- }
+ markForAddition(info, info.getFirstInstallTime());
}
}
@@ -142,7 +141,22 @@
ArrayList<ShortcutInfo> targetList =
(installTime <= mUserCreationTime + AUTO_ADD_TO_FOLDER_DURATION) ?
mWorkFolderApps : mHomescreenApps;
- targetList.add(ShortcutInfo.fromActivityInfo(info, mContext));
+ ShortcutInfo si = ShortcutInfo.fromActivityInfo(info, mContext);
+ mShortcutToInstallTimeMap.put(si, installTime);
+ targetList.add(si);
+ }
+
+ private void sortList(ArrayList<ShortcutInfo> infos) {
+ Collections.sort(infos, new Comparator<ShortcutInfo>() {
+
+ @Override
+ public int compare(ShortcutInfo lhs, ShortcutInfo rhs) {
+ Long lhsTime = mShortcutToInstallTimeMap.get(lhs);
+ Long rhsTime = mShortcutToInstallTimeMap.get(rhs);
+ return Utilities.longCompare(lhsTime == null ? 0 : lhsTime,
+ rhsTime == null ? 0 : rhsTime);
+ }
+ });
}
/**
@@ -152,13 +166,7 @@
if (mWorkFolderApps.isEmpty()) {
return;
}
- Collections.sort(mWorkFolderApps, new Comparator<ShortcutInfo>() {
-
- @Override
- public int compare(ShortcutInfo lhs, ShortcutInfo rhs) {
- return Utilities.longCompare(lhs.firstInstallTime, rhs.firstInstallTime);
- }
- });
+ sortList(mWorkFolderApps);
// Try to get a work folder.
String folderIdKey = USER_FOLDER_ID_PREFIX + mUserSerial;
@@ -222,6 +230,7 @@
finalizeWorkFolder();
if (addHomeScreenShortcuts && !mHomescreenApps.isEmpty()) {
+ sortList(mHomescreenApps);
mModel.addAndBindAddedWorkspaceItems(mContext, mHomescreenApps);
}
}
@@ -230,9 +239,7 @@
* Updates the list of installed apps and adds any new icons on homescreen or work folder.
*/
public void processPackageAdd(String[] packages) {
- mHomescreenApps = new ArrayList<>();
- mWorkFolderApps = new ArrayList<>();
-
+ initVars();
HashSet<String> packageSet = new HashSet<>();
final boolean userAppsExisted = getUserApps(packageSet);