Replacing hotseat icon to an appropriate system app
> During backupi, store the hotseat target app type, based on some predefined common system apps > During restore, save this app type in the restore flag, if it is a hotseat app > During first launcher load, if an app is not being restored, try to replace it with an appropriate replacement for that type, otherwise delete it. Bug: 18764649 Change-Id: Ic49e40bd707bd8d7de18bbab8b1e58a0a36426a2
This commit is contained in:
parent
3862e4d88b
commit
bb3b02f562
|
@ -72,6 +72,17 @@ message Journal {
|
|||
}
|
||||
|
||||
message Favorite {
|
||||
// Type of the app, this target represents
|
||||
enum TargetType {
|
||||
TARGET_NONE = 0;
|
||||
TARGET_PHONE = 1;
|
||||
TARGET_MESSENGER = 2;
|
||||
TARGET_EMAIL = 3;
|
||||
TARGET_BROWSER = 4;
|
||||
TARGET_GALLERY = 5;
|
||||
TARGET_CAMERA = 6;
|
||||
}
|
||||
|
||||
required int64 id = 1;
|
||||
required int32 itemType = 2;
|
||||
optional string title = 3;
|
||||
|
@ -90,6 +101,7 @@ message Favorite {
|
|||
optional string iconPackage = 16;
|
||||
optional string iconResource = 17;
|
||||
optional bytes icon = 18;
|
||||
optional TargetType targetType = 19 [default = TARGET_NONE];
|
||||
}
|
||||
|
||||
message Screen {
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2015 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.
|
||||
-->
|
||||
|
||||
<resolve xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3" >
|
||||
|
||||
<favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_BROWSER;end" />
|
||||
<favorite launcher:uri="http://www.example.com/" />
|
||||
|
||||
</resolve>
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2015 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.
|
||||
-->
|
||||
|
||||
<resolve xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3" >
|
||||
|
||||
<favorite launcher:uri="#Intent;action=android.media.action.STILL_IMAGE_CAMERA;end" />
|
||||
<favorite launcher:uri="#Intent;action=android.intent.action.CAMERA_BUTTON;end" />
|
||||
|
||||
</resolve>
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2015 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.
|
||||
-->
|
||||
|
||||
<resolve xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3" >
|
||||
|
||||
<favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_EMAIL;end" />
|
||||
<favorite launcher:uri="mailto:" />
|
||||
|
||||
</resolve>
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2015 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.
|
||||
-->
|
||||
|
||||
<resolve xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3" >
|
||||
|
||||
<favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_GALLERY;end" />
|
||||
<favorite launcher:uri="#Intent;type=images/*;end" />
|
||||
|
||||
</resolve>
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2015 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.
|
||||
-->
|
||||
|
||||
<resolve xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3" >
|
||||
|
||||
<favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MESSAGING;end" />
|
||||
<favorite launcher:uri="sms:" />
|
||||
<favorite launcher:uri="smsto:" />
|
||||
<favorite launcher:uri="mms:" />
|
||||
<favorite launcher:uri="mmsto:" />
|
||||
|
||||
</resolve>
|
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2015 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.
|
||||
-->
|
||||
|
||||
<resolve xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3" >
|
||||
|
||||
<favorite launcher:uri="#Intent;action=android.intent.action.DIAL;end" />
|
||||
<favorite launcher:uri="tel:123" />
|
||||
<favorite launcher:uri="#Intent;action=android.intent.action.CALL_BUTTON;end" />
|
||||
|
||||
</resolve>
|
|
@ -112,7 +112,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;
|
||||
|
@ -122,13 +122,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;
|
||||
|
@ -139,8 +146,7 @@ public class AutoInstallsLayout {
|
|||
|
||||
mSourceRes = res;
|
||||
mLayoutId = layoutId;
|
||||
mHotseatAllAppsRank = LauncherAppState.getInstance()
|
||||
.getDynamicGrid().getDeviceProfile().hotseatAllAppsRank;
|
||||
mHotseatAllAppsRank = hotseatAllAppsRank;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -29,16 +29,16 @@ 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 TAG_INCLUDE = "include";
|
||||
|
||||
private static final String ATTR_URI = "uri";
|
||||
protected static final String ATTR_URI = "uri";
|
||||
private static final String ATTR_WORKSPACE = "workspace";
|
||||
private static final String ATTR_CONTAINER = "container";
|
||||
private static final String ATTR_SCREEN = "screen";
|
||||
|
@ -47,7 +47,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
|
||||
|
@ -218,7 +223,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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1955,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);
|
||||
|
@ -1986,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2018,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);
|
||||
|
@ -2067,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",
|
||||
|
@ -2301,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);
|
||||
|
@ -2455,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,
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue