Package phones :: Module com_lgax8600
[hide private]
[frames] | no frames]

Source Code for Module phones.com_lgax8600

  1  #!/usr/bin/env python 
  2   
  3  ### BITPIM 
  4  ### 
  5  ###  
  6  ### 
  7  ### This program is free software; you can redistribute it and/or modify 
  8  ### it under the terms of the BitPim license as detailed in the LICENSE file. 
  9  ### by David Ritter 7/10/07 
 10  ### Write to phonebook working msg ringtones not supported 
 11  ### Write to Calendar, wallpaper, and ringtones is working 
 12  ### 
 13  ### $Id: com_lgax8600.py 4595 2008-02-14 03:04:01Z djpham $ 
 14   
 15  """ 
 16  Communicate with the LG AX8600 cell phone 
 17  """ 
 18  # standard modules 
 19  import re 
 20  import time 
 21  import cStringIO 
 22  import sha 
 23  import datetime 
 24   
 25  # BitPim modules 
 26  import common 
 27  import commport 
 28  import fileinfo 
 29  import guihelper 
 30  import os 
 31  import copy 
 32  import com_lgvx4400 
 33  import p_brew 
 34  import p_lgax8600 
 35  import com_lgvx8100 
 36  import com_brew 
 37  import com_phone 
 38  import com_lg 
 39  import prototypes 
 40  import bpcalendar 
 41  import call_history 
 42  import sms 
 43  import memo 
 44  import playlist 
 45  import helpids 
 46   
 47  #------------------------------------------------------------------------------- 
 48  #Went back to vx8100 to inherit to avoid LGUncountedIndexedMedia 
 49   
 50  parentphone=com_lgvx8100.Phone 
51 -class Phone(parentphone):
52 "Talk to LG AX-8600 cell phone" 53 54 desc="LG-AX8600" 55 helpid=helpids.ID_PHONE_LGAX8600 56 protocolclass=p_lgax8600 57 serialsname='lgax8600' 58 59 external_storage_root='mmc1/' 60 61 my_model='AX8600' 62 #Media locations for reading FROM the phone 63 builtinringtones= () 64 ringtonelocations= ( 65 # type index-file size-file directory-to-use lowest-index-to-use maximum-entries type-major icon index_offset 66 67 ( 'sounds', 'dload/lg_mysound.dat', 'dload/lg_mysoundsize.dat', 'brew/media/lk/ms', 150, 100, 0x402, 0, 0), 68 69 # ( 'sounds(sd)', 'dload/sd_sound.dat', 'mmc1/lg_media/other_sounds', '', 100, 0x02, None), 70 71 ) 72 73 calendarlocation="sch/schedule.dat" 74 calendarexceptionlocation="sch/schexception.dat" 75 calenderrequiresreboot=1 76 memolocation="sch/memo.dat" 77 78 builtinwallpapers = () 79 wallpaperlocations= ( 80 81 # type index-file size-file directory-to-use lowest-index-to-use maximum-entries type-major icon index_offset 82 ( 'images', 'dload/lg_image.dat', 'dload/lg_imagesize.dat', 'brew/media/lk/mp', 150 , 100, 0x400, 0, 0), 83 84 # ( 'images(sd)', 'dload/sd_image.dat', 'mmc1/lg_media/other_image', '', 100, 0x10, None), 85 # ( 'video', 'dload/lg_video.dat', 'brew/media/lk/mf', '', 100, 0x10, None), 86 # ( 'video(sd)', 'dload/sd_video.dat', 'mmc1/lg_media/other_video', '', 100, 0x13, None), 87 ) 88 # _rs_path=() 89 # for removable media (miniSD cards) 90 _rs_path='mmc1/' 91 _rs_ringers_path=_rs_path+'lg_media/other_sounds' 92 _rs_images_path=_rs_path+'lg_media/other_image' 93 94 #Media locations for writing TO the phone 95 96 media_info={ 'sounds': { 97 'localpath': 'brew/media/lk/ms', 98 'rspath': None, 99 'vtype': protocolclass.MEDIA_TYPE_SOUND, 100 'icon': protocolclass.MEDIA_IMAGE_DEFAULT_ICON, 101 'index': 150, 102 'maxsize': 100, 103 'indexfile': 'dload/lg_mysound.dat', 104 'sizefile': 'dload/lg_mysoundsize.dat', 105 'dunno': 0, 106 'date': False, 107 'dunno1': 0 }, 108 'images': { 109 'localpath': 'brew/media/lk/mp', 110 'rspath': None, 111 'vtype': protocolclass.MEDIA_TYPE_IMAGE, 112 'icon': protocolclass.MEDIA_IMAGE_DEFAULT_ICON, 113 'index': 150, 114 'maxsize': 100, 115 'indexfile': 'dload/lg_image.dat', 116 'sizefile': 'dload/lg_imagesize.dat', 117 'dunno': 0, 118 'date': False, 119 'dunno1': 0 }, 120 # 'video': { 121 # 'localpath': 'brew/16452/mf', 122 # 'rspath': None, 123 # 'vtype': protocolclass.MEDIA_TYPE_VIDEO, 124 # 'icon': protocolclass.MEDIA_VIDEO_DEFAULT_ICON, 125 # 'index': 1000, 126 # 'maxsize': 155, 127 # 'indexfile': 'dload/video.dat', 128 # 'sizefile': 'dload/videosize.dat', 129 # 'dunno': 0, 'date': True }, 130 } 131 132 133
134 - def __init__(self, logtarget, commport):
135 parentphone.__init__(self, logtarget, commport) 136 p_brew.PHONE_ENCODING=self.protocolclass.PHONE_ENCODING 137 self.mode=self.MODENONE
138 139
140 - def setDMversion(self):
141 self._DMv5=True 142 self._timeout = 30
143
144 - def get_esn(self, data=None):
145 # return the ESN of this phone 146 return self.get_brew_esn()
147
148 - def get_detect_data(self, res):
149 com_lgvx8100.Phone.get_detect_data(self, res) 150 res[self.esn_file_key]=self.get_esn()
151 152 #------------------------------------------------------ 153 # must be included for difference between alltel and verizon
154 - def getcallhistory(self, result):
155 res={} 156 # read the incoming call history file 157 # the telus lg8100 programmers were on something when they wrote their code. 158 if hasattr(self.protocolclass, 'this_takes_the_prize_for_the_most_brain_dead_call_history_file_naming_ive_seen'): 159 self._readhistoryfile("pim/missed_log.dat", 'Incoming', res) 160 self._readhistoryfile("pim/outgoing_log.dat", 'Missed', res) 161 self._readhistoryfile("pim/incoming_log.dat", 'Outgoing', res) 162 else: 163 self._readhistoryfile("pim/missed_log.dat", 'Missed', res) 164 self._readhistoryfile("pim/outgoing_log.dat", 'Outgoing', res) 165 self._readhistoryfile("pim/incoming_log.dat", 'Incoming', res) 166 self._readhistoryfile("pim/data_log.data", 'Data', res) 167 result['call_history']=res 168 return result
169 170 # Don't need this if there're no changes 171 ## def _readhistoryfile(self, fname, folder, res): 172 ## try: 173 ## buf=prototypes.buffer(self.getfilecontents(fname)) 174 ## ch=self.protocolclass.callhistory() 175 ## ch.readfrombuffer(buf, logtitle="Call History") 176 ## for call_idx in range(ch.numcalls): 177 ## call=ch.calls[call_idx] 178 ## if call.number=='' and call.name=='': 179 ## continue 180 ## entry=call_history.CallHistoryEntry() 181 ## entry.folder=folder 182 ## if call.duration: 183 ## entry.duration=call.duration 184 ## entry.datetime=((call.GPStime)) 185 ## if call.number=='': # restricted calls have no number 186 ## entry.number=call.name 187 ## else: 188 ## entry.number=call.number 189 ## if call.name: 190 ## entry.name=call.name 191 ## res[entry.id]=entry 192 ## except (com_brew.BrewNoSuchFileException, 193 ## IndexError): 194 ## pass # do nothing if file doesn't exist or is corrupted 195 ## return 196
197 - def getcalendar(self,result):
198 res={} 199 # Read exceptions file first 200 try: 201 buf=prototypes.buffer(self.getfilecontents(self.calendarexceptionlocation)) 202 ex=self.protocolclass.scheduleexceptionfile() 203 ex.readfrombuffer(buf, logtitle="Calendar exceptions") 204 exceptions={} 205 for i in ex.items: 206 try: 207 exceptions[i.pos].append( (i.year,i.month,i.day) ) 208 except KeyError: 209 exceptions[i.pos]=[ (i.year,i.month,i.day) ] 210 except com_brew.BrewNoSuchFileException: 211 exceptions={} 212 213 # Now read schedule 214 try: 215 buf=prototypes.buffer(self.getfilecontents(self.calendarlocation)) 216 if len(buf.getdata())<3: 217 # file is empty, and hence same as non-existent 218 raise com_brew.BrewNoSuchFileException() 219 sc=self.protocolclass.schedulefile() 220 sc.readfrombuffer(buf, logtitle="Calendar") 221 for event in sc.events: 222 # the vx8100 has a bad entry when the calender is empty 223 # stop processing the calender when we hit this record 224 if event.pos==0: #invalid entry 225 continue 226 entry=bpcalendar.CalendarEntry() 227 entry.desc_loc=event.description 228 try: # delete events are still in the calender file but have garbage dates 229 entry.start=event.start 230 if self.protocolclass.CALENDAR_HAS_SEPARATE_END_TIME_AND_DATE: 231 if event.repeat[0] == 0: # MIC: If non-repeating event 232 entry.end = event.end_time # MIC: Set entry.end to full end_time 233 else: 234 _,_,_,hour,minute=event.end_time 235 year,month,day,_,_=event.end_date 236 entry.end=(year,month,day,hour,minute) 237 else: 238 entry.end=event.end 239 except ValueError: 240 continue 241 if event.alarmindex_vibrate&0x1: 242 entry.vibrate=0 # vibarate bit is inverted in phone 0=on, 1=off 243 else: 244 entry.vibrate=1 245 entry.repeat = self.makerepeat(event.repeat) 246 min=event.alarmminutes 247 hour=event.alarmhours 248 if min==0x64 or hour==0x64: 249 entry.alarm=None # no alarm set 250 else: 251 entry.alarm=hour*60+min 252 if self.protocolclass.CALENDAR_HAS_SEPARATE_END_TIME_AND_DATE: 253 # MIC Unlike previous phones, the VX8300 passes the ringtone 254 # via both the index, and a path. If the index is set to 100 255 # (0x64), then the ringtone information will be found in the 256 # "ringpath", the last 256 bytes of the calendar packet. If 257 # the index is between 0 and 15, inclusive, then it is using 258 # one of the builtin ringers, and the ringpath is set to 259 # null. 260 try: 261 if (event.ringtone == 100): # MIC Ringer is downloaded to phone or microSD 262 entry.ringtone = common.basename(event.ringpath) 263 else: # MIC Ringer is built-in 264 entry.ringtone=self.builtinringtones[event.ringtone] 265 except: 266 # hack, not having a phone makes it hard to figure out the best approach 267 if entry.alarm==None: 268 entry.ringtone='No Ring' 269 else: 270 entry.ringtone='Loud Beeps' 271 else: 272 #hack for alltel 273 if entry.alarm==None: 274 entry.ringtone='No Ring' 275 else: 276 entry.ringtone='Loud Beeps' 277 # entry.ringtone=result['ringtone-index'][event.ringtone]['name'] ## This doesn't work on alltel 278 entry.snoozedelay=0 279 # check for exceptions and remove them 280 if event.repeat[3] and exceptions.has_key(event.pos): 281 for year, month, day in exceptions[event.pos]: 282 entry.suppress_repeat_entry(year, month, day) 283 res[entry.id]=entry 284 285 assert sc.numactiveitems==len(res) 286 except com_brew.BrewNoSuchFileException: 287 pass # do nothing if file doesn't exist 288 result['calendar']=res 289 return result
290
291 - def _build_pb_info(self, fundamentals):
292 # build a dict of info to update pbentry 293 pbook=fundamentals.get('phonebook', {}) 294 wallpaper_index=fundamentals.get('wallpaper-index', {}) 295 ringtone_index=fundamentals.get('ringtone-index', {}) 296 r1={} 297 for k,e in pbook.items(): 298 r1[e['bitpimserial']['id']]={ 'wallpaper': \ 299 self._findmediainindex(wallpaper_index, 300 e['wallpaper'], 301 e['name'], 302 'wallpaper'), 303 'msgringtone': \ 304 self._findmediainindex(ringtone_index, 305 e['msgringtone'], 306 e['name'], 307 'message ringtone')} 308 serialupdates=fundamentals.get("serialupdates", []) 309 r2={} 310 for bps, serials in serialupdates: 311 r2[serials['serial1']]=r1[bps['id']] 312 return r2
313
314 - def _update_pb_file(self, pb, fundamentals, pbinfo):
315 # update the pbentry file 316 update_flg=False 317 for e in pb.items: 318 _info=pbinfo.get(e.serial1, None) 319 if _info: 320 wp=_info.get('wallpaper', None) 321 if wp is not None and wp!=e.wallpaper: 322 update_flg=True 323 e.wallpaper=wp 324 rt=_info.get('ringtone', None) 325 if rt is not None and rt!=e.ringtone: 326 update_flg=True 327 e.ringtone=rt 328 if update_flg: 329 self.log('Updating wallpaper index') 330 buf=prototypes.buffer() 331 pb.writetobuffer(buf, logtitle="Updated index "+self.protocolclass.pb_file_name) 332 self.writefile(self.protocolclass.pb_file_name, buf.getvalue())
333 334
335 - def _update_pb_info(self, pbentries, fundamentals):
336 # Manually update phonebook data that the normal protocol should have 337 _pbinfo=self._build_pb_info(fundamentals) 338 self._update_pb_file(pbentries, fundamentals, _pbinfo)
339
340 - def _write_path_index(self, pbentries, pbmediakey, media_index, 341 index_file, invalid_values):
342 _path_entry=self.protocolclass.PathIndexEntry() 343 _path_file=self.protocolclass.PathIndexFile() 344 for _ in range(self.protocolclass.NUMPHONEBOOKENTRIES): 345 _path_file.items.append(_path_entry) 346 for _entry in pbentries.items: 347 _idx=getattr(_entry, pbmediakey) 348 if _idx in invalid_values or not media_index.has_key(_idx): 349 continue 350 if media_index[_idx].get('origin', None)=='builtin': 351 _filename=media_index[_idx]['name'] 352 elif media_index[_idx].get('filename', None): 353 _filename=media_index[_idx]['filename'] 354 else: 355 continue 356 _path_file.items[_entry.entrynumber]=self.protocolclass.PathIndexEntry( 357 pathname=_filename) 358 _buf=prototypes.buffer() 359 _path_file.writetobuffer(_buf, logtitle='Writing Path ID') 360 self.writefile(index_file, _buf.getvalue())
361 362 # Must be included due to changed indexfile format
363 - def _write_index_file(self, type):
364 _info=self.media_info.get(type, None) 365 366 if not _info: 367 return 368 _files={} 369 _local_dir=_info['localpath'] 370 371 _rs_dir=_info['rspath'] 372 _vtype=_info['vtype'] 373 _icon=_info['icon'] 374 _index=_info['index'] 375 _maxsize=_info['maxsize'] 376 _dunno=_info['dunno'] 377 _dunno1=_info['dunno1'] 378 indexfile=_info['indexfile'] 379 sizefile=_info['sizefile'] 380 _need_date=_info['date'] 381 try: 382 383 _files=self.listfiles(_local_dir) 384 except (com_brew.BrewNoSuchDirectoryException, 385 com_brew.BrewBadPathnameException): 386 pass 387 try: 388 if _rs_dir: 389 _rs_files=self.listfiles(_rs_dir) 390 391 if type=='ringers': 392 self._mark_files(_files, _rs_files, _local_dir) 393 _files.update(_rs_files) 394 except (com_brew.BrewNoSuchDirectoryException, 395 com_brew.BrewBadPathnameException): 396 # dir does not exist, no media files available 397 pass 398 # del all the markers (empty files) ringers 399 if type=='ringers': 400 _keys=_files.keys() 401 for _key in _keys: 402 if not _files[_key]['size']: 403 del _files[_key] 404 # dict of all indices 405 _idx_keys={} 406 for _i in xrange(_index, _index+_maxsize): 407 _idx_keys[_i]=True 408 # assign existing indices 409 for _item in self.getindex(indexfile): 410 if _files.has_key(_item.filename): 411 412 _files[_item.filename]['index']=_item.index 413 _idx_keys[_item.index]=False 414 # available new indices 415 _idx_keys_list=[k for k,x in _idx_keys.items() if x] 416 _idx_keys_list.sort() 417 _idx_cnt=0 418 # assign new indices 419 _file_list=[x for x in _files if not _files[x].get('index', None)] 420 _file_list.sort() 421 422 if len(_file_list)>len(_idx_keys_list): 423 _file_list=_file_list[:len(_idx_keys_list)] 424 for i in _file_list: 425 _files[i]['index']=_idx_keys_list[_idx_cnt] 426 _idx_cnt+=1 427 # (index, file name) list for writing 428 _res_list=[(x['index'],k) for k,x in _files.items() if x.get('index', None)] 429 _res_list.sort() 430 _res_list.reverse() 431 432 # writing the index file 433 ifile=self.protocolclass.indexfile() 434 _file_size=0 435 for index,idx in _res_list: 436 _fs_size=_files[idx]['size'] 437 ie=self.protocolclass.indexentry() 438 ie.index=index 439 ie.type=_vtype 440 ie.filename=idx 441 if _need_date: 442 # need to fill in the date value 443 _stat=self.statfile(_files[idx]['name']) 444 if _stat: 445 ie.date=_stat['datevalue']-time.timezone 446 ie.dunno=_dunno 447 ie.dunno1=_dunno1 448 ie.icon=_icon 449 ie.size=_fs_size 450 451 ifile.items.append(ie) 452 if not self._is_rs_file(idx): 453 _file_size+=_fs_size 454 buf=prototypes.buffer() 455 456 ifile.writetobuffer(buf, logtitle="Index file "+indexfile) 457 self.log("Writing index file "+indexfile+" for type "+type+" with "+`len(_res_list)`+" entries.") 458 self.writefile(indexfile, buf.getvalue()) 459 # writing the size file 460 if sizefile: 461 szfile=self.protocolclass.sizefile() 462 szfile.size=_file_size 463 buf=prototypes.buffer() 464 szfile.writetobuffer(buf, logtitle="Updated size file for "+type) 465 self.log("You are using a total of "+`_file_size`+" bytes for "+type) 466 self.writefile(sizefile, buf.getvalue())
467 468 # Must be there since there are no msg ringtones on ax8600 469
470 - def savephonebook(self, data):
471 "Saves out the phonebook" 472 473 res=com_lgvx4400.Phone.savephonebook(self, data) 474 # retrieve the phonebook entries 475 476 _buf=prototypes.buffer(self.getfilecontents(self.protocolclass.pb_file_name)) 477 _pb_entries=self.protocolclass.pbfile() 478 _pb_entries.readfrombuffer(_buf, logtitle="Read phonebook file "+self.protocolclass.pb_file_name) 479 480 # update info that the phone software failed to do!! 481 self._update_pb_info(_pb_entries, data) 482 483 return res
484 485 # Misc Stuff----------------------------------------------------------------
486 - def get_firmware_version(self):
487 # return the firmware version 488 req=p_brew.firmwarerequest() 489 res=self.sendbrewcommand(req, self.protocolclass.firmwareresponse) 490 return res.firmware
491
492 - def _unlock_key(self):
493 _req=self.protocolclass.LockKeyReq(lock=1) 494 self.sendbrewcommand(_req, self.protocolclass.data)
495 - def _lock_key(self):
496 _req=self.protocolclass.LockKeyReq() 497 self.sendbrewcommand(_req, self.protocolclass.data)
498
499 - def _press_key(self, keys):
500 # simulate a series of keypress 501 if not keys: 502 return 503 _req=self.protocolclass.KeyPressReq() 504 for _k in keys: 505 _req.key=_k 506 self.sendbrewcommand(_req, self.protocolclass.data)
507 508 509 #------------------------------------------------------------------------------- 510 parentprofile=com_lgvx8100.Profile
511 -class Profile(parentprofile):
512 protocolclass=Phone.protocolclass 513 serialsname=Phone.serialsname 514 515 BP_Calendar_Version=3 516 phone_manufacturer='LG Electronics Inc' 517 phone_model='AX8600' 518 # inside screen resoluation 519 WALLPAPER_WIDTH=176 520 WALLPAPER_HEIGHT=220 521 522 MAX_WALLPAPER_BASENAME_LENGTH=32 523 WALLPAPER_FILENAME_CHARS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_() ." 524 WALLPAPER_CONVERT_FORMAT="jpg" 525 526 # the 8300 uses "W" for wait in the dialstring, it does not support "T" 527 DIALSTRING_CHARS="[^0-9PW#*]" 528 529 MAX_RINGTONE_BASENAME_LENGTH=32 530 RINGTONE_FILENAME_CHARS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_() ." 531 RINGTONE_LIMITS= { 532 'MAXSIZE': 200000 533 } 534 # MSGRINGTONE=None 535 536 imageorigins={} 537 imageorigins.update(common.getkv(parentprofile.stockimageorigins, "images")) 538 # imageorigins.update(common.getkv(parentprofile.stockimageorigins, "video")) 539 # imageorigins.update(common.getkv(parentprofile.stockimageorigins, "images(sd)")) 540 # imageorigins.update(common.getkv(parentprofile.stockimageorigins, "video(sd)")) 541 542 # our targets are the same for all origins 543 imagetargets={} 544 ## imagetargets.update(common.getkv(parentprofile.stockimagetargets, "fullscreen", 545 ## {'width': 176, 'height': 220, 'format': "JPEG"})) 546 imagetargets.update(common.getkv(parentprofile.stockimagetargets, "wallpaper", 547 {'width': 176, 'height': 220, 'format': "JPEG"})) 548 # imagetargets.update(common.getkv(parentprofile.stockimagetargets, "outsidelcd", 549 # {'width': 128, 'height': 160, 'format': "JPEG"})) 550 ## imagetargets.update(common.getkv(parentprofile.stockimagetargets, "pictureid", 551 ## {'width': 128, 'height': 142, 'format': "JPEG"})) 552 553
554 - def GetImageOrigins(self):
555 return self.imageorigins
556 557 ringtoneorigins=('ringers', 'sounds') 558 excluded_ringtone_origins=('ringers') 559 excluded_wallpaper_origins=('video') 560 561
562 - def GetTargetsForImageOrigin(self, origin):
563 return self.imagetargets
564
565 - def QueryAudio(self, origin, currentextension, afi):
566 _max_size=self.RINGTONE_LIMITS['MAXSIZE'] 567 setattr(afi, 'MAXSIZE', _max_size) 568 # we don't modify any of these 569 if afi.format in ("MIDI", "QCP", "PMD", "WMA"): 570 return currentextension, afi 571 # examine mp3 572 if afi.format=="MP3": 573 if afi.channels==1 and 8<=afi.bitrate<=64 and 16000<=afi.samplerate<=22050: 574 return currentextension, afi 575 # convert it 576 return ("mp3", fileinfo.AudioFileInfo(afi, **{'format': 'MP3', 577 'channels': 2, 578 'bitrate': 48, 579 'samplerate': 44100, 580 'MAXSIZE': _max_size }))
581
582 - def __init__(self):
583 parentprofile.__init__(self)
584 585 586 _supportedsyncs=( 587 ('phonebook', 'read', None), # all phonebook reading 588 ('calendar', 'read', None), # all calendar reading 589 ('wallpaper', 'read', None), # all wallpaper reading 590 ('ringtone', 'read', None), # all ringtone reading 591 ('call_history', 'read', None),# all call history list reading 592 ## ('sms', 'read', None), # all SMS list reading 593 ('memo', 'read', None), # all memo list reading 594 ('phonebook', 'write', 'OVERWRITE'), # only overwriting phonebook 595 ('calendar', 'write', 'OVERWRITE'), # only overwriting calendar 596 ('wallpaper', 'write', 'MERGE'), # merge and overwrite wallpaper 597 ('wallpaper', 'write', 'OVERWRITE'), 598 ('ringtone', 'write', 'MERGE'), # merge and overwrite ringtone 599 ('ringtone', 'write', 'OVERWRITE'), 600 ## ('sms', 'write', 'OVERWRITE'), # all SMS list writing 601 ('memo', 'write', 'OVERWRITE'), # all memo list writing 602 ## ('t9_udb', 'read', 'OVERWRITE'), 603 ## ('t9_udb', 'write', 'OVERWRITE'), 604 ) 605 606 field_color_data={ 607 'phonebook': { 608 'name': { 609 'first': 1, 'middle': 1, 'last': 1, 'full': 1, 610 'nickname': 0, 'details': 1 }, 611 'number': { 612 'type': 5, 'speeddial': 5, 'number': 5, 'details': 5 }, 613 'email': 2, 614 'address': { 615 'type': 0, 'company': 0, 'street': 0, 'street2': 0, 616 'city': 0, 'state': 0, 'postalcode': 0, 'country': 0, 617 'details': 0 }, 618 'url': 0, 619 'memo': 0, 620 'category': 1, 621 'wallpaper': 1, 622 'ringtone': 2, 623 'storage': 0, 624 }, 625 'calendar': { 626 'description': True, 'location': False, 'allday': False, 627 'start': True, 'end': True, 'priority': False, 628 'alarm': True, 'vibrate': True, 629 'repeat': True, 630 'memo': False, 631 'category': False, 632 'wallpaper': False, 633 'ringtone': True, 634 }, 635 'memo': { 636 'subject': True, 637 'date': True, 638 'secret': False, 639 'category': False, 640 'memo': True, 641 }, 642 'todo': { 643 'summary': False, 644 'status': False, 645 'due_date': False, 646 'percent_complete': False, 647 'completion_date': False, 648 'private': False, 649 'priority': False, 650 'category': False, 651 'memo': False, 652 }, 653 }
654