1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
|
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Eclipse Public License, Version 1.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.eclipse.org/org/documents/epl-v10.php
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.ide.common.api;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.ide.common.api.IDragElement.IDragAttribute;
import com.google.common.annotations.Beta;
import java.util.List;
/**
* Represents a view in the XML layout being edited.
* Each view or layout maps to exactly one XML node, thus the name.
* <p/>
* The primordial characteristic of a node is the fully qualified View class name that
* it represents (a.k.a FQCN), for example "android.view.View" or "android.widget.Button".
* <p/>
* There are 2 kind of nodes:
* - Nodes matching a view actually rendered in the layout canvas have a valid "bounds"
* rectangle that describe their position in pixels in the canvas. <br/>
* - Nodes created by IViewRule scripts but not yet rendered have an invalid bounds rectangle
* since they only exist in the uncommitted XML model and not yet in the rendered View model.
* <p>
* <b>NOTE: This is not a public or final API; if you rely on this be prepared
* to adjust your code for the next tools release.</b>
* </p>
*/
@Beta
public interface INode {
/**
* Returns the FQCN of the view class represented by this node.
*/
@NonNull
String getFqcn();
/**
* Returns the bounds of this node.
* <p/>
* The bounds are valid when this node maps a view that is already rendered.
* Typically, if the node is the target of a drag'n'drop operation then you can be
* guaranteed that its bounds are known and thus are valid.
* <p/>
* However the bounds are invalid (e.g. not known yet) for new XML elements
* that have just been created, e.g. by the {@link #appendChild(String)} method.
*
* @return A non-null rectangle, in canvas coordinates.
*/
@NonNull
Rect getBounds();
/**
* Returns the margins for this node.
*
* @return the margins for this node, never null
*/
@NonNull
Margins getMargins();
/**
* Returns the baseline of this node, or -1 if it has no baseline.
* The baseline is the distance from the top down to the baseline.
*
* @return the baseline, or -1 if not applicable
*/
int getBaseline();
// ---- Hierarchy handling ----
/**
* Returns the root element of the view hierarchy.
* <p/>
* When a node is not attached to a hierarchy, it is its own root node.
* This may return null if the {@link INode} was not created using a correct UiNode,
* which is unlikely.
*/
@Nullable
INode getRoot();
/**
* Returns the parent node of this node, corresponding to the parent view in the layout.
* The returned parent can be null when the node is the root element, or when the node is
* not yet or no longer attached to the hierarchy.
*/
@Nullable
INode getParent();
/**
* Returns the list of valid children nodes. The list can be empty but not null.
*/
@NonNull
INode[] getChildren();
// ---- XML Editing ---
/**
* Absolutely <em>all</em> calls that are going to edit the XML must be wrapped
* by an editXml() call. This call creates both an undo context wrapper and an
* edit-XML wrapper.
*
* @param undoName The UI name that will be given to the undo action.
* @param callback The code to execute.
*/
void editXml(@NonNull String undoName, @NonNull INodeHandler callback);
// TODO define an exception that methods below will throw if editXml() is not wrapping
// these calls.
/**
* Creates a new XML element as a child of this node's XML element.
* <p/>
* For this to work, the editor must have a descriptor for the given FQCN.
* <p/>
* This call must be done in the context of editXml().
*
* @param viewFqcn The FQCN of the element to create. The actual XML local name will
* depend on whether this is an Android view or a custom project view.
* @return The node for the newly created element. Can be null if we failed to create it.
*/
@NonNull
INode appendChild(@NonNull String viewFqcn);
/**
* Creates a new XML element as a child of this node's XML element and inserts
* it at the specified position in the children list.
* <p/>
* For this to work, the editor must have a descriptor for the given FQCN.
* <p/>
* This call must be done in the context of editXml().
*
* @param viewFqcn The FQCN of the element to create. The actual XML local name will
* depend on whether this is an Android view or a custom project view.
* @param index Index of the child to insert before. If the index is out of bounds
* (less than zero or larger that current last child), appends at the end.
* @return The node for the newly created element. Can be null if we failed to create it.
*/
@NonNull
INode insertChildAt(@NonNull String viewFqcn, int index);
/**
* Removes the given XML element child from this node's list of children.
* <p/>
* This call must be done in the context of editXml().
*
* @param node The child to be deleted.
*/
void removeChild(@NonNull INode node);
/**
* Sets an attribute for the underlying XML element.
* Attributes are not written immediately -- instead the XML editor batches edits and
* then commits them all together at once later.
* <p/>
* Custom attributes will be created on the fly.
* <p/>
* Passing an empty value actually <em>removes</em> an attribute from the XML.
* <p/>
* This call must be done in the context of editXml().
*
* @param uri The XML namespace URI of the attribute.
* @param localName The XML <em>local</em> name of the attribute to set.
* @param value It's value. Cannot be null. An empty value <em>removes</em> the attribute.
* @return Whether the attribute was actually set or not.
*/
boolean setAttribute(@Nullable String uri, @NonNull String localName, @NonNull String value);
/**
* Returns a given XML attribute.
* <p/>
* This looks up an attribute in the <em>current</em> XML source, not the in-memory model.
* That means that if called in the context of {@link #editXml(String, INodeHandler)}, the value
* returned here is not affected by {@link #setAttribute(String, String, String)} until
* the editXml closure is completed and the actual XML is updated.
*
* @param uri The XML name-space URI of the attribute.
* @param attrName The <em>local</em> name of the attribute.
* @return the attribute as a {@link String}, if it exists, or <code>null</code>.
*/
@Nullable
String getStringAttr(@Nullable String uri, @NonNull String attrName);
/**
* Returns the {@link IAttributeInfo} for a given attribute.
* <p/>
* The information is useful to determine the format of an attribute (e.g. reference, string,
* float, enum, flag, etc.) and in the case of enums and flags also gives the possible values.
* <p/>
* Note: in Android resources, an enum can only take one of the possible values (e.g.
* "visibility" can be either "visible" or "none"), whereas a flag can accept one or more
* value (e.g. "align" can be "center_vertical|center_horizontal".)
* <p/>
* Note that this method does not handle custom non-android attributes. It may either
* return null for these or it may return a synthetic "string" format attribute depending
* on how the attribute was loaded.
*
* @param uri The XML name-space URI of the attribute.
* @param attrName The <em>local</em> name of the attribute.
* @return the {@link IAttributeInfo} if the attribute is known, or <code>null</code>.
*/
@Nullable
public IAttributeInfo getAttributeInfo(@Nullable String uri, @NonNull String attrName);
/**
* Returns the list of all attributes declared by this node's descriptor.
* <p/>
* This returns a fixed list of all attributes known to the view or layout descriptor.
* This list does not depend on whether the attributes are actually used in the
* XML for this node.
* <p/>
* Note that for views, the list of attributes also includes the layout attributes
* inherited from the parent view. This means callers must not cache this list based
* solely on the type of the node, as its attribute list changes depending on the place
* of the view in the view hierarchy.
* <p/>
* If you want attributes actually written in the XML and their values, please use
* {@link #getStringAttr(String, String)} or {@link #getLiveAttributes()} instead.
*
* @return A non-null possibly-empty list of {@link IAttributeInfo}.
*/
@NonNull
public IAttributeInfo[] getDeclaredAttributes();
/**
* Returns the list of classes (fully qualified class names) that are
* contributing properties to the {@link #getDeclaredAttributes()} attribute
* list, in order from most specific to least specific (in other words,
* android.view.View will be last in the list.) This is usually the same as
* the super class chain of a view, but it skips any views that do not
* contribute attributes.
*
* @return a list of views classes that contribute attributes to this node,
* which is never null because at least android.view.View will
* contribute attributes.
*/
@NonNull
public List<String> getAttributeSources();
/**
* Returns the list of all attributes defined in the XML for this node.
* <p/>
* This looks up an attribute in the <em>current</em> XML source, not the in-memory model.
* That means that if called in the context of {@link #editXml(String, INodeHandler)}, the value
* returned here is not affected by {@link #setAttribute(String, String, String)} until
* the editXml closure is completed and the actual XML is updated.
* <p/>
* If you want a list of all possible attributes, whether used in the XML or not by
* this node, please see {@link #getDeclaredAttributes()} instead.
*
* @return A non-null possibly-empty list of {@link IAttribute}.
*/
@NonNull
public IAttribute[] getLiveAttributes();
// -----------
/**
* An XML attribute in an {@link INode} with a namespace URI, a name and its current value.
* <p/>
* The name cannot be empty.
* The namespace URI can be empty for an attribute without a namespace but is never null.
* The value can be empty but cannot be null.
*/
public static interface IAttribute extends IDragAttribute { }
}
|