├── debian ├── compat ├── cuacfmeego.install ├── changelog ├── rules └── control ├── cuacfmeego.conf ├── update-l10n ├── cuacfmeego.png ├── cuacfmeego-64.png ├── cuacfmeego-logo.png ├── resources └── default.png ├── data ├── =template.info ├── lunahiena.info ├── higherstreet.info ├── restosstock.info ├── alegria.info ├── dentrodeunorden.info ├── licenciapoetica.info ├── Makefile ├── elmundosentado.info ├── rbma.info ├── enboxes.info ├── laparadadelosmostruos.info ├── unanocheenlaopera.info ├── ardecoruna.info ├── lenadura.info ├── multiplex.info ├── unpasitomas.info ├── feedback.info ├── folkintrio.info ├── make-shows-json ├── laisla.info ├── minoriaabsoluta.info ├── discovery.info ├── granarmada.info ├── anatomiadeportiva.info ├── jichos.info ├── desinformativo.info ├── democracynow.info ├── masvoces.info ├── shows.json └── timetable.json ├── .gitignore ├── cuacfmeego.desktop ├── README.rst ├── cuacfmeego.qrc ├── qml ├── main.qml ├── MainPage.qml └── AboutPage.qml ├── qml-file.h ├── cuacfmeego.pro ├── cfm-main.cc ├── qml-file.cc ├── cfm-controller.h ├── l10n ├── en.ts └── es.ts ├── cfm-controller.cc └── cuacfmeego.svg /debian/compat: -------------------------------------------------------------------------------- 1 | 7 2 | -------------------------------------------------------------------------------- /cuacfmeego.conf: -------------------------------------------------------------------------------- 1 | [classify media] 2 | /opt/cuacfmeego/cuacfmeego 3 | -------------------------------------------------------------------------------- /update-l10n: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | exec lupdate qml/*.qml *.h *.cc -ts l10n/*.ts 3 | -------------------------------------------------------------------------------- /cuacfmeego.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aperezdc/cuacfmeego/master/cuacfmeego.png -------------------------------------------------------------------------------- /cuacfmeego-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aperezdc/cuacfmeego/master/cuacfmeego-64.png -------------------------------------------------------------------------------- /cuacfmeego-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aperezdc/cuacfmeego/master/cuacfmeego-logo.png -------------------------------------------------------------------------------- /resources/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aperezdc/cuacfmeego/master/resources/default.png -------------------------------------------------------------------------------- /data/=template.info: -------------------------------------------------------------------------------- 1 | "%HERE%": { 2 | "kind": "", 3 | "time": [ 4 | [], 5 | ], 6 | } 7 | 8 | // vim: ft=javascript 9 | -------------------------------------------------------------------------------- /data/lunahiena.info: -------------------------------------------------------------------------------- 1 | "Luna Hiena": { 2 | "kind": "Musical", 3 | "time": [ 4 | [4, 7, 8, false] 5 | ], 6 | } 7 | 8 | // vim: ft=javascript 9 | -------------------------------------------------------------------------------- /data/higherstreet.info: -------------------------------------------------------------------------------- 1 | "Higher street": { 2 | "kind": "Musical", 3 | "time": [ 4 | [3, 7, 8, false] 5 | ], 6 | } 7 | 8 | // vim: ft=javascript 9 | -------------------------------------------------------------------------------- /data/restosstock.info: -------------------------------------------------------------------------------- 1 | "Restos de Stock": { 2 | "kind": "Musical", 3 | "time": [ 4 | [2, 7, 8, false] 5 | ], 6 | } 7 | 8 | // vim: ft=javascript 9 | -------------------------------------------------------------------------------- /data/alegria.info: -------------------------------------------------------------------------------- 1 | "Alegría": { 2 | "kind": "Magacín", 3 | "time": [ 4 | [5, 9, 10, false], 5 | ], 6 | } 7 | 8 | // vim: ft=javascript 9 | 10 | -------------------------------------------------------------------------------- /data/dentrodeunorden.info: -------------------------------------------------------------------------------- 1 | "Dentro de un Orden": { 2 | "kind": "Magacín", 3 | "time": [ 4 | [1, 12, 14, false], 5 | ], 6 | } 7 | 8 | // vim: ft=javascript 9 | -------------------------------------------------------------------------------- /data/licenciapoetica.info: -------------------------------------------------------------------------------- 1 | "Licencia Poética": { 2 | "kind": "Magacín", 3 | "time": [ 4 | [1, 11, 12, false], 5 | ], 6 | } 7 | 8 | // vim: ft=javascript 9 | -------------------------------------------------------------------------------- /data/Makefile: -------------------------------------------------------------------------------- 1 | 2 | INFOS := $(filter-out =template.info,$(wildcard *.info)) 3 | 4 | all: shows.json 5 | 6 | shows.json: $(INFOS) 7 | ./make-shows-json $^ > $@ 8 | 9 | -------------------------------------------------------------------------------- /data/elmundosentado.info: -------------------------------------------------------------------------------- 1 | "El mundo sentado a mis piés": { 2 | "kind": "Magacín", 3 | "time": [ 4 | [1, 16, 17, true], 5 | ], 6 | } 7 | 8 | // vim: ft=javascript 9 | -------------------------------------------------------------------------------- /data/rbma.info: -------------------------------------------------------------------------------- 1 | "RBMA": { 2 | "icon": "rbma.png", 3 | "kind": "Musical", 4 | "time": [ 5 | [6, 8, 9, true], 6 | ], 7 | } 8 | 9 | // vim: ft=javascript 10 | -------------------------------------------------------------------------------- /data/enboxes.info: -------------------------------------------------------------------------------- 1 | "En Boxes": { 2 | "kind": "Informativo / Divulgación", 3 | "time": [ 4 | [4, 9, 10, false], 5 | ], 6 | } 7 | 8 | // vim: ft=javascript 9 | 10 | -------------------------------------------------------------------------------- /data/laparadadelosmostruos.info: -------------------------------------------------------------------------------- 1 | "La parada de los monstruos": { 2 | "kind": "Magacín", 3 | "time": [ 4 | [1, 17, 19, true], 5 | ], 6 | } 7 | 8 | // vim: ft=javascript 9 | -------------------------------------------------------------------------------- /data/unanocheenlaopera.info: -------------------------------------------------------------------------------- 1 | "Una noche en la ópera": { 2 | "kind": "Musical", 3 | "time": [ 4 | [2, 1, 2, false], 5 | ], 6 | } 7 | 8 | // vim: ft=javascript 9 | 10 | -------------------------------------------------------------------------------- /data/ardecoruna.info: -------------------------------------------------------------------------------- 1 | "Ar de Coruña": { 2 | "icon": "arde.jpg", 3 | "kind": "Magacín", 4 | "time": [ 5 | [1, 9, 11, false], 6 | ], 7 | } 8 | 9 | // vim: ft=javascript 10 | -------------------------------------------------------------------------------- /data/lenadura.info: -------------------------------------------------------------------------------- 1 | "Leña dura DF": { 2 | "kind": "Musical", 3 | "time": [ 4 | [1, 7, 8, false], 5 | [1, 15, 16, true], 6 | ], 7 | } 8 | 9 | // vim: ft=javascript 10 | -------------------------------------------------------------------------------- /data/multiplex.info: -------------------------------------------------------------------------------- 1 | "Multiplex": { 2 | "kind": "Musical", 3 | "time": [ 4 | [2, 15, 16, true], 5 | [1, 1, 2, false], 6 | ], 7 | } 8 | 9 | // vim: ft=javascript 10 | -------------------------------------------------------------------------------- /data/unpasitomas.info: -------------------------------------------------------------------------------- 1 | "Un pasito más": { 2 | "kind": "Informativo / Divulgación", 3 | "time": [ 4 | [3, 10, 11, false], 5 | ], 6 | } 7 | 8 | // vim: ft=javascript 9 | 10 | -------------------------------------------------------------------------------- /data/feedback.info: -------------------------------------------------------------------------------- 1 | "Feedback!": { 2 | "icon": "feedback.png", 3 | "kind": "Musical", 4 | "time": [ 5 | [6, 9, 10, true], 6 | ], 7 | } 8 | 9 | // vim: ft=javascript 10 | 11 | -------------------------------------------------------------------------------- /data/folkintrio.info: -------------------------------------------------------------------------------- 1 | "Folk in trio": { 2 | "icon": "folkin.jpg", 3 | "kind": "Musical", 4 | "time": [ 5 | [2, 0, 1, false], 6 | ], 7 | } 8 | 9 | // vim: ft=javascript 10 | 11 | -------------------------------------------------------------------------------- /data/make-shows-json: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | echo "{" 4 | for info in "$@" 5 | do 6 | grep -vE '^//' "${info}" 7 | echo "," 8 | done | grep -v '^[[:space:]]*$' 9 | echo "}" 10 | 11 | -------------------------------------------------------------------------------- /data/laisla.info: -------------------------------------------------------------------------------- 1 | "La Isla": { 2 | "kind": "Informativo / Divulgación", 3 | "time": [ 4 | [2, 12, 14, false], 5 | [7, 20, 22, true], 6 | ], 7 | } 8 | 9 | // vim:ft=javascript 10 | -------------------------------------------------------------------------------- /data/minoriaabsoluta.info: -------------------------------------------------------------------------------- 1 | "Minoría absoluta": { 2 | "kind": "Magacín", 3 | "time": [ 4 | [1, 23, 24, true], 5 | [2, 11, 12, false], 6 | ], 7 | } 8 | 9 | // vim: ft=javascript 10 | 11 | -------------------------------------------------------------------------------- /data/discovery.info: -------------------------------------------------------------------------------- 1 | "Discovery": { 2 | "icon": "discovery.png", 3 | "kind": "Musical", 4 | "time": [ 5 | [3, 21, 22, true], 6 | [1, 0, 1, false], 7 | ], 8 | } 9 | 10 | // vim:ft=javascript 11 | -------------------------------------------------------------------------------- /data/granarmada.info: -------------------------------------------------------------------------------- 1 | "La Gran Armada": { 2 | "icon": "armada.jpg", 3 | "kind": "Informativo / Divulgación", 4 | "time": [ 5 | [4, 10, 11, false], 6 | ], 7 | } 8 | 9 | // vim: ft=javascript 10 | 11 | -------------------------------------------------------------------------------- /data/anatomiadeportiva.info: -------------------------------------------------------------------------------- 1 | "Anatomía Deportiva": { 2 | "kind": "Informativo / Divulgación", 3 | "time": [ 4 | [1, 22, 23, true ], 5 | [3, 9, 10, false], 6 | ], 7 | } 8 | 9 | // vim: ft=javascript 10 | 11 | -------------------------------------------------------------------------------- /data/jichos.info: -------------------------------------------------------------------------------- 1 | "Jichos": { 2 | "kind": "?", 3 | "time": [ 4 | [2, 16, 17, false], 5 | [4, 17, 18, false], 6 | [6, 14, 15, true], 7 | [7, 18, 19, false], 8 | ], 9 | } 10 | 11 | // vim: ft=javascript 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | cuacfmeego 2 | qrc_*.cpp 3 | moc_*.cpp 4 | ,build/ 5 | Makefile 6 | l10n/*.qm 7 | debian/*.log 8 | debian/*.substvars 9 | debian/files 10 | debian/cuacfmeego-dbg/ 11 | debian/cuacfmeego.aegis 12 | *.sw[op] 13 | -------------------------------------------------------------------------------- /data/desinformativo.info: -------------------------------------------------------------------------------- 1 | "Desinformativo": { 2 | "icon": "desinformativo.png", 3 | "kind": "Informativo / Divulgación", 4 | "time": [ 5 | [2, 9, 11, false], 6 | [1, 20, 22, true], 7 | ], 8 | } 9 | 10 | // vim: ft=javascript 11 | -------------------------------------------------------------------------------- /debian/cuacfmeego.install: -------------------------------------------------------------------------------- 1 | cuacfmeego opt/cuacfmeego/ 2 | cuacfmeego.png opt/cuacfmeego/ 3 | cuacfmeego.svg opt/cuacfmeego/ 4 | cuacfmeego.desktop usr/share/applications/ 5 | cuacfmeego.conf usr/share/policy/etc/syspart.conf.d/ 6 | -------------------------------------------------------------------------------- /cuacfmeego.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Encoding=UTF-8 3 | Version=0.1.3 4 | Type=Application 5 | Name=Cuac FM 6 | Comment=Listen to Cuac FM over internet, and more. 7 | Exec=/usr/bin/invoker --single-instance --type=d /opt/cuacfmeego/cuacfmeego 8 | Icon=/opt/cuacfmeego/cuacfmeego.png 9 | -------------------------------------------------------------------------------- /data/democracynow.info: -------------------------------------------------------------------------------- 1 | "Democracy Now!": { 2 | "icon": "masvoces_democracy.png", 3 | "kind": "Informativo / Divulgación", 4 | "time": [ 5 | [1, 8.5, 9, true], 6 | [2, 8.5, 9, true], 7 | [3, 8.5, 9, true], 8 | [4, 8.5, 9, true], 9 | [5, 8.5, 9, true], 10 | ], 11 | } 12 | 13 | // vim: ft=javascript 14 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ====================== 2 | Cuac FM MeeGo player 3 | ====================== 4 | Author: Adrian Perez 5 | 6 | This is a player for the streaming service of the `Cuac FM`_ community radio 7 | station from A Coruña (Spain). The application is designed to work in the 8 | Nokia N9 and N950 handsets. 9 | 10 | 11 | .. _Cuac FM: http://cuacfm.org 12 | -------------------------------------------------------------------------------- /data/masvoces.info: -------------------------------------------------------------------------------- 1 | "Más Voces": { 2 | "icon": "masvoces_democracy.png", 3 | "kind": "Informativo / Divulgación", 4 | "time": [ 5 | [1, 8, 8.5, true], [1, 14, 14.5, true], 6 | [2, 8, 8.5, true], [2, 14, 14.5, true], 7 | [3, 8, 8.5, true], [3, 14, 14.5, true], 8 | [4, 8, 8.5, true], [4, 14, 14.5, true], 9 | [5, 8, 8.5, true], [5, 14, 14.5, true], 10 | ], 11 | } 12 | 13 | // vim: ft=javascript 14 | -------------------------------------------------------------------------------- /cuacfmeego.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | qml/main.qml 6 | qml/MainPage.qml 7 | qml/AboutPage.qml 8 | 9 | 10 | cuacfmeego-logo.png 11 | resources/default.png 12 | 13 | 14 | l10n/en.qm 15 | l10n/es.qm 16 | 17 | 18 | 19 | 21 | -------------------------------------------------------------------------------- /qml/main.qml: -------------------------------------------------------------------------------- 1 | /* 2 | * main.qml 3 | * Copyright (C) 2011 Adrian Perez 4 | * 5 | * Distributed under terms of the GPLv3 license. 6 | */ 7 | 8 | 9 | import QtQuick 1.1 10 | import com.nokia.meego 1.0 11 | 12 | PageStackWindow { 13 | id: appWindow 14 | 15 | Component.onCompleted: theme.inverted = true 16 | 17 | initialPage: mainPage 18 | showStatusBar: appWindow.inPortrait 19 | 20 | MainPage { 21 | id: mainPage 22 | orientationLock: PageOrientation.LockPortrait 23 | } 24 | 25 | platformStyle: PageStackWindowStyle { 26 | background: 'image://theme/meegotouch-video-background' 27 | backgroundFillMode: Image.Stretch 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | cuacfmeego (0.1.3) unstable; urgency=low 2 | 3 | * Added Application Manager icon (XB-Maemo-Icon-26) in control file. 4 | 5 | -- Adrian Perez Web, 11 Jul 2012 16:15:12 +0300 6 | 7 | cuacfmeego (0.1.2) unstable; urgency=low 8 | 9 | * Revamped and improved about page. 10 | * Added localization support. 11 | * Spanish translation. 12 | 13 | -- Adrian Perez Tue, 29 May 2012 13:34:13 +0300 14 | 15 | cuacfmeego (0.1.1) unstable; urgency=low 16 | 17 | * Added policy framework configuration file, this makes the volume keys in 18 | the N9 work as expected. 19 | 20 | -- Adrian Perez Sat, 26 May 2012 21:12:55 +0300 21 | 22 | cuacfmeego (0.1.0) unstable; urgency=low 23 | 24 | * Initial release. 25 | 26 | -- Adrian Perez Wed, 27 Dec 2011 12:57:00 +0200 27 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #! /usr/bin/make -f 2 | 3 | # export DH_VERBOSE = 1 4 | 5 | QMAKE_OPTIONS = -makefile -nocache 6 | 7 | 8 | ifneq (,$(filter debug,$(DEB_BUILD_OPTIONS))) 9 | QMAKE_OPTIONS += CONFIG*=debug CONFIG-=release 10 | CFLAGS = -O0 -g 11 | else 12 | QMAKE_OPTIONS += CONFIG*=release CONFIG-=debug 13 | endif 14 | 15 | 16 | ifneq (,$(filter noopt,$(DEB_BUILD_OPTIONS))) 17 | CFLAGS = -O0 -g 18 | endif 19 | 20 | 21 | # Teach qmake about CFLAGS, poor qmake doesn't know about it... 22 | # 23 | QMAKE_OPTIONS += QMAKE_CXXFLAGS_RELEASE='$(CFLAGS)' \ 24 | QMAKE_CXXFLAGS_DEBUG='$(CFLAGS)' \ 25 | QMAKE_CFLAGS_RELEASE='$(CFLAGS)' \ 26 | QMAKE_CFLAGS_DEBUG='$(CFLAGS)' \ 27 | QMAKE_STRIP=: \ 28 | 29 | 30 | %: 31 | dh --buildsystem=qmake $@ 32 | 33 | override_dh_auto_configure: 34 | qmake $(QMAKE_OPTIONS) 35 | 36 | override_dh_strip: 37 | dh_strip --dbg-package=cuacfmeego-dbg 38 | 39 | .PHONY: override_dh_strip override_dh_auto_configure 40 | -------------------------------------------------------------------------------- /qml-file.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * qml-file.h 4 | * Copyright (C) 2012 Adrian Perez 5 | * 6 | * Distributed under terms of the GPLv3 license. 7 | */ 8 | 9 | #ifndef __qml_file_h__ 10 | #define __qml_file_h__ 11 | 12 | #include 13 | #include 14 | 15 | class QmlFile: public QObject 16 | { 17 | Q_OBJECT 18 | 19 | public: 20 | explicit QmlFile(QObject *parent = 0); 21 | virtual ~QmlFile(); 22 | 23 | Q_PROPERTY(QString path 24 | READ path 25 | WRITE setPath 26 | NOTIFY pathChanged) 27 | 28 | Q_PROPERTY(QString content 29 | READ read 30 | WRITE write 31 | NOTIFY contentChanged) 32 | 33 | Q_INVOKABLE bool exists() const { 34 | return _file.exists(); 35 | } 36 | 37 | Q_INVOKABLE QString read(); 38 | Q_INVOKABLE bool write(const QString& data); 39 | 40 | const QString path() const { 41 | return _file.fileName(); 42 | } 43 | 44 | public slots: 45 | void setPath(const QString& path) { 46 | _file.setFileName(path); 47 | emit pathChanged(path); 48 | } 49 | 50 | signals: 51 | void contentChanged(const QString& newContent) const; 52 | void pathChanged(const QString& newPath) const; 53 | void error(const QString& message) const; 54 | 55 | private: 56 | QFile _file; 57 | }; 58 | 59 | #endif /* !__qml_file_h__ */ 60 | 61 | -------------------------------------------------------------------------------- /cuacfmeego.pro: -------------------------------------------------------------------------------- 1 | # vim: filetype=make expandtab 2 | 3 | TEMPLATE = app 4 | TARGET = cuacfmeego 5 | QT += declarative \ 6 | network \ 7 | xml 8 | CONFIG += link_pkgconfig \ 9 | meegotouch \ 10 | qt-boostable \ 11 | qdeclarative-boostable 12 | PKGCONFIG += gstreamer-net-0.10 \ 13 | gstreamer-base-0.10 \ 14 | gstreamer-audio-0.10 \ 15 | conic 16 | 17 | # Do not clutter the source directory 18 | # 19 | OBJECTS_DIR = ,build 20 | MOC_DIR = ,build 21 | 22 | # Needed for the booster to work 23 | # 24 | QMAKE_CXXFLAGS += -fPIC -fvisibility=hidden -fvisibility-inlines-hidden 25 | QMAKE_LFLAGS += -pie -rdynamic -Wl,--as-needed 26 | 27 | # Application sources 28 | # 29 | SOURCES += cfm-main.cc \ 30 | cfm-controller.cc \ 31 | qml-file.cc 32 | HEADERS += cfm-controller.h \ 33 | qml-file.h 34 | RESOURCES += cuacfmeego.qrc 35 | 36 | # l10n stuff 37 | # 38 | CODECFORTR = UTF-8 39 | TRANSLATIONS += l10n/en.ts \ 40 | l10n/es.ts 41 | 42 | # Rule for regenerating .qm files for translations (this is missing 43 | # from the default qmake rule set). 44 | # 45 | updateqm.input = TRANSLATIONS 46 | updateqm.output = ${QMAKE_FILE_PATH}/${QMAKE_FILE_BASE}.qm 47 | updateqm.commands = lrelease ${QMAKE_FILE_IN} -qm ${QMAKE_FILE_OUT} 48 | updateqm.CONFIG += no_link 49 | QMAKE_EXTRA_COMPILERS += updateqm 50 | PRE_TARGETDEPS = compiler_updateqm_make_all 51 | 52 | -------------------------------------------------------------------------------- /cfm-main.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * cfm-main.cc 3 | * Copyright (C) 2011 Adrian Perez 4 | * 5 | * Distributed under terms of the GPLv3 license. 6 | */ 7 | 8 | #include "cfm-controller.h" 9 | #include "qml-file.h" 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | 20 | static const QUrl INITIAL_VIEW("qrc:/qml/main.qml"); 21 | 22 | 23 | Q_DECL_EXPORT int 24 | main (int argc, char *argv[]) 25 | { 26 | qmlRegisterType("com.igalia.people.aperez", 1, 0, "File"); 27 | 28 | gst_init(&argc, &argv); 29 | 30 | QApplication *application(MDeclarativeCache::qApplication(argc, argv)); 31 | 32 | application->setApplicationName("CuacFM"); 33 | application->setOrganizationDomain("com.igalia.people"); 34 | application->setOrganizationName("aperez"); 35 | 36 | // Assume that strings in source fils are UTF-8 (as they should!) 37 | QTextCodec::setCodecForTr(QTextCodec::codecForName("utf-8")); 38 | 39 | QString locale(QLocale::system().name()); 40 | QTranslator translator; 41 | 42 | if (translator.load("l10n/" + locale, ":/") || translator.load("l10n/en", ":/")) 43 | application->installTranslator(&translator); 44 | 45 | QDeclarativeView *view(MDeclarativeCache::qDeclarativeView()); 46 | CFMController controller(view->rootContext()); 47 | view->setSource(INITIAL_VIEW); 48 | view->showFullScreen(); 49 | 50 | int result = application->exec(); 51 | 52 | delete view; 53 | delete application; 54 | gst_deinit(); 55 | 56 | return result; 57 | } 58 | 59 | -------------------------------------------------------------------------------- /qml-file.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * qml-file.cc 3 | * Copyright (C) 2012 Adrian Perez 4 | * 5 | * Distributed under terms of the GPLv3 license. 6 | */ 7 | 8 | #include "qml-file.h" 9 | #include 10 | #include 11 | 12 | static const QString noPathErrorString("No 'path' was set before attempting to read"); 13 | static const QString notExistsErrorString("File '%1' does not exist"); 14 | static const QString openForReadErrorString("File '%1' could not be opened for reading"); 15 | static const QString openForWriteErrorString("File '%1' could not be opened for writing"); 16 | 17 | 18 | QmlFile::QmlFile(QObject* parent) 19 | : QObject(parent) 20 | , _file() 21 | { 22 | } 23 | 24 | QmlFile::~QmlFile() 25 | { 26 | } 27 | 28 | QString QmlFile::read() 29 | { 30 | const QString path(_file.fileName()); 31 | 32 | if (path.isEmpty()) { 33 | emit error(noPathErrorString); 34 | return QString(); 35 | } 36 | if (!_file.exists()) { 37 | emit error(notExistsErrorString.arg(path)); 38 | return QString(); 39 | } 40 | if (!_file.open(QFile::ReadOnly)) { 41 | emit error(openForReadErrorString.arg(path)); 42 | return QString(); 43 | } 44 | 45 | QTextStream stream(&_file); 46 | QString data = stream.readAll(); 47 | _file.close(); 48 | return data; 49 | } 50 | 51 | bool QmlFile::write(const QString& data) 52 | { 53 | const QString path(_file.fileName()); 54 | 55 | if (path.isEmpty()) { 56 | emit error(noPathErrorString); 57 | return false; 58 | } 59 | if (!_file.open(QFile::WriteOnly | QFile::Truncate)) { 60 | emit error(openForWriteErrorString.arg(path)); 61 | return false; 62 | } 63 | 64 | QTextStream stream(&_file); 65 | stream << data; 66 | _file.close(); 67 | emit contentChanged(data); 68 | return true; 69 | } 70 | -------------------------------------------------------------------------------- /qml/MainPage.qml: -------------------------------------------------------------------------------- 1 | /* 2 | * MainPage.qml 3 | * Copyright (C) 2011 Adrian Perez 4 | * 5 | * Distributed under terms of the GPLv3 license. 6 | */ 7 | 8 | import QtQuick 1.1 9 | import com.nokia.meego 1.0 10 | import "file:///usr/lib/qt4/imports/com/meego/UIConstants.js" as UIConstants 11 | 12 | 13 | Page { 14 | id: window 15 | 16 | Component { 17 | id: aboutPage 18 | AboutPage { } 19 | } 20 | 21 | Label { 22 | text: controller.statusText 23 | color: UIConstants.COLOR_INVERTED_FOREGROUND 24 | anchors { 25 | horizontalCenter: parent.horizontalCenter 26 | top: parent.top 27 | topMargin: UIConstants.DEFAULT_MARGIN * 3 28 | } 29 | platformStyle: LabelStyle { 30 | fontPixelSize: UIConstants.FONT_XLARGE 31 | } 32 | } 33 | 34 | Image { 35 | anchors.centerIn: parent 36 | source: 'qrc:/resources/default.png' 37 | } 38 | 39 | Menu { 40 | id: mainMenu 41 | MenuLayout { 42 | MenuItem { 43 | //: Menu option in main page to open radio station website 44 | text: qsTr('Open website') 45 | onClicked: Qt.openUrlExternally('http://cuacfm.org') 46 | } 47 | MenuItem { 48 | //: Menu option in main page to open the about page 49 | text: qsTr('About') 50 | onClicked: appWindow.pageStack.push(aboutPage) 51 | } 52 | } 53 | } 54 | 55 | tools: ToolBarLayout { 56 | ToolIcon { 57 | iconId: 'toolbar-view-menu' 58 | anchors.right: parent.right 59 | onClicked: (mainMenu.status == DialogStatus.Closed) ? 60 | mainMenu.open() : mainMenu.close() 61 | } 62 | 63 | ToolButton { 64 | id: playButton 65 | //: Play/Pause button in the lower toolbar of the main page 66 | text: (controller.playing || controller.buffering) ? qsTr('Pause') : qsTr('Play') 67 | anchors.horizontalCenter: parent.horizontalCenter; 68 | onClicked: controller.setPlaying(!controller.isPlaying()) 69 | } 70 | } 71 | } 72 | 73 | -------------------------------------------------------------------------------- /cfm-controller.h: -------------------------------------------------------------------------------- 1 | /* 2 | * cfm-controller.h 3 | * Copyright (C) 2011 Adrian Perez 4 | * 5 | * Distributed under terms of the GPLv3 license. 6 | */ 7 | 8 | #ifndef __cfm_controller_h__ 9 | #define __cfm_controller_h__ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | 17 | class QDeclarativeContext; 18 | 19 | class CFMController: public QObject 20 | { 21 | Q_OBJECT 22 | 23 | public: 24 | CFMController(QDeclarativeContext* context); 25 | virtual ~CFMController(); 26 | 27 | Q_PROPERTY(int bufferingStatus 28 | READ getBufferingStatus 29 | NOTIFY bufferingStatusChanged) 30 | 31 | Q_PROPERTY(bool buffering 32 | READ isBuffering 33 | NOTIFY bufferingChanged) 34 | 35 | Q_PROPERTY(bool playing 36 | READ isPlaying 37 | WRITE setPlaying 38 | NOTIFY playingStatusChanged) 39 | 40 | Q_PROPERTY(bool muted 41 | READ isMuted 42 | WRITE setMuted 43 | NOTIFY mutedStatusChanged) 44 | 45 | Q_PROPERTY(bool networkAvailable 46 | READ isNetworkAvailable 47 | WRITE setNetworkAvailable 48 | NOTIFY networkAvailableChanged) 49 | 50 | Q_PROPERTY(QString statusText 51 | READ getStatusText 52 | NOTIFY statusTextChanged) 53 | 54 | public slots: 55 | bool isMuted() const; 56 | void setMuted(bool muteValue); 57 | 58 | bool isPlaying() const; 59 | void setPlaying(bool playValue); 60 | 61 | const QString& getStatusText() const; 62 | 63 | int getBufferingStatus() const; 64 | bool isBuffering() const; 65 | 66 | bool isNetworkAvailable() const; 67 | void setNetworkAvailable(bool availability); 68 | 69 | void openStoreClient(const QString& url) const; 70 | 71 | signals: 72 | void playbackError(const QString& message); 73 | void bufferingChanged(bool newStatus); 74 | void statusTextChanged(const QString& newStatus); 75 | void mutedStatusChanged(bool newStatus); 76 | void playingStatusChanged(bool newStatus); 77 | void bufferingStatusChanged(qreal fillRate); 78 | void networkAvailableChanged(bool newAvailability); 79 | 80 | private: 81 | QDeclarativeContext* _context; 82 | ConIcConnection* _connection; 83 | GstElement* _playbin; 84 | QString _statusText; 85 | gint _bufferFillRate; 86 | bool _playPending; 87 | bool _connected; 88 | 89 | void updateStatusText(); 90 | GstElement* gstPipeline(); 91 | void destroyGstPipeline(); 92 | 93 | static void handleGstMessage(GstBus* bus, 94 | GstMessage* message, 95 | gpointer controller); 96 | 97 | static void handleConnectionEvent(ConIcConnection* connection, 98 | ConIcConnectionEvent* event, 99 | gpointer controller); 100 | }; 101 | 102 | #endif /* !__cfm_controller_h__ */ 103 | 104 | -------------------------------------------------------------------------------- /data/shows.json: -------------------------------------------------------------------------------- 1 | { 2 | "Desinformativo": { 3 | "icon": "desinformativo.png", 4 | "kind": "Informativo / Divulgación", 5 | "time": [ 6 | [2, 9, 11, false], 7 | [1, 20, 22, true], 8 | ], 9 | } 10 | , 11 | "Feedback!": { 12 | "icon": "feedback.png", 13 | "kind": "Musical", 14 | "time": [ 15 | [6, 9, 10, true], 16 | ], 17 | } 18 | , 19 | "Licencia Poética": { 20 | "kind": "Magacín", 21 | "time": [ 22 | [1, 11, 12, false], 23 | ], 24 | } 25 | , 26 | "Dentro de un Orden": { 27 | "kind": "Magacín", 28 | "time": [ 29 | [1, 12, 14, false], 30 | ], 31 | } 32 | , 33 | "La Isla": { 34 | "kind": "Informativo / Divulgación", 35 | "time": [ 36 | [2, 12, 14, false], 37 | [7, 20, 22, true], 38 | ], 39 | } 40 | , 41 | "Folk in trio": { 42 | "icon": "folkin.jpg", 43 | "kind": "Musical", 44 | "time": [ 45 | [2, 0, 1, false], 46 | ], 47 | } 48 | , 49 | "Discovery": { 50 | "icon": "discovery.png", 51 | "kind": "Musical", 52 | "time": [ 53 | [3, 21, 22, true], 54 | [1, 0, 1, false], 55 | ], 56 | } 57 | , 58 | "Democracy Now!": { 59 | "icon": "masvoces_democracy.png", 60 | "kind": "Informativo / Divulgación", 61 | "time": [ 62 | [1, 8.5, 9, true], 63 | [2, 8.5, 9, true], 64 | [3, 8.5, 9, true], 65 | [4, 8.5, 9, true], 66 | [5, 8.5, 9, true], 67 | ], 68 | } 69 | , 70 | "Alegría": { 71 | "kind": "Magacín", 72 | "time": [ 73 | [5, 9, 10, false], 74 | ], 75 | } 76 | , 77 | "En Boxes": { 78 | "kind": "Informativo / Divulgación", 79 | "time": [ 80 | [4, 9, 10, false], 81 | ], 82 | } 83 | , 84 | "Luna Hiena": { 85 | "kind": "Musical", 86 | "time": [ 87 | [4, 7, 8, false] 88 | ], 89 | } 90 | , 91 | "Higher street": { 92 | "kind": "Musical", 93 | "time": [ 94 | [3, 7, 8, false] 95 | ], 96 | } 97 | , 98 | "Leña dura DF": { 99 | "kind": "Musical", 100 | "time": [ 101 | [1, 7, 8, false], 102 | [1, 15, 16, true], 103 | ], 104 | } 105 | , 106 | "Restos de Stock": { 107 | "kind": "Musical", 108 | "time": [ 109 | [2, 7, 8, false] 110 | ], 111 | } 112 | , 113 | "RBMA": { 114 | "icon": "rbma.png", 115 | "kind": "Musical", 116 | "time": [ 117 | [6, 8, 9, true], 118 | ], 119 | } 120 | , 121 | "Multiplex": { 122 | "kind": "Musical", 123 | "time": [ 124 | [2, 15, 16, true], 125 | [1, 1, 2, false], 126 | ], 127 | } 128 | , 129 | "Un pasito más": { 130 | "kind": "Informativo / Divulgación", 131 | "time": [ 132 | [3, 10, 11, false], 133 | ], 134 | } 135 | , 136 | "Jichos": { 137 | "kind": "?", 138 | "time": [ 139 | [2, 16, 17, false], 140 | [4, 17, 18, false], 141 | [6, 14, 15, true], 142 | [7, 18, 19, false], 143 | ], 144 | } 145 | , 146 | "Anatomía Deportiva": { 147 | "kind": "Informativo / Divulgación", 148 | "time": [ 149 | [1, 22, 23, true ], 150 | [3, 9, 10, false], 151 | ], 152 | } 153 | , 154 | "Una noche en la ópera": { 155 | "kind": "Musical", 156 | "time": [ 157 | [2, 1, 2, false], 158 | ], 159 | } 160 | , 161 | "El mundo sentado a mis piés": { 162 | "kind": "Magacín", 163 | "time": [ 164 | [1, 16, 17, true], 165 | ], 166 | } 167 | , 168 | "Ar de Coruña": { 169 | "icon": "arde.jpg", 170 | "kind": "Magacín", 171 | "time": [ 172 | [1, 9, 11, false], 173 | ], 174 | } 175 | , 176 | "Más Voces": { 177 | "icon": "masvoces_democracy.png", 178 | "kind": "Informativo / Divulgación", 179 | "time": [ 180 | [1, 8, 8.5, true], [1, 14, 14.5, true], 181 | [2, 8, 8.5, true], [2, 14, 14.5, true], 182 | [3, 8, 8.5, true], [3, 14, 14.5, true], 183 | [4, 8, 8.5, true], [4, 14, 14.5, true], 184 | [5, 8, 8.5, true], [5, 14, 14.5, true], 185 | ], 186 | } 187 | , 188 | "Minoría absoluta": { 189 | "kind": "Magacín", 190 | "time": [ 191 | [1, 23, 24, true], 192 | [2, 11, 12, false], 193 | ], 194 | } 195 | , 196 | "La Gran Armada": { 197 | "icon": "armada.jpg", 198 | "kind": "Informativo / Divulgación", 199 | "time": [ 200 | [4, 10, 11, false], 201 | ], 202 | } 203 | , 204 | "La parada de los monstruos": { 205 | "kind": "Magacín", 206 | "time": [ 207 | [1, 17, 19, true], 208 | ], 209 | } 210 | , 211 | } 212 | -------------------------------------------------------------------------------- /data/timetable.json: -------------------------------------------------------------------------------- 1 | { 2 | "Desinformativo": { 3 | "icon": "desinformativo.png", 4 | "kind": "Informativo / Divulgación", 5 | "time": [ 6 | [2, 9, 11, false], 7 | [1, 20, 22, true], 8 | ], 9 | } 10 | , 11 | "Feedback!": { 12 | "icon": "feedback.png", 13 | "kind": "Musical", 14 | "time": [ 15 | [6, 9, 10, true], 16 | ], 17 | } 18 | , 19 | "Licencia Poética": { 20 | "kind": "Magacín", 21 | "time": [ 22 | [1, 11, 12, false], 23 | ], 24 | } 25 | , 26 | "Dentro de un Orden": { 27 | "kind": "Magacín", 28 | "time": [ 29 | [1, 12, 14, false], 30 | ], 31 | } 32 | , 33 | "La Isla": { 34 | "kind": "Informativo / Divulgación", 35 | "time": [ 36 | [2, 12, 14, false], 37 | [7, 20, 22, true], 38 | ], 39 | } 40 | , 41 | "Folk in trio": { 42 | "icon": "folkin.jpg", 43 | "kind": "Musical", 44 | "time": [ 45 | [2, 0, 1, false], 46 | ], 47 | } 48 | , 49 | "Discovery": { 50 | "icon": "discovery.png", 51 | "kind": "Musical", 52 | "time": [ 53 | [3, 21, 22, true], 54 | [1, 0, 1, false], 55 | ], 56 | } 57 | , 58 | "Democracy Now!": { 59 | "icon": "masvoces_democracy.png", 60 | "kind": "Informativo / Divulgación", 61 | "time": [ 62 | [1, 8.5, 9, true], 63 | [2, 8.5, 9, true], 64 | [3, 8.5, 9, true], 65 | [4, 8.5, 9, true], 66 | [5, 8.5, 9, true], 67 | ], 68 | } 69 | , 70 | "Alegría": { 71 | "kind": "Magacín", 72 | "time": [ 73 | [5, 9, 10, false], 74 | ], 75 | } 76 | , 77 | "En Boxes": { 78 | "kind": "Informativo / Divulgación", 79 | "time": [ 80 | [4, 9, 10, false], 81 | ], 82 | } 83 | , 84 | "Luna Hiena": { 85 | "kind": "Musical", 86 | "time": [ 87 | [4, 7, 8, false] 88 | ], 89 | } 90 | , 91 | "Higher street": { 92 | "kind": "Musical", 93 | "time": [ 94 | [3, 7, 8, false] 95 | ], 96 | } 97 | , 98 | "Leña dura DF": { 99 | "kind": "Musical", 100 | "time": [ 101 | [1, 7, 8, false], 102 | [1, 15, 16, true], 103 | ], 104 | } 105 | , 106 | "Restos de Stock": { 107 | "kind": "Musical", 108 | "time": [ 109 | [2, 7, 8, false] 110 | ], 111 | } 112 | , 113 | "RBMA": { 114 | "icon": "rbma.png", 115 | "kind": "Musical", 116 | "time": [ 117 | [6, 8, 9, true], 118 | ], 119 | } 120 | , 121 | "Multiplex": { 122 | "kind": "Musical", 123 | "time": [ 124 | [2, 15, 16, true], 125 | [1, 1, 2, false], 126 | ], 127 | } 128 | , 129 | "Un pasito más": { 130 | "kind": "Informativo / Divulgación", 131 | "time": [ 132 | [3, 10, 11, false], 133 | ], 134 | } 135 | , 136 | "Jichos": { 137 | "kind": "?", 138 | "time": [ 139 | [2, 16, 17, false], 140 | [4, 17, 18, false], 141 | [6, 14, 15, true], 142 | [7, 18, 19, false], 143 | ], 144 | } 145 | , 146 | "Anatomía Deportiva": { 147 | "kind": "Informativo / Divulgación", 148 | "time": [ 149 | [1, 22, 23, true ], 150 | [3, 9, 10, false], 151 | ], 152 | } 153 | , 154 | "Una noche en la ópera": { 155 | "kind": "Musical", 156 | "time": [ 157 | [2, 1, 2, false], 158 | ], 159 | } 160 | , 161 | "El mundo sentado a mis piés": { 162 | "kind": "Magacín", 163 | "time": [ 164 | [1, 16, 17, true], 165 | ], 166 | } 167 | , 168 | "Ar de Coruña": { 169 | "icon": "arde.jpg", 170 | "kind": "Magacín", 171 | "time": [ 172 | [1, 9, 11, false], 173 | ], 174 | } 175 | , 176 | "Más Voces": { 177 | "icon": "masvoces_democracy.png", 178 | "kind": "Informativo / Divulgación", 179 | "time": [ 180 | [1, 8, 8.5, true], [1, 14, 14.5, true], 181 | [2, 8, 8.5, true], [2, 14, 14.5, true], 182 | [3, 8, 8.5, true], [3, 14, 14.5, true], 183 | [4, 8, 8.5, true], [4, 14, 14.5, true], 184 | [5, 8, 8.5, true], [5, 14, 14.5, true], 185 | ], 186 | } 187 | , 188 | "Minoría absoluta": { 189 | "kind": "Magacín", 190 | "time": [ 191 | [1, 23, 24, true], 192 | [2, 11, 12, false], 193 | ], 194 | } 195 | , 196 | "La Gran Armada": { 197 | "icon": "armada.jpg", 198 | "kind": "Informativo / Divulgación", 199 | "time": [ 200 | [4, 10, 11, false], 201 | ], 202 | } 203 | , 204 | "La parada de los monstruos": { 205 | "kind": "Magacín", 206 | "time": [ 207 | [1, 17, 19, true], 208 | ], 209 | } 210 | , 211 | } 212 | -------------------------------------------------------------------------------- /l10n/en.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AboutPage 6 | 7 | 8 | Recommend this application 9 | 10 | 11 | 12 | 13 | Tell us what you think 14 | 15 | 16 | 17 | 18 | Rate us in the Nokia Store 19 | 20 | 21 | 22 | 23 | Follow us on Twitter 24 | 25 | 26 | 27 | 28 | Check our other applications 29 | 30 | 31 | 32 | 33 | This application displays content from <a href="http://cuacfm.org/">cuacfm.org</a> but is not endorsed nor certified by Cuac FM. 34 | Disclaimer in about page 35 | 36 | 37 | 38 | 39 | Licensing terms 40 | Button which open the license text popup from the about page 41 | 42 | 43 | 44 | 45 | Dismiss 46 | Button which closes the license popup in the about page 47 | 48 | 49 | 50 | 51 | CFMController 52 | 53 | 54 | Buffering... %1% 55 | 56 | 57 | 58 | 59 | %1 - Muted 60 | 61 | 62 | 63 | 64 | Playing 65 | 66 | 67 | 68 | 69 | Paused 70 | 71 | 72 | 73 | 74 | Connecting... 75 | 76 | 77 | 78 | 79 | Disconnected 80 | 81 | 82 | 83 | 84 | MainPage 85 | 86 | 87 | Open website 88 | Menu option in main page to open radio station website 89 | 90 | 91 | 92 | 93 | About 94 | Menu option in main page to open the about page 95 | 96 | 97 | 98 | 99 | Pause 100 | Play/Pause button in the lower toolbar of the main page 101 | 102 | 103 | 104 | 105 | Play 106 | Play/Pause button in the lower toolbar of the main page 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /l10n/es.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AboutPage 6 | 7 | 8 | Recommend this application 9 | Recomendar esta aplicación 10 | 11 | 12 | 13 | Tell us what you think 14 | Cuéntanos tu opinión 15 | 16 | 17 | 18 | Rate us in the Nokia Store 19 | Valóranos en la Nokia Store 20 | 21 | 22 | 23 | Follow us on Twitter 24 | Síguenos en Twitter 25 | 26 | 27 | 28 | Check our other applications 29 | Nuestras aplicaciones 30 | 31 | 32 | 33 | This application displays content from <a href="http://cuacfm.org/">cuacfm.org</a> but is not endorsed nor certified by Cuac FM. 34 | Disclaimer in about page 35 | Esta aplicación muestra contenido de <a href="http://cuacfm.org/">cuacfm.org</a> pero no está promocionado o certificado por Cuac FM. 36 | 37 | 38 | 39 | Licensing terms 40 | Button which open the license text popup from the about page 41 | Licencia 42 | 43 | 44 | 45 | Dismiss 46 | Button which closes the license popup in the about page 47 | Cerrar 48 | 49 | 50 | 51 | CFMController 52 | 53 | 54 | Buffering... %1% 55 | Llenando búfer... %1% 56 | 57 | 58 | 59 | %1 - Muted 60 | %1 - Silenciado 61 | 62 | 63 | 64 | Playing 65 | Reproduciendo 66 | 67 | 68 | 69 | Paused 70 | Pausado 71 | 72 | 73 | 74 | Connecting... 75 | Conectando... 76 | 77 | 78 | 79 | Disconnected 80 | Desconectado 81 | 82 | 83 | 84 | MainPage 85 | 86 | 87 | Open website 88 | Menu option in main page to open radio station website 89 | Abrir página web 90 | 91 | 92 | 93 | About 94 | Menu option in main page to open the about page 95 | Acerca de 96 | 97 | 98 | 99 | Pause 100 | Play/Pause button in the lower toolbar of the main page 101 | Pausar 102 | 103 | 104 | 105 | Play 106 | Play/Pause button in the lower toolbar of the main page 107 | Reproducir 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: cuacfmeego 2 | Section: user/network 3 | Priority: extra 4 | Maintainer: Adrian Perez 5 | Build-Depends: debhelper (>= 7.0.50), applauncherd-dev 6 | Standards-Version: 3.8.4 7 | Homepage: http://gitorious.org/aperez-sandbox/cuacfmeego 8 | 9 | Package: cuacfmeego 10 | Architecture: any 11 | Depends: ${misc:Depends}, ${shlibs:Depends}, qt-components, 12 | gstreamer0.10-plugins-base, 13 | gstreamer0.10-plugins-good, 14 | gstreamer0.10-plugins-bad, 15 | Description: Tuner for the streaming service of Cuac FM. 16 | A Corunna community radio broadcasts in FM since 1996, and 17 | has had Internet streaming since 2001. The application allows 18 | to listen to the stream, podcasts of programmes, and more. 19 | XB-Maemo-Display-Name: Cuac FM 20 | XB-Maemo-Icon-26: 21 | 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 95 | 96 | Package: cuacfmeego-dbg 97 | Architecture: any 98 | Depends: ${misc:Depends}, ${shlibs:Depends}, cuacfmeego (= ${binary:Version}) 99 | Description: Tuner for the streaming service of Cuac FM. 100 | A Corunna community radio broadcasts in FM since 1996, and 101 | has had Internet streaming since 2001. The application allows 102 | to listen to the stream, podcasts of programmes, and more. 103 | . 104 | This package contains the debugging symbols for Cuac FM. It 105 | is only needed for development purposes. 106 | -------------------------------------------------------------------------------- /qml/AboutPage.qml: -------------------------------------------------------------------------------- 1 | /* 2 | * about.qml 3 | * Copyright (C) 2011 Simon Pena 4 | * Copyright (C) 2011-2012 Adrian Perez 5 | * 6 | * Distributed under terms of the GPLv3 license. 7 | */ 8 | 9 | import QtQuick 1.1 10 | import com.nokia.meego 1.0 11 | import "file:///usr/lib/qt4/imports/com/meego/UIConstants.js" as UIConstants 12 | 13 | Page { 14 | property string license: 'This program is free software: you can redistribute it and/or modify ' + 15 | 'it under the terms of the GNU General Public License as published by ' + 16 | 'the Free Software Foundation, either version 3 of the License, or ' + 17 | '(at your option) any later version.

' + 18 | 19 | 'This package is distributed in the hope that it will be useful, ' + 20 | 'but WITHOUT ANY WARRANTY; without even the implied warranty of ' + 21 | 'MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ' + 22 | 'GNU General Public License for more details.

' + 23 | 24 | 'You should have received a copy of the GNU General Public License ' + 25 | 'along with this program. If not, see ' + 26 | 'http://www.gnu.org/licenses

' 27 | 28 | tools: ToolBarLayout { 29 | ToolIcon { 30 | iconId: 'toolbar-back' 31 | onClicked: pageStack.pop() 32 | } 33 | } 34 | 35 | Component.onCompleted: { 36 | aboutOptions.get(0).title = qsTr('Recommend this application') 37 | aboutOptions.get(1).title = qsTr('Tell us what you think') 38 | aboutOptions.get(2).title = qsTr('Rate us in the Nokia Store') 39 | aboutOptions.get(3).title = qsTr('Follow us on Twitter') 40 | aboutOptions.get(4).title = qsTr('Check our other applications') 41 | } 42 | 43 | ListModel { 44 | id: aboutOptions 45 | ListElement { 46 | title: 'Recommend this application' 47 | action: 'openExternally' 48 | data: 'mailto:?subject=Download%20Cuac%20FM&body=Available%20at%20http://store.ovi.com/content/273753' 49 | } 50 | ListElement { 51 | title: 'Tell us what you think' 52 | action: 'openExternally' 53 | data: 'mailto:aperez@igalia.com?subject=Cuac%20FM' 54 | } 55 | ListElement { 56 | title: 'Rate us in the Nokia Store' 57 | action: 'openStore' 58 | data: 'http://store.ovi.com/content/273753' 59 | } 60 | ListElement { 61 | title: 'Follow us on Twitter' 62 | action: 'openExternally' 63 | data: 'http://twitter.com/aperezdc' 64 | } 65 | ListElement { 66 | title: 'Check our other applications' 67 | action: 'openStore' 68 | data: 'http://store.nokia.com/publisher/Igalia' 69 | } 70 | } 71 | 72 | Flickable { 73 | id: flick 74 | clip: true 75 | anchors.fill: parent 76 | anchors { 77 | topMargin: UIConstants.DEFAULT_MARGIN 78 | leftMargin: UIConstants.DEFAULT_MARGIN 79 | rightMargin: UIConstants.DEFAULT_MARGIN 80 | bottomMargin: UIConstants.DEFAULT_MARGIN 81 | } 82 | contentHeight: contentColumn.height 83 | 84 | Column { 85 | id: contentColumn 86 | spacing: UIConstants.DEFAULT_MARGIN 87 | width: parent.width 88 | 89 | Image { 90 | id: aboutImage 91 | anchors.horizontalCenter: parent.horizontalCenter 92 | source: 'qrc:/cuacfmeego-logo.png' 93 | } 94 | 95 | Label { 96 | id: aboutVersion 97 | text: 'Cuac FM 0.1.3' 98 | width: parent.width 99 | horizontalAlignment: Text.AlignHCenter 100 | color: UIConstants.COLOR_INVERTED_FOREGROUND 101 | platformStyle: LabelStyle { 102 | fontPixelSize: UIConstants.FONT_XLARGE 103 | } 104 | } 105 | 106 | Label { 107 | id: aboutCopyright 108 | text: 'Copyright © 2012 Adrian Perez' 109 | width: parent.width 110 | horizontalAlignment: Text.AlignHCenter 111 | color: UIConstants.COLOR_INVERTED_FOREGROUND 112 | platformStyle: LabelStyle { 113 | fontPixelSize: UIConstants.FONT_LSMALL 114 | fontFamily: UIConstants.FONT_FAMILY_LIGHT 115 | } 116 | } 117 | 118 | Column { 119 | height: repeater.model.count * UIConstants.LIST_ITEM_HEIGHT_SMALL 120 | width: parent.width 121 | anchors { 122 | topMargin: UIConstants.DEFAULT_MARGIN * 2 123 | bottomMargin: UIConstants.DEFAULT_MARGIN * 2 124 | } 125 | 126 | Repeater { 127 | id: repeater 128 | model: aboutOptions 129 | Item { 130 | height: UIConstants.LIST_ITEM_HEIGHT_SMALL 131 | width: parent.parent.width 132 | BorderImage { 133 | anchors.fill: parent 134 | visible: mouseArea.pressed 135 | source: 'image://theme/meegotouch-list-fullwidth-inverted-background-pressed-vertical-center' 136 | } 137 | Rectangle { 138 | anchors.bottom: parent.top 139 | width: parent.width 140 | height: 1 141 | color: '#444' 142 | } 143 | Label { 144 | text: model.title 145 | anchors { 146 | left: parent.left 147 | leftMargin: UIConstants.DEFAULT_MARGIN 148 | verticalCenter: parent.verticalCenter 149 | } 150 | platformStyle: LabelStyle { 151 | fontPixelSize: UIConstants.FONT_SLARGE 152 | } 153 | color: UIConstants.COLOR_INVERTED_FOREGROUND 154 | } 155 | Image { 156 | source: 'image://theme/icon-m-common-drilldown-arrow-inverse' 157 | anchors { 158 | right: parent.right 159 | rightMargin: UIConstants.DEFAULT_MARGIN 160 | verticalCenter: parent.verticalCenter 161 | } 162 | } 163 | Rectangle { 164 | anchors.bottom: parent.bottom 165 | width: parent.width 166 | height: 1 167 | color: '#444' 168 | visible: index === repeater.model.count - 1 169 | } 170 | MouseArea { 171 | id: mouseArea 172 | anchors.fill: parent 173 | onClicked: { 174 | if (model.action === 'openStore') { 175 | controller.openStoreClient(model.data) 176 | } else if (model.action === 'openExternally') { 177 | Qt.openUrlExternally(model.data) 178 | } 179 | } 180 | } 181 | } 182 | } 183 | } 184 | 185 | Text { 186 | id: aboutContentDisclaimer 187 | font.family: UIConstants.FONT_FAMILY 188 | font.pixelSize: UIConstants.FONT_LSMALL 189 | color: UIConstants.COLOR_INVERTED_FOREGROUND 190 | width: parent.width 191 | wrapMode: Text.WordWrap 192 | //: Disclaimer in about page 193 | text: qsTr('This application displays content from cuacfm.org but is not endorsed nor certified by Cuac FM.') 194 | onLinkActivated: Qt.openUrlExternally(link) 195 | } 196 | 197 | Button { 198 | anchors.horizontalCenter: parent.horizontalCenter 199 | //: Button which open the license text popup from the about page 200 | text: qsTr('Licensing terms') 201 | onClicked: licenseDialog.open() 202 | } 203 | } 204 | } 205 | 206 | QueryDialog { 207 | id: licenseDialog 208 | message: license 209 | //: Button which closes the license popup in the about page 210 | acceptButtonText: qsTr('Dismiss') 211 | } 212 | 213 | ScrollDecorator { 214 | flickableItem: flick 215 | anchors.rightMargin: -UIConstants.DEFAULT_MARGIN 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /cfm-controller.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * cfm-controller.cc 3 | * Copyright (C) 2011 Adrian Perez 4 | * 5 | * Distributed under terms of the MIT license. 6 | */ 7 | 8 | #include "cfm-controller.h" 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | 17 | static const gchar CUACFM_STREAM_URL[] = "http://cien30.udc.es/cuacfm.mp3"; 18 | static const QString STORE_DBUS_IFACE("com.nokia.OviStoreClient"); 19 | 20 | 21 | static void setProperty(void* object, 22 | const gchar* name, 23 | const gchar* newValue) 24 | { 25 | GValue value; 26 | memset(&value, 0x00, sizeof(GValue)); 27 | g_value_init(&value, G_TYPE_STRING); 28 | g_value_set_string(&value, newValue); 29 | g_object_set_property(G_OBJECT(object), name, &value); 30 | g_value_unset(&value); 31 | } 32 | 33 | 34 | static void setProperty(void* object, 35 | const gchar* name, 36 | gboolean newValue) 37 | { 38 | GValue value; 39 | memset(&value, 0x00, sizeof(GValue)); 40 | g_value_init(&value, G_TYPE_BOOLEAN); 41 | g_value_set_boolean(&value, newValue); 42 | g_object_set_property(G_OBJECT(object), name, &value); 43 | g_value_unset(&value); 44 | } 45 | 46 | 47 | template T 48 | getProperty(void* object, const gchar* name) 49 | { 50 | T::this_is_unimplemented; 51 | } 52 | 53 | template<> bool 54 | getProperty(void* object, const gchar* name) 55 | { 56 | GValue value; 57 | memset(&value, 0x00, sizeof(GValue)); 58 | g_value_init(&value, G_TYPE_BOOLEAN); 59 | g_object_get_property(G_OBJECT(object), name, &value); 60 | gboolean returnValue = g_value_get_boolean(&value); 61 | g_value_reset(&value); 62 | return returnValue; 63 | } 64 | 65 | 66 | CFMController::CFMController(QDeclarativeContext* context): 67 | _context(context), 68 | _connection(NULL), 69 | _playbin(NULL), 70 | _statusText(""), 71 | _bufferFillRate(-1), 72 | _playPending(false), 73 | _connected(false) 74 | { 75 | Q_ASSERT(context); 76 | 77 | DBusConnection *systembus = dbus_bus_get(DBUS_BUS_SYSTEM, NULL); 78 | if (!systembus) { 79 | qFatal("Could not connect to the D-Bus system bus"); 80 | } 81 | dbus_connection_setup_with_g_main(systembus, NULL); 82 | 83 | if ((_connection = con_ic_connection_new ()) == NULL) { 84 | qFatal("Coult not create ConIcConnection object"); 85 | } 86 | 87 | g_signal_connect(G_OBJECT(_connection), "connection-event", 88 | G_CALLBACK(CFMController::handleConnectionEvent), this); 89 | ::setProperty(_connection, "automatic-connection-events", true); 90 | 91 | // Register the controller in the QML context. 92 | _context->setContextProperty("controller", this); 93 | } 94 | 95 | 96 | GstElement* CFMController::gstPipeline() 97 | { 98 | if (!_playbin) { 99 | if ((_playbin = gst_element_factory_make("playbin2", "playbin")) == NULL) { 100 | qFatal("Could not create GStreamer playbin2 element"); 101 | } 102 | g_signal_connect(gst_pipeline_get_bus(GST_PIPELINE(_playbin)), "message", 103 | G_CALLBACK(CFMController::handleGstMessage), this); 104 | ::setProperty(_playbin, "uri", CUACFM_STREAM_URL); 105 | qDebug("gstreamer: created playbin2 element"); 106 | } 107 | return _playbin; 108 | } 109 | 110 | 111 | void CFMController::destroyGstPipeline() 112 | { 113 | if (_playbin) { 114 | gst_element_set_state(_playbin, GST_STATE_PAUSED); 115 | gst_object_unref(_playbin); 116 | _bufferFillRate = -1; 117 | _playbin = NULL; 118 | qDebug("gstreamer: destroyed playbin2 element"); 119 | } 120 | } 121 | 122 | 123 | CFMController::~CFMController() 124 | { 125 | destroyGstPipeline(); 126 | g_object_unref(_connection); 127 | } 128 | 129 | 130 | bool CFMController::isMuted() const 131 | { 132 | return _playbin ? ::getProperty(_playbin, "mute") : false; 133 | } 134 | 135 | 136 | void CFMController::setMuted(bool muteValue) 137 | { 138 | if (_playbin) { 139 | ::setProperty(_playbin, "mute", muteValue); 140 | emit mutedStatusChanged(muteValue); 141 | updateStatusText(); 142 | } 143 | } 144 | 145 | 146 | bool CFMController::isPlaying() const 147 | { 148 | if (!_playbin) 149 | return false; 150 | 151 | if (_playPending) 152 | return false; 153 | 154 | GstState state; 155 | GstState nextState; 156 | 157 | gst_element_get_state(_playbin, 158 | &state, 159 | &nextState, 160 | GST_CLOCK_TIME_NONE); 161 | 162 | /* 163 | * If the element is pending to transition to a different state, 164 | * use the next state as reference, because GStreamer will be 165 | * changing it behind our back to that. 166 | */ 167 | if (nextState != GST_STATE_VOID_PENDING) 168 | state = nextState; 169 | 170 | switch (state) { 171 | case GST_STATE_NULL: /* Initial state (of course, not playing) */ 172 | case GST_STATE_READY: /* Ready to go to pause, but not playing either. */ 173 | case GST_STATE_PAUSED: 174 | return false; 175 | 176 | case GST_STATE_PLAYING: 177 | return true; 178 | 179 | default: 180 | qCritical("Unrecognized GStreamer pipeline state!\n" 181 | "CMFController::isPlaying() returning false anyway"); 182 | return false; 183 | } 184 | } 185 | 186 | 187 | void CFMController::setPlaying(bool playingValue) 188 | { 189 | if (playingValue) { 190 | if (!isNetworkAvailable()) { 191 | if (!_playPending) { 192 | qDebug("playback: no network, requesting -> pending"); 193 | _playPending = true; 194 | setNetworkAvailable(true); 195 | } 196 | else { 197 | qDebug("playback: no network, still pending..."); 198 | } 199 | } 200 | else { 201 | qDebug("playback: network available -> playing"); 202 | _playPending = false; 203 | gst_element_set_state(gstPipeline(), GST_STATE_PLAYING); 204 | emit playingStatusChanged(true); 205 | } 206 | } 207 | else { 208 | qDebug("playback: stopped"); 209 | _playPending = false; 210 | destroyGstPipeline(); 211 | emit playingStatusChanged(false); 212 | } 213 | updateStatusText(); 214 | } 215 | 216 | 217 | bool CFMController::isBuffering() const 218 | { 219 | return _playbin && _bufferFillRate >= 0 && _bufferFillRate < 100; 220 | } 221 | 222 | 223 | int CFMController::getBufferingStatus() const 224 | { 225 | return _bufferFillRate; 226 | } 227 | 228 | 229 | void CFMController::handleGstMessage(GstBus *bus, 230 | GstMessage *message, 231 | gpointer controllerptr) 232 | { 233 | CFMController *controller = (CFMController*) controllerptr; 234 | 235 | Q_ASSERT(controller); 236 | Q_ASSERT(controller->_playbin); 237 | Q_UNUSED(bus); 238 | 239 | switch (GST_MESSAGE_TYPE(message)) { 240 | /* 241 | * Update the buffering status and handle pausing the playback when 242 | * the fill rate of the buffer is below 100% -- recommended in docs. 243 | */ 244 | case GST_MESSAGE_BUFFERING: { 245 | bool wasBuffering = controller->isBuffering(); 246 | 247 | gst_message_parse_buffering(message, &controller->_bufferFillRate); 248 | 249 | controller->emit bufferingStatusChanged(controller->_bufferFillRate); 250 | if (wasBuffering != controller->isBuffering()) 251 | controller->emit bufferingChanged(controller->isBuffering()); 252 | 253 | controller->updateStatusText(); 254 | 255 | qDebug("Buffering: %u%%", controller->_bufferFillRate); 256 | if (controller->_bufferFillRate < 100 && controller->isPlaying()) 257 | controller->setPlaying(false); 258 | else if (!controller->isPlaying()) 259 | controller->setPlaying(true); 260 | break; 261 | } 262 | 263 | /* 264 | * Theorically the stream is infinite, still EOS may be received 265 | * e.g. if the server closes the connection. Handle it by pausing 266 | * the playback. 267 | */ 268 | case GST_MESSAGE_EOS: 269 | controller->setPlaying(false); 270 | break; 271 | 272 | /* 273 | * Errors are both printed printed to stderr and signaled. 274 | * Warnings are printed out to stderr using qWarning(). 275 | */ 276 | case GST_MESSAGE_WARNING: 277 | case GST_MESSAGE_ERROR: { 278 | gchar *debuginfo = NULL; 279 | GError *error = NULL; 280 | 281 | // Note that playback must be stopped on errors. 282 | if (GST_MESSAGE_TYPE(message) == GST_MESSAGE_ERROR) { 283 | gst_message_parse_error(message, &error, &debuginfo); 284 | controller->setPlaying(false); 285 | } 286 | else { 287 | gst_message_parse_warning(message, &error, &debuginfo); 288 | } 289 | 290 | qWarning("GStreamer: %s", error->message); 291 | qDebug (" - debug: %s", debuginfo); 292 | 293 | if (GST_MESSAGE_TYPE(message) == GST_MESSAGE_ERROR) 294 | controller->emit playbackError(QString(error->message)); 295 | 296 | g_error_free(error); 297 | g_free(debuginfo); 298 | break; 299 | } 300 | 301 | default: /* For the rest of messages, do nothing. */ 302 | break; 303 | } 304 | } 305 | 306 | 307 | bool CFMController::isNetworkAvailable() const 308 | { 309 | return _connected; 310 | } 311 | 312 | 313 | void CFMController::setNetworkAvailable(bool availability) 314 | { 315 | if (!_connected && availability) { 316 | qDebug("network: connection requested"); 317 | if (!con_ic_connection_connect(_connection, CON_IC_CONNECT_FLAG_NONE)) 318 | qFatal("Failed sending D-Bus message to connection manager"); 319 | } 320 | } 321 | 322 | 323 | void CFMController::handleConnectionEvent(ConIcConnection *connection, 324 | ConIcConnectionEvent *event, 325 | gpointer controllerptr) 326 | { 327 | CFMController *controller = (CFMController*) controllerptr; 328 | 329 | Q_ASSERT(controller); 330 | Q_UNUSED(connection); 331 | 332 | ConIcConnectionStatus status = con_ic_connection_event_get_status(event); 333 | 334 | switch (status) { 335 | case CON_IC_STATUS_CONNECTED: 336 | qDebug("network: connected"); 337 | // If playing was requested and we were waiting for a connection 338 | // event, start playing now. 339 | controller->emit networkAvailableChanged((controller->_connected = true)); 340 | if (controller->_playPending) { 341 | controller->setPlaying(true); 342 | controller->_playPending = false; 343 | } 344 | break; 345 | 346 | case CON_IC_STATUS_DISCONNECTING: 347 | qDebug("network: disconnecting..."); 348 | // If playing was enabled, then we need to pause and tell the 349 | // user about the connection being lost. 350 | // 351 | // TODO: Show notification on connection lost. 352 | controller->emit networkAvailableChanged((controller->_connected = false)); 353 | controller->setPlaying(false); 354 | break; 355 | 356 | case CON_IC_STATUS_NETWORK_UP: 357 | qDebug("network: connecting..."); 358 | break; 359 | 360 | case CON_IC_STATUS_DISCONNECTED: 361 | qDebug("network: disconnected"); 362 | break; 363 | } 364 | 365 | controller->updateStatusText(); 366 | } 367 | 368 | 369 | void CFMController::updateStatusText() 370 | { 371 | static const QString bufferingText(tr("Buffering... %1%")); 372 | static const QString mutedText(tr("%1 - Muted")); 373 | QString newText; 374 | 375 | if (_connected) { 376 | if (isBuffering()) 377 | newText = bufferingText.arg(getBufferingStatus()); 378 | else if (isPlaying()) 379 | newText = tr("Playing"); 380 | else 381 | newText = tr("Paused"); 382 | } 383 | else { 384 | // No connection 385 | if (_playPending) 386 | newText = tr("Connecting..."); 387 | else 388 | newText = tr("Disconnected"); 389 | } 390 | 391 | if (isMuted()) 392 | newText = mutedText.arg(newText); 393 | 394 | if (newText != _statusText) 395 | emit statusTextChanged((_statusText = newText)); 396 | } 397 | 398 | 399 | const QString& CFMController::getStatusText() const 400 | { 401 | return _statusText; 402 | } 403 | 404 | 405 | void CFMController::openStoreClient(const QString& url) const 406 | { 407 | // Based on 408 | // https://gitorious.org/n9-apps-client/n9-apps-client/blobs/master/daemon/notificationhandler.cpp#line178 409 | 410 | QDBusInterface storeif(STORE_DBUS_IFACE, "/", STORE_DBUS_IFACE, 411 | QDBusConnection::sessionBus()); 412 | 413 | QStringList params; 414 | params << url; 415 | 416 | storeif.asyncCall("LaunchWithLink", QVariant::fromValue(params)); 417 | } 418 | -------------------------------------------------------------------------------- /cuacfmeego.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 20 | 22 | image/svg+xml 23 | 25 | 26 | 27 | 28 | 48 | 50 | 57 | 61 | 65 | 66 | 68 | 73 | 76 | 81 | 86 | 87 | 94 | 101 | 108 | 115 | 122 | 129 | 136 | 143 | 150 | 157 | 164 | 171 | 178 | 185 | 192 | 199 | 206 | 213 | 220 | 227 | 234 | 241 | 248 | 255 | 262 | 269 | 276 | 283 | 290 | 292 | 297 | 300 | 305 | 310 | 311 | 318 | 322 | 326 | 327 | 329 | 333 | 337 | 338 | 347 | 350 | 355 | 356 | 357 | 362 | 367 | 372 | 376 | 382 | 387 | 393 | 394 | 395 | --------------------------------------------------------------------------------