Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(809)

Side by Side Diff: build/android/pylib/device/shared_prefs.py

Issue 2101243005: Add a snapshot of flutter/engine/src/build to our sdk (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: add README.dart Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 # Copyright 2015 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 """Helper object to read and modify Shared Preferences from Android apps.
6
7 See e.g.:
8 http://developer.android.com/reference/android/content/SharedPreferences.html
9 """
10
11 import collections
12 import logging
13 import posixpath
14
15 from xml.etree import ElementTree
16
17
18 _XML_DECLARATION = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
19
20
21 class BasePref(object):
22 """Base class for getting/setting the value of a specific preference type.
23
24 Should not be instantiated directly. The SharedPrefs collection will
25 instantiate the appropriate subclasses, which directly manipulate the
26 underlying xml document, to parse and serialize values according to their
27 type.
28
29 Args:
30 elem: An xml ElementTree object holding the preference data.
31
32 Properties:
33 tag_name: A string with the tag that must be used for this preference type.
34 """
35 tag_name = None
36
37 def __init__(self, elem):
38 if elem.tag != type(self).tag_name:
39 raise TypeError('Property %r has type %r, but trying to access as %r' %
40 (elem.get('name'), elem.tag, type(self).tag_name))
41 self._elem = elem
42
43 def __str__(self):
44 """Get the underlying xml element as a string."""
45 return ElementTree.tostring(self._elem)
46
47 def get(self):
48 """Get the value of this preference."""
49 return self._elem.get('value')
50
51 def set(self, value):
52 """Set from a value casted as a string."""
53 self._elem.set('value', str(value))
54
55 @property
56 def has_value(self):
57 """Check whether the element has a value."""
58 return self._elem.get('value') is not None
59
60
61 class BooleanPref(BasePref):
62 """Class for getting/setting a preference with a boolean value.
63
64 The underlying xml element has the form, e.g.:
65 <boolean name="featureEnabled" value="false" />
66 """
67 tag_name = 'boolean'
68 VALUES = {'true': True, 'false': False}
69
70 def get(self):
71 """Get the value as a Python bool."""
72 return type(self).VALUES[super(BooleanPref, self).get()]
73
74 def set(self, value):
75 """Set from a value casted as a bool."""
76 super(BooleanPref, self).set('true' if value else 'false')
77
78
79 class FloatPref(BasePref):
80 """Class for getting/setting a preference with a float value.
81
82 The underlying xml element has the form, e.g.:
83 <float name="someMetric" value="4.7" />
84 """
85 tag_name = 'float'
86
87 def get(self):
88 """Get the value as a Python float."""
89 return float(super(FloatPref, self).get())
90
91
92 class IntPref(BasePref):
93 """Class for getting/setting a preference with an int value.
94
95 The underlying xml element has the form, e.g.:
96 <int name="aCounter" value="1234" />
97 """
98 tag_name = 'int'
99
100 def get(self):
101 """Get the value as a Python int."""
102 return int(super(IntPref, self).get())
103
104
105 class LongPref(IntPref):
106 """Class for getting/setting a preference with a long value.
107
108 The underlying xml element has the form, e.g.:
109 <long name="aLongCounter" value="1234" />
110
111 We use the same implementation from IntPref.
112 """
113 tag_name = 'long'
114
115
116 class StringPref(BasePref):
117 """Class for getting/setting a preference with a string value.
118
119 The underlying xml element has the form, e.g.:
120 <string name="someHashValue">249b3e5af13d4db2</string>
121 """
122 tag_name = 'string'
123
124 def get(self):
125 """Get the value as a Python string."""
126 return self._elem.text
127
128 def set(self, value):
129 """Set from a value casted as a string."""
130 self._elem.text = str(value)
131
132
133 class StringSetPref(StringPref):
134 """Class for getting/setting a preference with a set of string values.
135
136 The underlying xml element has the form, e.g.:
137 <set name="managed_apps">
138 <string>com.mine.app1</string>
139 <string>com.mine.app2</string>
140 <string>com.mine.app3</string>
141 </set>
142 """
143 tag_name = 'set'
144
145 def get(self):
146 """Get a list with the string values contained."""
147 value = []
148 for child in self._elem:
149 assert child.tag == 'string'
150 value.append(child.text)
151 return value
152
153 def set(self, value):
154 """Set from a sequence of values, each casted as a string."""
155 for child in list(self._elem):
156 self._elem.remove(child)
157 for item in value:
158 ElementTree.SubElement(self._elem, 'string').text = str(item)
159
160
161 _PREF_TYPES = {c.tag_name: c for c in [BooleanPref, FloatPref, IntPref,
162 LongPref, StringPref, StringSetPref]}
163
164
165 class SharedPrefs(object):
166 def __init__(self, device, package, filename):
167 """Helper object to read and update "Shared Prefs" of Android apps.
168
169 Such files typically look like, e.g.:
170
171 <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
172 <map>
173 <int name="databaseVersion" value="107" />
174 <boolean name="featureEnabled" value="false" />
175 <string name="someHashValue">249b3e5af13d4db2</string>
176 </map>
177
178 Example usage:
179
180 prefs = shared_prefs.SharedPrefs(device, 'com.my.app', 'my_prefs.xml')
181 prefs.Load()
182 prefs.GetString('someHashValue') # => '249b3e5af13d4db2'
183 prefs.SetInt('databaseVersion', 42)
184 prefs.Remove('featureEnabled')
185 prefs.Commit()
186
187 The object may also be used as a context manager to automatically load and
188 commit, respectively, upon entering and leaving the context.
189
190 Args:
191 device: A DeviceUtils object.
192 package: A string with the package name of the app that owns the shared
193 preferences file.
194 filename: A string with the name of the preferences file to read/write.
195 """
196 self._device = device
197 self._xml = None
198 self._package = package
199 self._filename = filename
200 self._path = '/data/data/%s/shared_prefs/%s' % (package, filename)
201 self._changed = False
202
203 def __repr__(self):
204 """Get a useful printable representation of the object."""
205 return '<{cls} file {filename} for {package} on {device}>'.format(
206 cls=type(self).__name__, filename=self.filename, package=self.package,
207 device=str(self._device))
208
209 def __str__(self):
210 """Get the underlying xml document as a string."""
211 return _XML_DECLARATION + ElementTree.tostring(self.xml)
212
213 @property
214 def package(self):
215 """Get the package name of the app that owns the shared preferences."""
216 return self._package
217
218 @property
219 def filename(self):
220 """Get the filename of the shared preferences file."""
221 return self._filename
222
223 @property
224 def path(self):
225 """Get the full path to the shared preferences file on the device."""
226 return self._path
227
228 @property
229 def changed(self):
230 """True if properties have changed and a commit would be needed."""
231 return self._changed
232
233 @property
234 def xml(self):
235 """Get the underlying xml document as an ElementTree object."""
236 if self._xml is None:
237 self._xml = ElementTree.Element('map')
238 return self._xml
239
240 def Load(self):
241 """Load the shared preferences file from the device.
242
243 A empty xml document, which may be modified and saved on |commit|, is
244 created if the file does not already exist.
245 """
246 if self._device.FileExists(self.path):
247 self._xml = ElementTree.fromstring(
248 self._device.ReadFile(self.path, as_root=True))
249 assert self._xml.tag == 'map'
250 else:
251 self._xml = None
252 self._changed = False
253
254 def Clear(self):
255 """Clear all of the preferences contained in this object."""
256 if self._xml is not None and len(self): # only clear if not already empty
257 self._xml = None
258 self._changed = True
259
260 def Commit(self):
261 """Save the current set of preferences to the device.
262
263 Only actually saves if some preferences have been modified.
264 """
265 if not self.changed:
266 return
267 self._device.RunShellCommand(
268 ['mkdir', '-p', posixpath.dirname(self.path)],
269 as_root=True, check_return=True)
270 self._device.WriteFile(self.path, str(self), as_root=True)
271 self._device.KillAll(self.package, as_root=True, quiet=True)
272 self._changed = False
273
274 def __len__(self):
275 """Get the number of preferences in this collection."""
276 return len(self.xml)
277
278 def PropertyType(self, key):
279 """Get the type (i.e. tag name) of a property in the collection."""
280 return self._GetChild(key).tag
281
282 def HasProperty(self, key):
283 try:
284 self._GetChild(key)
285 return True
286 except KeyError:
287 return False
288
289 def GetBoolean(self, key):
290 """Get a boolean property."""
291 return BooleanPref(self._GetChild(key)).get()
292
293 def SetBoolean(self, key, value):
294 """Set a boolean property."""
295 self._SetPrefValue(key, value, BooleanPref)
296
297 def GetFloat(self, key):
298 """Get a float property."""
299 return FloatPref(self._GetChild(key)).get()
300
301 def SetFloat(self, key, value):
302 """Set a float property."""
303 self._SetPrefValue(key, value, FloatPref)
304
305 def GetInt(self, key):
306 """Get an int property."""
307 return IntPref(self._GetChild(key)).get()
308
309 def SetInt(self, key, value):
310 """Set an int property."""
311 self._SetPrefValue(key, value, IntPref)
312
313 def GetLong(self, key):
314 """Get a long property."""
315 return LongPref(self._GetChild(key)).get()
316
317 def SetLong(self, key, value):
318 """Set a long property."""
319 self._SetPrefValue(key, value, LongPref)
320
321 def GetString(self, key):
322 """Get a string property."""
323 return StringPref(self._GetChild(key)).get()
324
325 def SetString(self, key, value):
326 """Set a string property."""
327 self._SetPrefValue(key, value, StringPref)
328
329 def GetStringSet(self, key):
330 """Get a string set property."""
331 return StringSetPref(self._GetChild(key)).get()
332
333 def SetStringSet(self, key, value):
334 """Set a string set property."""
335 self._SetPrefValue(key, value, StringSetPref)
336
337 def Remove(self, key):
338 """Remove a preference from the collection."""
339 self.xml.remove(self._GetChild(key))
340
341 def AsDict(self):
342 """Return the properties and their values as a dictionary."""
343 d = {}
344 for child in self.xml:
345 pref = _PREF_TYPES[child.tag](child)
346 d[child.get('name')] = pref.get()
347 return d
348
349 def __enter__(self):
350 """Load preferences file from the device when entering a context."""
351 self.Load()
352 return self
353
354 def __exit__(self, exc_type, _exc_value, _traceback):
355 """Save preferences file to the device when leaving a context."""
356 if not exc_type:
357 self.Commit()
358
359 def _GetChild(self, key):
360 """Get the underlying xml node that holds the property of a given key.
361
362 Raises:
363 KeyError when the key is not found in the collection.
364 """
365 for child in self.xml:
366 if child.get('name') == key:
367 return child
368 raise KeyError(key)
369
370 def _SetPrefValue(self, key, value, pref_cls):
371 """Set the value of a property.
372
373 Args:
374 key: The key of the property to set.
375 value: The new value of the property.
376 pref_cls: A subclass of BasePref used to access the property.
377
378 Raises:
379 TypeError when the key already exists but with a different type.
380 """
381 try:
382 pref = pref_cls(self._GetChild(key))
383 old_value = pref.get()
384 except KeyError:
385 pref = pref_cls(ElementTree.SubElement(
386 self.xml, pref_cls.tag_name, {'name': key}))
387 old_value = None
388 if old_value != value:
389 pref.set(value)
390 self._changed = True
391 logging.info('Setting property: %s', pref)
OLDNEW
« no previous file with comments | « build/android/pylib/device/logcat_monitor_test.py ('k') | build/android/pylib/device/shared_prefs_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698