class PhonePrefs(Configurator): """ Configurator for setting the phone behavior. """ ICON = theme.prefs_icon_phone TITLE = "Phone Behavior" DESCRIPTION = "Configure the phone behavior" def __init__(self): Configurator.__init__(self) self.__list = ThumbableGridView() self.add(self.__list) lbl = LabelItem("Behavior after a phone call:") self.__list.append_item(lbl) chbox = OptionItem("Resume playing", config.RESUME_AUTOMATIC, "Stay paused", config.RESUME_MANUAL) chbox.connect_changed(self.__on_select_phonecall_resume) self.__list.append_item(chbox) self.__label_info = LabelItem("") self.__list.append_item(self.__label_info) chbox.select_by_value(config.get_phonecall_resume()) def __on_select_phonecall_resume(self, value): config.set_phonecall_resume(value) self.__label_info.set_text(_DESCRIPTIONS[value]) self.__list.invalidate() self.__list.render()
class DisplayLightPrefs(Configurator): """ Configurator for setting the display light behavior. """ ICON = theme.prefs_icon_displaylight TITLE = "Display Light" DESCRIPTION = "Configure the display light" def __init__(self): Configurator.__init__(self) self.__list = ThumbableGridView() self.add(self.__list) lbl = LabelItem("Keep display lit:") #lbl.set_font(theme.font_mb_headline) self.__list.append_item(lbl) chbox = OptionItem("never", "no", "while playing", "playing") #"yes", "yes") chbox.connect_changed(self.__on_select_display_lit) self.__list.append_item(chbox) self.__label_lit = LabelItem("") self.__list.append_item(self.__label_lit) chbox.select_by_value(config.get_display_lit()) def __on_select_display_lit(self, value): config.set_display_lit(value) self.__label_lit.set_text(_DESCRIPTIONS[value]) self.__list.invalidate() self.__list.render()
class ConfigBackend(Configurator): """ Configurator for mapping media backends to media file types. """ ICON = theme.prefs_icon_backend TITLE = "Player Backends" DESCRIPTION = "Choose the player backend for each media format" def __init__(self): Configurator.__init__(self) self.__list = ThumbableGridView() self.add(self.__list) self.__update_list() def __on_item_clicked(self, item, idx): backends = mediaplayer.get_backends() backends.sort() mediatype = item.get_media_type() backend = item.get_backend() try: i = backends.index(backend) except: i = 0 i += 1 i %= len(backends) item.set_backend(backends[i]) item.set_backend_icon(mediaplayer.get_backend_icon(backends[i])) self.__list.invalidate_item(idx) #self.__list.fx_cycle_item(idx) self.__list.render() mediaplayer.set_backend_for(mediatype, backends[i]) mediaplayer.write_user_mapping() def __update_list(self): mediatypes = mediaplayer.get_media_types() mediatypes.sort() idx = 0 for mt in mediatypes: backend = mediaplayer.get_backend_for(mt) if (backend != "dummy"): item = BackendListItem(mt, backend) item.set_backend_icon(mediaplayer.get_backend_icon(backend)) #item.set_size(w, 80) self.__list.append_item(item) item.connect_clicked(self.__on_item_clicked, item, idx) idx += 1
class DownloadManager(Dialog): """ Dialog for managing active downloads. """ def __init__(self): # table: download ID -> item self.__items = {} Dialog.__init__(self) self.set_title("Active Downloads") self.__list = ThumbableGridView() self.add(self.__list) def __on_click_item(self, download_id): dlg = OptionDialog("Abort this download?") dlg.add_option(None, "Yes, abort") dlg.add_option(None, "No, continue") if (dlg.run() == dlg.RETURN_OK): choice = dlg.get_choice() if (choice == 0): self.emit_message(msgs.DOWNLOADER_ACT_ABORT, download_id) #end if def handle_DOWNLOADER_EV_STARTED(self, download_id, url, destination): item = DownloadItem(url, destination) self.__items[download_id] = item self.__list.append_item(item) item.connect_clicked(self.__on_click_item, download_id) def handle_DOWNLOADER_EV_FINISHED(self, download_id): item = self.__items.get(download_id) if (item): del self.__items[download_id] pos = self.__list.get_items().index(item) self.__list.remove_item(pos) self.__list.invalidate() self.__list.render() def handle_DOWNLOADER_EV_ABORTED(self, download_id): item = self.__items.get(download_id) if (item): del self.__items[download_id] pos = self.__list.get_items().index(item) self.__list.remove_item(pos) self.__list.invalidate() self.__list.render() def handle_DOWNLOADER_EV_PROGRESS(self, download_id, name, amount, total): item = self.__items.get(download_id) if (item): item.set_amount(amount, total) item.set_destination(name) idx = self.__list.get_items().index(item) self.__list.invalidate_item(idx)
class WebAccess(Configurator): """ Component for remote-access with a web browser. The web server accepts: - MediaBox media paths (e.g. media:///) in URL-safe form - theme graphics paths (e.g. theme.mb_btn_dir_up_1) The parameter 'clientid' is supplied to associate the client with a path history in the navigator. Stateless requests may omit this parameter. The parameter 'action' specifies how the server should react on a certain path: - open: opens a directory (this is the default action) - load: transfers the file's contents to the client - play: plays media in MediaBox - nav-up: moves to the parent folder - nav-shelf: moves to the shelf - media-pause: play/pause action on MediaBox - media-previous: goes to the previous track - media-next: goes to the next track """ ICON = theme.mb_logo TITLE = "Web Access" DESCRIPTION = "Access and remote-control MediaBox" __client_cnt = 0 def __init__(self): # table: clientid -> path_stack self.__path_stacks = {} self.__current_file = None self.__artist = "" self.__title = "" Configurator.__init__(self) self.__list = ThumbableGridView() self.add(self.__list) lbl = LabelItem("MediaBox WebAccess lets you access your media and " \ "remote-control MediaBox with a web browser.") self.__list.append_item(lbl) lbl = LabelItem("This is still an experimental feature and not " \ "fully working yet!") self.__list.append_item(lbl) chbox = OptionItem("WebAccess is Off", "off", "WebAccess is On", "on") chbox.select_by_value("off") chbox.connect_changed(self.__on_toggle_webaccess) self.__list.append_item(chbox) self.__lbl_info = LabelItem("") self.__list.append_item(self.__lbl_info) def __on_toggle_webaccess(self, value): """ Reacts on toggling the WebAccess in the configurator. """ ip = network.get_ip() if (value == "on"): error = self.call_service(msgs.HTTPSERVER_SVC_BIND, self, ip, _PORT) if (error): self.__lbl_info.set_text("Error: %s" % error) else: self.__lbl_info.set_text("WebAccess-URL: http://%s:%d" % (ip, _PORT)) self.__list.render() else: self.call_service(msgs.HTTPSERVER_SVC_UNBIND, self, ip, _PORT) self.__lbl_info.set_text("") self.__list.invalidate() self.__list.render() def __send_contents(self, request, clientid, folder, contents): """ Sends the list of contents to the client. """ if (self.__title): now_playing = self.__title if (self.__artist): now_playing += " / " + self.__artist elif (self.__current_file): now_playing = self.__current_file.name + " " + \ self.__current_file.info else: now_playing = "" html = pages.render_json_contents(clientid, contents) request.send_html(html) def handle_HTTPSERVER_EV_REQUEST(self, owner, request): def on_child(f, folder, contents): if (f): # look up thumbnail if (not f.icon): icon, is_final = self.call_service( msgs.THUMBNAIL_SVC_LOOKUP_THUMBNAIL, f) else: icon = f.icon contents.append((f, icon)) else: self.__send_contents(request, clientid, folder, contents) return True if (owner != self): return path = urlquote.unquote(request.get_path()) #if (not path): # path = "media:///" if (path.startswith("theme.")): path = getattr(theme, path[6:]).get_path() # get parameters params = request.get_query() action = params.get("action", ["open"])[0] clientid = params.get("clientid", [""])[0] if (not clientid): clientid = str(self.__client_cnt) self.__client_cnt += 1 # prepare path stack for client if (not clientid in self.__path_stacks): self.__path_stacks[clientid] = [] path_stack = self.__path_stacks[clientid] print "requesting", clientid, path, action if (path == "/"): request.send_html(pages.render_page_browser(clientid)) elif (path == "/nav-home"): f = self.call_service(msgs.CORE_SVC_GET_FILE, "media:///") path_stack[:] = [f] f.get_contents(0, 0, on_child, f, []) elif (path == "/nav-up"): if (len(path_stack) > 1): path_stack.pop() f = path_stack.pop() else: f = self.call_service(msgs.CORE_SVC_GET_FILE, "media:///") path_stack[:] = [] path_stack.append(f) f.get_contents(0, 0, on_child, f, []) elif (path == "/open"): filepath = urlquote.unquote(params["path"][0]) f = self.call_service(msgs.CORE_SVC_GET_FILE, filepath) if (f and f.mimetype.endswith("-folder")): path_stack.append(f) f.get_contents(0, 0, on_child, f, []) elif (f): parent = path_stack[-1] self.emit_message(msgs.MEDIA_ACT_LOAD, f) self.emit_message(msgs.MEDIA_ACT_CHANGE_PLAY_FOLDER, parent) request.send_html("<html><body>OK</body></html>") else: request.send_not_found("MediaBox WebAccess", filepath) elif (path == "/file"): filepath = urlquote.unquote(params["path"][0]) f = self.call_service(msgs.CORE_SVC_GET_FILE, filepath) print "FILE", f, f.is_local if (f and f.is_local): request.send_file(open(f.get_resource(), "r"), f.name, f.mimetype) elif (path == "/theme"): filepath = urlquote.unquote(params["path"][0]) pbuf = getattr(theme, filepath) request.send_file(open(pbuf.get_path(), "r"), filepath, "image/x-png") elif (action == "volume-down"): self.emit_message(msgs.INPUT_EV_VOLUME_DOWN, True) request.send_html("<html><body>OK</body></html>") elif (action == "volume-up"): self.emit_message(msgs.INPUT_EV_VOLUME_UP, True) request.send_html("<html><body>OK</body></html>") elif (action == "media-previous"): self.emit_message(msgs.MEDIA_ACT_PREVIOUS) request.send_html("<html><body>OK</body></html>") elif (action == "media-next"): self.emit_message(msgs.MEDIA_ACT_NEXT) request.send_html("<html><body>OK</body></html>") elif (action == "media-pause"): self.emit_message(msgs.MEDIA_ACT_PAUSE) request.send_html("<html><body>OK</body></html>") elif (action == "ui-fullscreen"): self.emit_message(msgs.INPUT_EV_FULLSCREEN, True) request.send_html("<html><body>OK</body></html>") elif (action == "nav-up"): if (len(path_stack) > 1): path_stack.pop() f = path_stack.pop() else: f = self.call_service(msgs.CORE_SVC_GET_FILE, "media:///") path_stack[:] = [] path_stack.append(f) f.get_contents(0, 0, on_child, f, []) elif (action == "nav-shelf"): f = self.call_service(msgs.CORE_SVC_GET_FILE, "media:///") path_stack[:] = [f] f.get_contents(0, 0, on_child, f, []) elif (action == "open"): f = self.call_service(msgs.CORE_SVC_GET_FILE, path) if (f): path_stack.append(f) print "opening", f.name f.get_contents(0, 0, on_child, f, []) else: request.send_not_found("MediaBox WebAccess", path) elif (action == "play"): f = self.call_service(msgs.CORE_SVC_GET_FILE, path) if (f): print "loading" parent = path_stack[-1] self.emit_message(msgs.MEDIA_ACT_LOAD, f) self.emit_message(msgs.MEDIA_ACT_CHANGE_PLAY_FOLDER, parent) request.send_html("<html><body>OK</body></html>") else: request.send_not_found("MediaBox WebAccess", path) else: f = self.call_service(msgs.CORE_SVC_GET_FILE, path) if (f and f.is_local): request.send_file(open(f.get_resource(), "r"), f.name, f.mimetype) elif (f and not f.is_local): request.send_redirect(f.get_resource()) else: request.send_not_found("MediaBox WebAccess", path) #end if def handle_MEDIA_EV_LOADED(self, player, f): self.__current_file = f self.__artist = "" self.__title = "" def handle_MEDIA_EV_TAG(self, tag, value): if (tag == "ARTIST"): self.__artist = value elif (tag == "TITLE"): self.__title = value
class RotationPrefs(Configurator): """ Configurator for display rotation. """ ICON = theme.prefs_icon_asr TITLE = "Display Orientation" DESCRIPTION = "Configure the display orientation" def __init__(self): self.__have_unapplied_changes = False Configurator.__init__(self) self.__list = ThumbableGridView() self.add(self.__list) #lbl = LabelItem("Orientation:") #self.__list.append_item(lbl) if (platforms.MAEMO5): chbox = OptionItem("Landscape Mode", config.ORIENTATION_LANDSCAPE, "Portrait Mode", config.ORIENTATION_PORTRAIT, "Automatic", config.ORIENTATION_AUTOMATIC) else: chbox = OptionItem("Landscape Mode", config.ORIENTATION_LANDSCAPE, "Portrait Mode", config.ORIENTATION_PORTRAIT) chbox.connect_changed(self.__on_select_orientation) chbox.select_by_value(config.orientation()) self.__list.append_item(chbox) # abusing empty label for space... TODO: implement space item :) #lbl = LabelItem("") #self.__list.append_item(lbl) chk = CheckBoxItem("Swap volume/zoom keys in portrait mode", config.portrait_swap_volume()) chk.connect_checked(self.__on_check_swap) self.__list.append_item(chk) def _visibility_changed(self): Configurator._visibility_changed(self) if (not self.is_visible() and self.__have_unapplied_changes): self.__have_unapplied_changed = False orientation = config.orientation() self.__set_orientation(orientation) def __on_select_orientation(self, value): self.__have_unapplied_changes = True self.__list.invalidate() self.__list.render() config.set_orientation(value) def __set_orientation(self, value): if (value == config.ORIENTATION_LANDSCAPE): self.emit_message(msgs.ASR_ACT_ENABLE, False) self.emit_message(msgs.ASR_EV_LANDSCAPE) logging.info("[rotation] landscape orientation") elif (value == config.ORIENTATION_PORTRAIT): self.emit_message(msgs.ASR_ACT_ENABLE, False) self.emit_message(msgs.ASR_EV_PORTRAIT) logging.info("[rotation] portrait orientation") else: self.emit_message(msgs.ASR_ACT_ENABLE, True) self.emit_message(msgs.ASR_EV_LANDSCAPE) logging.info("[rotation] automatic orientation") def __on_check_swap(self, v): config.set_portrait_swap_volume(v) self.__list.invalidate() self.__list.render() def __restore_orientation(self): o = config.orientation() self.__set_orientation(o) #if (o == config.ORIENTATION_PORTRAIT): # self.emit_message(msgs.ASR_EV_PORTRAIT) #elif (o == config.ORIENTATION_AUTOMATIC): # self.emit_message(msgs.ASR_ACT_ENABLE, True) # self.emit_message(msgs.ASR_EV_LANDSCAPE) def handle_COM_EV_APP_STARTED(self): self.__restore_orientation() def handle_ASR_ACT_RESTORE(self): self.__restore_orientation()
class FileScannerPrefs(Configurator): """ Configurator for file indexing. """ ICON = theme.prefs_icon_fileindex TITLE = "Media Indexing" DESCRIPTION = "Configure file indexing" def __init__(self): Configurator.__init__(self) self.__list = ThumbableGridView() self.add(self.__list) if (platforms.MAEMO4 or platforms.MAEMO5): if (platforms.MAEMO4): tracker = "Metalayer Crawler" elif (platforms.MAEMO5): tracker = "Tracker" lbl = LabelItem("Your device provides a tracker service (%s) for " "discovering new media. MediaBox can use it for " "scanning for new media." % tracker) self.__list.append_item(lbl) btn = ButtonItem("Look for new media now") btn.connect_clicked(self.__on_click_update) self.__list.append_item(btn) chk = CheckBoxItem("Look for new media at startup (recommended)", mb_config.scan_at_startup()) chk.connect_checked(self.__on_check_startup) self.__list.append_item(chk) else: lbl = LabelItem("Your device does not provide a tracker service for " "discovering new media automatically. You can " \ "scan for new media manually by selecting " \ "'Scan for Media' from the item popup menu of a "\ "local folder.") self.__list.append_item(lbl) lbl = LabelItem("") self.__list.append_item(lbl) #end if lbl = LabelItem("By clearing the index, MediaBox forgets about all " "media files until you have it scan for media again.") self.__list.append_item(lbl) btn = ButtonItem("Clear index now") btn.connect_clicked(self.__on_click_clear) self.__list.append_item(btn) lbl = LabelItem("At startup, MediaBox removes media that no longer " "exists from the index. If necessary, you can trigger " \ "this manually anytime, too") self.__list.append_item(lbl) btn = ButtonItem("Remove dead entries from index") btn.connect_clicked(self.__on_click_bury) self.__list.append_item(btn) def __on_check_startup(self, value): mb_config.set_scan_at_startup(value) self.__list.invalidate() self.__list.render() def __on_click_update(self): self.emit_message(msgs.UI_ACT_SHOW_INFO, "Updating media index.") self.emit_message(msgs.FILEINDEX_ACT_SCAN) def __on_click_clear(self): self.emit_message(msgs.UI_ACT_SHOW_INFO, "Clearing media index.") self.emit_message(msgs.FILEINDEX_SVC_CLEAR) def __on_click_bury(self): self.emit_message(msgs.UI_ACT_SHOW_INFO, "Removing dead entries from index.") self.emit_message(msgs.FILEINDEX_SVC_BURY)