1 /* <lambda>null2 * Copyright (C) 2019 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.util 17 18 import android.util.Xml 19 import com.android.launcher3.AutoInstallsLayout.ATTR_CLASS_NAME 20 import com.android.launcher3.AutoInstallsLayout.ATTR_CONTAINER 21 import com.android.launcher3.AutoInstallsLayout.ATTR_PACKAGE_NAME 22 import com.android.launcher3.AutoInstallsLayout.ATTR_RANK 23 import com.android.launcher3.AutoInstallsLayout.ATTR_SCREEN 24 import com.android.launcher3.AutoInstallsLayout.ATTR_SHORTCUT_ID 25 import com.android.launcher3.AutoInstallsLayout.ATTR_SPAN_X 26 import com.android.launcher3.AutoInstallsLayout.ATTR_SPAN_Y 27 import com.android.launcher3.AutoInstallsLayout.ATTR_TITLE 28 import com.android.launcher3.AutoInstallsLayout.ATTR_TITLE_TEXT 29 import com.android.launcher3.AutoInstallsLayout.ATTR_USER_TYPE 30 import com.android.launcher3.AutoInstallsLayout.ATTR_X 31 import com.android.launcher3.AutoInstallsLayout.ATTR_Y 32 import com.android.launcher3.AutoInstallsLayout.TAG_APPWIDGET 33 import com.android.launcher3.AutoInstallsLayout.TAG_AUTO_INSTALL 34 import com.android.launcher3.AutoInstallsLayout.TAG_FOLDER 35 import com.android.launcher3.AutoInstallsLayout.TAG_SHORTCUT 36 import com.android.launcher3.AutoInstallsLayout.TAG_WORKSPACE 37 import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP 38 import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT 39 import com.android.launcher3.LauncherSettings.Favorites.containerToString 40 import java.io.IOException 41 import java.io.StringWriter 42 import java.io.Writer 43 import org.xmlpull.v1.XmlSerializer 44 45 /** Helper class to build xml for Launcher Layout */ 46 class LauncherLayoutBuilder { 47 private val nodes = ArrayList<Node>() 48 49 fun atHotseat(rank: Int) = 50 ItemTarget( 51 mapOf( 52 ATTR_CONTAINER to containerToString(CONTAINER_HOTSEAT), 53 ATTR_RANK to rank.toString() 54 ) 55 ) 56 57 fun atWorkspace(x: Int, y: Int, screen: Int) = 58 ItemTarget( 59 mapOf( 60 ATTR_CONTAINER to containerToString(CONTAINER_DESKTOP), 61 ATTR_X to x.toString(), 62 ATTR_Y to y.toString(), 63 ATTR_SCREEN to screen.toString() 64 ) 65 ) 66 67 @Throws(IOException::class) fun build() = StringWriter().apply { build(this) }.toString() 68 69 @Throws(IOException::class) 70 fun build(writer: Writer) { 71 Xml.newSerializer().apply { 72 setOutput(writer) 73 setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true) 74 startDocument("UTF-8", true) 75 startTag(null, TAG_WORKSPACE) 76 writeNodes(nodes) 77 endTag(null, TAG_WORKSPACE) 78 endDocument() 79 flush() 80 } 81 } 82 83 open inner class ItemTarget(private val baseValues: Map<String, String>) { 84 @JvmOverloads 85 fun putApp(packageName: String, className: String?, userType: String? = null) = 86 addItem( 87 TAG_AUTO_INSTALL, 88 userType, 89 mapOf( 90 ATTR_PACKAGE_NAME to packageName, 91 ATTR_CLASS_NAME to (className ?: packageName) 92 ) 93 ) 94 95 @JvmOverloads 96 fun putShortcut(packageName: String, shortcutId: String, userType: String? = null) = 97 addItem( 98 TAG_SHORTCUT, 99 userType, 100 mapOf(ATTR_PACKAGE_NAME to packageName, ATTR_SHORTCUT_ID to shortcutId) 101 ) 102 103 @JvmOverloads 104 fun putWidget( 105 packageName: String, 106 className: String, 107 spanX: Int, 108 spanY: Int, 109 userType: String? = null 110 ) = 111 addItem( 112 TAG_APPWIDGET, 113 userType, 114 mapOf( 115 ATTR_PACKAGE_NAME to packageName, 116 ATTR_CLASS_NAME to className, 117 ATTR_SPAN_X to spanX.toString(), 118 ATTR_SPAN_Y to spanY.toString() 119 ) 120 ) 121 122 fun putFolder(titleResId: Int) = putFolder(ATTR_TITLE, titleResId.toString()) 123 124 fun putFolder(title: String?) = putFolder(ATTR_TITLE_TEXT, title) 125 126 protected open fun addItem( 127 tag: String, 128 userType: String?, 129 props: Map<String, String>, 130 children: List<Node>? = null 131 ): LauncherLayoutBuilder { 132 nodes.add( 133 Node( 134 tag, 135 HashMap(baseValues).apply { 136 putAll(props) 137 userType?.let { put(ATTR_USER_TYPE, it) } 138 }, 139 children 140 ) 141 ) 142 return this@LauncherLayoutBuilder 143 } 144 145 protected open fun putFolder(titleKey: String, titleValue: String?): FolderBuilder { 146 val folderBuilder = FolderBuilder() 147 addItem(TAG_FOLDER, null, mapOf(titleKey to (titleValue ?: "")), folderBuilder.children) 148 return folderBuilder 149 } 150 } 151 152 inner class FolderBuilder : ItemTarget(mapOf()) { 153 154 val children = ArrayList<Node>() 155 156 fun addApp(packageName: String, className: String?): FolderBuilder { 157 putApp(packageName, className) 158 return this 159 } 160 161 fun addShortcut(packageName: String, shortcutId: String): FolderBuilder { 162 putShortcut(packageName, shortcutId) 163 return this 164 } 165 166 override fun addItem( 167 tag: String, 168 userType: String?, 169 props: Map<String, String>, 170 childrenIgnored: List<Node>? 171 ): LauncherLayoutBuilder { 172 children.add( 173 Node(tag, HashMap(props).apply { userType?.let { put(ATTR_USER_TYPE, it) } }) 174 ) 175 return this@LauncherLayoutBuilder 176 } 177 178 override fun putFolder(titleKey: String, titleValue: String?): FolderBuilder { 179 throw IllegalArgumentException("Can't have folder inside a folder") 180 } 181 182 fun build() = this@LauncherLayoutBuilder 183 } 184 185 @Throws(IOException::class) 186 private fun XmlSerializer.writeNodes(nodes: List<Node>) { 187 nodes.forEach { node -> 188 startTag(null, node.name) 189 node.attrs.forEach { (key, value) -> attribute(null, key, value) } 190 node.children?.let { writeNodes(it) } 191 endTag(null, node.name) 192 } 193 } 194 195 data class Node( 196 val name: String, 197 val attrs: Map<String, String>, 198 val children: List<Node>? = null 199 ) 200 } 201