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

Source Code for Module phones.com_lgvx8100

  1  ### BITPIM 
  2  ### 
  3  ### Copyright (C) 2003-2006 Roger Binns <rogerb@rogerbinns.com> 
  4  ### Copyright (C) 2006-2006 Simon Capper <skyjunky@sbcglobal.net> 
  5  ### Copyright (C) 2006 Michael Cohen <mikepublic@nc.rr.com> 
  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  ### 
 10  ### $Id: com_lgvx8100.py 4481 2007-12-05 22:39:17Z djpham $ 
 11   
 12  """Communicate with the LG VX8100 cell phone 
 13   
 14  The VX8100 is similar to the VX7000 but also supports video. 
 15   
 16  """ 
 17   
 18  # standard modules 
 19  import re 
 20  import time 
 21  import cStringIO 
 22  import sha 
 23   
 24  # my modules 
 25  import common 
 26  import commport 
 27  import copy 
 28  import com_lgvx4400 
 29  import p_brew 
 30  import p_lgvx8100 
 31  import com_lgvx7000 
 32  import com_brew 
 33  import com_phone 
 34  import com_lg 
 35  import prototypes 
 36  import bpcalendar 
 37  import call_history 
 38  import sms 
 39  import memo 
 40  import helpids 
 41   
 42   
 43  from prototypes import * 
 44   
 45   
 46   
47 -class Phone(com_lg.LGNewIndexedMedia2, com_lg.LGDMPhone, com_lgvx7000.Phone):
48 "Talk to the LG VX8100 cell phone" 49 50 desc="LG-VX8100" 51 helpid=helpids.ID_PHONE_LGVX8100 52 protocolclass=p_lgvx8100 53 serialsname='lgvx8100' 54 55 builtinringtones= ('Low Beep Once', 'Low Beeps', 'Loud Beep Once', 'Loud Beeps', 'VZW Default Ringtone') + \ 56 tuple(['Ringtone '+`n` for n in range(1,11)]) + \ 57 ('No Ring',) 58 59 ringtonelocations= ( 60 # type index-file size-file directory-to-use lowest-index-to-use maximum-entries type-major icon 61 ( 'ringers', 'dload/my_ringtone.dat', 'dload/my_ringtonesize.dat', 'brew/16452/lk/mr', 100, 150, 0x201, 1, 0), 62 # the sound index file uses the same index as the ringers, bitpim does not support this (yet) 63 ( 'sounds', 'dload/mysound.dat', 'dload/mysoundsize.dat', 'brew/16452/ms', 100, 150, 2, 0, 151), 64 ) 65 66 calendarlocation="sch/newschedule.dat" 67 calendarexceptionlocation="sch/newschexception.dat" 68 calenderrequiresreboot=0 69 memolocation="sch/neomemo.dat" 70 71 builtinwallpapers = () # none 72 73 wallpaperlocations= ( 74 ( 'images', 'dload/image.dat', 'dload/imagesize.dat', 'brew/16452/mp', 100, 50, 0, 0, 0), 75 ( 'video', 'dload/video.dat', None, 'brew/16452/mf', 1000, 50, 0x0304, 0, 0), 76 ) 77 78 # for removable media (miniSD cards) 79 _rs_path='mmc1/' 80 _rs_ringers_path=_rs_path+'ringers' 81 _rs_images_path=_rs_path+'images' 82 media_info={ 'ringers': { 83 'localpath': 'brew/16452/lk/mr', 84 'rspath': _rs_ringers_path, 85 'vtype': protocolclass.MEDIA_TYPE_RINGTONE, 86 'icon': protocolclass.MEDIA_RINGTONE_DEFAULT_ICON, 87 'index': 100, # starting index 88 'maxsize': 155, 89 'indexfile': 'dload/my_ringtone.dat', 90 'sizefile': 'dload/my_ringtonesize.dat', 91 'dunno': 0, 'date': False, 92 }, 93 'sounds': { 94 'localpath': 'brew/16452/ms', 95 'rspath': None, 96 'vtype': protocolclass.MEDIA_TYPE_SOUND, 97 'icon': protocolclass.MEDIA_IMAGE_DEFAULT_ICON, 98 'index': 100, 99 'maxsize': 155, 100 'indexfile': 'dload/mysound.dat', 101 'sizefile': 'dload/mysoundsize.dat', 102 'dunno': 0, 'date': False }, 103 'images': { 104 'localpath': 'brew/16452/mp', 105 'rspath': _rs_images_path, 106 'vtype': protocolclass.MEDIA_TYPE_IMAGE, 107 'icon': protocolclass.MEDIA_IMAGE_DEFAULT_ICON, 108 'index': 100, 109 'maxsize': 155, 110 'indexfile': 'dload/image.dat', 111 'sizefile': 'dload/imagesize.dat', 112 'dunno': 0, 'date': False }, 113 'video': { 114 'localpath': 'brew/16452/mf', 115 'rspath': None, 116 'vtype': protocolclass.MEDIA_TYPE_VIDEO, 117 'icon': protocolclass.MEDIA_VIDEO_DEFAULT_ICON, 118 'index': 1000, 119 'maxsize': 155, 120 'indexfile': 'dload/video.dat', 121 'sizefile': 'dload/videosize.dat', 122 'dunno': 0, 'date': True }, 123 } 124
125 - def __init__(self, logtarget, commport):
126 com_lgvx4400.Phone.__init__(self, logtarget, commport) 127 com_lg.LGDMPhone.__init__(self) 128 self.mode=self.MODENONE
129
130 - def get_firmware_version(self):
131 # return the firmware version 132 req=p_brew.firmwarerequest() 133 res=self.sendbrewcommand(req, self.protocolclass.firmwareresponse) 134 return res.firmware
135
136 - def setDMversion(self):
137 """Define the DM version required for this phone, default to DMv5""" 138 # not sure what the 8100 requirements are, default to v4 only 139 self._DMv5=False
140 141 # Fundamentals: 142 # - get_esn - /nvm/$ESN.SYS (same as LG VX-4400) 143 # - getgroups - defined below 144 # - getwallpaperindices - LGNewIndexedMedia2 145 # - getringtoneindices - LGNewIndexedMedia2 146 # - DM Version - 4 to access brew/16452/lk/mr on newer firmwares 147
148 - def getgroups(self, results):
149 self.log("Reading group information") 150 buf=prototypes.buffer(self.getfilecontents("pim/pbgroup.dat")) 151 g=self.protocolclass.pbgroups() 152 g.readfrombuffer(buf, logtitle="Groups read") 153 groups={} 154 for i in range(len(g.groups)): 155 if len(g.groups[i].name): # sometimes have zero length names 156 groups[i]={'name': g.groups[i].name } 157 results['groups']=groups 158 return groups
159
160 - def savegroups(self, data):
161 groups=data['groups'] 162 keys=groups.keys() 163 keys.sort() 164 g=self.protocolclass.pbgroups() 165 for k in keys: 166 e=self.protocolclass.pbgroup() 167 e.name=groups[k]['name'] 168 g.groups.append(e) 169 buffer=prototypes.buffer() 170 g.writetobuffer(buffer, logtitle="New group file") 171 self.writefile("pim/pbgroup.dat", buffer.getvalue())
172
173 - def getmemo(self, result):
174 # read the memo file 175 res={} 176 try: 177 stat_res=self.statfile(self.memolocation) 178 # allow for zero length file 179 if stat_res!=None and stat_res['size']!=0: 180 buf=prototypes.buffer(self.getfilecontents(self.memolocation)) 181 text_memo=self.protocolclass.textmemofile() 182 text_memo.readfrombuffer(buf, logtitle="Read memo file"+self.memolocation) 183 for m in text_memo.items: 184 entry=memo.MemoEntry() 185 entry.text=m.text 186 try: 187 entry.set_date_isostr("%d%02d%02dT%02d%02d00" % ((m.memotime))) 188 except ValueError: 189 # deleted memos can remain in file but have a bogus date 190 continue 191 res[entry.id]=entry 192 except com_brew.BrewNoSuchFileException: 193 pass 194 result['memo']=res 195 return result
196
197 - def savememo(self, result, merge):
198 text_memo=self.protocolclass.textmemofile() 199 memo_dict=result.get('memo', {}) 200 keys=memo_dict.keys() 201 keys.sort() 202 text_memo.itemcount=len(keys) 203 for k in keys: 204 entry=self.protocolclass.textmemo() 205 entry.text=memo_dict[k].text 206 t=time.strptime(memo_dict[k].date, '%b %d, %Y %H:%M') 207 entry.memotime=(t.tm_year, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min) 208 if 'GPStime' in entry.getfields(): 209 entry.GPStime = (t.tm_year, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec) 210 text_memo.items.append(entry) 211 buf=prototypes.buffer() 212 text_memo.writetobuffer(buf, logtitle="Updated memo "+self.memolocation) 213 self.writefile(self.memolocation, buf.getvalue()) 214 return result
215
216 - def getexceptions (self):
217 try: 218 buf=prototypes.buffer(self.getfilecontents(self.calendarexceptionlocation)) 219 ex=self.protocolclass.scheduleexceptionfile() 220 ex.readfrombuffer(buf, logtitle="Calendar exceptions") 221 exceptions={} 222 for i in ex.items: 223 try: 224 exceptions[i.pos].append( (i.year,i.month,i.day) ) 225 except KeyError: 226 exceptions[i.pos]=[ (i.year,i.month,i.day) ] 227 except com_brew.BrewNoSuchFileException: 228 exceptions={} 229 230 return exceptions
231
232 - def getcalendarcommon(self, entry, event):
233 entry.desc_loc=event.description 234 entry.start=event.start 235 if self.protocolclass.CALENDAR_HAS_SEPARATE_END_TIME_AND_DATE: 236 if event.repeat[0] == 0: # MIC: If non-repeating event 237 entry.end = event.end_time # MIC: Set entry.end to full end_time 238 else: 239 _,_,_,hour,minute=event.end_time 240 year,month,day,_,_=event.end_date 241 entry.end=(year,month,day,hour,minute) 242 else: 243 entry.end=event.end 244 if event.alarmindex_vibrate&0x1: 245 entry.vibrate=0 # vibarate bit is inverted in phone 0=on, 1=off 246 else: 247 entry.vibrate=1 248 entry.repeat = self.makerepeat(event.repeat) 249 min=event.alarmminutes 250 hour=event.alarmhours 251 if min==0x64 or hour==0x64: 252 entry.alarm=None # no alarm set 253 else: 254 entry.alarm=hour*60+min 255 entry.snoozedelay=0
256
257 - def getcalendar(self,result):
258 res={} 259 # Read exceptions file first 260 exceptions = self.getexceptions () 261 262 # Now read schedule 263 try: 264 buf=prototypes.buffer(self.getfilecontents(self.calendarlocation)) 265 if len(buf.getdata())<3: 266 # file is empty, and hence same as non-existent 267 raise com_brew.BrewNoSuchFileException() 268 sc=self.protocolclass.schedulefile() 269 sc.readfrombuffer(buf, logtitle="Calendar") 270 for event in sc.events: 271 # the vx8100 has a bad entry when the calender is empty 272 # stop processing the calender when we hit this record 273 if event.pos==0: #invalid entry 274 continue 275 entry=bpcalendar.CalendarEntry() 276 try: # delete events are still in the calender file but have garbage dates 277 self.getcalendarcommon (entry, event) 278 except ValueError: 279 continue 280 if self.protocolclass.CALENDAR_HAS_SEPARATE_END_TIME_AND_DATE: 281 # MIC Unlike previous phones, the VX8300 passes the ringtone 282 # via both the index, and a path. If the index is set to 100 283 # (0x64), then the ringtone information will be found in the 284 # "ringpath", the last 256 bytes of the calendar packet. If 285 # the index is between 0 and 15, inclusive, then it is using 286 # one of the builtin ringers, and the ringpath is set to 287 # null. 288 try: 289 if (event.ringtone == 100): # MIC Ringer is downloaded to phone or microSD 290 entry.ringtone = common.basename(event.ringpath) 291 else: # MIC Ringer is built-in 292 entry.ringtone=self.builtinringtones[event.ringtone] 293 except: 294 # hack, not having a phone makes it hard to figure out the best approach 295 if entry.alarm==None: 296 entry.ringtone='No Ring' 297 else: 298 entry.ringtone='Loud Beeps' 299 else: 300 entry.ringtone=result['ringtone-index'][event.ringtone]['name'] 301 # check for exceptions and remove them 302 if event.repeat[3] and exceptions.has_key(event.pos): 303 for year, month, day in exceptions[event.pos]: 304 entry.suppress_repeat_entry(year, month, day) 305 res[entry.id]=entry 306 307 assert sc.numactiveitems==len(res) 308 except com_brew.BrewNoSuchFileException: 309 pass # do nothing if file doesn't exist 310 result['calendar']=res 311 return result
312
313 - def makerepeat(self, repeat):
314 # get all the variables out of the repeat tuple 315 # and convert into a bpcalender RepeatEntry 316 type,dow,interval,interval2,exceptions=repeat 317 if type==0: 318 repeat_entry=None 319 else: 320 repeat_entry=bpcalendar.RepeatEntry() 321 if type==1: #daily 322 repeat_entry.repeat_type=repeat_entry.daily 323 repeat_entry.interval=interval 324 elif type==5: #'monfri' 325 repeat_entry.repeat_type=repeat_entry.daily 326 repeat_entry.interval=0 327 elif type==2: #'weekly' 328 repeat_entry.repeat_type=repeat_entry.weekly 329 repeat_entry.dow=dow 330 repeat_entry.interval=interval 331 elif type==3: #'monthly' 332 repeat_entry.repeat_type=repeat_entry.monthly 333 repeat_entry.interval2=interval2 334 repeat_entry.dow=0 335 elif type==6: #'monthly' #Xth Y day (e.g. 2nd friday each month) 336 repeat_entry.repeat_type=repeat_entry.monthly 337 repeat_entry.interval=interval #X 338 repeat_entry.dow=dow #Y 339 repeat_entry.interval2=interval2 340 else: # =4 'yearly' 341 repeat_entry.repeat_type=repeat_entry.yearly 342 return repeat_entry
343
344 - def _scheduleexceptions(self, entry, data, exceptionsf):
345 exceptions=0 346 if entry.repeat!=None: 347 if self.protocolclass.CALENDAR_HAS_SEPARATE_END_TIME_AND_DATE: 348 if data.end_date[:3]==entry.no_end_date: 349 year_s,month_s,day_s,hour_s,minute_s=entry.start 350 data.end_date=(year_s + 5, month_s, day_s, 31, 63) 351 else: 352 if data.end[:3]==entry.no_end_date: 353 data.end=(2100, 12, 31)+data.end[3:] 354 for i in entry.repeat.suppressed: 355 de=self.protocolclass.scheduleexception() 356 de.pos=data.pos 357 de.day=i.date.day 358 de.month=i.date.month 359 de.year=i.date.year 360 exceptions=1 361 exceptionsf.items.append(de) 362 if entry.repeat != None: 363 data.repeat=(self.getrepeattype(entry, exceptions)) 364 else: 365 data.repeat=((0,0,0,0,0))
366
367 - def _schedulecommon(self, entry, data):
368 data.description=entry.desc_loc 369 data.start=entry.start 370 if self.protocolclass.CALENDAR_HAS_SEPARATE_END_TIME_AND_DATE: 371 year_e,month_e,day_e,hour_e,minute_e=entry.end 372 year_s,month_s,day_s,hour_s,minute_s=entry.start 373 if entry.repeat!=None: # MIC: Added check for repeating event 374 data.end_date = (year_e,month_e,day_e,31,63) 375 else: 376 data.end_date = (year_s+5,month_s,day_s,31,63) 377 data.end_time = (year_s,month_s,day_s,hour_e,minute_e) 378 else: 379 data.end=entry.end 380 data.ringtone=0 381 data.unknown1=0
382
383 - def savecalendar(self, dict, merge):
384 # ::TODO:: 385 # what will be written to the files 386 eventsf=self.protocolclass.schedulefile() 387 exceptionsf=self.protocolclass.scheduleexceptionfile() 388 # what are we working with 389 cal=dict['calendar'] 390 newcal={} 391 #sort into start order, makes it possible to see if the calendar has changed 392 keys=[(x.start, k) for k,x in cal.items()] 393 keys.sort() 394 # apply limiter 395 keys=keys[:self.protocolclass.NUMCALENDARENTRIES] 396 # number of entries 397 eventsf.numactiveitems=len(keys) 398 pos=1 399 contains_alarms=False 400 # play with each entry 401 for (_,k) in keys: 402 # entry is what we will return to user 403 entry=cal[k] 404 data=self.protocolclass.scheduleevent() 405 data.pos=eventsf.packetsize() 406 self._schedulecommon(entry, data) 407 alarm_set=self.setalarm(entry, data) 408 if alarm_set: 409 if entry.ringtone=="No Ring" and not entry.vibrate: 410 alarm_name="Low Beep Once" 411 else: 412 alarm_name=entry.ringtone 413 else: # set alarm to "No Ring" gets rid of alarm icon on phone 414 alarm_name="No Ring" 415 if self.protocolclass.CALENDAR_HAS_SEPARATE_END_TIME_AND_DATE: 416 # MIC VX8300 handles the ringtone differently than previous 417 # phones. Except for the 16 builtin ringers, ringers stored 418 # directly on the phone or on the microSD are specified by 419 # their fully qualified path, and an index of 100. 420 for i in dict['ringtone-index']: 421 if dict['ringtone-index'][i]['name']==alarm_name: 422 if dict['ringtone-index'][i].get('filename', None): 423 data.ringtone=100 424 data.ringpath=dict['ringtone-index'][i]['filename'] 425 else: 426 # builtin ringer 427 data.ringtone=i # Set to proper index 428 break 429 else: 430 data.unknown2=0 # MIC Only unknown in VX8100 and prior 431 # Moved up from line below, as we do not 432 # want it defined for the VX8300+ 433 for i in dict['ringtone-index']: 434 if dict['ringtone-index'][i]['name']==alarm_name: 435 data.ringtone=i 436 break 437 # check for exceptions and add them to the exceptions list 438 self._scheduleexceptions(entry, data, exceptionsf) 439 if data.alarmindex_vibrate!=1: # if alarm set 440 contains_alarms=True 441 # put entry in nice shiny new dict we are building 442 entry=copy.copy(entry) 443 newcal[data.pos]=entry 444 eventsf.events.append(data) 445 446 buf=prototypes.buffer() 447 eventsf.writetobuffer(buf, logtitle="New Calendar") 448 self.writefile(self.calendarlocation, buf.getvalue()) 449 if contains_alarms or self.calenderrequiresreboot: 450 self.log("Your phone has to be rebooted due to the calendar changing") 451 dict["rebootphone"]=True 452 453 buf=prototypes.buffer() 454 exceptionsf.writetobuffer(buf, logtitle="Writing calendar exceptions") 455 self.writefile(self.calendarexceptionlocation, buf.getvalue()) 456 457 # fix passed in dict 458 dict['calendar']=newcal 459 460 return dict
461
462 - def getrepeattype(self, entry, exceptions):
463 #convert the bpcalender type into vx8100 type 464 repeat_entry=bpcalendar.RepeatEntry() 465 interval2=0 466 if entry.repeat.repeat_type==repeat_entry.monthly: 467 dow=entry.repeat.dow 468 interval2=entry.repeat.interval2 469 if entry.repeat.dow==0: 470 # set interval for month type 4 to start day of month, (required by vx8100) 471 interval=entry.start[2] 472 type=3 473 else: 474 interval=entry.repeat.interval 475 type=6 476 elif entry.repeat.repeat_type==repeat_entry.daily: 477 dow=entry.repeat.dow 478 interval=entry.repeat.interval 479 if entry.repeat.interval==0: 480 type=5 481 else: 482 type=1 483 elif entry.repeat.repeat_type==repeat_entry.weekly: 484 dow=entry.repeat.dow 485 interval=entry.repeat.interval 486 type=2 487 elif entry.repeat.repeat_type==repeat_entry.yearly: 488 # set interval to start day of month, (required by vx8100) 489 interval=entry.start[2] 490 # set dow to start month, (required by vx8100) 491 dow=entry.start[1] 492 type=4 493 return (type, dow, interval, interval2, exceptions)
494
495 - def setalarm(self, entry, data):
496 # vx8100 only allows certain repeat intervals, adjust to fit, it also stores an index to the interval 497 rc=True 498 if entry.alarm>=2880: 499 entry.alarm=2880 500 data.alarmminutes=0 501 data.alarmhours=48 502 data.alarmindex_vibrate=0x10 503 elif entry.alarm>=1440: 504 entry.alarm=1440 505 data.alarmminutes=0 506 data.alarmhours=24 507 data.alarmindex_vibrate=0xe 508 elif entry.alarm>=120: 509 entry.alarm=120 510 data.alarmminutes=0 511 data.alarmhours=2 512 data.alarmindex_vibrate=0xc 513 elif entry.alarm>=60: 514 entry.alarm=60 515 data.alarmminutes=0 516 data.alarmhours=1 517 data.alarmindex_vibrate=0xa 518 elif entry.alarm>=15: 519 entry.alarm=15 520 data.alarmminutes=15 521 data.alarmhours=0 522 data.alarmindex_vibrate=0x8 523 elif entry.alarm>=10: 524 entry.alarm=10 525 data.alarmminutes=10 526 data.alarmhours=0 527 data.alarmindex_vibrate=0x6 528 elif entry.alarm>=5: 529 entry.alarm=5 530 data.alarmminutes=5 531 data.alarmhours=0 532 data.alarmindex_vibrate=0x4 533 elif entry.alarm>=0: 534 entry.alarm=0 535 data.alarmminutes=0 536 data.alarmhours=0 537 data.alarmindex_vibrate=0x2 538 else: # no alarm 539 data.alarmminutes=0x64 540 data.alarmhours=0x64 541 data.alarmindex_vibrate=1 542 rc=False 543 544 # set the vibrate bit 545 if data.alarmindex_vibrate > 1 and entry.vibrate==0: 546 data.alarmindex_vibrate+=1 547 return rc
548 549 my_model='VX8100' 550
551 - def getphoneinfo(self, phone_info):
552 self.log('Getting Phone Info') 553 try: 554 s=self.getfilecontents('brew/version.txt') 555 if s[:6]==self.my_model: 556 phone_info.append('Model:', self.my_model) 557 phone_info.append('ESN:', self.get_brew_esn()) 558 req=p_brew.firmwarerequest() 559 res=self.sendbrewcommand(req, self.protocolclass.firmwareresponse) 560 phone_info.append('Firmware Version:', res.firmware) 561 txt=self.getfilecontents("nvm/nvm/nvm_cdma")[180:190] 562 phone_info.append('Phone Number:', txt) 563 except: 564 pass 565 return
566 567 # Media stuff---------------------------------------------------------------
568 - def _is_rs_file(self, filename):
569 return filename.startswith(self._rs_path)
570
571 - def getmedia(self, maps, results, key):
572 origins={} 573 # signal that we are using the new media storage that includes the origin and timestamp 574 origins['new_media_version']=1 575 576 for type, indexfile, sizefile, directory, lowestindex, maxentries, typemajor, def_icon, idx_ofs in maps: 577 media={} 578 for item in self.getindex(indexfile): 579 data=None 580 timestamp=None 581 try: 582 stat_res=self.statfile(item.filename) 583 if stat_res!=None: 584 timestamp=stat_res['date'][0] 585 if not self._is_rs_file(item.filename): 586 data=self.getfilecontents(item.filename, True) 587 except (com_brew.BrewNoSuchFileException,com_brew.BrewBadPathnameException,com_brew.BrewNameTooLongException): 588 self.log("It was in the index, but not on the filesystem") 589 except com_brew.BrewAccessDeniedException: 590 # firmware wouldn't let us read this file, just mark it then 591 self.log('Failed to read file: '+item.filename) 592 data='' 593 if data!=None: 594 media[common.basename(item.filename)]={ 'data': data, 'timestamp': timestamp} 595 origins[type]=media 596 597 results[key]=origins 598 return results
599
600 - def _mark_files(self, local_files, rs_files, local_dir):
601 # create empty local files as markers for remote files 602 _empty_files=[common.basename(x) for x,_entry in local_files.items() \ 603 if not _entry['size']] 604 _remote_files=[common.basename(x) for x in rs_files] 605 for _file in _remote_files: 606 if _file not in _empty_files: 607 # mark this one 608 self.writefile(local_dir+'/'+_file, '') 609 for _file in _empty_files: 610 if _file not in _remote_files: 611 # remote file no longer exists, del the marker 612 self.rmfile(local_dir+'/'+_file)
613
614 - def _write_index_file(self, type):
615 _info=self.media_info.get(type, None) 616 if not _info: 617 return 618 _files={} 619 _local_dir=_info['localpath'] 620 _rs_dir=_info['rspath'] 621 _vtype=_info['vtype'] 622 _icon=_info['icon'] 623 _index=_info['index'] 624 _maxsize=_info['maxsize'] 625 _dunno=_info['dunno'] 626 indexfile=_info['indexfile'] 627 sizefile=_info['sizefile'] 628 _need_date=_info['date'] 629 try: 630 _files=self.listfiles(_local_dir) 631 except (com_brew.BrewNoSuchDirectoryException, 632 com_brew.BrewBadPathnameException): 633 pass 634 try: 635 if _rs_dir: 636 _rs_files=self.listfiles(_rs_dir) 637 if type=='ringers': 638 self._mark_files(_files, _rs_files, _local_dir) 639 _files.update(_rs_files) 640 except (com_brew.BrewNoSuchDirectoryException, 641 com_brew.BrewBadPathnameException): 642 # dir does not exist, no media files available 643 pass 644 # del all the markers (empty files) ringers 645 if type=='ringers': 646 _keys=_files.keys() 647 for _key in _keys: 648 if not _files[_key]['size']: 649 del _files[_key] 650 # dict of all indices 651 _idx_keys={} 652 for _i in xrange(_index, _index+_maxsize): 653 _idx_keys[_i]=True 654 # assign existing indices 655 for _item in self.getindex(indexfile): 656 if _files.has_key(_item.filename): 657 _files[_item.filename]['index']=_item.index 658 _idx_keys[_item.index]=False 659 # available new indices 660 _idx_keys_list=[k for k,x in _idx_keys.items() if x] 661 _idx_keys_list.sort() 662 _idx_cnt=0 663 # assign new indices 664 _file_list=[x for x in _files if not _files[x].get('index', None)] 665 _file_list.sort() 666 if len(_file_list)>len(_idx_keys_list): 667 _file_list=_file_list[:len(_idx_keys_list)] 668 for i in _file_list: 669 _files[i]['index']=_idx_keys_list[_idx_cnt] 670 _idx_cnt+=1 671 # (index, file name) list for writing 672 _res_list=[(x['index'],k) for k,x in _files.items() if x.get('index', None)] 673 _res_list.sort() 674 _res_list.reverse() 675 # writing the index file 676 ifile=self.protocolclass.indexfile() 677 _file_size=0 678 for index,idx in _res_list: 679 _fs_size=_files[idx]['size'] 680 ie=self.protocolclass.indexentry() 681 ie.index=index 682 ie.type=_vtype 683 ie.filename=idx 684 if _need_date: 685 # need to fill in the date value 686 _stat=self.statfile(_files[idx]['name']) 687 if _stat: 688 ie.date=_stat['datevalue']-time.timezone 689 ie.dunno=_dunno 690 ie.icon=_icon 691 ie.size=_fs_size 692 ifile.items.append(ie) 693 if not self._is_rs_file(idx): 694 _file_size+=_fs_size 695 buf=prototypes.buffer() 696 ifile.writetobuffer(buf, logtitle="Index file "+indexfile) 697 self.log("Writing index file "+indexfile+" for type "+type+" with "+`len(_res_list)`+" entries.") 698 self.writefile(indexfile, buf.getvalue()) 699 # writing the size file 700 if sizefile: 701 szfile=self.protocolclass.sizefile() 702 szfile.size=_file_size 703 buf=prototypes.buffer() 704 szfile.writetobuffer(buf, logtitle="Updated size file for "+type) 705 self.log("You are using a total of "+`_file_size`+" bytes for "+type) 706 self.writefile(sizefile, buf.getvalue())
707
708 - def savemedia(self, mediakey, mediaindexkey, maps, results, merge, reindexfunction):
709 """Actually saves out the media 710 711 @param mediakey: key of the media (eg 'wallpapers' or 'ringtones') 712 @param mediaindexkey: index key (eg 'wallpaper-index') 713 @param maps: list index files and locations 714 @param results: results dict 715 @param merge: are we merging or overwriting what is there? 716 @param reindexfunction: the media is re-indexed at the end. this function is called to do it 717 """ 718 719 # take copies of the lists as we modify them 720 wp=results[mediakey].copy() # the media we want to save 721 wpi=results[mediaindexkey].copy() # what is already in the index files 722 723 # remove builtins 724 for k in wpi.keys(): 725 if wpi[k].get('origin', "")=='builtin': 726 del wpi[k] 727 728 # build up list into init 729 init={} 730 for type,_,_,_,lowestindex,_,typemajor,_,_ in maps: 731 init[type]={} 732 for k in wpi.keys(): 733 if wpi[k]['origin']==type: 734 index=k 735 name=wpi[k]['name'] 736 fullname=wpi[k]['filename'] 737 vtype=wpi[k]['vtype'] 738 icon=wpi[k]['icon'] 739 data=None 740 del wpi[k] 741 for w in wp.keys(): 742 # does wp contain a reference to this same item? 743 if wp[w]['name']==name and wp[w]['origin']==type: 744 data=wp[w]['data'] 745 del wp[w] 746 if not merge and data is None: 747 # delete the entry 748 continue 749 ## assert index>=lowestindex 750 init[type][index]={'name': name, 'data': data, 'filename': fullname, 'vtype': vtype, 'icon': icon} 751 752 # init now contains everything from wallpaper-index 753 # wp contains items that we still need to add, and weren't in the existing index 754 assert len(wpi)==0 755 print init.keys() 756 757 # now look through wallpapers and see if anything was assigned a particular 758 # origin 759 for w in wp.keys(): 760 o=wp[w].get("origin", "") 761 if o is not None and len(o) and o in init: 762 idx=-1 763 while idx in init[o]: 764 idx-=1 765 init[o][idx]=wp[w] 766 del wp[w] 767 768 # wp will now consist of items that weren't assigned any particular place 769 # so put them in the first available space 770 for type,_,_,_,lowestindex,maxentries,typemajor,def_icon,_ in maps: 771 # fill it up 772 for w in wp.keys(): 773 if len(init[type])>=maxentries: 774 break 775 idx=-1 776 while idx in init[type]: 777 idx-=1 778 init[type][idx]=wp[w] 779 del wp[w] 780 781 # time to write the files out 782 for type, indexfile, sizefile, directory, lowestindex, maxentries,typemajor,def_icon,_ in maps: 783 # get the index file so we can work out what to delete 784 names=[init[type][x]['name'] for x in init[type]] 785 for item in self.getindex(indexfile): 786 if common.basename(item.filename) not in names and \ 787 not self._is_rs_file(item.filename): 788 self.log(item.filename+" is being deleted") 789 try: 790 self.rmfile(item.filename) 791 except (com_brew.BrewNoSuchDirectoryException, 792 com_brew.BrewBadPathnameException, 793 com_brew.BrewNoSuchFileException): 794 pass 795 except: 796 if __debug__: 797 raise 798 # fixup the indices 799 fixups=[k for k in init[type].keys() if k<lowestindex] 800 fixups.sort() 801 for f in fixups: 802 for ii in xrange(lowestindex, lowestindex+maxentries): 803 # allocate an index 804 if ii not in init[type]: 805 init[type][ii]=init[type][f] 806 del init[type][f] 807 break 808 # any left over? 809 fixups=[k for k in init[type].keys() if k<lowestindex] 810 for f in fixups: 811 self.log("There is no space in the index for "+type+" for "+init[type][f]['name']) 812 del init[type][f] 813 # write each entry out 814 for idx in init[type].keys(): 815 entry=init[type][idx] 816 filename=entry.get('filename', directory+"/"+entry['name']) 817 entry['filename']=filename 818 fstat=self.statfile(filename) 819 if 'data' not in entry: 820 # must be in the filesystem already 821 if fstat is None: 822 self.log("Entry "+entry['name']+" is in index "+indexfile+" but there is no data for it and it isn't in the filesystem. The index entry will be removed.") 823 del init[type][idx] 824 continue 825 # check len(data) against fstat->length 826 data=entry['data'] 827 if data is None: 828 assert merge 829 continue # we are doing an add and don't have data for this existing entry 830 elif not data: 831 self.log('Not writing file: '+filename) 832 continue # data is blank, could be a result of not being able to do a previous get 833 if fstat is not None and len(data)==fstat['size']: 834 self.log("Not writing "+filename+" as a file of the same name and length already exists.") 835 else: 836 self.writefile(filename, data) 837 # write out index 838 self._write_index_file(type) 839 return reindexfunction(results)
840 841 #------------------------------------------------------------------------------- 842 parentprofile=com_lgvx7000.Profile
843 -class Profile(parentprofile):
844 protocolclass=Phone.protocolclass 845 serialsname=Phone.serialsname 846 847 BP_Calendar_Version=3 848 phone_manufacturer='LG Electronics Inc' 849 phone_model='VX8100' 850 851 WALLPAPER_WIDTH=176 852 WALLPAPER_HEIGHT=184 853 MAX_WALLPAPER_BASENAME_LENGTH=32 854 WALLPAPER_FILENAME_CHARS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789()_ .-" 855 WALLPAPER_CONVERT_FORMAT="jpg" 856 857 # the 8100 uses "W" for wait in the dialstring, it does not support "T" 858 DIALSTRING_CHARS="[^0-9PW#*]" 859 860 MAX_RINGTONE_BASENAME_LENGTH=32 861 RINGTONE_FILENAME_CHARS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789()_ .-" 862 863 # there is an origin named 'aod' - no idea what it is for except maybe 864 # 'all other downloads' 865 866 # the vx8100 supports bluetooth for connectivity to the PC, define the "bluetooth_mgd_id" 867 # to enable bluetooth discovery during phone detection 868 # the bluetooth address starts with LG's the three-octet OUI, all LG phone 869 # addresses start with this, it provides a way to identify LG bluetooth devices 870 # during phone discovery 871 # OUI=Organizationally Unique Identifier 872 # see http://standards.ieee.org/regauth/oui/index.shtml for more info 873 bluetooth_mfg_id="001256" 874 875 ringtoneorigins=('ringers', 'sounds') 876 excluded_ringtone_origins=('sounds') 877 excluded_wallpaper_origins=('video') 878 879 880 # the 8100 doesn't have seperate origins - they are all dumped in "images" 881 imageorigins={} 882 imageorigins.update(common.getkv(parentprofile.stockimageorigins, "images")) 883 imageorigins.update(common.getkv(parentprofile.stockimageorigins, "video"))
884 - def GetImageOrigins(self):
885 return self.imageorigins
886 887 # our targets are the same for all origins 888 imagetargets={} 889 imagetargets.update(common.getkv(parentprofile.stockimagetargets, "wallpaper", 890 {'width': 176, 'height': 184, 'format': "JPEG"})) 891 imagetargets.update(common.getkv(parentprofile.stockimagetargets, "outsidelcd", 892 {'width': 128, 'height': 112, 'format': "JPEG"})) 893
894 - def GetTargetsForImageOrigin(self, origin):
895 return self.imagetargets
896 897
898 - def __init__(self):
899 parentprofile.__init__(self)
900 901 _supportedsyncs=( 902 ('phonebook', 'read', None), # all phonebook reading 903 ('calendar', 'read', None), # all calendar reading 904 ('wallpaper', 'read', None), # all wallpaper reading 905 ('ringtone', 'read', None), # all ringtone reading 906 ('call_history', 'read', None),# all call history list reading 907 ('sms', 'read', None), # all SMS list reading 908 ('memo', 'read', None), # all memo list reading 909 ('phonebook', 'write', 'OVERWRITE'), # only overwriting phonebook 910 ('calendar', 'write', 'OVERWRITE'), # only overwriting calendar 911 ('wallpaper', 'write', 'MERGE'), # merge and overwrite wallpaper 912 ('wallpaper', 'write', 'OVERWRITE'), 913 ('ringtone', 'write', 'MERGE'), # merge and overwrite ringtone 914 ('ringtone', 'write', 'OVERWRITE'), 915 ('sms', 'write', 'OVERWRITE'), # all SMS list writing 916 ('memo', 'write', 'OVERWRITE'), # all memo list writing 917 ) 918 919 field_color_data={ 920 'phonebook': { 921 'name': { 922 'first': 1, 'middle': 1, 'last': 1, 'full': 1, 923 'nickname': 0, 'details': 1 }, 924 'number': { 925 'type': 5, 'speeddial': 5, 'number': 5, 'details': 5 }, 926 'email': 2, 927 'address': { 928 'type': 0, 'company': 0, 'street': 0, 'street2': 0, 929 'city': 0, 'state': 0, 'postalcode': 0, 'country': 0, 930 'details': 0 }, 931 'url': 0, 932 'memo': 1, 933 'category': 1, 934 'wallpaper': 1, 935 'ringtone': 2, 936 'storage': 0, 937 }, 938 'calendar': { 939 'description': True, 'location': False, 'allday': True, 940 'start': True, 'end': True, 'priority': False, 941 'alarm': True, 'vibrate': True, 942 'repeat': True, 943 'memo': False, 944 'category': False, 945 'wallpaper': False, 946 'ringtone': True, 947 }, 948 'memo': { 949 'subject': True, 950 'date': True, 951 'secret': False, 952 'category': False, 953 'memo': True, 954 }, 955 'todo': { 956 'summary': False, 957 'status': False, 958 'due_date': False, 959 'percent_complete': False, 960 'completion_date': False, 961 'private': False, 962 'priority': False, 963 'category': False, 964 'memo': False, 965 }, 966 }
967