diff options
Diffstat (limited to 'src/com/android')
-rw-r--r-- | src/com/android/launcher3/AutoInstallsLayout.java | 14 | ||||
-rw-r--r-- | src/com/android/launcher3/CommonAppTypeParser.java | 152 | ||||
-rw-r--r-- | src/com/android/launcher3/DefaultLayoutParser.java | 15 | ||||
-rw-r--r-- | src/com/android/launcher3/Folder.java | 41 | ||||
-rw-r--r-- | src/com/android/launcher3/FolderAutoScrollHelper.java | 59 | ||||
-rw-r--r-- | src/com/android/launcher3/Launcher.java | 52 | ||||
-rw-r--r-- | src/com/android/launcher3/LauncherBackupHelper.java | 117 | ||||
-rw-r--r-- | src/com/android/launcher3/LauncherModel.java | 113 | ||||
-rw-r--r-- | src/com/android/launcher3/LauncherProvider.java | 190 | ||||
-rw-r--r-- | src/com/android/launcher3/ShortcutInfo.java | 12 | ||||
-rw-r--r-- | src/com/android/launcher3/Workspace.java | 7 |
11 files changed, 471 insertions, 301 deletions
diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java index 382066094..908bd3d79 100644 --- a/src/com/android/launcher3/AutoInstallsLayout.java +++ b/src/com/android/launcher3/AutoInstallsLayout.java @@ -116,7 +116,7 @@ public class AutoInstallsLayout { private final Context mContext; private final AppWidgetHost mAppWidgetHost; - private final LayoutParserCallback mCallback; + protected final LayoutParserCallback mCallback; protected final PackageManager mPackageManager; protected final Resources mSourceRes; @@ -126,13 +126,20 @@ public class AutoInstallsLayout { private final long[] mTemp = new long[2]; private final ContentValues mValues; - private final String mRootTag; + protected final String mRootTag; protected SQLiteDatabase mDb; public AutoInstallsLayout(Context context, AppWidgetHost appWidgetHost, LayoutParserCallback callback, Resources res, int layoutId, String rootTag) { + this(context, appWidgetHost, callback, res, layoutId, rootTag, + LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile().hotseatAllAppsRank); + } + + public AutoInstallsLayout(Context context, AppWidgetHost appWidgetHost, + LayoutParserCallback callback, Resources res, + int layoutId, String rootTag, int hotseatAllAppsRank) { mContext = context; mAppWidgetHost = appWidgetHost; mCallback = callback; @@ -143,8 +150,7 @@ public class AutoInstallsLayout { mSourceRes = res; mLayoutId = layoutId; - mHotseatAllAppsRank = LauncherAppState.getInstance() - .getDynamicGrid().getDeviceProfile().hotseatAllAppsRank; + mHotseatAllAppsRank = hotseatAllAppsRank; } /** diff --git a/src/com/android/launcher3/CommonAppTypeParser.java b/src/com/android/launcher3/CommonAppTypeParser.java new file mode 100644 index 000000000..fe2fbd75f --- /dev/null +++ b/src/com/android/launcher3/CommonAppTypeParser.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3; + +import android.content.ContentValues; +import android.content.Context; +import android.content.Intent; +import android.content.res.XmlResourceParser; +import android.database.sqlite.SQLiteDatabase; +import android.util.Log; + +import com.android.launcher3.AutoInstallsLayout.LayoutParserCallback; +import com.android.launcher3.LauncherSettings.Favorites; +import com.android.launcher3.backup.BackupProtos.Favorite; + +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; + +/** + * A class that parses content values corresponding to some common app types. + */ +public class CommonAppTypeParser implements LayoutParserCallback { + private static final String TAG = "CommonAppTypeParser"; + + // Including TARGET_NONE + public static final int SUPPORTED_TYPE_COUNT = 7; + + private static final int RESTORE_FLAG_BIT_SHIFT = 4; + + + private final long mItemId; + private final int mResId; + private final Context mContext; + + ContentValues parsedValues; + Intent parsedIntent; + String parsedTitle; + + public CommonAppTypeParser(long itemId, int itemType, Context context) { + mItemId = itemId; + mContext = context; + mResId = getResourceForItemType(itemType); + } + + @Override + public long generateNewItemId() { + return mItemId; + } + + @Override + public long insertAndCheck(SQLiteDatabase db, ContentValues values) { + parsedValues = values; + + // Remove unwanted values + values.put(Favorites.ICON_TYPE, (Integer) null); + values.put(Favorites.ICON_PACKAGE, (String) null); + values.put(Favorites.ICON_RESOURCE, (String) null); + values.put(Favorites.ICON, (byte[]) null); + return 1; + } + + /** + * Tries to find a suitable app to the provided app type. + */ + public boolean findDefaultApp() { + if (mResId == 0) { + return false; + } + + parsedIntent = null; + parsedValues = null; + new MyLayoutParser().parseValues(); + return (parsedValues != null) && (parsedIntent != null); + } + + private class MyLayoutParser extends DefaultLayoutParser { + + public MyLayoutParser() { + super(mContext, null, CommonAppTypeParser.this, + mContext.getResources(), mResId, TAG_RESOLVE, 0); + } + + @Override + protected long addShortcut(String title, Intent intent, int type) { + if (type == Favorites.ITEM_TYPE_APPLICATION) { + parsedIntent = intent; + parsedTitle = title; + } + return super.addShortcut(title, intent, type); + } + + public void parseValues() { + XmlResourceParser parser = mSourceRes.getXml(mLayoutId); + try { + beginDocument(parser, mRootTag); + new ResolveParser().parseAndAdd(parser); + } catch (IOException | XmlPullParserException e) { + Log.e(TAG, "Unable to parse default app info", e); + } + parser.close(); + } + } + + public static int getResourceForItemType(int type) { + switch (type) { + case Favorite.TARGET_PHONE: + return R.xml.app_target_phone; + + case Favorite.TARGET_MESSENGER: + return R.xml.app_target_messenger; + + case Favorite.TARGET_EMAIL: + return R.xml.app_target_email; + + case Favorite.TARGET_BROWSER: + return R.xml.app_target_browser; + + case Favorite.TARGET_GALLERY: + return R.xml.app_target_gallery; + + case Favorite.TARGET_CAMERA: + return R.xml.app_target_camera; + + default: + return 0; + } + } + + public static int encodeItemTypeToFlag(int itemType) { + return itemType << RESTORE_FLAG_BIT_SHIFT; + } + + public static int decodeItemTypeFromFlag(int flag) { + return (flag & ShortcutInfo.FLAG_RESTORED_APP_TYPE) >> RESTORE_FLAG_BIT_SHIFT; + } + +} diff --git a/src/com/android/launcher3/DefaultLayoutParser.java b/src/com/android/launcher3/DefaultLayoutParser.java index 986ae81f5..6c3008b6e 100644 --- a/src/com/android/launcher3/DefaultLayoutParser.java +++ b/src/com/android/launcher3/DefaultLayoutParser.java @@ -28,15 +28,15 @@ import java.util.List; public class DefaultLayoutParser extends AutoInstallsLayout { private static final String TAG = "DefaultLayoutParser"; - private static final String TAG_RESOLVE = "resolve"; + protected static final String TAG_RESOLVE = "resolve"; private static final String TAG_FAVORITES = "favorites"; - private static final String TAG_FAVORITE = "favorite"; + protected static final String TAG_FAVORITE = "favorite"; private static final String TAG_APPWIDGET = "appwidget"; private static final String TAG_SHORTCUT = "shortcut"; private static final String TAG_FOLDER = "folder"; private static final String TAG_PARTNER_FOLDER = "partner-folder"; - private static final String ATTR_URI = "uri"; + protected static final String ATTR_URI = "uri"; private static final String ATTR_CONTAINER = "container"; private static final String ATTR_SCREEN = "screen"; private static final String ATTR_FOLDER_ITEMS = "folderItems"; @@ -44,7 +44,12 @@ public class DefaultLayoutParser extends AutoInstallsLayout { public DefaultLayoutParser(Context context, AppWidgetHost appWidgetHost, LayoutParserCallback callback, Resources sourceRes, int layoutId) { super(context, appWidgetHost, callback, sourceRes, layoutId, TAG_FAVORITES); - Log.e(TAG, "Default layout parser initialized"); + } + + public DefaultLayoutParser(Context context, AppWidgetHost appWidgetHost, + LayoutParserCallback callback, Resources sourceRes, int layoutId, String rootTag, + int hotseatAllAppsRank) { + super(context, appWidgetHost, callback, sourceRes, layoutId, rootTag, hotseatAllAppsRank); } @Override @@ -196,7 +201,7 @@ public class DefaultLayoutParser extends AutoInstallsLayout { /** * Contains a list of <favorite> nodes, and accepts the first successfully parsed node. */ - private class ResolveParser implements TagParser { + protected class ResolveParser implements TagParser { private final AppShortcutWithUriParser mChildParser = new AppShortcutWithUriParser(); diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index 66b656882..69254776c 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -21,12 +21,13 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; +import android.annotation.TargetApi; import android.content.Context; import android.content.res.Resources; import android.graphics.PointF; import android.graphics.Rect; +import android.os.Build; import android.os.SystemClock; -import android.support.v4.widget.AutoScrollHelper; import android.text.InputType; import android.text.Selection; import android.text.Spannable; @@ -123,8 +124,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList private boolean mDestroyed; - private AutoScrollHelper mAutoScrollHelper; - private Runnable mDeferredAction; private boolean mDeferDropAfterUninstall; private boolean mUninstallSuccessful; @@ -208,7 +207,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList mFolderName.setSelectAllOnFocus(true); mFolderName.setInputType(mFolderName.getInputType() | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS | InputType.TYPE_TEXT_FLAG_CAP_WORDS); - mAutoScrollHelper = new FolderAutoScrollHelper(mScrollView); } private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() { @@ -700,6 +698,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList } } + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) public boolean isLayoutRtl() { return (getLayoutDirection() == LAYOUT_DIRECTION_RTL); } @@ -715,29 +714,19 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList final MotionEvent translatedEv = MotionEvent.obtain( downTime, downTime, MotionEvent.ACTION_MOVE, d.x, d.y, 0); - if (!mAutoScrollHelper.isEnabled()) { - mAutoScrollHelper.setEnabled(true); - } - - final boolean handled = mAutoScrollHelper.onTouch(this, translatedEv); translatedEv.recycle(); - - if (handled) { + mTargetCell = mContent.findNearestArea( + (int) r[0], (int) r[1] + scrollOffset, 1, 1, mTargetCell); + if (isLayoutRtl()) { + mTargetCell[0] = mContent.getCountX() - mTargetCell[0] - 1; + } + if (mTargetCell[0] != mPreviousTargetCell[0] + || mTargetCell[1] != mPreviousTargetCell[1]) { mReorderAlarm.cancelAlarm(); - } else { - mTargetCell = mContent.findNearestArea( - (int) r[0], (int) r[1] + scrollOffset, 1, 1, mTargetCell); - if (isLayoutRtl()) { - mTargetCell[0] = mContent.getCountX() - mTargetCell[0] - 1; - } - if (mTargetCell[0] != mPreviousTargetCell[0] - || mTargetCell[1] != mPreviousTargetCell[1]) { - mReorderAlarm.cancelAlarm(); - mReorderAlarm.setOnAlarmListener(mReorderAlarmListener); - mReorderAlarm.setAlarm(REORDER_DELAY); - mPreviousTargetCell[0] = mTargetCell[0]; - mPreviousTargetCell[1] = mTargetCell[1]; - } + mReorderAlarm.setOnAlarmListener(mReorderAlarmListener); + mReorderAlarm.setAlarm(REORDER_DELAY); + mPreviousTargetCell[0] = mTargetCell[0]; + mPreviousTargetCell[1] = mTargetCell[1]; } } @@ -783,8 +772,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList } public void onDragExit(DragObject d) { - // Exiting folder; stop the auto scroller. - mAutoScrollHelper.setEnabled(false); // We only close the folder if this is a true drag exit, ie. not because // a drop has occurred above the folder. if (!d.dragComplete) { diff --git a/src/com/android/launcher3/FolderAutoScrollHelper.java b/src/com/android/launcher3/FolderAutoScrollHelper.java deleted file mode 100644 index 40e888464..000000000 --- a/src/com/android/launcher3/FolderAutoScrollHelper.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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. - */ - -package com.android.launcher3; - -import android.support.v4.widget.AutoScrollHelper; -import android.widget.ScrollView; - -/** - * An implementation of {@link AutoScrollHelper} that knows how to scroll - * through a {@link Folder}. - */ -public class FolderAutoScrollHelper extends AutoScrollHelper { - private static final float MAX_SCROLL_VELOCITY = 1500f; - - private final ScrollView mTarget; - - public FolderAutoScrollHelper(ScrollView target) { - super(target); - - mTarget = target; - - setActivationDelay(0); - setEdgeType(EDGE_TYPE_INSIDE_EXTEND); - setExclusive(true); - setMaximumVelocity(MAX_SCROLL_VELOCITY, MAX_SCROLL_VELOCITY); - setRampDownDuration(0); - setRampUpDuration(0); - } - - @Override - public void scrollTargetBy(int deltaX, int deltaY) { - mTarget.scrollBy(deltaX, deltaY); - } - - @Override - public boolean canTargetScrollHorizontally(int direction) { - // List do not scroll horizontally. - return false; - } - - @Override - public boolean canTargetScrollVertically(int direction) { - return mTarget.canScrollVertically(direction); - } -}
\ No newline at end of file diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 5d8e136cd..61915b755 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -115,9 +115,6 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.text.DateFormat; import java.util.ArrayList; import java.util.Collection; @@ -757,7 +754,7 @@ public class Launcher extends Activity mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded, ON_ACTIVITY_RESULT_ANIMATION_DELAY, false); } else if (resultCode == RESULT_OK) { - addAppWidgetImpl(appWidgetId, (PendingAddWidgetInfo) mPendingAddInfo, null, + addAppWidgetImpl(appWidgetId, mPendingAddInfo, null, mPendingAddWidgetInfo, ON_ACTIVITY_RESULT_ANIMATION_DELAY); } return; @@ -1686,40 +1683,19 @@ public class Launcher extends Activity * Sets up transparent navigation and status bars in LMP. * This method is a no-op for other platform versions. */ - @TargetApi(19) + @TargetApi(Build.VERSION_CODES.LOLLIPOP) private void setupTransparentSystemBarsForLmp() { - // TODO(sansid): use the APIs directly when compiling against L sdk. - // Currently we use reflection to access the flags and the API to set the transparency - // on the System bars. if (Utilities.isLmpOrAbove()) { - try { - getWindow().getAttributes().systemUiVisibility |= - (View.SYSTEM_UI_FLAG_LAYOUT_STABLE - | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); - getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS - | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); - Field drawsSysBackgroundsField = WindowManager.LayoutParams.class.getField( - "FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS"); - getWindow().addFlags(drawsSysBackgroundsField.getInt(null)); - - Method setStatusBarColorMethod = - Window.class.getDeclaredMethod("setStatusBarColor", int.class); - Method setNavigationBarColorMethod = - Window.class.getDeclaredMethod("setNavigationBarColor", int.class); - setStatusBarColorMethod.invoke(getWindow(), Color.TRANSPARENT); - setNavigationBarColorMethod.invoke(getWindow(), Color.TRANSPARENT); - } catch (NoSuchFieldException e) { - Log.w(TAG, "NoSuchFieldException while setting up transparent bars"); - } catch (NoSuchMethodException ex) { - Log.w(TAG, "NoSuchMethodException while setting up transparent bars"); - } catch (IllegalAccessException e) { - Log.w(TAG, "IllegalAccessException while setting up transparent bars"); - } catch (IllegalArgumentException e) { - Log.w(TAG, "IllegalArgumentException while setting up transparent bars"); - } catch (InvocationTargetException e) { - Log.w(TAG, "InvocationTargetException while setting up transparent bars"); - } finally {} + 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); } } @@ -2243,12 +2219,12 @@ public class Launcher extends Activity mPendingAddInfo.dropPos = null; } - void addAppWidgetImpl(final int appWidgetId, final PendingAddWidgetInfo info, final + void addAppWidgetImpl(final int appWidgetId, final ItemInfo info, final AppWidgetHostView boundWidget, final LauncherAppWidgetProviderInfo appWidgetInfo) { addAppWidgetImpl(appWidgetId, info, boundWidget, appWidgetInfo, 0); } - void addAppWidgetImpl(final int appWidgetId, final PendingAddWidgetInfo info, + void addAppWidgetImpl(final int appWidgetId, final ItemInfo info, final AppWidgetHostView boundWidget, final LauncherAppWidgetProviderInfo appWidgetInfo, int delay) { if (appWidgetInfo.configure != null) { diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java index 32bea5baa..353bf3f00 100644 --- a/src/com/android/launcher3/LauncherBackupHelper.java +++ b/src/com/android/launcher3/LauncherBackupHelper.java @@ -19,13 +19,16 @@ import android.app.backup.BackupDataInputStream; import android.app.backup.BackupDataOutput; import android.app.backup.BackupHelper; import android.app.backup.BackupManager; -import android.appwidget.AppWidgetProviderInfo; import android.content.ComponentName; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ResolveInfo; +import android.content.res.XmlResourceParser; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; @@ -51,6 +54,9 @@ import com.android.launcher3.compat.UserManagerCompat; 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; @@ -58,7 +64,6 @@ import java.io.IOException; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.HashSet; import java.util.zip.CRC32; @@ -138,6 +143,7 @@ public class LauncherBackupHelper implements BackupHelper { private final Context mContext; private final HashSet<String> mExistingKeys; private final ArrayList<Key> mKeys; + private final ItemTypeMatcher[] mItemTypeMatchers; private IconCache mIconCache; private BackupManager mBackupManager; @@ -154,6 +160,7 @@ public class LauncherBackupHelper implements BackupHelper { mExistingKeys = new HashSet<String>(); mKeys = new ArrayList<Key>(); restoreSuccessful = true; + mItemTypeMatchers = new ItemTypeMatcher[CommonAppTypeParser.SUPPORTED_TYPE_COUNT]; } private void dataChanged() { @@ -753,6 +760,17 @@ public class LauncherBackupHelper implements BackupHelper { return checksum.getValue(); } + /** + * @return true if its an hotseat item, that can be replaced during restore. + * TODO: Extend check for folders in hotseat. + */ + private boolean isReplaceableHotseatItem(Favorite favorite) { + return favorite.container == Favorites.CONTAINER_HOTSEAT + && favorite.intent != null + && (favorite.itemType == Favorites.ITEM_TYPE_APPLICATION + || favorite.itemType == Favorites.ITEM_TYPE_SHORTCUT); + } + /** Serialize a Favorite for persistence, including a checksum wrapper. */ private Favorite packFavorite(Cursor c) { Favorite favorite = new Favorite(); @@ -785,9 +803,10 @@ public class LauncherBackupHelper implements BackupHelper { favorite.title = title; } String intentDescription = c.getString(INTENT_INDEX); + Intent intent = null; if (!TextUtils.isEmpty(intentDescription)) { try { - Intent intent = Intent.parseUri(intentDescription, 0); + intent = Intent.parseUri(intentDescription, 0); intent.removeExtra(ItemInfo.EXTRA_PROFILE); favorite.intent = intent.toUri(0); } catch (URISyntaxException e) { @@ -803,6 +822,31 @@ public class LauncherBackupHelper implements BackupHelper { } } + if (isReplaceableHotseatItem(favorite)) { + if (intent != null && intent.getComponent() != null) { + PackageManager pm = mContext.getPackageManager(); + ActivityInfo activity = null;; + try { + activity = pm.getActivityInfo(intent.getComponent(), 0); + } catch (NameNotFoundException e) { + Log.e(TAG, "Target not found", e); + } + if (activity == null) { + return favorite; + } + for (int i = 0; i < mItemTypeMatchers.length; i++) { + if (mItemTypeMatchers[i] == null) { + mItemTypeMatchers[i] = new ItemTypeMatcher( + CommonAppTypeParser.getResourceForItemType(i)); + } + if (mItemTypeMatchers[i].matches(activity, pm)) { + favorite.targetType = i; + break; + } + } + } + } + return favorite; } @@ -810,6 +854,7 @@ public class LauncherBackupHelper implements BackupHelper { private ContentValues unpackFavorite(byte[] buffer, int dataSize) throws IOException { Favorite favorite = unpackProto(new Favorite(), buffer, dataSize); + ContentValues values = new ContentValues(); values.put(Favorites._ID, favorite.id); values.put(Favorites.SCREEN, favorite.screen); @@ -860,8 +905,17 @@ public class LauncherBackupHelper implements BackupHelper { throw new InvalidBackupException("Widget not in screen bounds, aborting restore"); } } else { - // Let LauncherModel know we've been here. - values.put(LauncherSettings.Favorites.RESTORED, 1); + // Check if it is an hotseat item, that can be replaced. + if (isReplaceableHotseatItem(favorite) + && favorite.targetType != Favorite.TARGET_NONE + && favorite.targetType < CommonAppTypeParser.SUPPORTED_TYPE_COUNT) { + Log.e(TAG, "Added item type flag"); + values.put(LauncherSettings.Favorites.RESTORED, + 1 | CommonAppTypeParser.encodeItemTypeToFlag(favorite.targetType)); + } else { + // Let LauncherModel know we've been here. + values.put(LauncherSettings.Favorites.RESTORED, 1); + } // Verify placement if (favorite.container == Favorites.CONTAINER_HOTSEAT) { @@ -1128,6 +1182,9 @@ public class LauncherBackupHelper implements BackupHelper { } private class InvalidBackupException extends IOException { + + private static final long serialVersionUID = 8931456637211665082L; + private InvalidBackupException(Throwable cause) { super(cause); } @@ -1136,4 +1193,54 @@ public class LauncherBackupHelper implements BackupHelper { super(reason); } } + + /** + * A class to check if an activity can handle one of the intents from a list of + * predefined intents. + */ + private class ItemTypeMatcher { + + private final ArrayList<Intent> mIntents; + + ItemTypeMatcher(int xml_res) { + mIntents = xml_res == 0 ? new ArrayList<Intent>() : parseIntents(xml_res); + } + + private ArrayList<Intent> parseIntents(int xml_res) { + ArrayList<Intent> intents = new ArrayList<Intent>(); + XmlResourceParser parser = mContext.getResources().getXml(xml_res); + try { + DefaultLayoutParser.beginDocument(parser, DefaultLayoutParser.TAG_RESOLVE); + final int depth = parser.getDepth(); + int type; + while (((type = parser.next()) != XmlPullParser.END_TAG || + parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) { + if (type != XmlPullParser.START_TAG) { + continue; + } else if (DefaultLayoutParser.TAG_FAVORITE.equals(parser.getName())) { + final String uri = DefaultLayoutParser.getAttributeValue( + parser, DefaultLayoutParser.ATTR_URI); + intents.add(Intent.parseUri(uri, 0)); + } + } + } catch (URISyntaxException | XmlPullParserException | IOException e) { + Log.e(TAG, "Unable to parse " + xml_res, e); + } finally { + parser.close(); + } + return intents; + } + + public boolean matches(ActivityInfo activity, PackageManager pm) { + for (Intent intent : mIntents) { + intent.setPackage(activity.packageName); + ResolveInfo info = pm.resolveActivity(intent, 0); + if (info != null && (info.activityInfo.name.equals(activity.name) + || info.activityInfo.name.equals(activity.targetActivity))) { + return true; + } + } + return false; + } + } } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 7b5f8466a..78790688a 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -75,7 +75,6 @@ import java.util.Iterator; import java.util.List; import java.util.Map.Entry; import java.util.Set; -import java.util.TreeMap; /** * Maintains in-memory state of the Launcher. It is expected that there should be only one @@ -491,13 +490,7 @@ public class LauncherModel extends BroadcastReceiver Runnable r = new Runnable() { public void run() { final ArrayList<Long> addedWorkspaceScreensFinal = new ArrayList<Long>(); - - ArrayList<Long> workspaceScreens = new ArrayList<Long>(); - TreeMap<Integer, Long> orderedScreens = loadWorkspaceScreensDb(context); - for (Integer i : orderedScreens.keySet()) { - long screenId = orderedScreens.get(i); - workspaceScreens.add(screenId); - } + ArrayList<Long> workspaceScreens = loadWorkspaceScreensDb(context); // Find appropriate space for the item. Pair<Long, int[]> coords = findSpaceForItem(context, preferredScreen, @@ -549,13 +542,7 @@ public class LauncherModel extends BroadcastReceiver // Get the list of workspace screens. We need to append to this list and // can not use sBgWorkspaceScreens because loadWorkspace() may not have been // called. - ArrayList<Long> workspaceScreens = new ArrayList<Long>(); - TreeMap<Integer, Long> orderedScreens = loadWorkspaceScreensDb(context); - for (Integer i : orderedScreens.keySet()) { - long screenId = orderedScreens.get(i); - workspaceScreens.add(screenId); - } - + ArrayList<Long> workspaceScreens = loadWorkspaceScreensDb(context); synchronized(sBgLock) { for (ItemInfo item : workspaceApps) { if (!allowDuplicate) { @@ -1443,40 +1430,31 @@ public class LauncherModel extends BroadcastReceiver } } - /** Loads the workspace screens db into a map of Rank -> ScreenId */ - private static TreeMap<Integer, Long> loadWorkspaceScreensDb(Context context) { + /** + * Loads the workspace screen ids in an ordered list. + */ + private static ArrayList<Long> loadWorkspaceScreensDb(Context context) { final ContentResolver contentResolver = context.getContentResolver(); final Uri screensUri = LauncherSettings.WorkspaceScreens.CONTENT_URI; - final Cursor sc = contentResolver.query(screensUri, null, null, null, null); - TreeMap<Integer, Long> orderedScreens = new TreeMap<Integer, Long>(); + // Get screens ordered by rank. + final Cursor sc = contentResolver.query(screensUri, null, null, null, + LauncherSettings.WorkspaceScreens.SCREEN_RANK); + ArrayList<Long> screenIds = new ArrayList<Long>(); try { - final int idIndex = sc.getColumnIndexOrThrow( - LauncherSettings.WorkspaceScreens._ID); - final int rankIndex = sc.getColumnIndexOrThrow( - LauncherSettings.WorkspaceScreens.SCREEN_RANK); + final int idIndex = sc.getColumnIndexOrThrow(LauncherSettings.WorkspaceScreens._ID); while (sc.moveToNext()) { try { - long screenId = sc.getLong(idIndex); - int rank = sc.getInt(rankIndex); - orderedScreens.put(rank, screenId); + screenIds.add(sc.getLong(idIndex)); } catch (Exception e) { - Launcher.addDumpLog(TAG, "Desktop items loading interrupted - invalid screens: " + e, true); + Launcher.addDumpLog(TAG, "Desktop items loading interrupted" + + " - invalid screens: " + e, true); } } } finally { sc.close(); } - - // Log to disk - Launcher.addDumpLog(TAG, "11683562 - loadWorkspaceScreensDb()", true); - ArrayList<String> orderedScreensPairs= new ArrayList<String>(); - for (Integer i : orderedScreens.keySet()) { - orderedScreensPairs.add("{ " + i + ": " + orderedScreens.get(i) + " }"); - } - Launcher.addDumpLog(TAG, "11683562 - screens: " + - TextUtils.join(", ", orderedScreensPairs), true); - return orderedScreens; + return screenIds; } public boolean isAllAppsLoaded() { @@ -1977,6 +1955,7 @@ public class LauncherModel extends BroadcastReceiver user = mUserManager.getUserForSerialNumber(serialNumber); int promiseType = c.getInt(restoredIndex); int disabledState = 0; + boolean itemReplaced = false; if (user == null) { // User has been deleted remove the item. itemsToRemove.add(id); @@ -2008,9 +1987,7 @@ public class LauncherModel extends BroadcastReceiver ContentValues values = new ContentValues(); values.put(LauncherSettings.Favorites.INTENT, intent.toUri(0)); - String where = BaseColumns._ID + "= ?"; - String[] args = {Long.toString(id)}; - contentResolver.update(contentUri, values, where, args); + updateItem(id, values); } } @@ -2040,10 +2017,27 @@ public class LauncherModel extends BroadcastReceiver ContentValues values = new ContentValues(); values.put(LauncherSettings.Favorites.RESTORED, promiseType); - String where = BaseColumns._ID + "= ?"; - String[] args = {Long.toString(id)}; - contentResolver.update(contentUri, values, where, args); - + updateItem(id, values); + } else if ((promiseType & ShortcutInfo.FLAG_RESTORED_APP_TYPE) != 0) { + // This is a common app. Try to replace this. + int appType = CommonAppTypeParser.decodeItemTypeFromFlag(promiseType); + CommonAppTypeParser parser = new CommonAppTypeParser(id, appType, context); + if (parser.findDefaultApp()) { + // Default app found. Replace it. + intent = parser.parsedIntent; + cn = intent.getComponent(); + ContentValues values = parser.parsedValues; + values.put(LauncherSettings.Favorites.RESTORED, 0); + updateItem(id, values); + restored = false; + itemReplaced = true; + + } else if (REMOVE_UNRESTORED_ICONS) { + Launcher.addDumpLog(TAG, + "Unrestored package removed: " + cn, true); + itemsToRemove.add(id); + continue; + } } else if (REMOVE_UNRESTORED_ICONS) { Launcher.addDumpLog(TAG, "Unrestored package removed: " + cn, true); @@ -2089,7 +2083,16 @@ public class LauncherModel extends BroadcastReceiver continue; } - if (restored) { + if (itemReplaced) { + if (user.equals(UserHandleCompat.myUserHandle())) { + info = getShortcutInfo(manager, intent, user, context, null, + iconIndex, titleIndex, mLabelCache, false); + } else { + // Don't replace items for other profiles. + itemsToRemove.add(id); + continue; + } + } else if (restored) { if (user.equals(UserHandleCompat.myUserHandle())) { Launcher.addDumpLog(TAG, "constructing info for partially restored package", @@ -2323,9 +2326,7 @@ public class LauncherModel extends BroadcastReceiver providerName); values.put(LauncherSettings.Favorites.RESTORED, appWidgetInfo.restoreStatus); - String where = BaseColumns._ID + "= ?"; - String[] args = {Long.toString(id)}; - contentResolver.update(contentUri, values, where, args); + updateItem(id, values); } } sBgItemsIdMap.put(appWidgetInfo.id, appWidgetInfo); @@ -2422,10 +2423,7 @@ public class LauncherModel extends BroadcastReceiver } LauncherAppState.getLauncherProvider().updateMaxItemId(maxItemId); } else { - TreeMap<Integer, Long> orderedScreens = loadWorkspaceScreensDb(mContext); - for (Integer i : orderedScreens.keySet()) { - sBgWorkspaceScreens.add(orderedScreens.get(i)); - } + sBgWorkspaceScreens.addAll(loadWorkspaceScreensDb(mContext)); // Log to disk Launcher.addDumpLog(TAG, "11683562 - sBgWorkspaceScreens: " + TextUtils.join(", ", sBgWorkspaceScreens), true); @@ -2480,6 +2478,17 @@ public class LauncherModel extends BroadcastReceiver return loadedOldDb; } + /** + * Partially updates the item without any notification. Must be called on the worker thread. + */ + private void updateItem(long itemId, ContentValues update) { + mContext.getContentResolver().update( + LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, + update, + BaseColumns._ID + "= ?", + new String[]{Long.toString(itemId)}); + } + /** Filters the set of items who are directly or indirectly (via another container) on the * specified screen. */ private void filterCurrentWorkspaceItems(long currentScreenId, diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index 4bdbdf407..01c603d3a 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -57,7 +57,6 @@ public class LauncherProvider extends ContentProvider { private static final String TAG = "Launcher.LauncherProvider"; private static final boolean LOGD = false; - private static final int MIN_DATABASE_VERSION = 12; private static final int DATABASE_VERSION = 21; static final String OLD_AUTHORITY = "com.android.launcher2.settings"; @@ -492,117 +491,100 @@ public class LauncherProvider extends ContentProvider { @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { if (LOGD) Log.d(TAG, "onUpgrade triggered: " + oldVersion); - - int version = oldVersion; - if (version < MIN_DATABASE_VERSION) { - // The version cannot be lower that this, as Launcher3 never supported a lower + switch (oldVersion) { + // The version cannot be lower that 12, as Launcher3 never supported a lower // version of the DB. - createEmptyDB(db); - version = DATABASE_VERSION; - } - - if (version < 13) { - // With the new shrink-wrapped and re-orderable workspaces, it makes sense - // to persist workspace screens and their relative order. - mMaxScreenId = 0; - - addWorkspacesTable(db); - version = 13; - } - - if (version < 14) { - db.beginTransaction(); - try { - // Insert new column for holding widget provider name - db.execSQL("ALTER TABLE favorites " + - "ADD COLUMN appWidgetProvider TEXT;"); - db.setTransactionSuccessful(); - version = 14; - } catch (SQLException ex) { - // Old version remains, which means we wipe old data - Log.e(TAG, ex.getMessage(), ex); - } finally { - db.endTransaction(); + case 12: { + // With the new shrink-wrapped and re-orderable workspaces, it makes sense + // to persist workspace screens and their relative order. + mMaxScreenId = 0; + addWorkspacesTable(db); } - } - - if (version < 15) { - db.beginTransaction(); - try { - // Insert new column for holding update timestamp - db.execSQL("ALTER TABLE favorites " + - "ADD COLUMN modified INTEGER NOT NULL DEFAULT 0;"); - db.execSQL("ALTER TABLE workspaceScreens " + - "ADD COLUMN modified INTEGER NOT NULL DEFAULT 0;"); - db.setTransactionSuccessful(); - version = 15; - } catch (SQLException ex) { - // Old version remains, which means we wipe old data - Log.e(TAG, ex.getMessage(), ex); - } finally { - db.endTransaction(); + case 13: { + db.beginTransaction(); + try { + // Insert new column for holding widget provider name + db.execSQL("ALTER TABLE favorites " + + "ADD COLUMN appWidgetProvider TEXT;"); + db.setTransactionSuccessful(); + } catch (SQLException ex) { + Log.e(TAG, ex.getMessage(), ex); + // Old version remains, which means we wipe old data + break; + } finally { + db.endTransaction(); + } } - } - - - if (version < 16) { - db.beginTransaction(); - try { - // Insert new column for holding restore status - db.execSQL("ALTER TABLE favorites " + - "ADD COLUMN restored INTEGER NOT NULL DEFAULT 0;"); - db.setTransactionSuccessful(); - version = 16; - } catch (SQLException ex) { - // Old version remains, which means we wipe old data - Log.e(TAG, ex.getMessage(), ex); - } finally { - db.endTransaction(); + case 14: { + db.beginTransaction(); + try { + // Insert new column for holding update timestamp + db.execSQL("ALTER TABLE favorites " + + "ADD COLUMN modified INTEGER NOT NULL DEFAULT 0;"); + db.execSQL("ALTER TABLE workspaceScreens " + + "ADD COLUMN modified INTEGER NOT NULL DEFAULT 0;"); + db.setTransactionSuccessful(); + } catch (SQLException ex) { + Log.e(TAG, ex.getMessage(), ex); + // Old version remains, which means we wipe old data + break; + } finally { + db.endTransaction(); + } } - } - - if (version < 17) { - // We use the db version upgrade here to identify users who may not have seen - // clings yet (because they weren't available), but for whom the clings are now - // available (tablet users). Because one of the possible cling flows (migration) - // is very destructive (wipes out workspaces), we want to prevent this from showing - // until clear data. We do so by marking that the clings have been shown. - LauncherClings.synchonouslyMarkFirstRunClingDismissed(mContext); - version = 17; - } - - if (version < 18) { - // No-op - version = 18; - } - - if (version < 19) { - // Due to a data loss bug, some users may have items associated with screen ids - // which no longer exist. Since this can cause other problems, and since the user - // will never see these items anyway, we use database upgrade as an opportunity to - // clean things up. - removeOrphanedItems(db); - version = 19; - } - - if (version < 20) { - // Add userId column - if (addProfileColumn(db)) { - version = 20; + case 15: { + db.beginTransaction(); + try { + // Insert new column for holding restore status + db.execSQL("ALTER TABLE favorites " + + "ADD COLUMN restored INTEGER NOT NULL DEFAULT 0;"); + db.setTransactionSuccessful(); + } catch (SQLException ex) { + Log.e(TAG, ex.getMessage(), ex); + // Old version remains, which means we wipe old data + break; + } finally { + db.endTransaction(); + } } - // else old version remains, which means we wipe old data - } - - if (version < 21) { - if (updateFolderItemsRank(db, true)) { - version = 21; + case 16: { + // We use the db version upgrade here to identify users who may not have seen + // clings yet (because they weren't available), but for whom the clings are now + // available (tablet users). Because one of the possible cling flows (migration) + // is very destructive (wipes out workspaces), we want to prevent this from showing + // until clear data. We do so by marking that the clings have been shown. + LauncherClings.synchonouslyMarkFirstRunClingDismissed(mContext); + } + case 17: { + // No-op + } + case 18: { + // Due to a data loss bug, some users may have items associated with screen ids + // which no longer exist. Since this can cause other problems, and since the user + // will never see these items anyway, we use database upgrade as an opportunity to + // clean things up. + removeOrphanedItems(db); + } + case 19: { + // Add userId column + if (!addProfileColumn(db)) { + // Old version remains, which means we wipe old data + break; + } + } + case 20: + if (!updateFolderItemsRank(db, true)) { + break; + } + case 21: { + // DB Upgraded successfully + return; } } - if (version != DATABASE_VERSION) { - Log.w(TAG, "Destroying all old data."); - createEmptyDB(db); - } + // DB was not upgraded + Log.w(TAG, "Destroying all old data."); + createEmptyDB(db); } @Override diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java index 01f79314e..15d6a3e1c 100644 --- a/src/com/android/launcher3/ShortcutInfo.java +++ b/src/com/android/launcher3/ShortcutInfo.java @@ -46,18 +46,24 @@ public class ShortcutInfo extends ItemInfo { * be present along with {@link #FLAG_RESTORED_ICON}, and is set during default layout * parsing. */ - public static final int FLAG_AUTOINTALL_ICON = 2; + public static final int FLAG_AUTOINTALL_ICON = 2; //0B10; /** * 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; + public static final int FLAG_INSTALL_SESSION_ACTIVE = 4; // 0B100; /** * Indicates that the widget restore has started. */ - public static final int FLAG_RESTORE_STARTED = 8; + public static final int FLAG_RESTORE_STARTED = 8; //0B1000; + + /** + * Indicates if it represents a common type mentioned in {@link CommonAppTypeParser}. + * Upto 15 different types supported. + */ + public static final int FLAG_RESTORED_APP_TYPE = 0B0011110000; /** * The intent used to start the application. diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 44d77571b..66e370b08 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -48,7 +48,6 @@ import android.os.AsyncTask; import android.os.Handler; import android.os.IBinder; import android.os.Parcelable; -import android.support.v4.view.ViewCompat; import android.util.AttributeSet; import android.util.Log; import android.util.SparseArray; @@ -492,7 +491,7 @@ public class Workspace extends SmoothPagedView CellLayout cl = ((CellLayout) child); cl.setOnInterceptTouchListener(this); cl.setClickable(true); - cl.setImportantForAccessibility(ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO); + cl.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); super.onChildViewAdded(parent, child); } @@ -2210,8 +2209,8 @@ public class Workspace extends SmoothPagedView private void updateAccessibilityFlags() { int accessible = mState == State.NORMAL ? - ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES : - ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS; + IMPORTANT_FOR_ACCESSIBILITY_YES : + IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS; setImportantForAccessibility(accessible); } |