0001 ### BITPIM 0002 ### 0003 ### Copyright (C) 2008 Joe Pham <djpham@bitpim.org> 0004 ### 0005 ### This program is free software; you can redistribute it and/or modify 0006 ### it under the terms of the BitPim license as detailed in the LICENSE file. 0007 ### 0008 ### $Id: com_lglx570.py 4678 2008-08-13 23:46:56Z djpham $ 0009 0010 """Communicate with the LG LX570 (Musiq) cell phone""" 0011 0012 import common 0013 import com_brew 0014 import com_lg 0015 import com_lgvx4400 0016 import p_lglx570 0017 import prototypes 0018 import helpids 0019 import sms 0020 0021 #------------------------------------------------------------------------------- 0022 parentphone=com_lgvx4400.Phone 0023 class Phone(com_brew.RealBrewProtocol2, parentphone): 0024 "Talk to the LG LX570 (Musiq) cell phone" 0025 0026 desc="LG-LX570" 0027 helpid=helpids.ID_PHONE_LGLX570 0028 protocolclass=p_lglx570 0029 serialsname='lglx570' 0030 my_model='LX570' 0031 0032 builtinringtones=( 'Tone 1', 'Tone 2', 'Tone 3', 'Tone 4', 'Tone 5', 'Tone 6', 0033 'Tone 7', 'Tone 8', 'Tone 9', 'Tone 10', 0034 'Alert 1', 'Alert 2', 'Alert 3', 'Alert 4', 'Alert 5') 0035 ringtonelocations=( 0036 # offset, index file, files location, type, maximumentries 0037 (0x1100, "setas/voicememoRingerIndex.map", "VoiceDB/All/Memos", "voice memo", 35), 0038 (0x1200, "setas/mcRingerIndex.map", "melodyComposer", "my melodies", 20), 0039 ) 0040 builtinimages=() 0041 imagelocations=( 0042 # offset, index file, files location, type, maximumentries 0043 (0x600, "setas/dcamIndex.map", "Dcam/Wallet", "images", 255), 0044 ) 0045 wallpaperdirs=('Dcam/Review', 'Dcam/Wallet') 0046 0047 def __init__(self, logtarget, commport): 0048 parentphone.__init__(self, logtarget, commport) 0049 0050 # supporting routines for getfundamentals 0051 def get_esn(self, data=None): 0052 # return the ESN of this phone 0053 return self.get_brew_esn() 0054 0055 def getgroups(self, result): 0056 self.log("Reading group information") 0057 _buf=prototypes.buffer(self.getfilecontents2( 0058 self.protocolclass.PB_FILENAME, 0x1E000, 2016)) 0059 _groups={} 0060 _grp=self.protocolclass.pbgroup() 0061 while True: 0062 _grp.readfrombuffer(_buf) 0063 if _grp.valid: 0064 _groups[_grp.groupid]={ 'name': _grp.name } 0065 else: 0066 break 0067 result['groups']=_groups 0068 return _groups 0069 0070 # Media stuff--------------------------------------------------------------- 0071 def getwallpaperindices(self, results): 0072 # index the list of files in known camera dirs 0073 _res={} 0074 _idx=1 0075 for _dir in self.wallpaperdirs: 0076 for _file in self.listfiles(_dir): 0077 _res[_idx]={ 'name': common.basename(_file), 0078 'filename': _file, 0079 'origin': 'images' } 0080 _idx+=1 0081 results['wallpaper-index']=_res 0082 return results 0083 0084 def getwallpapers(self, result): 0085 # retrieve all camera images 0086 _media={} 0087 for _wp in result.get('wallpaper-index', {}).values(): 0088 _media[_wp['name']]=self.getfilecontents(_wp['filename'], True) 0089 result['wallpapers']=_media 0090 return result 0091 0092 def getringtoneindices(self, results): 0093 return self.getmediaindex(self.builtinringtones, 0094 self.ringtonelocations, 0095 results, 'ringtone-index') 0096 0097 def _fix_melodies_filename(self, name): 0098 # the "My Melodies" file name must have '.mid' extension and 0099 # no other '.' character. 0100 if name[-4:]=='.mid': 0101 # name already has the correct extenstion 0102 _new_name=name[:-4] 0103 else: 0104 _new_name=name 0105 return _new_name.replace('.', '_')+'.mid' 0106 0107 def _update_media_file(self, name, data): 0108 # Check the if the size of the media file is different than the data 0109 # and rewrite the file if necessary. 0110 self.log('Updating media file: %s'%name) 0111 _filename='%s/%s'%(self.protocolclass.RT_MC_PATH, name) 0112 try: 0113 _stat=self.statfile(_filename) 0114 except com_brew.BrewNoSuchFileException: 0115 _stat=None 0116 if _stat and _stat.get('size', None)==len(data): 0117 # file size and data size are the same, bail 0118 self.log('File %s is unchanged'%_filename) 0119 return 0120 self.writefile(_filename, data) 0121 0122 def saveringtones(self, results, merge): 0123 # Saving My Melodies media files to be used as ringtones 0124 _media_index=results.get('ringtone-index', {}) 0125 _media=results.get('ringtone', {}) 0126 # build a dict of my "my melodies" (file) name and data 0127 _melodies_data={} 0128 for _item in _media.values(): 0129 if _item.get('origin', None)=='my melodies': 0130 _melodies_data[self._fix_melodies_filename(_item['name'])]=_item['data'] 0131 # read the existing index file 0132 _indexfile=self.readobject(self.protocolclass.RT_MC_INDEX_FILENAME, 0133 self.protocolclass.indexfile, 0134 logtitle='Reading MC Index File', 0135 uselocalfs=False) 0136 # go through the index file and rewrite media files as needed 0137 self.log('Updating media files') 0138 for _idx in range(_indexfile.numactiveitems): 0139 _item=_indexfile.items[_idx] 0140 if _item.name and _melodies_data.has_key(_item.name): 0141 # check the file size and rewrite the file as needed 0142 self._update_media_file(_item.name, _melodies_data[_item.name]) 0143 # remove the entry from the dict 0144 del _melodies_data[_item.name] 0145 # go through the index file and add new items 0146 _empty_index=range(_indexfile.numactiveitems, len(_indexfile.items)) 0147 _available_media=_melodies_data.keys() 0148 self.log('Adding new media files') 0149 for _idx, _name in zip(_empty_index, _available_media): 0150 # update the index entry 0151 _indexfile.items[_idx].name=_name 0152 # write out the media file 0153 self._update_media_file(_name, _melodies_data[_name]) 0154 # update the count 0155 _indexfile.numactiveitems+=1 0156 # write the new index file 0157 self.writeobject(self.protocolclass.RT_MC_INDEX_FILENAME, 0158 _indexfile, logtitle='Writing new MC Index file', 0159 uselocalfs=False) 0160 # and re-read the index file 0161 self.getringtoneindices(results) 0162 return results 0163 0164 # Phonebook stuff----------------------------------------------------------- 0165 def _assignpbtypeandspeeddialsbyposition(self, entry, speeds, res): 0166 # numbers 0167 res['numbers']=[] 0168 for i in range(self.protocolclass.NUMPHONENUMBERS): 0169 num=entry.numbers[i].number 0170 numtype=entry.numbertypes[i].numbertype 0171 if len(num): 0172 t=self.protocolclass.numbertypetab[numtype] 0173 if t[-1]=='2': 0174 t=t[:-1] 0175 _numdict={ 'number': num, 'type': t } 0176 if entry.speeddials[i].speeddial!=0xff: 0177 _numdict['speeddial']=entry.speeddials[i].speeddial 0178 res['numbers'].append(_numdict) 0179 return res 0180 0181 # Copy this from the VX4400 module, with changes to support for 0182 # different handling of speed dials data 0183 def savephonebook(self, data): 0184 "Saves out the phonebook" 0185 # we can't save groups 0186 progressmax=len(data['phonebook'].keys()) 0187 0188 # To write the phone book, we scan through all existing entries 0189 # and record their record number and serials. 0190 # We then delete any entries that aren't in data 0191 # We then write out our records, using overwrite or append 0192 # commands as necessary 0193 serialupdates=[] 0194 existingpbook={} # keep track of the phonebook that is on the phone 0195 self.mode=self.MODENONE 0196 self.setmode(self.MODEBREW) # see note in getphonebook() for why this is necessary 0197 self.setmode(self.MODEPHONEBOOK) 0198 # similar loop to reading 0199 req=self.protocolclass.pbinforequest() 0200 res=self.sendpbcommand(req, self.protocolclass.pbinforesponse) 0201 numexistingentries=res.numentries 0202 if numexistingentries<0 or numexistingentries>1000: 0203 self.log("The phone is lying about how many entries are in the phonebook so we are doing it the hard way") 0204 numexistingentries=0 0205 firstserial=None 0206 loop=xrange(0,1000) 0207 hardway=True 0208 else: 0209 self.log("There are %d existing entries" % (numexistingentries,)) 0210 progressmax+=numexistingentries 0211 loop=xrange(0, numexistingentries) 0212 hardway=False 0213 progresscur=0 0214 # reset cursor 0215 self.sendpbcommand(self.protocolclass.pbinitrequest(), self.protocolclass.pbinitresponse) 0216 for i in loop: 0217 ### Read current entry 0218 if hardway: 0219 numexistingentries+=1 0220 progressmax+=1 0221 req=self.protocolclass.pbreadentryrequest() 0222 res=self.sendpbcommand(req, self.protocolclass.pbreadentryresponse) 0223 0224 entry={ 'number': res.entry.entrynumber, 'serial1': res.entry.serial1, 0225 'serial2': res.entry.serial2, 'name': res.entry.name} 0226 assert entry['serial1']==entry['serial2'] # always the same 0227 self.log("Reading entry "+`i`+" - "+entry['name']) 0228 if hardway and firstserial is None: 0229 firstserial=res.entry.serial1 0230 existingpbook[i]=entry 0231 self.progress(progresscur, progressmax, "existing "+entry['name']) 0232 #### Advance to next entry 0233 req=self.protocolclass.pbnextentryrequest() 0234 res=self.sendpbcommand(req, self.protocolclass.pbnextentryresponse) 0235 progresscur+=1 0236 if hardway: 0237 # look to see if we have looped 0238 if res.serial==firstserial or res.serial==0: 0239 break 0240 # we have now looped around back to begining 0241 0242 # Find entries that have been deleted 0243 pbook=data['phonebook'] 0244 dellist=[] 0245 for i in range(0, numexistingentries): 0246 ii=existingpbook[i] 0247 serial=ii['serial1'] 0248 item=self._findserial(serial, pbook) 0249 if item is None: 0250 dellist.append(i) 0251 0252 progressmax+=len(dellist) # more work to do 0253 0254 # Delete those entries 0255 for i in dellist: 0256 progresscur+=1 0257 numexistingentries-=1 # keep count right 0258 ii=existingpbook[i] 0259 self.log("Deleting entry "+`i`+" - "+ii['name']) 0260 req=self.protocolclass.pbdeleteentryrequest() 0261 req.serial1=ii['serial1'] 0262 req.serial2=ii['serial2'] 0263 req.entrynumber=ii['number'] 0264 self.sendpbcommand(req, self.protocolclass.pbdeleteentryresponse) 0265 self.progress(progresscur, progressmax, "Deleting "+ii['name']) 0266 # also remove them from existingpbook 0267 del existingpbook[i] 0268 0269 # counter to keep track of record number (otherwise appends don't work) 0270 counter=0 0271 # Now rewrite out existing entries 0272 keys=existingpbook.keys() 0273 existingserials=[] 0274 keys.sort() # do in same order as existingpbook 0275 for i in keys: 0276 progresscur+=1 0277 ii=pbook[self._findserial(existingpbook[i]['serial1'], pbook)] 0278 self.log("Rewriting entry "+`i`+" - "+ii['name']) 0279 self.progress(progresscur, progressmax, "Rewriting "+ii['name']) 0280 entry=self.makeentry(counter, ii, data) 0281 counter+=1 0282 existingserials.append(existingpbook[i]['serial1']) 0283 req=self.protocolclass.pbupdateentryrequest() 0284 req.entry=entry 0285 res=self.sendpbcommand(req, self.protocolclass.pbupdateentryresponse) 0286 serialupdates.append( ( ii["bitpimserial"], 0287 {'sourcetype': self.serialsname, 'serial1': res.serial1, 'serial2': res.serial1, 0288 'sourceuniqueid': data['uniqueserial']}) 0289 ) 0290 assert ii['serial1']==res.serial1 # serial should stay the same 0291 0292 # Finally write out new entries 0293 keys=pbook.keys() 0294 keys.sort() 0295 for i in keys: 0296 try: 0297 ii=pbook[i] 0298 if ii['serial1'] in existingserials: 0299 continue # already wrote this one out 0300 progresscur+=1 0301 entry=self.makeentry(counter, ii, data) 0302 counter+=1 0303 self.log("Appending entry "+ii['name']) 0304 self.progress(progresscur, progressmax, "Writing "+ii['name']) 0305 req=self.protocolclass.pbappendentryrequest() 0306 req.entry=entry 0307 res=self.sendpbcommand(req, self.protocolclass.pbappendentryresponse) 0308 serialupdates.append( ( ii["bitpimserial"], 0309 {'sourcetype': self.serialsname, 'serial1': res.newserial, 'serial2': res.newserial, 0310 'sourceuniqueid': data['uniqueserial']}) 0311 ) 0312 except: 0313 self.log('Failed to write entry: '+ii['name']) 0314 if __debug__: 0315 raise 0316 data["serialupdates"]=serialupdates 0317 self.progress(progressmax, progressmax, "Rebooting phone") 0318 data["rebootphone"]=True 0319 return data 0320 0321 def makeentry(self, counter, entry, data): 0322 """Creates pbentry object 0323 0324 @param counter: The new entry number 0325 @param entry: The phonebook object (as returned from convertphonebooktophone) that we 0326 are using as the source 0327 @param data: The main dictionary, which we use to get access to media indices amongst 0328 other things 0329 """ 0330 e=self.protocolclass.pbentry() 0331 e.entrynumber=counter 0332 for k in entry: 0333 # special treatment for lists 0334 if k in ('emails', 'numbers', 'numbertypes', 'speeddials'): 0335 l=getattr(e,k) 0336 for item in entry[k]: 0337 l.append(item) 0338 elif k=='ringtone': 0339 e.ringtone=self._findmediainindex(data['ringtone-index'], entry['ringtone'], entry['name'], 'ringtone') 0340 elif k in e.getfields(): 0341 # everything else we just set 0342 setattr(e,k,entry[k]) 0343 return e 0344 0345 # SMS Stuff ---------------------------------------------------------------- 0346 def _getquicktext(self): 0347 quicks=[] 0348 try: 0349 sf=self.readobject(self.protocolclass.SMS_CANNED_FILENAME, 0350 self.protocolclass.sms_canned_file, 0351 logtitle="SMS quicktext file sms/mediacan000.dat", 0352 uselocalfs=False) 0353 for rec in sf.msgs: 0354 if rec.msg: 0355 quicks.append({ 'text': rec.msg, 0356 'type': sms.CannedMsgEntry.user_type }) 0357 except com_brew.BrewNoSuchFileException: 0358 pass # do nothing if file doesn't exist 0359 return quicks 0360 0361 def _getinboxmessage(self, sf): 0362 entry=sms.SMSEntry() 0363 entry.folder=entry.Folder_Inbox 0364 entry.datetime="%d%02d%02dT%02d%02d%02d" % (sf.GPStime) 0365 entry._from=self._getsender(sf.sender, sf.sender_length) 0366 entry.subject=sf.subject 0367 entry.locked=sf.locked 0368 entry.priority=sms.SMSEntry.Priority_Normal 0369 entry.read=sf.read 0370 entry.text=sf.msg 0371 entry.callback=sf.callback 0372 return entry 0373 0374 def _getoutboxmessage(self, sf): 0375 entry=sms.SMSEntry() 0376 entry.folder=entry.Folder_Saved if sf.saved \ 0377 else entry.Folder_Sent 0378 entry.datetime="%d%02d%02dT%02d%02d00" % ((sf.timesent)) 0379 # add all the recipients 0380 for r in sf.recipients: 0381 if r.number: 0382 confirmed=(r.status==2) 0383 confirmed_date=None 0384 if confirmed: 0385 confirmed_date="%d%02d%02dT%02d%02d00" % r.time 0386 entry.add_recipient(r.number, confirmed, confirmed_date) 0387 entry.subject=sf.msg[:28] 0388 entry.text=sf.msg 0389 entry.priority=entry.Priority_High if sf.priority else \ 0390 entry.Priority_Normal 0391 entry.locked=sf.locked 0392 entry.callback=sf.callback 0393 return entry 0394 0395 def _readsms(self): 0396 res={} 0397 # go through the sms directory looking for messages 0398 for item in self.listfiles("sms").values(): 0399 folder=None 0400 for f,pat in self.protocolclass.SMS_PATTERNS.items(): 0401 if pat.match(item['name']): 0402 folder=f 0403 break 0404 if folder: 0405 buf=prototypes.buffer(self.getfilecontents(item['name'], True)) 0406 self.logdata("SMS message file " +item['name'], buf.getdata()) 0407 if folder=='Inbox': 0408 sf=self.protocolclass.sms_in() 0409 sf.readfrombuffer(buf, logtitle="SMS inbox item") 0410 entry=self._getinboxmessage(sf) 0411 res[entry.id]=entry 0412 elif folder=='Sent': 0413 sf=self.protocolclass.sms_out() 0414 sf.readfrombuffer(buf, logtitle="SMS sent item") 0415 entry=self._getoutboxmessage(sf) 0416 res[entry.id]=entry 0417 0418 return res 0419 0420 def _setquicktext(self, result): 0421 sf=self.protocolclass.sms_canned_file() 0422 quicktext=result.get('canned_msg', [])[:self.protocolclass.SMS_CANNED_MAX_ITEMS] 0423 if quicktext: 0424 for entry in quicktext: 0425 msg_entry=self.protocolclass.sms_quick_text() 0426 msg_entry.msg=entry['text'][:self.protocolclass.SMS_CANNED_MAX_LENGTH-1] 0427 sf.msgs.append(msg_entry) 0428 self.writeobject(self.protocolclass.SMS_CANNED_FILENAME, 0429 sf, logtitle="Writing quicktext", 0430 uselocalfs=False) 0431 0432 #------------------------------------------------------------------------------- 0433 parentprofile=com_lgvx4400.Profile 0434 class Profile(parentprofile): 0435 protocolclass=Phone.protocolclass 0436 serialsname=Phone.serialsname 0437 0438 BP_Calendar_Version=3 0439 phone_manufacturer='LG Electronics Inc' 0440 phone_model='LX570' 0441 0442 # Need to update this 0443 WALLPAPER_WIDTH=176 0444 WALLPAPER_HEIGHT=220 0445 # outside LCD: 128x160 0446 0447 imageorigins={} 0448 imageorigins.update(common.getkv(parentprofile.stockimageorigins, "images")) 0449 def GetImageOrigins(self): 0450 return self.imageorigins 0451 0452 ringtoneorigins=('my melodies', 'voice memo') 0453 excluded_ringtone_origins=('ringers', 'sounds', 'my melodies', 'voice memo') 0454 excluded_wallpaper_origins=('images',) 0455 0456 # our targets are the same for all origins 0457 # Need to work the correct resolutions 0458 imagetargets={} 0459 imagetargets.update(common.getkv(parentprofile.stockimagetargets, "wallpaper", 0460 {'width': 176, 'height': 220, 'format': "JPEG"})) 0461 imagetargets.update(common.getkv(parentprofile.stockimagetargets, "outsidelcd", 0462 {'width': 128, 'height': 160, 'format': "JPEG"})) 0463 0464 def GetTargetsForImageOrigin(self, origin): 0465 return self.imagetargets 0466 0467 0468 def convertphonebooktophone(self, helper, data): 0469 """Converts the data to what will be used by the phone 0470 0471 @param data: contains the dict returned by getfundamentals 0472 as well as where the results go""" 0473 results={} 0474 0475 self.normalisegroups(helper, data) 0476 0477 for pbentry in data['phonebook']: 0478 if len(results)==self.protocolclass.NUMPHONEBOOKENTRIES: 0479 break 0480 e={} # entry out 0481 entry=data['phonebook'][pbentry] # entry in 0482 try: 0483 # serials 0484 serial1=helper.getserial(entry.get('serials', []), self.serialsname, data['uniqueserial'], 'serial1', 0) 0485 serial2=helper.getserial(entry.get('serials', []), self.serialsname, data['uniqueserial'], 'serial2', serial1) 0486 0487 e['serial1']=serial1 0488 e['serial2']=serial2 0489 for ss in entry["serials"]: 0490 if ss["sourcetype"]=="bitpim": 0491 e['bitpimserial']=ss 0492 assert e['bitpimserial'] 0493 0494 # name 0495 e['name']=helper.getfullname(entry.get('names', []),1,1,22)[0] 0496 0497 # categories/groups 0498 cat=helper.makeone(helper.getcategory(entry.get('categories', []),0,1,22), None) 0499 if cat is None: 0500 e['group']=0 0501 else: 0502 key,value=self._getgroup(cat, data['groups']) 0503 if key is not None: 0504 e['group']=key 0505 else: 0506 # sorry no space for this category 0507 e['group']=0 0508 0509 # email addresses 0510 emails=helper.getemails(entry.get('emails', []) ,0,self.protocolclass.NUMEMAILS,48) 0511 e['emails']=helper.filllist(emails, self.protocolclass.NUMEMAILS, "") 0512 0513 # url 0514 e['url']=helper.makeone(helper.geturls(entry.get('urls', []), 0,1,48), "") 0515 0516 # memo (-1 is to leave space for null terminator - not all software puts it in, but we do) 0517 e['memo']=helper.makeone(helper.getmemos(entry.get('memos', []), 0, 1, self.protocolclass.MEMOLENGTH-1), "") 0518 0519 # phone numbers 0520 # there must be at least one email address or phonenumber 0521 minnumbers=1 0522 if len(emails): minnumbers=0 0523 numbers=helper.getnumbers(entry.get('numbers', []),minnumbers,self.protocolclass.NUMPHONENUMBERS) 0524 e['numbertypes']=[] 0525 e['numbers']=[] 0526 e['speeddials']=[] 0527 for numindex in range(len(numbers)): 0528 num=numbers[numindex] 0529 # deal with type 0530 b4=len(e['numbertypes']) 0531 type=num['type'] 0532 for i,t in enumerate(self.protocolclass.numbertypetab): 0533 if type==t: 0534 # some voodoo to ensure the second home becomes home2 0535 if i in e['numbertypes'] and t[-1]!='2': 0536 type+='2' 0537 continue 0538 e['numbertypes'].append(i) 0539 break 0540 if t=='none': # conveniently last entry 0541 e['numbertypes'].append(i) 0542 break 0543 if len(e['numbertypes'])==b4: 0544 # we couldn't find a type for the number 0545 helper.add_error_message('Number %s (%s/%s) not supported and ignored.'% 0546 (num['number'], e['name'], num['type'])) 0547 continue 0548 # deal with number 0549 number=self.phonize(num['number']) 0550 if len(number)==0: 0551 # no actual digits in the number 0552 continue 0553 if len(number)>48: # get this number from somewhere sensible 0554 # ::TODO:: number is too long and we have to either truncate it or ignore it? 0555 number=number[:48] # truncate for moment 0556 e['numbers'].append(number) 0557 # deal with speed dial 0558 sd=num.get("speeddial", -1) 0559 if self.protocolclass.NUMSPEEDDIALS: 0560 if sd>=self.protocolclass.FIRSTSPEEDDIAL and sd<=self.protocolclass.LASTSPEEDDIAL: 0561 e['speeddials'].append(sd) 0562 else: 0563 e['speeddials'].append(0xff) 0564 0565 if len(e['numbers'])<minnumbers: 0566 # we couldn't find any numbers 0567 # for this entry, so skip it, entries with no numbers cause error 0568 helper.add_error_message("Name: %s. No suitable numbers or emails found" % e['name']) 0569 continue 0570 e['numbertypes']=helper.filllist(e['numbertypes'], 5, 0) 0571 e['numbers']=helper.filllist(e['numbers'], 5, "") 0572 e['speeddials']=helper.filllist(e['speeddials'], 5, 0xff) 0573 0574 # ringtones, wallpaper 0575 e['ringtone']=helper.getringtone(entry.get('ringtones', []), 'call', None) 0576 0577 0578 results[pbentry]=e 0579 0580 except helper.ConversionFailed: 0581 continue 0582 0583 data['phonebook']=results 0584 return data 0585 0586 _supportedsyncs=( 0587 ('phonebook', 'read', None), # all phonebook reading 0588 ## ('calendar', 'read', None), # all calendar reading 0589 ('wallpaper', 'read', None), # all wallpaper reading 0590 ('ringtone', 'read', None), # all ringtone reading 0591 ## ('call_history', 'read', None),# all call history list reading 0592 ('sms', 'read', None), # all SMS list reading 0593 ('memo', 'read', None), # all memo list reading 0594 ('phonebook', 'write', 'OVERWRITE'), # only overwriting phonebook 0595 ## ('calendar', 'write', 'OVERWRITE'), # only overwriting calendar 0596 ## ('wallpaper', 'write', 'MERGE'), # merge and overwrite wallpaper 0597 ## ('wallpaper', 'write', 'OVERWRITE'), 0598 ('ringtone', 'write', 'MERGE'), # merge and overwrite ringtone 0599 ## ('ringtone', 'write', 'OVERWRITE'), 0600 ('sms', 'write', 'OVERWRITE'), # all SMS list writing 0601 ('memo', 'write', 'OVERWRITE'), # all memo list writing 0602 ## ('playlist', 'read', 'OVERWRITE'), 0603 ## ('playlist', 'write', 'OVERWRITE'), 0604 ) 0605 0606 field_color_data={ 0607 'phonebook': { 0608 'name': { 0609 'first': 1, 'middle': 1, 'last': 1, 'full': 1, 0610 'nickname': 0, 'details': 1 }, 0611 'number': { 0612 'type': 5, 'speeddial': 5, 'number': 5, 0613 'details': 5, 0614 'ringtone': False, 'wallpaper': False }, 0615 'email': 3, 0616 'email_details': { 0617 'emailspeeddial': False, 'emailringtone': False, 0618 'emailwallpaper': False }, 0619 'address': { 0620 'type': 0, 'company': 0, 'street': 0, 'street2': 0, 0621 'city': 0, 'state': 0, 'postalcode': 0, 'country': 0, 0622 'details': 0 }, 0623 'url': 1, 0624 'memo': 1, 0625 'category': 1, 0626 'wallpaper': 0, 0627 'ringtone': 0, 0628 'storage': 0, 0629 }, 0630 'calendar': { 0631 'description': False, 'location': False, 'allday': False, 0632 'start': False, 'end': False, 'priority': False, 0633 'alarm': False, 'vibrate': False, 0634 'repeat': False, 0635 'memo': False, 0636 'category': False, 0637 'wallpaper': False, 0638 'ringtone': False, 0639 }, 0640 'memo': { 0641 'subject': False, 0642 'date': False, 0643 'secret': False, 0644 'category': False, 0645 'memo': True, 0646 }, 0647 'todo': { 0648 'summary': False, 0649 'status': False, 0650 'due_date': False, 0651 'percent_complete': False, 0652 'completion_date': False, 0653 'private': False, 0654 'priority': False, 0655 'category': False, 0656 'memo': False, 0657 }, 0658 } 0659
Generated by PyXR 0.9.4