• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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