1 /*
2  * 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 
17 package com.android.launcher3.util
18 
19 import android.appwidget.AppWidgetHostView
20 import android.content.Context
21 import android.view.LayoutInflater
22 import android.view.View
23 import android.view.View.OnClickListener
24 import android.view.View.OnFocusChangeListener
25 import android.view.ViewGroup
26 import com.android.launcher3.BubbleTextView
27 import com.android.launcher3.LauncherSettings.Favorites
28 import com.android.launcher3.R
29 import com.android.launcher3.apppairs.AppPairIcon
30 import com.android.launcher3.folder.FolderIcon
31 import com.android.launcher3.model.ModelWriter
32 import com.android.launcher3.model.data.AppPairInfo
33 import com.android.launcher3.model.data.FolderInfo
34 import com.android.launcher3.model.data.ItemInfo
35 import com.android.launcher3.model.data.LauncherAppWidgetInfo
36 import com.android.launcher3.model.data.WorkspaceItemFactory
37 import com.android.launcher3.model.data.WorkspaceItemInfo
38 import com.android.launcher3.views.ActivityContext
39 import com.android.launcher3.widget.LauncherWidgetHolder
40 import com.android.launcher3.widget.PendingAppWidgetHostView
41 import com.android.launcher3.widget.WidgetInflater
42 
43 /** Utility class to inflate View for a model item */
44 class ItemInflater<T>(
45     private val context: T,
46     private val widgetHolder: LauncherWidgetHolder,
47     private val clickListener: OnClickListener,
48     private val focusListener: OnFocusChangeListener,
49     private val defaultParent: ViewGroup
50 ) where T : Context, T : ActivityContext {
51 
52     private val widgetInflater = WidgetInflater(context)
53 
54     @JvmOverloads
inflateItemnull55     fun inflateItem(item: ItemInfo, writer: ModelWriter, nullableParent: ViewGroup? = null): View? {
56         val parent = nullableParent ?: defaultParent
57         when (item.itemType) {
58             Favorites.ITEM_TYPE_APPLICATION,
59             Favorites.ITEM_TYPE_DEEP_SHORTCUT,
60             Favorites.ITEM_TYPE_SEARCH_ACTION -> {
61                 var info =
62                     if (item is WorkspaceItemFactory) {
63                         (item as WorkspaceItemFactory).makeWorkspaceItem(context)
64                     } else {
65                         item as WorkspaceItemInfo
66                     }
67                 if (info.container == Favorites.CONTAINER_PREDICTION) {
68                     // Came from all apps prediction row -- make a copy
69                     info = WorkspaceItemInfo(info)
70                 }
71                 return createShortcut(info, parent)
72             }
73             Favorites.ITEM_TYPE_FOLDER ->
74                 return FolderIcon.inflateFolderAndIcon(
75                     R.layout.folder_icon,
76                     context,
77                     parent,
78                     item as FolderInfo
79                 )
80             Favorites.ITEM_TYPE_APP_PAIR ->
81                 return AppPairIcon.inflateIcon(
82                     R.layout.app_pair_icon,
83                     context,
84                     parent,
85                     item as AppPairInfo,
86                     BubbleTextView.DISPLAY_WORKSPACE
87                 )
88             Favorites.ITEM_TYPE_APPWIDGET,
89             Favorites.ITEM_TYPE_CUSTOM_APPWIDGET ->
90                 return inflateAppWidget(item as LauncherAppWidgetInfo, writer)
91             else -> throw RuntimeException("Invalid Item Type")
92         }
93     }
94 
95     /**
96      * Creates a view representing a shortcut inflated from the specified resource.
97      *
98      * @param parent The group the shortcut belongs to. This is not necessarily the group where the
99      *   shortcut should be added.
100      * @param info The data structure describing the shortcut.
101      * @return A View inflated from layoutResId.
102      */
createShortcutnull103     private fun createShortcut(info: WorkspaceItemInfo, parent: ViewGroup): View {
104         val favorite =
105             LayoutInflater.from(parent.context).inflate(R.layout.app_icon, parent, false)
106                 as BubbleTextView
107         favorite.applyFromWorkspaceItem(info)
108         favorite.setOnClickListener(clickListener)
109         favorite.onFocusChangeListener = focusListener
110         return favorite
111     }
112 
inflateAppWidgetnull113     private fun inflateAppWidget(item: LauncherAppWidgetInfo, writer: ModelWriter): View? {
114         TraceHelper.INSTANCE.beginSection("BIND_WIDGET_id=" + item.appWidgetId)
115         try {
116             val (type, reason, _, isUpdate, widgetInfo) = widgetInflater.inflateAppWidget(item)
117             if (type == WidgetInflater.TYPE_DELETE) {
118                 writer.deleteItemFromDatabase(item, reason)
119                 return null
120             }
121             if (isUpdate) {
122                 writer.updateItemInDatabase(item)
123             }
124             val view =
125                 if (type == WidgetInflater.TYPE_PENDING || widgetInfo == null)
126                     PendingAppWidgetHostView(context, widgetHolder, item, widgetInfo)
127                 else widgetHolder.createView(item.appWidgetId, widgetInfo)
128             prepareAppWidget(view, item)
129             return view
130         } finally {
131             TraceHelper.INSTANCE.endSection()
132         }
133     }
134 
prepareAppWidgetnull135     fun prepareAppWidget(hostView: AppWidgetHostView, item: LauncherAppWidgetInfo) {
136         hostView.tag = item
137         hostView.isFocusable = true
138         hostView.onFocusChangeListener = focusListener
139     }
140 }
141