1 /**
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * 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, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 package com.android.launcher3.util;
17 
18 
19 import android.text.TextUtils;
20 import android.util.Pair;
21 import android.util.Xml;
22 
23 import org.xmlpull.v1.XmlSerializer;
24 
25 import java.io.IOException;
26 import java.io.StringWriter;
27 import java.io.Writer;
28 import java.util.ArrayList;
29 import java.util.HashMap;
30 import java.util.Map;
31 
32 /**
33  * Helper class to build xml for Launcher Layout
34  */
35 public class LauncherLayoutBuilder {
36 
37     // Object Tags
38     private static final String TAG_WORKSPACE = "workspace";
39     private static final String TAG_AUTO_INSTALL = "autoinstall";
40     private static final String TAG_FOLDER = "folder";
41     private static final String TAG_APPWIDGET = "appwidget";
42     private static final String TAG_EXTRA = "extra";
43 
44     private static final String ATTR_CONTAINER = "container";
45     private static final String ATTR_RANK = "rank";
46 
47     private static final String ATTR_PACKAGE_NAME = "packageName";
48     private static final String ATTR_CLASS_NAME = "className";
49     private static final String ATTR_TITLE = "title";
50     private static final String ATTR_SCREEN = "screen";
51 
52     // x and y can be specified as negative integers, in which case -1 represents the
53     // last row / column, -2 represents the second last, and so on.
54     private static final String ATTR_X = "x";
55     private static final String ATTR_Y = "y";
56     private static final String ATTR_SPAN_X = "spanX";
57     private static final String ATTR_SPAN_Y = "spanY";
58 
59     private static final String ATTR_CHILDREN = "children";
60 
61 
62     // Style attrs -- "Extra"
63     private static final String ATTR_KEY = "key";
64     private static final String ATTR_VALUE = "value";
65 
66     private static final String CONTAINER_DESKTOP = "desktop";
67     private static final String CONTAINER_HOTSEAT = "hotseat";
68 
69     private final ArrayList<Pair<String, HashMap<String, Object>>> mNodes = new ArrayList<>();
70 
atHotseat(int rank)71     public Location atHotseat(int rank) {
72         Location l = new Location();
73         l.items.put(ATTR_CONTAINER, CONTAINER_HOTSEAT);
74         l.items.put(ATTR_RANK, Integer.toString(rank));
75         return l;
76     }
77 
atWorkspace(int x, int y, int screen)78     public Location atWorkspace(int x, int y, int screen) {
79         Location l = new Location();
80         l.items.put(ATTR_CONTAINER, CONTAINER_DESKTOP);
81         l.items.put(ATTR_X, Integer.toString(x));
82         l.items.put(ATTR_Y, Integer.toString(y));
83         l.items.put(ATTR_SCREEN, Integer.toString(screen));
84         return l;
85     }
86 
build()87     public String build() throws IOException {
88         StringWriter writer = new StringWriter();
89         build(writer);
90         return writer.toString();
91     }
92 
build(Writer writer)93     public void build(Writer writer) throws IOException {
94         XmlSerializer serializer = Xml.newSerializer();
95         serializer.setOutput(writer);
96 
97         serializer.startDocument("UTF-8", true);
98         serializer.startTag(null, TAG_WORKSPACE);
99         writeNodes(serializer, mNodes);
100         serializer.endTag(null, TAG_WORKSPACE);
101         serializer.endDocument();
102         serializer.flush();
103     }
104 
writeNodes(XmlSerializer serializer, ArrayList<Pair<String, HashMap<String, Object>>> nodes)105     private static void writeNodes(XmlSerializer serializer,
106             ArrayList<Pair<String, HashMap<String, Object>>> nodes) throws IOException {
107         for (Pair<String, HashMap<String, Object>> node : nodes) {
108             ArrayList<Pair<String, HashMap<String, Object>>> children = null;
109 
110             serializer.startTag(null, node.first);
111             for (Map.Entry<String, Object> attr : node.second.entrySet()) {
112                 if (ATTR_CHILDREN.equals(attr.getKey())) {
113                     children = (ArrayList<Pair<String, HashMap<String, Object>>>) attr.getValue();
114                 } else {
115                     serializer.attribute(null, attr.getKey(), (String) attr.getValue());
116                 }
117             }
118 
119             if (children != null) {
120                 writeNodes(serializer, children);
121             }
122             serializer.endTag(null, node.first);
123         }
124     }
125 
126     public class Location {
127 
128         final HashMap<String, Object> items = new HashMap<>();
129 
putApp(String packageName, String className)130         public LauncherLayoutBuilder putApp(String packageName, String className) {
131             items.put(ATTR_PACKAGE_NAME, packageName);
132             items.put(ATTR_CLASS_NAME, TextUtils.isEmpty(className) ? packageName : className);
133             mNodes.add(Pair.create(TAG_AUTO_INSTALL, items));
134             return LauncherLayoutBuilder.this;
135         }
136 
putWidget(String packageName, String className, int spanX, int spanY)137         public LauncherLayoutBuilder putWidget(String packageName, String className,
138                 int spanX, int spanY) {
139             items.put(ATTR_PACKAGE_NAME, packageName);
140             items.put(ATTR_CLASS_NAME, className);
141             items.put(ATTR_SPAN_X, Integer.toString(spanX));
142             items.put(ATTR_SPAN_Y, Integer.toString(spanY));
143             mNodes.add(Pair.create(TAG_APPWIDGET, items));
144             return LauncherLayoutBuilder.this;
145         }
146 
putFolder(int titleResId)147         public FolderBuilder putFolder(int titleResId) {
148             FolderBuilder folderBuilder = new FolderBuilder();
149             items.put(ATTR_TITLE, Integer.toString(titleResId));
150             items.put(ATTR_CHILDREN, folderBuilder.mChildren);
151             mNodes.add(Pair.create(TAG_FOLDER, items));
152             return folderBuilder;
153         }
154     }
155 
156     public class FolderBuilder {
157 
158         final ArrayList<Pair<String, HashMap<String, Object>>> mChildren = new ArrayList<>();
159 
addApp(String packageName, String className)160         public FolderBuilder addApp(String packageName, String className) {
161             HashMap<String, Object> items = new HashMap<>();
162             items.put(ATTR_PACKAGE_NAME, packageName);
163             items.put(ATTR_CLASS_NAME, TextUtils.isEmpty(className) ? packageName : className);
164             mChildren.add(Pair.create(TAG_AUTO_INSTALL, items));
165             return this;
166         }
167 
build()168         public LauncherLayoutBuilder build() {
169             return LauncherLayoutBuilder.this;
170         }
171     }
172 }
173