1 /* <lambda>null2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.launcher3.model 17 18 import android.content.ContentValues 19 import android.content.Intent 20 import android.util.Log 21 import com.android.launcher3.LauncherSettings 22 import com.android.launcher3.LauncherSettings.Favorites.CELLX 23 import com.android.launcher3.LauncherSettings.Favorites.CELLY 24 import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION 25 import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET 26 import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR 27 import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_FOLDER 28 import com.android.launcher3.LauncherSettings.Favorites.SCREEN 29 import com.android.launcher3.LauncherSettings.Favorites.SPANX 30 import com.android.launcher3.LauncherSettings.Favorites.SPANY 31 import com.android.launcher3.model.data.ItemInfo 32 import com.android.launcher3.util.ContentWriter 33 import java.util.Objects 34 35 class DbEntry : ItemInfo(), Comparable<DbEntry> { 36 @JvmField var mIntent: String? = null 37 @JvmField var mProvider: String? = null 38 @JvmField var mFolderItems: MutableMap<String, Set<Int>> = HashMap() 39 40 /** Id of the specific widget. */ 41 @JvmField var appWidgetId: Int = NO_ID 42 43 /** Comparator according to the reading order */ 44 override fun compareTo(other: DbEntry): Int { 45 if (screenId != other.screenId) { 46 return screenId.compareTo(other.screenId) 47 } 48 if (cellY != other.cellY) { 49 return cellY.compareTo(other.cellY) 50 } 51 return cellX.compareTo(other.cellX) 52 } 53 54 override fun equals(other: Any?): Boolean { 55 if (this === other) return true 56 if (other !is DbEntry) return false 57 return getEntryMigrationId() == other.getEntryMigrationId() 58 } 59 60 override fun hashCode(): Int = Objects.hash(getEntryMigrationId()) 61 62 /** 63 * Puts the updated DbEntry values into ContentValues which we then use to insert the entry to 64 * the DB. 65 */ 66 fun updateContentValues(values: ContentValues) = 67 values.apply { 68 put(SCREEN, screenId) 69 put(CELLX, cellX) 70 put(CELLY, cellY) 71 put(SPANX, spanX) 72 put(SPANY, spanY) 73 } 74 75 override fun writeToValues(writer: ContentWriter) { 76 super.writeToValues(writer) 77 writer.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId) 78 } 79 80 override fun readFromValues(values: ContentValues) { 81 super.readFromValues(values) 82 appWidgetId = values.getAsInteger(LauncherSettings.Favorites.APPWIDGET_ID) 83 } 84 85 /** 86 * This id is not used in the DB is only used while doing the migration and it identifies an 87 * entry on each workspace. For example two calculator icons would have the same migration id 88 * even thought they have different database ids. 89 */ 90 private fun getEntryMigrationId(): String? { 91 when (itemType) { 92 ITEM_TYPE_FOLDER, 93 ITEM_TYPE_APP_PAIR -> return getFolderMigrationId() 94 ITEM_TYPE_APPWIDGET -> 95 // mProvider is the app the widget belongs to and appWidgetId it's the unique 96 // is of the widget, we need both because if you remove a widget and then add it 97 // again, then it can change and the WidgetProvider would not know the widget. 98 return mProvider + appWidgetId 99 ITEM_TYPE_APPLICATION -> { 100 val intentStr = mIntent?.let { cleanIntentString(it) } 101 try { 102 val i = Intent.parseUri(intentStr, 0) 103 return Objects.requireNonNull(i.component).toString() 104 } catch (e: Exception) { 105 return intentStr 106 } 107 } 108 109 else -> return mIntent?.let { cleanIntentString(it) } 110 } 111 } 112 113 /** 114 * This method should return an id that should be the same for two folders containing the same 115 * elements. 116 */ 117 private fun getFolderMigrationId(): String = 118 mFolderItems.keys 119 .map { intentString: String -> 120 mFolderItems[intentString]?.size.toString() + cleanIntentString(intentString) 121 } 122 .sorted() 123 .joinToString(",") 124 125 /** 126 * This is needed because sourceBounds can change and make the id of two equal items different. 127 */ 128 private fun cleanIntentString(intentStr: String): String { 129 try { 130 return Intent.parseUri(intentStr, 0).apply { sourceBounds = null }.toURI() 131 } catch (e: Exception) { 132 Log.e(TAG, "Unable to parse Intent string: $intentStr", e) 133 return intentStr 134 } 135 } 136 137 companion object { 138 private const val TAG = "DbEntry" 139 } 140 } 141