Canonical Voices

Hardik Dalwadi

Hello world!

Welcome to Canonical Voices. This is your first post. Edit or delete it, then start blogging!

Read more
Jouni Helminen

Ubuntu community devs Andrew Hayzen and Victor Thompson chat with lead designer Jouni Helminen. Andrew and Victor have been working in open source projects for a couple of years and have done a great job on the Music application that is now rolling out on phone, tablet and desktop. In this chat they are sharing their thoughts on open source, QML, app development, and tips on how to get started contributing and developing apps.

If you want to start writing apps for Ubuntu, it’s easy. Check out http://developer.ubuntu.com, get involved on Google+ Ubuntu App Dev – https://plus.google.com/communities/1… – or contact alan.pope@canonical.com – you are in good hands!

Check out the video interview here :)

Read more
Daniel Holbach

In the past weeks Nick, David, a few others and I worked on an app / a website, which could easily collect information which will give users of an Ubuntu device a head-start. All our collective experience and knowledge, easily added and translated.

We achieved quite a bit. We’re now very close to getting a first version of it online (both as an app in the store and as a website). We can quite reliably integrate translations and add new content.

We still have a few TODO items and it would be great if you could help out. If you can write a bit of documentation, translate content or fix some HTML/CSS bits or help out with testability. Any help at all will be appreciated.

Tasks:

  • Add content. Just check out our branch and propose a merge. Read the HACKING doc beforehand.
  • Translate. The content is likely going to change a bit in the next days still, but every edit or translation will be appreciated.
  • Hack! We have a number of things we still want to improve. Read the HACKING doc beforehand. Here’s a list of things:
    • Styling/theming/navigation:
      • Bug 1416385: Fix bullet points in the phone theme
      • Bug 1428671: Remove traces of developer.ubuntu.com
      • Bug 1428669: Clean up required CSS/JS
      • Bug 1425025: Automatically load translated pages according to the user language.
    • Testing
    • and there’s more.

Ping me on IRC, or balloons or dpm if you want to get involved. We look forward to working with you and we’ll post more updates soon.

Read more
facundo


Algunas, varias y sueltas.

A nivel de proyectos, le estuvimos metiendo bastante con Nico a fades. La verdad es que la versión 2 que sacamos la semana pasada está piolísima... si usás virtualenvs, no dejes de pegarle una mirada.

Otro proyecto con el que estuve es CDPedia... la parte de internacionalización está bastante potable, y eso también me llevó a renovar la página principal que te muestra cuando la abrís, así que puse a tirar una nueva versión de la de español, y luego seguirá una de portugués (¡cada imagen tarda como una semana!).

Hace un rato subí a la página de tutoriales de Python Argentina el Tutorial de Django en español (¡gracias Matías Bordese por el material!). Este tuto antes estaba en un dominio que ahora venció, y nos pareció interesante que esté todo en el mismo lugar, facilita que la gente lo encuentre.

Finalmente, empecé a organizar mi Segundo Curso Abierto de Python. Esta vez lo quiero hacer por la zona de Palermo, o alrededores (la vez pasada fue en microcentro). Todavía no tengo reservado un lugar, y menos fechas establecidas, pero el formato va a ser similar al anterior. Con respecto al sitio, si alguien conoce un buen lugar para alquilar "aulas", me avisa, :)

Read more
bmichaelsen

Around the world, Around the world
— Daft Punk, Around the world

So, you still heard that unfounded myth that it is hard to get involved with and to start contributing to LibreOffice? Still? Even though that there are our Easy Hacks and the LibreOffice developer are a friendly bunch that will help you get started on mailing lists and on IRC? If those alone do not convince you, it might be because it is admittedly much easier to get started if you meet people face to face — like on one of our upcoming Events! Especially our Hackfests are a good way to get started. The next one will be at the University de Las Palmas de Gran Canaria were we had been guests last year already. We presented some introduction talks to the students of the university and then went on to hack on LibreOffice from fixing bugs to implementing new stuff. Here is how that looked like last year:

LibreOffice Hackfest Gran Canaria 2014

LibreOffice Hackfest Gran Canaria 2014

One thing we learned from previous Hackfests was that it is great if newcomers have a way to start working on code right away. While it is rather easy to do that as the 5 minute video on our wiki shows, it might still take some time on some notebooks. So what if you spontaneously show up at the event without a pre-build LibreOffice? Well for that, we now have — thanks to Christian Lohmaier of the Document Foundation staffremote virtual machines prepared for Hackfests, that allow you to get started right away with everything prepared — on rather beefy hardware even, that is.

If you are a student at ULPGC or live in Las Palmas or on the Canary Islands, we invite you to join us to learn how to get started. For students, this is also a very good opportunity get involved and prepare for a Google Summer of Code on LibreOffice. Furthermore, if you are a even casual contributor to LibreOffice code already and want to help out sharing and deepen knowledge on how to work on LibreOffice code, you should get in contact with the Document Foundation — while the event is already very soon now, there still might be travel reimbursal available. You will find all the details on the wiki page for the Hackfest in Las Palmas de Gran Canaria 2015.

LibreOffice Evening Hacking

LibreOffice Evening Hacking in Las Palmas 2014

On the other hand, if two weeks is too short a notice for you, but the rest of this sounds really tempting, there is already the next Hackfest planned, which will take place in Cambridge in the United Kingdom in May. We will be there with a Hackfest for the first time and invite you to join us from anywhere in Europe if you either are a LibreOffice code contributor or if you are interested in learning more on how to become one. Again, there is a wiki page with the details on the LibreOffice Hackfest in Cambridge 2015, and travel reimbursals are available. Contact us!

LibreOffice Evening Hacking

How I imagine Cambridge in May — Photo by Andrew Dunn CC-BY-SA 2.0 via Wikimedia


Read more
Joseph Salisbury

Meeting Minutes

IRC Log of the meeting.

Meeting minutes.

Agenda

20150303 Meeting Agenda


Release Metrics and Incoming Bugs

Release metrics and incoming bug data can be reviewed at the following link:

  • http://people.canonical.com/~kernel/reports/kt-meeting.txt


Status: Vivid Development Kernel

We have officially uploaded our v3.19 kernel for Vivid to the archive,
ie. 3.19.0-7.7. Please test and let us know your results.
This is also an early reminder that kernel freeze for Vivid is on Thurs
Apr 9. If you have any patches which need to land for 15.04′s release,
please make sure to submit those sooner rather than later.
—–
Important upcoming dates:
Thurs Mar 26 – Final Beta (~3 weeks away)
Thurs Apr 09 – Kernel Freeze (~5 weeks away)
Thurs Apr 23 – 15.04 Release (~7 weeks away)


Status: CVE’s

The current CVE status can be reviewed at the following link:

http://people.canonical.com/~kernel/cve/pkg/ALL-linux.html


Status: Stable, Security, and Bugfix Kernel Updates – Utopic/Trusty/Precise/Lucid

Status for the main kernels, until today:

  • Lucid – Kernel Prep
  • Precise – Kernel Prep
  • Trusty – Kernel Prep
  • Utopic – Kernel Prep

    Current opened tracking bugs details:

  • http://kernel.ubuntu.com/sru/kernel-sru-workflow.html

    For SRUs, SRU report is a good source of information:

  • http://kernel.ubuntu.com/sru/sru-report.html

    Schedule:

    cycle: 27-Feb through 21-Mar
    ====================================================================
    27-Feb Last day for kernel commits for this cycle
    01-Mar – 07-Mar Kernel prep week.
    08-Mar – 21-Mar Bug verification; Regression testing; Release


Open Discussion or Questions? Raise your hand to be recognized

No open discussion.

Read more
Michael Hall

A couple of weeks ago I had the opportunity to attend the thirteenth Southern California Linux Expo, more commonly known at SCaLE 13x. It was my first time back in five years, since I attended 9x, and my first time as a speaker. I had a blast at SCaLE, and a wonderful time with UbuCon. If you couldn’t make it this year, it should definitely be on your list of shows to attend in 2016.

UbuCon

Thanks to the efforts of Richard Gaskin, we had a room all day Friday to hold an UbuCon. For those of you who haven’t attended an UbuCon before, it’s basically a series of presentations by members of the Ubuntu community on how to use it, contribute to it, or become involved in the community around it. SCaLE was one of the pioneering host conferences for these, and this year they provided a double-sized room for us to use, which we still filled to capacity.

image20150220_100226891I was given the chance to give not one but two talks during UbuCon, one on community and one on the Ubuntu phone. We also had presentations from my former manager and good friend Jono Bacon, current coworkers Jorge Castro and Marco Ceppi, and inspirational community members Philip Ballew and Richard Gaskin.

I’d like thank Richard for putting this all together, and for taking such good care of those of us speaking (he made sure we always had mints and water). UbuCon was a huge success because of the amount of time and work he put into it. Thanks also to Canonical for providing us, on rather short notice, a box full of Ubuntu t-shirts to give away. And of course thanks to the SCaLE staff and organizers for providing us the room and all of the A/V equipment in it to use.

The room was recorded all day, so each of these sessions can be watched now on youtube. My own talks are at 4:00:00 and 5:00:00.

Ubuntu Booth

In addition to UbuCon, we also had an Ubuntu booth in the SCaLE expo hall, which was registered and operated by members of the Ubuntu California LoCo team. These guys were amazing, they ran the booth all day over all three days, managed the whole setup and tear down, and did an excellent job talking to everybody who came by and explaining everything from Ubuntu’s cloud offerings, to desktops and even showing off Ubuntu phones.

image20150221_162940413Our booth wouldn’t have happened without the efforts of Luis Caballero, Matt Mootz, Jose Antonio Rey, Nathan Haines, Ian Santopietro, George Mulak, and Daniel Gimpelevich, so thank you all so much! We also had great support from Carl Richell at System76 who let us borrow 3 of their incredible laptops running Ubuntu to show off our desktop, Canonical who loaned us 2 Nexus 4 phones running Ubuntu as well as one of the Orange Box cloud demonstration boxes, Michael Newsham from TierraTek who sent us a fanless PC and NAS, which we used to display a constantly-repeating video (from Canonical’s marketing team) showing the Ubuntu phone’s Scopes on a television monitor provided to us by Eäär Oden at Video Resources. Oh, and of course Stuart Langridge, who gave up his personal, first-edition Bq Ubuntu phone for the entire weekend so we could show it off at the booth.

image20150222_132142752Like Ubuntu itself, this booth was not the product of just one organization’s work, but the combination of efforts and resources from many different, but connected, individuals and groups. We are what we are, because of who we all are. So thank you all for being a part of making this booth amazing.

Read more
bmichaelsen

“The problem is all inside your head” she said to me
“The answer is easy if you take it logically”
— Paul Simon, 50 ways to leave your lover

So recently I tweaked around with these newfangled C++11 initializer lists and created an EasyHack to use them to initialize property sequences in a readable way. This caused a short exchange on the LibreOffice mailing list, which I assumed had its part in motivating Stephans interesting post “On filling a vector”. For all the points being made (also in the quick follow up on IRC), I wondered how much the theoretical “can use a move constructor” discussed etc. really meant when the C++ is translated to e.g. GENERIC, then GIMPLE, then amd64 assembler, then to the internal RISC instructions of the CPU – with multiple levels of caching in addition.

So I quickly wrote the following (thanks so much for C++11 having the nice std::chrono now).

data.hxx:

#include <vector>
struct Data {
    Data();
    Data(int a);
    int m_a;
};
void DoSomething(std::vector<Data>&);

data.cxx:

#include "data.hxx"
// noop in different compilation unit to prevent optimizing out what we want to measure
void DoSomething(std::vector<Data>&) {};
Data::Data() : m_a(4711) {};
Data::Data(int a) : m_a(a+4711) {};

main.cxx:

#include "data.hxx"
#include <iostream>
#include <vector>
#include <chrono>
#include <functional>

void A1(long count) {
    while(--count) {
        std::vector<Data> vec { Data(), Data(), Data() };
        DoSomething(vec);
    }
}

void A2(long count) {
    while(--count) {
        std::vector<Data> vec { {}, {}, {} };
        DoSomething(vec);
    }
}

void A3(long count) {
    while(--count) {
        std::vector<Data> vec { 0, 0, 0 };
        DoSomething(vec);
    }
}

void B1(long count) {
    while(--count) {
        std::vector<Data> vec;
        vec.reserve(3);
        vec.push_back(Data());
        vec.push_back(Data());
        vec.push_back(Data());
        DoSomething(vec);
    }
}

void B2(long count) {
    while(--count) {
        std::vector<Data> vec;
        vec.reserve(3);
        vec.push_back({});
        vec.push_back({});
        vec.push_back({});
        DoSomething(vec);
    }
}

void B3(long count) {
    while(--count) {
        std::vector<Data> vec;
        vec.reserve(3);
        vec.push_back(0);
        vec.push_back(0);
        vec.push_back(0);
        DoSomething(vec);
    }
}

void C1(long count) {
    while(--count) {
        std::vector<Data> vec;
        vec.reserve(3);
        vec.emplace_back(Data());
        vec.emplace_back(Data());
        vec.emplace_back(Data());
        DoSomething(vec);
    }
}

void C3(long count) {
    while(--count) {
        std::vector<Data> vec;
        vec.reserve(3);
        vec.emplace_back(0);
        vec.emplace_back(0);
        vec.emplace_back(0);
        DoSomething(vec);
    }
}

double benchmark(const char* name, std::function<void (long)> testfunc, const long count) {
    const auto start = std::chrono::system_clock::now();
    testfunc(count);
    const auto end = std::chrono::system_clock::now();
    const std::chrono::duration<double> delta = end-start;
    std::cout << count << " " << name << " iterations took " << delta.count() << " seconds." << std::endl;
    return delta.count();
}

int main(int, char**) {
    long count = 10000000;
    while(benchmark("A1", &A1, count) < 60l)
        count <<= 1;
    std::cout << "Going with " << count << " iterations." << std::endl;
    benchmark("A1", &A1, count);
    benchmark("A2", &A2, count);
    benchmark("A3", &A3, count);
    benchmark("B1", &B1, count);
    benchmark("B2", &B2, count);
    benchmark("B3", &B3, count);
    benchmark("C1", &C1, count);
    benchmark("C3", &C3, count);
    return 0;
}

Makefile:

CFLAGS?=-O2
main: main.o data.o
    g++ -o $@ $^

%.o: %.cxx data.hxx
    g++ $(CFLAGS) -std=c++11 -o $@ -c $<

Note the object here is small and trivial to copy as one would expect from objects passed around as values (as expensive to copy objects mostly can be passed around with a std::shared_ptr). So what did this measure? Here are the results:

Time for 1280000000 iterations on a Intel i5-4200U@1.6GHz (-march=core-avx2) compiled with gcc 4.8.3 without inline constructors:

implementation / CFLAGS -Os -O2 -O3 -O3 -march=…
A1 89.1 s 79.0 s 78.9 s 78.9 s
A2 89.1 s 78.1 s 78.0 s 80.5 s
A3 90.0 s 78.9 s 78.8 s 79.3 s
B1 103.6 s 97.8 s 79.0 s 78.0 s
B2 99.4 s 95.6 s 78.5 s 78.0 s
B3 107.4 s 90.9 s 79.7 s 79.9 s
C1 99.4 s 94.4 s 78.0 s 77.9 s
C3 98.9 s 100.7 s 78.1 s 81.7 s

creating a three element vector without inlined constructors
And, for comparison, following are the results, if one allows the constructors to be inlined.
Time for 1280000000 iterations on a Intel i5-4200U@1.6GHz (-march=core-avx2) compiled with gcc 4.8.3 with inline constructors:

implementation / CFLAGS -Os -O2 -O3 -O3 -march=…
A1 85.6 s 74.7 s 74.6 s 74.6 s
A2 85.3 s 74.6 s 73.7 s 74.5 s
A3 91.6 s 73.8 s 74.4 s 74.5 s
B1 93.4 s 90.2 s 72.8 s 72.0 s
B2 93.7 s 88.3 s 72.0 s 73.7 s
B3 97.6 s 88.3 s 72.8 s 72.0 s
C1 93.4 s 88.3 s 72.0 s 73.7 s
C3 96.2 s 88.3 s 71.9 s 73.7 s

creating a three element vector without inlined constructors
Some observations on these measurements:

  • -march=... is at best neutral: The measured times do not change much in general, they only even slightly improve performance in five out of 16 cases, and the two cases with the most significant change in performance (over 3%) are actually hurting the performance. So for the rest of this post, -march=... will be ignored. Sorry gentooers. ;)
  • There is no silver bullet with regard to the different implementations: A1, A2 and A3 are the faster implementations when not inlining constructors and using -Os or -O2 (the quickest A* is ~10% faster than the quickest B*/C*). However when inlining constructors and using -O3, the same implementations are the slowest (by 2.4%).
  • Most common release builds are still done with -O2 these days. For those, using initializer lists (A1/A2/A3) seem too have a significant edge over the alternatives, whether constructors are inlined or not. This is in contrast to the conclusions made from “constructor counting”, which assumed these to be slow because of additional calls needed.
  • The numbers printed in bold are either the quickest implementation in a build scenario or one that is within 1.5% of the quickest implementation. A1 and A2 are sharing the title here by being in that group five times each.
  • With constructors inlined, everything in the loop except DoSomething() could be inline. It seems to me that the compiler could — at least in theory — figure out that it is asked the same thing in all cases. Namely, reserve space for three ints on the heap, fill them each with 4711 and make the ::std::vector<int> data structure on the stack reflect that, then hand that to the DoSomething() function that you know nothing about. If the compiler would figure that out, it would take the same time for all implementations. This doesnt happen either on -O2 (differ by ~18% from quickest to slowest) nor on -O3 (differ by ~3.6%).

One common mantra in applications development is “trust the compiler to optimize”. The above observations show a few cracks in the foundations of that, esp. if you take into account that this is all on the same version of the same compiler running on the same platform and hardware with the same STL implementation. For huge objects with expensive constructors, the constructor counting approach might still be valid. Then again, those are rarely statically initialized as a bigger bunch into a vector. For the more common scenario of smaller objects with cheap constructors, my tentative conclusion so far would be to go with A1/A2/A3 — not so much because they are quickest in the most common build scenarios on my platform, but rather because the readability of them is a value on its own while the performance picture is muddy at best.

And hey, if you want to run the tests above on other platforms or compilers, I would be interested in results!

Note: I did these runs for each scenario only once, thus no standard deviation is given. In general, they seemed to be rather stable, but this being wallclock measurements, one or the other might be outliers. caveat emptor.


Read more
UbuntuTouch

[原]Ubuntu OS系统融合(英文视频)

在这个视频里,我们可以看见Ubuntu系统在不断地演进。在未来手机,平板,电视及桌面将使用一个操作系统。Ubuntu手机操作系统正在为这一切做准备。


http://v.youku.com/v_show/id_XOTA5NDA0OTUy.html

作者:UbuntuTouch 发表于2015/3/11 13:26:59 原文链接
阅读:309 评论:0 查看评论

Read more
UbuntuTouch

[原]Ubuntu 手机开发培训准备

在这篇文章中,我们将介绍学生如何做培训准备前的准备工作。提前准备并安装好自己的环境是做好一个培训非常重要的步骤。否则我们将浪费我们自己很多的宝贵的时间在课堂上!


                 

1)安装好自己的SDK


如果想在自己的电脑上安装Ubntu系统

学生可以按照文章“Ubuntu SDK 安装”安装好自己的Ubuntu系统及SDK。让后根据文章“创建第一个Ubuntu for phone应用”来检验自己安装的环境是否正确。这种安装通常需要在电脑上安装多个系统,或虚拟机(模拟器在虚拟机的效果可能并不好,在虚拟机中模拟器目前不能正常启动)。

如果想做一个专为Ubuntu手机开发而做的Live USB

请参照文章“如何制作Ubuntu SDK Live USB盘”来专门制作一个可以启动的Live USB盘。这个盘可以直接插入到电脑中的USB口中,并启动Ubuntu系统。这个USB盘中已经安装好整个可以供开发的SDK,不需要安装任何额外的软件即可开发。

a) 在BIOS中启动硬件虚拟化功能,这样会使得模拟器的运行速度加快
b) 在BIOS中设置优选顺序以使得USB可以优先启动,或在启动的时候按下F12功能键,并选择由USB来启动Ubuntu

在启动Ubuntu系统后,Ubuntu SDK已经完全安装好了。开发者可以直接进行开发了。建议参阅文章“创建第一个Ubuntu for phone应用”来检验自己安装的环境是否正确。


在开发过程中如果使用手机进行安装时,如果需要密码解锁手机的话,这个密码是“0000”。

2)Ubuntu手机介绍


对不熟悉Ubuntu手机的开发者来说,可以先观看视频“如何使用Ubuntu手机”来了解Ubuntu手机。如果你想对Ubuntu SDK有更深的认识,请观看视频“如何使用Ubuntu SDK (视频)”。开发者也可以观看Ubuntu手机的官方宣传视频来更进一步了解。

你可以在地址“Ubuntu手机介绍”下载有关Ubuntu手机介绍的幻灯片,并在地址观看相应的视频


3)QML应用开发


Flickr应用开发

阅读文章“使用Ubuntu SDK开发Flickr应用教程”,并观看视频“Ubuntu手机应用QML开发 (视频)”。幻灯片“Ubuntu应用开发”。

教程的源码在: bzr branch lp:~liu-xiao-guo/debiantrial/flickr7
我们可以在Shell中输入以上的指令来下载源码。

DeveloperNews RSS阅读器

首先我们可以阅读文章“从零开始创建一个Ubuntu应用--一个小的RSS阅读器”及文章“如何在Ubuntu中使用条件布局”。视频在“在Ubuntu平台上开发Qt Quick QML应用 (视频)

教程的源码在:bzr branch lp:~liu-xiao-guo/debiantrial/developernews4

我们可以在Shell中输入以上的指令来下载源码。


4)Scope 开发


大家可以先观看视频“Ubuntu Scope简介及开发流程”来了解Ubuntu OS上的Scope开发流程。

阅读文章“在Ubuntu OS上创建一个dianping Scope (Qt JSON)”,并观看视频“如何在Ubuntu OS上开发Scope (视频)”。教程的另外一个视频在地址观看。

幻灯片“Scope技术开发”。幻灯片讲演的视频在地址观看。

教程的源码在: bzr branch lp:~liu-xiao-guo/debiantrial/dianpianclient8
我们可以在Shell中输入以上的指令来下载源码。

更多关于Scope开发的例程可以在链接找到。


5)HTML 5开发


我们可以参阅文章“在Ubuntu手机平台上创建一个HTML 5的应用”来学习如何在Ubuntu平台上开发HTML 5的应用。源码在地址下载:

git clone https://gitcafe.com/ubuntu/html-rssreader6.git

Ubuntu上的HTML5开发幻灯片:Ubuntu上的HTML5开发。幻灯片视频

大家可以利用在线Webapp生成器来生产我们喜欢的网页的click安装包。具体教程“如何使用在线Webapp生成器生成安装包

更多例程:
  • 百度翻译: bzr branch lp:~liu-xiao-guo/debiantrial/baidutranslator
  • 字典: bzr branch lp:~liu-xiao-guo/debiantrial/meanings

6)更多的培训材料


我们也有更多的英文的培训材料。开发者可以在地址下载。


如果有任何问题,请在该文章处评论。我会尽力回答你们的问题。大家也可以到Ubuntu手机专有讨论区来讨论问题



在教学中的过程中如果需要联网,请使用如下的用户名及密码

CT:  ChinaNet-B07E 密码:1d18258a
CM: Huawei-E5375-E16E 密码:ji69ea97  
 

手机的解锁密码为:0000


作者:UbuntuTouch 发表于2015/1/4 15:36:54 原文链接
阅读:1899 评论:6 查看评论

Read more
UbuntuTouch

在这篇文章中,我们来介绍如何判断一个QML应用被推到后台或前台。我们知道,在Ubuntu手机平台中,它是一个单应用的操作系统。当一个应用被推到后台后,应用就被挂起,不能运行。我们有时需要这个标志来判断我们的应用什么时候是在前台,什么时候是在后台。


我们用Ubuntu SDK创建一个简单的QML应用:


import QtQuick 2.0
import Ubuntu.Components 1.1

/*!
    \brief MainView with a Label and Button elements.
*/

MainView {
    id: main
    // objectName for functional testing purposes (autopilot-qt5)
    objectName: "mainView"

    // Note! applicationName needs to match the "name" field of the click manifest
    applicationName: "com.ubuntu.developer.liu-xiao-guo.foregrounddetect"

    /*
     This property enables the application to change orientation
     when the device is rotated. The default is false.
    */
    //automaticOrientation: true

    // Removes the old toolbar and enables new features of the new header.
    useDeprecatedToolbar: false

    width: units.gu(100)
    height: units.gu(75)


    Page {
        title: i18n.tr("ForegroundDetect")

        Connections {
             target: Qt.application
             onActiveChanged: {
                 console.log("Qt.application.active: " + Qt.application.active);
             }
         }
    }
}

在这里,我们使用Qt.application这个变量的"active"属性来判断一个应用是否被推到后台或前台。我们运行应用结果如下:




当我们在手机上把应用推到后台时,就会显示false;当我们把应用推到前台时,就会显示true。

整个应用的源码在:bzr branch lp:~liu-xiao-guo/debiantrial/foregrounddetect


作者:UbuntuTouch 发表于2015/1/5 10:56:25 原文链接
阅读:609 评论:0 查看评论

Read more
UbuntuTouch

在这个视频里,我们从“0”开始来开发一个mini的RSS阅读器。通过这个练习,开发者可以对QML的编程有一个基本的了解,并了解在Ubuntu平台上的一些开发的流程。应用的图片如下:


  



作者:UbuntuTouch 发表于2015/1/13 15:08:31 原文链接
阅读:532 评论:0 查看评论

Read more
UbuntuTouch

[原]如何制作Ubuntu SDK Live USB盘

对于一些想开发Ubuntu手机应用或Scope的开发者来说,不想重新买一个电脑安装Ubuntu操作系统或在自己的硬盘上重新安装一个Ubuntu系统,那么可以考虑制作一个Ubuntu系统的Live USB盘。这个USB包括如下的部分:


  • Ubuntu Kylin 14.10操作系统
  • Ubuntu SDK (包括已经安装好的SDK,模拟器及编译环境)

使用这个Live USB盘,开发者就不用安装任何的东西,直接插入电脑的USB口中。在电脑启动的过程中,选择我们制作好的USB启动盘进行启动(在电脑启动的过程中,按下“F12”键)。在启动的过程中选择“Try Ubuntu Kylin without installing




虽然这是一个Ubuntu OS的启动盘,但是它可以保存我们在开发过程中所创建的项目(存于Home目录中)及一些设置(比如wifi设置密码等)。


当我们选择USB时,我们最好是选择USB 3.0并把USB盘放入到电脑USB 3.0的口中。一般来说,电脑上的USB 3.0口是用蓝色标示的。建议使用质量较好,速度较快一点的USB这样可以使得系统的启动和运行更快更流畅。目前我们使用SanDisk CZ80来做测试,效果还是不错的。USB需要有16G的存储。


为了使得我们的模拟器能够更加流畅及模拟器不会出现黑色的屏幕,我们需要在电脑的BIOS里启动硬件虚拟化功能。开发者需要到自己的电脑的BIOS里的设置启动VT-X/AMD-V。开发者可以参考文章“Ubuntu SDK 安装”来检查自己的电脑是否支持virtualization。




如果开发者想要在自己的电脑上安装Ubuntu系统并在上面开发的话,可以参考文章“Ubuntu SDK 安装”来一步一步地安装Ubuntu SDK。



1)如何在Ubuntu系统下制作Live USB盘


启动Ubuntu操作系统,打开浏览器并在如下的地址下载最新的image:


https://mega.co.nz/#F!S8QSRZyI!2HBWgXk4kmc_2bcCcpBR3Q


下载的文件包含:

  • kylin-live-20150133.iso (md5sum 13cd61270bf98eb462dc0497df8eee33) 
  • casper-rw-20150113.tar.bz2  (md5sum 8c69f94a03481275bf66aa883b69ae1b)
  • post-usb-creator-window.sh(在Windows下制作需要这个)
  • README.md (简单的说明文件)

我们把下载的文件存于到我们想要的一个目录中,比如在自己的Home下的“usb”目录中。


在Dash中输入“usb”,并启动“Startup Disk Creator/启动盘创建器”






我们按照如下的方法来制作我们的USB启动盘。





在设置“储存在额外保留空间”时,它的值应该为非零的值。等USB盘已经制作好以后,你将会看到如下的画面:







重新挂载USB盘,因为在前一步会自动卸载USB盘,或者在Ubuntu中的文件浏览器中点击USB所在的device。这样就可以完成重新挂载USB:





然后按下面运行自带的脚本,参数为 USB 盘挂载的路径。


解压已经下载的casper-rw-2015xxxx.tar.bz2文件


等文件都被解压完后,进入解压文件所在的目录,并在shell中执行如下的指令:


liuxg@liuxg:~/usb$ ./post-usb-creator-linux.sh /media/liuxg/BD52-7153/


这里“/liuxg/BD52-7153”为USB盘挂载的路径。根据自己USB盘所在的路径替换。


2)如何在Windows 平台下制作启动盘


http://www.ubuntu.com/download/desktop/create-a-usb-stick-on-windows

下载制作工具,与 Linux 平台的工具相似。




单我们在选择“Persistent file”时,它的大小应该是非零的一个值。在我们填入“Step 2”时,我们不应该把拷贝好的字符串拷到该输入框中,否则在“Step 3”中的输入框就会是灰色的。我们应该点击“Browse”按钮,并按照如下的方式进行输入image的路径:




解压已经下载的casper-rw-2015xxxx.tar.bz2文件,并解压后的casper-rw 文件拷贝到USB的主目录下:





:如果只想使用英文版的Ubuntu系统就不需要进行下面的步骤。如果想要支持中文版,请把 post-usb-creator-window.sh 也拷贝到 USB盘的根目录下。从USB 盘启动Ubuntu系统后, 在dash中启动Terminal,






并在Terminal中执行如下的命令:


$ cd /cdrom/

$ sudo ./post-usb-creator-window.sh


再次重新启动后,会进入中文版的Ubuntu系统。


3)测试已经制作好的USB启动盘


我们可以把我们的Live USB盘插入电脑,我们可以通文章“创建第一个Ubuntu for phone应用”来检验我们是否有一个完好的Ubuntu SDK。


在我们启动模拟器时,如果需要输入密码,请使用默认的密码“0000”。如果开发者需要自己修改这个密码,请到Ubuntu SDK模拟器中的“系统设置”中去修改。


对于应用开发者来说,在Qt Creator中的热键组合“Ctrl + Space”键有它独特的用处。可是,在Ubuntu系统中,“Ctrl + Space”被用来转换中英文输入法。建议开发者参考文章“怎么在Ubuntu OS上面安装搜狗输入法及对Qt Creator的支持”来重新定义键的组合。


已知问题 (known issues)

如果你在使用的过程中,发现有如下的乱码的情况(极少情况下出现),请重新启动你的机器来纠正这个问题。




在个别电脑上不能启动的问题


我们发现在联想 E455 出现不能启动的问题,目前怀疑是和 AMD 显卡驱动有关,问题仍在调查中,如果遇到些问题,请在系统上安装14.04 LTS版本并安装相应的ubuntu-sdk包来尝试学习ubuntu phone的开发知识,其中的基本概念都是一样。


注:如果想长时间致力于ubuntu phone的开发建议在电脑上安装一个ubuntu系统,最好是utopic (14.10),而不是在Live环境下进行学习,一是以防数据的丢失,二是在使用性能上有更快速的体验。



作者:UbuntuTouch 发表于2015/1/22 15:35:55 原文链接
阅读:693 评论:0 查看评论

Read more
UbuntuTouch

Read more
UbuntuTouch

[原]Ubuntu OS上的QML应用框架

在我们编写QML应用的时候,我们有时事先需要来考虑我们怎么使用一个好的框架来完成我们的应用。我们的应用有多少个页面,页面之间的导航到底是怎么样子的。这个对于我们一开始来设计我们的应用来说非常中要。在这篇文章中,我们来介绍如何在上层来设计我们的应用框架。


1)使用tab来创建一个平面的导航应用

我们可以使用我们的Ubuntu SDK来创建一个最基本的叫做TabApp的应用:

   

 

这样我们就生成了我们的一个最基本的应用。我们把应用的宽度和高度设为如下的值:

    width: units.gu(50)
    height: units.gu(75)

同时,我们也修改我们的Main.qml如下:

import QtQuick 2.0
import Ubuntu.Components 1.1
import Ubuntu.Components.ListItems 1.0 as ListItem

MainView {
    // objectName for functional testing purposes (autopilot-qt5)
    objectName: "mainView"

    // Note! applicationName needs to match the "name" field of the click manifest
    applicationName: "tabapp.ubuntu"

    /*
     This property enables the application to change orientation
     when the device is rotated. The default is false.
    */
    //automaticOrientation: true

    // Removes the old toolbar and enables new features of the new header.
    useDeprecatedToolbar: false

    width: units.gu(50)
    height: units.gu(75)


    Tabs {
        id: tabs

        Tab1 {
            objectName: "Tab1"
        }

        Tab2 {
            objectName: "Tab2"
        }
    }
}

在这里我们定义了两个Tab页面,分别为Tab1及Tab2。它们的内容分别为:

import QtQuick 2.0
import Ubuntu.Components 1.1

Tab {
    title: i18n.tr("Tab 1")

    Action {
        id: reloadAction
        text: "Reload"
        iconName: "reload"
        onTriggered: {
            console.log("reload is clicked")
        }
    }

    page: Page {
        Label {
            anchors.centerIn: parent
            text: i18n.tr("This is page one")
        }

        tools: ToolbarItems {
            ToolbarButton {
                action: reloadAction
            }
        }
    }
}

import QtQuick 2.0
import Ubuntu.Components 1.1

Tab {
    title: i18n.tr("Tab 2")

    page: Page {
        Label {
            anchors.centerIn: parent
            text: i18n.tr("This is page two")
        }
    }
}

这是一个最简单的Tab导航应用。我们在手机上运行:

    


所有的源码可以在地址下载:

git clone https://gitcafe.com/ubuntu/TabApp1.git


我们也可以把我们的Main.qml修改如下:

import QtQuick 2.0
import Ubuntu.Components 1.1
import Ubuntu.Components.ListItems 1.0 as ListItem

MainView {
    // objectName for functional testing purposes (autopilot-qt5)
    objectName: "mainView"

    // Note! applicationName needs to match the "name" field of the click manifest
    applicationName: "tabapp.ubuntu"

    /*
     This property enables the application to change orientation
     when the device is rotated. The default is false.
    */
    //automaticOrientation: true

    // Removes the old toolbar and enables new features of the new header.
    useDeprecatedToolbar: false

    width: units.gu(50)
    height: units.gu(75)

    Action {
        id: reloadAction
        text: "Reload"
        iconName: "reload"
        onTriggered: {
            console.log("reload is clicked")
        }
    }

    Tabs {
        id: tabs
        Tab {
            title: i18n.tr("Simple page")
            page: Page {
                Label {
                    id: label
                    anchors.centerIn: parent
                    text: "A centered label"
                }
                tools: ToolbarItems {
                    ToolbarButton {
                        action: reloadAction
                    }
                }
            }
        }

        Tab {
            id: externalTab
            title: i18n.tr("External")

            page: Loader {
                id: loader
                anchors.fill: parent
                source: (tabs.selectedTab === externalTab) ? Qt.resolvedUrl("ExternalPage.qml") : ""

                onLoaded: {
                    console.log( loader.source + " is loaded")
                }
            }
        }

        Tab {
            title: i18n.tr("List view")
            page: Page {
                ListView {
                    clip: true
                    anchors.fill: parent
                    model: 20
                    delegate: ListItem.Standard {
                        iconName: "compose"
                        text: "Item "+modelData
                    }
                }
            }
        }
    }
}

运行我们的应用:


   

所有的源码在:

https://gitcafe.com/ubuntu/TabApp4.git

我们如果想在Tab架构中使用pagestack的话,我们对我们的应用必须做一些修改。我们只能把Tabs作为第一个页面推到PageStack的栈中。Main.qml具体实现如下:

import QtQuick 2.0
import Ubuntu.Components 1.1
import Ubuntu.Components.ListItems 1.0 as ListItem

MainView {
    // objectName for functional testing purposes (autopilot-qt5)
    objectName: "mainView"

    // Note! applicationName needs to match the "name" field of the click manifest
    applicationName: "tabapp.ubuntu"

    /*
     This property enables the application to change orientation
     when the device is rotated. The default is false.
    */
    //automaticOrientation: true

    // Removes the old toolbar and enables new features of the new header.
    useDeprecatedToolbar: false

    width: units.gu(50)
    height: units.gu(75)

    Action {
        id: reloadAction
        text: "Reload"
        iconName: "reload"
        onTriggered: {
            console.log("reload is clicked")
        }
    }

    PageStack {
        id: pageStack
        Component.onCompleted: push(tabs)

        Tabs {
            id: tabs
            Tab {
                title: "Tab 1"
                page: Page {
                    Button {
                        anchors.centerIn: parent
                        onClicked: pageStack.push(page3)
                        text: "Press"
                    }
                }
            }
            Tab {
                title: "Tab 2"
                page: Page {
                    Label {
                        anchors.centerIn: parent
                        text: "Use header to navigate between tabs"
                    }
                }
            }
        }
        Page {
            id: page3
            visible: false
            title: "Page on stack"
            Label {
                anchors.centerIn: parent
                text: "Press back to return to the tabs"
            }
        }
    }
}


运行我们的应用,我们可以看到:

   

我们可以看见在上面显示的那样,有一个叫做“Page on stack”。可以通过按下换回箭头回到上一个页面。

具体的代码:

git clone https://gitcafe.com/ubuntu/TabApp3.git

2)使用PageStack来导航


在这一节中,我们将介绍如何使用PageStack来管理我们的页面。当用户进入下一个页面完成自己的工作后,可以通过按下标题栏中的返回箭头回到上一个页面。按照上面同样的步骤,我们可以创建一个叫做PageStack的项目。Main.qml的设计如下:

import QtQuick 2.0
import Ubuntu.Components 1.1
import Ubuntu.Components.ListItems 1.0 as ListItem

/*!
    \brief MainView with a Label and Button elements.
*/

MainView {
    // objectName for functional testing purposes (autopilot-qt5)
    objectName: "mainView"

    // Note! applicationName needs to match the "name" field of the click manifest
    applicationName: "pagestack.ubuntu"

    /*
     This property enables the application to change orientation
     when the device is rotated. The default is false.
    */
    //automaticOrientation: true

    // Removes the old toolbar and enables new features of the new header.
    useDeprecatedToolbar: false

    width: units.gu(50)
    height: units.gu(75)

    PageStack {
        id: pageStack
        Component.onCompleted: {
            push(page0)
        }

        Page {
            id: page0
            title: i18n.tr("Root page")
            visible: false

            Column {
                anchors.fill: parent

                ListItem.Standard {
                    text: i18n.tr("Page one")
                    onClicked: pageStack.push(page1, {color: UbuntuColors.orange})
                    progression: true
                }
                ListItem.Standard {
                    text: i18n.tr("Page two")
                    onClicked: pageStack.push(Qt.resolvedUrl("Page2.qml"))
                    progression: true
                }
            }
        }

        Page {
            title: "Rectangle"
            id: page1
            visible: false
            property alias color: rectangle.color
            Rectangle {
                id: rectangle
                anchors {
                    fill: parent
                    margins: units.gu(5)
                }
            }
        }
    }
}


这里我们在应用启动时创建一个PageStack,并同时把“page0”压入栈中。使它成为第一个页面。在“page0”中,我们有有两个列表项,分别可以进入到下一个页面中。

运行我们的应用:

   

我们可以在每个页面看见有个返回的箭头。

整个项目的源码在:

git clone https://gitcafe.com/ubuntu/PageStack.git




作者:UbuntuTouch 发表于2015/3/25 10:20:09 原文链接
阅读:265 评论:0 查看评论

Read more
UbuntuTouch

在这个视频里,介绍如何在开发Ubuntu平台上开发Webapps及HTML 5的应用。


http://v.youku.com/v_show/id_XODkwNDQxMDQ0.html

作者:UbuntuTouch 发表于2015/2/10 19:26:33 原文链接
阅读:496 评论:0 查看评论

Read more
UbuntuTouch

无论你是互联网世界的一个高手或是一个从来没有接触过互联网的新手,这篇文章将给你带来完整的在Ubuntu平台上开发HTML 5的应用。我们将慢慢地通过这个练习让你很自然地进入并熟悉整个的HTML 5应用的开发流程。在这个练习中,我们将设计一个最简单的RSS阅读器。当我们的应用设计完整后,应用的显示如下:


       

如果你是一个固执的HTM 5黑客,你可以选择任何你所喜欢的工具及工具包来开发你的HTML 5应用。它们将会很好地工作于Ubuntu手机上。我们将只专注于Ubuntu SDK提供的工具及工具包。更多关于HTML 5开发的信息可以在Ubuntu的官方网站中文网站得到。


如果你还没有安装好自己的Ubuntu SDK,请参照文章“Ubuntu SDK 安装”来安装好自己的SDK。


1)创建一个新的项目


在这一步,我们将使用Ubuntu SDK来生产一个项目:




     

      


请按照上面的步骤来创建一个新的项目“html5-rssreader”。我们首先在Desktop上运行该应用(使用热键Ctrl +R):




可以看出是以非常简单的HTML 5应用。我们可以点击应用的按钮在浏览器中打开上面显示的链接。


2)如何调试我们的应用


我们知道调试应用是非常重要的。我们怎么调试我们的HTML 5应用呢?我们开发的步骤是一般现在自己的电脑上开发好我们直接的应用,然后再打包并部署到自己的手机上或模拟器中。当我们在SDK中运行我们自己的HTML 5应用时,我们可以看到:




在Application Output输出窗口,我们可以看到一个地址:

http://192.168.1.106:9221


这个就是我们用来调试的地址。我们把这个地址拷到我们的Chrome或Chromium地址栏中,并进入:



在我们调试时,我们只能运行一个应用的实例,否则,我们将看不到任何的输出。


3)删除默认项目中不需要的代码



在这一节中,我们将删除项目中不需要的部分代码。

在项目的www/目录中,你可以看到如下的内容:
  • HTML 文件
  • images (在www/img目录下)
  • javascript文件 (在www/js目录下)
  • CSS文件 (在www/css目录下)
首先我们来对index.html进行修改:

1)首先,我们来查看如下的代码:

<div data-role="content">

2)就像你看到的那样,有一个<div>在那里。它定义了一个叫做hello-page的tab。<div>包含了最直接的HTML组成部分。我们删除所有包含在

 <div data-role="content">

里面的内容。这样整个body的内容为:

<body>

    <div data-role="mainview">

        <header data-role="header">
            <ul data-role="tabs">
                <li data-role="tabitem" data-page="hello-page">Hello World</li>
            </ul>
        </header>

        <div data-role="content">
        </div>
    </div>

    <script src="js/app.js"></script>
</body>

重新运行我们的应用,我们可以看到:



我们可以看到应用中没有显示任何的东西。这也并不奇怪,因为我们本来就没有放任何的东西在里面。我们接下来也需要修改应用的标题。


4)删除不必要的代码


在上面的一节中,我们主要对index.html进行了修改。在这个章节里,我们将对css及javascript文件进行修改。

1)我们把css/app.css中的所有内容删除掉。这里的内容是针对helloworld模版的。我们不需要它们

2)在js/app.js文件中,我们删除如下的代码:

 function addClass(elem, className) {
       elem.className += ' ' + className;
   };

   function removeClass(elem, className) {
       elem.className = elem.className.replace(className, '');
   };

同时,我们删除从如下的行:

   // Detect if Cordova script is uncommented or not and show the appropriate status.

到最后的一行的前面:

};

等我们完成所有的操作之后,最后js/app.js文件的内容如下:

/**
* Wait before the DOM has been loaded before initializing the
 Ubuntu UI layer
*/
window.onload = function () {
   var UI = new UbuntuUI();
   UI.init();
};

UbuntuUI是Ubuntu HTML 5应用的一个关键的架构类。我们需要构造它并初始化它以生产我们需要的Ubuntu HTML 5应用。我们可以通过这个对象来方位Ubuntu HTML 5的对象(对应于Ubuntu DOM元)及方法。到目前位置,这是我们最基本的一个最小的一个HTML 5应用虽然没有任何的东西。运行我们的应用,我们可以看到:




所有的源码在地址找到 git clone https://gitcafe.com/ubuntu/html-rssreader1.git


5)设计我们自己的应用


在这个章节中,我们将专注于设计我们自己的应用,所以我们我们将删除所有和helloworld相关的东西。


1)把如下的行

   <title>An Ubuntu HTML5 application</title>
   <meta name="description" content="An Ubuntu HTML5 application">

改为:

   <title>RSS Mobile Reader</title>
   <meta name="description" content="RSS Mobile Reader">

2)删除如下的代码:

   <ul data-role="tabs">
               <li data-role="tabitem" data-page="hello-page">Hello World</li>
   </ul>

取而代之的是,我们将使用pagestack。关于应用的两种布局,我们可以参考网址以了解更多的信息。

我们在如下行

 <div data-role="content">

的下面,加入如下的代码:

    <div data-role="pagestack">
         <div data-role="page" id="main" data-title="RSS Mobile Reader">
         </div>
   </div>


3)在js/app.js文件中,我们加入如下的一行代码在UbuntuUI初始化之后:

 UI.pagestack.push("main");

这里,"pagestack"以一种stack的形式管理所有的pages。最初始,没有任何的page,push()方法通常是在应用启动时,把第一个page推进stack并显示出来。

如果我们现在再运行我们的应用,我们可以看到如下的内容。我们可以看到我们的应用的page标题已经发生变化。我们同时也可以注意到在页面的最下面,我们可以看到“back”字样。这是和上面的显示是不同的地方。


6)把我们的内容放到应用中去


在这个一节中,我们将把我们想要显示的内容放到我们的应用中。我们回顾一下我们的在该文章中一开始显示设计,在第一页中,实际上是一个列表的显示。它显示了我们需要的展示的RSS feed的列表。在接下来的第二页中,我们显示的是所选中的RSS feed所包含的所有每个entry的列表。最后一个页面显示的是每个entry的具体的内容。

现在我们开始在index.html中加入我们先前已经定义好的“main”的页面:

1)在index.html中加入如下的行到javascript的插入列表中:

<script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/list.js"></script>

这个对于我们使用列表来说是必须的。在“main”页面中,我们也加入称作为“yourfeeds”的list

 <section data-role="list" id="yourfeeds"></section>

2)在完成好我们上面的代码后,我们接下来主要的代码将在js/app.js文件中。我们可以是使用外部的库来帮我们完成我们的工作。在js/app.js文件中,我们在推入“main”页面过后,即如下的行:

 UI.pagestack.push("main");

加入如下的代码:

 if (typeof localStorage["feeds"] == "undefined") {
       restoreDefault();
   }
   //load local storage feeds
   var feeds = eval(localStorage["feeds"]);
   if (feeds !== null) {
      var feeds_list = UI.list('#yourfeeds');
      feeds_list.removeAllItems();
      feeds_list.setHeader('My feeds');

       for (var i = 0; i < feeds.length; i++) {
           feeds_list.append(feeds[i],
                            null,
                            null,
                            function (target, thisfeed) { loadFeed(thisfeed); },
                            feeds[i]);
       }
   }

我们使用localStorage来存储我们所需要的feeds。如果最早没有被定义,就是“underdefined”,我们将调用restoreDefault()(在下面的部分来实现)来初始化我们所需要的feeds。如果在localStorage中已经有我们所需要的feeds,我们先清除“yourfeeds”列表中的内容,并把list的header设为“My feeds”。每当列表中的某一项被点击,我们就调用loadFeed()(在下面的代码中来实现)来下载这个feed。


3)在js/app.js文件的最后部分加入如下的代码:

function restoreDefault() {
   localStorage.clear();
   var feeds = [];
   feeds.push("http://daker.me/feed.xml");
   feeds.push("http://www.omgubuntu.co.uk/feed");
   feeds.push("http://hespress.com/feed/index.rss");
   feeds.push("http://rss.slashdot.org/Slashdot/slashdot");
   feeds.push("http://www.reddit.com/.rss");
   feeds.push("http://www.guokr.com/rss/");

   try {
       localStorage.setItem("feeds", JSON.stringify(feeds));
       window.location.reload();
   } catch (e) {
       if (e == QUOTA_EXCEEDED_ERR) {
           console.log("Error: Local Storage limit exceeds.");
       } else {
           console.log("Error: Saving to local storage.");
       }
   }
}

在这里,我们定义了restoreDefault()函数。它首先清除localStorage的数据,并加入一个JSON格式的RSS feed到localStorage中。代码中也同时加入了一些exception处理。

我再次运行我们的应用,当我们点击我们的列表中的每一项时,没有任何的反应。这是因为我们根本就没有实现loadFeed函数。




到目前为止的素有代码在如下的地址可以下载:

git clone https://gitcafe.com/ubuntu/html-rssreader2.git


7)从Internet下载内容


到目前为止,我们的代码并不复杂。互联网上有很多东西是值得我们借鉴和利用的。事实上,你可以从互联网上借鉴使用任何你所想要的库来完成你所需要的工作。在今天的练习中,我们借鉴jquery来完成我们的一部分工作。

1)打开一个Teminal, 并进入到js/app.js所在的目录中,在它里面打入如下的命令:

cp /usr/share/javascript/jquery/jquery.min.js .

这里把Desktop中的jquery考入到我们的应用中,并成为应用的一部分。


我们需要一个RSS的parser。虽然google也有一个自己的API,但是由于一些原因这个API在中国并不能被使用。我们在Terminal中打入如下的命令:

git clone https://github.com/jfhovinne/jFeed.git

等我们下载完所有的源码后,我们进入目录:jFeed/build/dist,并把文件“jquery.jfeed.pack.js”拷入到我们的js/app.js目录中。


3)在index.html文件中,在<head>加入如下的语句:

    <!-- External javascript imports -->
    <script type="text/javascript" src="js/jquery.min.js"></script>
    <script type="text/javascript" src="js/jquery.jfeed.pack.js"></script>

在“main”页面下,加入另外一个叫做“results”的页面:

        <div data-role="content">
            <div data-role="pagestack">
                 <div data-role="page" id="main" data-title="RSS Mobile Reader">
                     <section data-role="list" id="yourfeeds"></section>
                 </div>
                 <div data-role="page" id="results" data-title="Articles >">
                    <section data-role="list" id="resultscontent"></section>
                 </div>
            </div>
            <div data-role="dialog" id="loading"><section><progress></progress></section></div>
        </div>

这里,我们也加入了一个等待的“loading”对话框。在下载数据时,显示“loading”字样。

4)现在,我们更进一步在js/app.js文件中做一些修改,我们首先把UbuntuUI变成一个全球的变量,把如下的代码:

window.onload = function () {
var UI = new UbuntuUI();

改为:

var UI = new UbuntuUI();

$(document).ready(function () {

我们同时也需要在函数的最后把};修改为});。这样整个代码为:

$(document).ready(function () {
    UI.init();
    UI.pagestack.push("main");

    if (typeof localStorage["feeds"] == "undefined") {
        restoreDefault();
    }

    //load local storage feeds
    var feeds = eval(localStorage["feeds"]);
    if (feeds !== null) {
        var feeds_list = UI.list('#yourfeeds');
        feeds_list.removeAllItems();
        feeds_list.setHeader('My feeds');

        for (var i = 0; i < feeds.length; i++) {
            feeds_list.append(feeds[i],
                              null,
                              null,
                              function (target, thisfeed) { loadFeed(thisfeed); },
                              feeds[i]);
        }
    }
});


5)现在我们来完成loadFeed方法的实现。在js/app.js的末尾,我们加入如下的代码:

function loadFeed(url) {
   UI.pagestack.push("results");
   UI.dialog("loading").show()

    console.log("url is: " + url );

    $.getFeed( {
                  url: url,
                  success: function(feed) {
                      UI.dialog("loading").hide();

                      var results_list = UI.list('#resultscontent');
                      results_list.removeAllItems();
                      results_list.setHeader(feed.title);

                      console.log("title: " + feed.title);

                      // walk through the feeds
                      for( var i = 0; i < feed.items.length; i++ ) {
                          var item = feed.items[ i ];

//                          console.log("title: " + item.title);
//                          console.log("link: " +  item.link);
//                          console.log("content: " + item.description);

                            results_list.append(
                                item.title.replace(/"/g, "'"),
                                null,
                                null,
                                function (target, result_infos) {
                                  showArticle.apply(null, result_infos); },
                                [ escape(item.title),
                                  escape(item.link),
                                  escape(item.description) ] );
                      }
                    }

    });
}

重新运行我们的应用,我们可以点击我们的每个rss feed,并看到每个rss feed的标题列表:

  

所有的源码在如下的地址找到:

git clone https://gitcafe.com/ubuntu/html-rssreader3.git


8)显示实际的文章


如果细心的开发者仔细看了我们上一节的代码后,你可能会发现,我们已经提到了“showArticle”,但是我们还是没有实现它。当我们点击RSS feed的标题后,没有任何的东西显示。它的实现如下:

1)在index.html中,加入“article”页面:

        <div data-role="content">
            <div data-role="pagestack">
                 <div data-role="page" id="main" data-title="RSS Mobile Reader">
                     <section data-role="list" id="yourfeeds"></section>
                 </div>
                 <div data-role="page" id="results" data-title="Articles >">
                    <section data-role="list" id="resultscontent"></section>
                 </div>
                 <div data-role="page" id="article" data-title="Article >">
                     <section id="articleinfo"></section>
                 </div>
            </div>
            <div data-role="dialog" id="loading"><section><progress></progress></section></div>
        </div>

2)在js/app.js文件的最后面,加入如下的方法:

function showArticle(title, url, desc) {
   UI.pagestack.push("article");

   if (typeof desc == "undefined")
       desc = "(No description provided)";
   $("#articleinfo").html("<p>" + unescape(title) + "</p><p>" + unescape(desc) + "</p><p><a target=\"_blank\" href=\"" + unescape(url) + "\">" + unescape(url) + "</a></p>");

}


这样我们就可以得到文章的标题及其内容。为了展示文章的内容,我们显示文章的title,内容及一个url。为了展示文章的内容及title,我们使用“unescaped”。

重新运行我们的应用,我们可以看到如下的画面:



整个项目的源码在:

git clone https://gitcafe.com/ubuntu/html-rssreader4.git


9)添加RSS feed


现在我们的RSS feed都是固定,预先设定好的。如果一个用户需要添加一个新的RSS feed该怎么办呢?

1)在index.html文件中,在javascript的import区域,加入如下的代码:

   <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/toolbars.js"></script>

2)在main页面中加入如下的footer:

                 <div data-role="page" id="main" data-title="RSS Mobile Reader">
                     <section data-role="list" id="yourfeeds"></section>
                     <footer id="footer1" data-role="footer" class="revealed">
                         <nav>
                             <ul>
                                 <li>
                                     <a href="#" id="addfeed">
                                         <img src="/usr/share/ubuntu-html5-ui-toolkit/0.1//ambiance/img/actions/add.svg" alt="Add feed" />
                                         <span>Add feed</span>
                                     </a>
                                 </li>
                             </ul>
                         </nav>
                     </footer>                     
                 </div>

我们在footer中加入了一个toolbar以添加一个RSS feed。

3)我们添加一个对话框来输入所需要的RSS feed的详细信息:

在“loading”对话框的前面或后面,添加对话框:

            <div data-role="dialog" id="loading"><section><progress></progress></section></div>
            <div data-role="dialog" id="addfeeddialog">
             <section>
                 <h1>Add a new feed</h1>
                 <p>Type the url feed you want to add</p>
                 <input type="url" id="rssFeed" placeholder="http://">
                 <menu>
                     <button data-role="button" id="no">Cancel</button>
                     <button data-role="button" class="success" id="yes">Add</button>
                 </menu>
             </section>

这里我们定义了一个叫做“addfeeddialog”的对话框。它让用户来输入一个RSS feed的url。


4)在如下函数:

$(document).ready(function () {

结束的前一行,添加如下的代码:

   UI.button('yes').click(function (e) {
       var url = $("#rssFeed").val();
       if (url !== "") {
           var feeds = eval(localStorage["feeds"]);
           feeds.push(url);
           localStorage.setItem("feeds", JSON.stringify(feeds));
           window.location.reload();
       }
   });

   UI.button('addfeed').click(function () {
       $('#addfeeddialog').show();
   });

重新运行我们的应用,并点击“Add feed”按钮

  

添加一个RSS feed是不是在弹指之间呢?所有的源码在:

git clone https://gitcafe.com/ubuntu/html-rssreader5.git


10)你的挑战


我们现在对话框的“Cancel”还不能工作,你可以帮我完成它的代码吗?


11)美化我们的应用


在这个章节里,我们将通过使用css来达到美化我们的应用的目的。


在css/app.css文件中,我们加入如下的代码:

#articleinfo {
   padding: 10px;
   -webkit-box-sizing: border-box;
   box-sizing: border-box;
}

#articleinfo iframe {
   max-width: 100%;
}

#articleinfo p {
   margin: 7px 0;
}

#articleinfo a{
   text-decoration: none;
   color: #787878;
   font-weight: bold;
}

重新运行我们的应用,我们可以看到Article显示得更加漂亮。到目前为止,所有的源码在如下的地址可以找到:

git clone https://gitcafe.com/ubuntu/html-rssreader6.git

如果大家有任何的问题,欢迎在文章的评论中提出来。







作者:UbuntuTouch 发表于2015/3/3 14:39:17 原文链接
阅读:492 评论:9 查看评论

Read more
UbuntuTouch

在这篇文章中,我们将使用Ubuntu SDK所提供的online account API来访问微博的API并显示所需要的内容。这篇文章的重点是展示如何使用online account API的使用。我们将创建一个简单的QML应用。我们最终显示的画面如下:


      


更多关于QML应用开发的资料可以在地址:https://developer.ubuntu.com/zh-cn/apps/qml/


1)创建一个最基本的应用


我们还是像以前一样使用我们的Ubuntu SDK来创建一个最基本的weibo QML应用。


   


  

这样我们就创建了一个最基本的weibo QML应用。你可以使用热键Ctrl + R来运行它虽然它并不能做什么事。


2)加入online account所需要的文件



我们可以参考文章来对online account API有更深的了解。为了能够访问,需要创建如下的文件:

1)weibo.application

它的具体内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<application>
  <description>Weibo QML</description>
  <desktop-entry>weibo.ubuntu_weibo.desktop</desktop-entry>
  <services>
    <service id="qml-weibo.ubuntu_qmlweibo">
      <description>Watch your favorite Weibo messages</description>
    </service>
  </services>
</application>

2)weibo.service

它的具体的内容为:

<?xml version="1.0" encoding="UTF-8"?>
<service>
  <type>sharing</type>
  <name>Weibo QML</name>
  <icon>qml-weibo.png</icon>
  <provider>qml-weibo.ubuntu_plugin</provider>
  <translations>unity-scope-weibo</translations>
</service>

3)创建一个plugin文件目录并在它的下面创建

 a)Main.qml, 它的内容为:

import Ubuntu.OnlineAccounts.Plugin 1.0

OAuthMain {}

    b) qml-weibo.ubuntu_plugin.provider,它的内容为:

<?xml version="1.0" encoding="UTF-8"?>
<provider>
  <name>Weibo</name>
  <icon>weibo/icon.png</icon>
  <translations>unity-scope-weibo</translations>
  <plugin>generic-oauth</plugin>
  <domains>.*weibo\.com</domains>
  <single-account>true</single-account>

  <template>
    <group name="auth">
      <setting name="method">oauth2</setting>
      <setting name="mechanism">web_server</setting>
      <group name="oauth2">
        <group name="web_server">
          <setting name="Host">api.weibo.com</setting>
          <setting name="AuthPath">oauth2/authorize</setting>
          <setting name="TokenPath">oauth2/access_token</setting>
          <setting name="RedirectUri">https://api.weibo.com/oauth2/default.html</setting>
          <setting name="ResponseType">code</setting>
          <setting name="ClientId">your developer key</setting>
          <setting type="as" name="AllowedSchemes">['https','http']</setting>
          <setting name="ClientSecret">your developer secret</setting>
          <setting name="ForceClientAuthViaRequestBody" type="b">true</setting>
      </group>
      </group>
    </group>
  </template>
</provider>

在上面的文件中一定要输入自己的“your developer key”及“your developer secret”。这个你需要在微博API的网站上去申请。整个文件的架构为:




4)修改manifest.json文件如下

{
    "name": "qml-weibo.ubuntu",
    "description": "description of qml-weibo",
    "architecture": "all",
    "title": "qml-weibo",
    "hooks": {
        "qmlweibo": {
            "account-application": "weibo.application",
            "account-service": "weibo.service",
            "apparmor": "qmlweibo.apparmor",
            "desktop": "qmlweibo.desktop"
        },
        "plugin": {
            "account-provider": "plugin/qml-weibo.ubuntu_plugin.provider",
            "account-qml-plugin": "plugin/qml"
        }
    },
    "version": "0.1",
    "maintainer": "XiaoGuo, Liu <xiaoguo.liu@canonical.com>",
    "framework": "ubuntu-sdk-14.10"
}


这里添加了account及plugin的一些设置。


5)为了能够让.service,.application及.provider文件能正常得到显示,我们对“qml-weibo.qmlproject”添加如下的东西

    Files {
        filter: "*.service"
    }
    Files {
        filter: "*.application"
    }
    Files {
        filter: "*.provider"
    }


到这里,我们已经基本上已经修改或添加好我们所需要的文件。


3)设计应用的UI



我们来修改我们的main.qml文件。我们使用了一个Column的列layout manager来管理我们的控件。

  • 使用一个ListView (accountsList)来显示所有我们相关的账号。这个可以在“系统设置”中的“账号”可以看到

  • 使用一个叫做“createAccountBtn”的按钮来创建一个weibo的online账号
  • 使用另外一个ListView (weiboList)列表来显示微博的内容


import QtQuick 2.0
import Ubuntu.Components 0.1
import Ubuntu.Components.ListItems 0.1 as ListItem
import Ubuntu.OnlineAccounts 0.1
import Ubuntu.OnlineAccounts.Client 0.1

import "weiboapi.js" as API

/*!
    \brief MainView with a Label and Button elements.
*/

MainView {
    id: main
    // objectName for functional testing purposes (autopilot-qt5)
    objectName: "mainView"

    // Note! applicationName needs to match the "name" field of the click manifest
    applicationName: "qml-weibo.ubuntu"

    /*
     This property enables the application to change orientation
     when the device is rotated. The default is false.
    */
    //automaticOrientation: true

    property string accesstoken:""

    width: units.gu(100)
    height: units.gu(90)

    Page {
        id: root
        clip:true
        title: i18n.tr("Weibo")

        Column {
            spacing: units.gu(2)
            width: parent.width

            ListView {
                id: accountsList

                anchors.horizontalCenter: parent.Center
                anchors.verticalCenter: parent.Center

                height: contentHeight
                width: parent.width

                spacing: units.gu(1)
                model: accountsModel

                delegate: Rectangle {
                    id: wrapper
                    width: accountsList.width
                    height: units.gu(10)
                    color: accts.enabled ? "green" : "red"

                    AccountService {
                        id: accts
                        objectHandle: accountServiceHandle
                        onAuthenticated: {
                            console.log("reply: " + JSON.stringify(reply));
                            var obj = JSON.parse(JSON.stringify(reply));
                            console.log("AccessToken: " + obj.AccessToken);
                            main.accesstoken = obj.AccessToken;

                            // Make the authenticate button invisile
                            accountsList.visible = false;

                            // Start to get the data
                            API.getStatus();
                        }
                        onAuthenticationError: {
                            console.log("Authentication failed, code " + error.code)
                            //                          accountsList.headerItem.text = "Error " + error.code
                            authenticateBtn.text = displayName + " Error " + error.code + " please do it again";
                        }
                    }

                    Button {
                        id: authenticateBtn

                        anchors.fill: parent
                        anchors.margins: units.gu(2)
                        text: i18n.tr("Authenticate %1").arg(model.displayName + " " + model.providerName)

                        onClicked: {
                            var params = {}
                            accts.authenticate(params)
                        }
                    }
                }

            }

            Button {
                id: createAccountBtn

                anchors.horizontalCenter: parent.horizontalCenter
                width: parent.width
                height: units.gu(8)
                text: i18n.tr("Request access")
                onClicked: setup.exec()
            }


            ListModel {
                id: modelWeibo
            }

            ListView {
                id: weiboList
                width: parent.width
                height: parent.height
                spacing: units.gu(1)


                model: modelWeibo

                delegate: Rectangle {
                    width: parent.width
                    height: units.gu(8)
                    color: "lightgrey"

                    BorderImage {
                        id: image
                        source: {return JSON.parse('' + JSON.stringify(user) + '').profile_image_url}
                        width: parent.height; height: parent.height
                        border.left: 5; border.top: 5
                        border.right: 5; border.bottom: 5
                    }

                    Column {
                        spacing: units.gu(1)
                        x: image.width + 10
                        width: parent.width - image.width - 20
                        height: parent.height

                        Text {
                            id: title

                            text: {return JSON.parse('' + JSON.stringify(user) + '').name }
                        }

                        Text {
                            id: subtitle

                            text: {return model.text }
                        }
                    }
                }
            }
        }
    }

    AccountServiceModel {
        id: accountsModel
        //        serviceType: ""
        applicationId: "qml-weibo.ubuntu_qmlweibo"
    }

    Setup {
        id: setup
        providerId: "qml-weibo.ubuntu_plugin"
        applicationId: "qml-weibo.ubuntu_qmlweibo"
        onFinished: {
            createAccountBtn.visible = false;
        }
    }

    Component.onCompleted: {
        if ( accountsModel.count == 0 ) {
            // If there is no such an account, we need to create such one
            setup.exec();
        } else {
            // hide the button since the account has already been created
            createAccountBtn.visible = false
        }
    }
}



我们通过设计weiboapi.js文件来帮我们来得到微博的具体的内容:

//////////////////////////////////////////////////////////////////////////////////////     weibo
//home status
function weiboHomeStatus(token, page , observer)
{
    var url = "https://api.weibo.com/2/statuses/home_timeline.json?access_token=" + token + "&page=" + page

    console.log("home status start...")
    console.log("url: " + url);

    var doc = new XMLHttpRequest();
    doc.onreadystatechange = function() {
            if (doc.readyState == XMLHttpRequest.HEADERS_RECEIVED) {
                console.log(" home status in progress...");
            }
            else if (doc.readyState == XMLHttpRequest.DONE)
            {
                if(doc.status != 200) {
                    console.log("!!!Network connection failed!")
                    observer.update("error", doc.status)
                }
                else {
                    console.log("-- home status succeeded !");

                    if(doc.responseText == null) {
                        observer.update("null", doc.status)
                    }
                    else {
                        // console.log("home status: done" + doc.responseText)
                        console.log("Some data received!")
                        var json = JSON.parse('' + doc.responseText+ '');
                        observer.update("fine", json);
                    }
                }
            }
    }

    doc.open("GET", url, true);
    doc.setRequestHeader("Content-type", "application/json");
    doc.send();
}

function getStatus() {
    // We can get some data from the web and display them
    console.log("Going to get some data from web")
    function observer() {}
    observer.prototype = {
        update: function(status, result)
        {
            console.log("got updated!");

            if(status != "error"){
                if( result.error ) {
                    console.log("result.error: " + result.error);
                }else {
                    console.log("got updated1!");
                    for ( var i = 0; i < result.statuses.length; i++) {
                        modelWeibo.append( result.statuses[i] )
                    }
                }
            }else{
                console.log("Something is wrong!");
            }
        }
    }

    weiboHomeStatus(main.accesstoken, 1, new observer() );
}

整个应用的源码在地址 

git clone https://gitcafe.com/ubuntu/weiboqml.git

作者:UbuntuTouch 发表于2015/3/10 12:33:33 原文链接
阅读:246 评论:0 查看评论

Read more
UbuntuTouch

尽管我们的developer网站有丰富的API介绍,但是,有些API的介绍可能并不全,有些API也在不断地演进中。为了得到更详细的API,我们可以通过如下的命令来得到更加详细的信息。比如我们对“SingleDownload”API来得到更加多的信息。


$qmlplugindump Ubuntu.DownloadManager 0.1



显示的结果如下:


import QtQuick.tooling 1.1


// This file describes the plugin-supplied types contained in the library.
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
// 'qmlplugindump Ubuntu.DownloadManager 0.1'


Module {
    Component {
        name: "Ubuntu::DownloadManager::DownloadError"
        prototype: "QObject"
        exports: ["Error 0.1"]
        exportMetaObjectRevisions: [0]
        Property { name: "type"; type: "string"; isReadonly: true }
        Property { name: "message"; type: "string"; isReadonly: true }
    }
    Component {
        name: "Ubuntu::DownloadManager::SingleDownload"
        prototype: "QObject"
        exports: ["SingleDownload 0.1"]
        exportMetaObjectRevisions: [0]
        Property { name: "autoStart"; type: "bool" }
        Property { name: "errorMessage"; type: "string"; isReadonly: true }
        Property { name: "isCompleted"; type: "bool"; isReadonly: true }
        Property { name: "downloadInProgress"; type: "bool"; isReadonly: true }
        Property { name: "allowMobileDownload"; type: "bool" }
        Property { name: "throttle"; type: "qulonglong" }
        Property { name: "progress"; type: "int"; isReadonly: true }
        Property { name: "downloading"; type: "bool"; isReadonly: true }
        Property { name: "downloadId"; type: "string"; isReadonly: true }
        Property { name: "headers"; type: "QVariantMap" }
        Signal {
            name: "canceled"
            Parameter { name: "success"; type: "bool" }
        }
        Signal {
            name: "finished"
            Parameter { name: "path"; type: "string" }
        }
        Signal {
            name: "paused"
            Parameter { name: "success"; type: "bool" }
        }
        Signal {
            name: "processing"
            Parameter { name: "path"; type: "string" }
        }
        Signal {
            name: "progressReceived"
            Parameter { name: "received"; type: "qulonglong" }
            Parameter { name: "total"; type: "qulonglong" }
        }
        Signal {
            name: "resumed"
            Parameter { name: "success"; type: "bool" }
        }
        Signal {
            name: "started"
            Parameter { name: "success"; type: "bool" }
        }
        Signal {
            name: "errorFound"
            Parameter { name: "error"; type: "DownloadError&" }
        }
        Signal { name: "errorChanged" }
        Method {
            name: "registerError"
            Parameter { name: "error"; type: "Error"; isPointer: true }
        }
        Method {
            name: "bindDownload"
            Parameter { name: "download"; type: "Download"; isPointer: true }
        }
        Method {
            name: "unbindDownload"
            Parameter { name: "download"; type: "Download"; isPointer: true }
        }
        Method {
            name: "onFinished"
            Parameter { name: "path"; type: "string" }
        }
        Method {
            name: "onProgress"
            Parameter { name: "received"; type: "qulonglong" }
            Parameter { name: "total"; type: "qulonglong" }
        }
        Method {
            name: "onPaused"
            Parameter { name: "wasPaused"; type: "bool" }
        }
        Method {
            name: "onResumed"
            Parameter { name: "wasResumed"; type: "bool" }
        }
        Method {
            name: "onStarted"
            Parameter { name: "wasStarted"; type: "bool" }
        }
        Method {
            name: "onCanceled"
            Parameter { name: "wasCanceled"; type: "bool" }
        }
        Method { name: "start" }
        Method { name: "pause" }
        Method { name: "resume" }
        Method { name: "cancel" }
        Method {
            name: "download"
            Parameter { name: "url"; type: "string" }
        }
    }
    Component {
        name: "Ubuntu::DownloadManager::UbuntuDownloadManager"
        prototype: "QObject"
        exports: ["DownloadManager 0.1"]
        exportMetaObjectRevisions: [0]
        Property { name: "autoStart"; type: "bool" }
        Property { name: "cleanDownloads"; type: "bool" }
        Property { name: "errorMessage"; type: "string"; isReadonly: true }
        Property { name: "downloads"; type: "QVariantList"; isReadonly: true }
        Signal { name: "errorChanged" }
        Method {
            name: "download"
            Parameter { name: "url"; type: "string" }
        }
    }
}


我们通过使用“finished”信号可以得到下载完成的事件而进行分别的处理!


另外一个例子(Push notification)


liuxg@liuxg:~$ qmlplugindump Ubuntu.PushNotifications 0.1
import QtQuick.tooling 1.1

// This file describes the plugin-supplied types contained in the library.
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
// 'qmlplugindump Ubuntu.PushNotifications 0.1'

Module {
    Component {
        name: "PushClient"
        prototype: "QObject"
        exports: ["PushClient 0.1"]
        exportMetaObjectRevisions: [0]
        Property { name: "appId"; type: "string" }
        Property { name: "token"; type: "string"; isReadonly: true }
        Property { name: "notifications"; type: "QStringList"; isReadonly: true }
        Property { name: "status"; type: "string"; isReadonly: true }
        Property { name: "persistent"; type: "QStringList"; isReadonly: true }
        Property { name: "count"; type: "int" }
        Signal {
            name: "countChanged"
            Parameter { type: "int" }
        }
        Signal {
            name: "notificationsChanged"
            Parameter { type: "QStringList" }
        }
        Signal {
            name: "persistentChanged"
            Parameter { type: "QStringList" }
        }
        Signal {
            name: "appIdChanged"
            Parameter { type: "string" }
        }
        Signal {
            name: "error"
            Parameter { type: "string" }
        }
        Signal {
            name: "tokenChanged"
            Parameter { type: "string" }
        }
        Signal {
            name: "statusChanged"
            Parameter { type: "string" }
        }
        Method { name: "getNotifications" }
        Method {
            name: "notified"
            Parameter { name: "appId"; type: "string" }
        }
        Method { name: "emitError" }
        Method {
            name: "clearPersistent"
            Parameter { name: "tags"; type: "QStringList" }
        }
    }
}


作者:UbuntuTouch 发表于2015/3/12 14:55:15 原文链接
阅读:197 评论:5 查看评论

Read more
UbuntuTouch

在这篇文章中,我们将使用Ubuntu SDK所提供的online account API来访问微博的API并显示所需要的内容。这篇文章的重点是展示如何在HTML 5中使用online account API。我们将创建一个简单的HTML 5应用。我们最终显示的画面如下:


  


更多关于HTML5应用开发的资料可以在地址:https://developer.ubuntu.com/en/apps/html-5/


1)创建一个最基本的应用


我们还是像以前一样使用我们的Ubuntu SDK来创建一个最基本的weibo HTML 5应用。


  

  

这样我们就创建了一个最基本的weibo HTML 5应用。你可以使用热键Ctrl + R来运行它虽然它并不能做什么事。






2)加入online account所需要的文件



我们可以参考文章来对online account API有更深的了解。为了能够访问,需要创建如下的文件:


1)weibo.application

<?xml version="1.0" encoding="UTF-8"?>
<application>
  <description>Weibo scope</description>
  <desktop-entry>weibo.ubuntu_weibo.desktop</desktop-entry>
  <services>
    <service id="html5-weibo.ubuntu_html5weibo">
      <description>Watch your favorite Weibo messages</description>
    </service>
  </services>
</application>

2)weibo.service


<?xml version="1.0" encoding="UTF-8"?>
<service>
  <type>sharing</type>
  <name>Weibo scope</name>
  <icon>html5-weibo.png</icon>
  <provider>html5-weibo.ubuntu_account-plugin</provider>
  <translations>unity-scope-weibo</translations>
</service><strong>
</strong>

3)创建一个plugin文件目录并在它的下面创建

 a)Main.qml, 它的内容为:

import Ubuntu.OnlineAccounts.Plugin 1.0

OAuthMain {}

    b) qml-weibo.ubuntu_plugin.provider,它的内容为:

<?xml version="1.0" encoding="UTF-8"?>
<provider>
  <name>Weibo</name>
  <icon>weibo.png</icon>
  <translations>unity-scope-weibo</translations>
  <plugin>generic-oauth</plugin>
  <domains>.*weibo\.com</domains>
  <single-account>true</single-account>

  <template>
    <group name="auth">
      <setting name="method">oauth2</setting>
      <setting name="mechanism">web_server</setting>
      <group name="oauth2">
        <group name="web_server">
          <setting name="Host">api.weibo.com</setting>
          <setting name="AuthPath">oauth2/authorize</setting>
          <setting name="TokenPath">oauth2/access_token</setting>
          <setting name="RedirectUri">https://api.weibo.com/oauth2/default.html</setting>
          <setting name="ResponseType">code</setting>
          <setting name="ClientId">Your developer key</setting>
          <setting type="as" name="AllowedSchemes">['https','http']</setting>
          <setting name="ClientSecret">Your developer secret</setting>
          <setting name="ForceClientAuthViaRequestBody" type="b">true</setting>
      </group>
      </group>
    </group>
  </template>
</provider>


在上面的文件中一定要输入自己的“your developer key”及“your developer secret”。这个你需要在微博API的网站上去申请。整个文件的架构为:




4)修改manifest.json文件如下

{
    "name": "html5-weibo.ubuntu",
    "description": "description of html5-weibo",
    "architecture": "all",
    "title": "html5-weibo",
    "hooks": {
        "html5weibo": {
            "apparmor": "html5weibo.apparmor",
            "desktop": "html5weibo.desktop",
            "account-application": "weibo.application",
            "account-service": "weibo.service"
        },
        "account-plugin": {
            "account-provider": "plugin/html5-weibo.ubuntu_account-plugin.provider",
            "account-qml-plugin": "plugin/qml"
        }
    },
    "version": "0.1",
    "maintainer": "XiaoGuo, Liu <xiaoguo.liu@canonical.com>",
    "framework": "ubuntu-sdk-14.10"
}

这里添加了account及plugin的一些设置。


5)为了能够让.service,.application及.provider文件能正常得到显示,我们对“qml-weibo.qmlproject”添加如下的东西

    Files {
        filter: "*.service"
    }
    Files {
        filter: "*.application"
    }
    Files {
        filter: "*.provider"
    }


到这里,我们已经基本上已经修改或添加好我们所需要的文件。


3)设计应用的UI


修改我们的“index.html”文件:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>An Ubuntu HTML5 application</title>
    <meta name="description" content="An Ubuntu HTML5 application">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">

    <!--
         Application stylesheets
         Include your own stylesheets below to style your application.
      -->
    <link rel="stylesheet" href="css/app.css" type="text/css" />

    <!-- 
        Ubuntu UI javascript imports - Ambiance theme
        Ubuntu provides building blocks that you can use in your application. For more information, you can check out the documentation at http://design.ubuntu.com/apps.
     -->
    <!-- Ubuntu UI Style imports - Ambiance theme -->
    <link href="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/css/appTemplate.css" rel="stylesheet" type="text/css" />    
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/fast-buttons.js"></script>
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/core.js"></script>
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/buttons.js"></script>
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/page.js"></script>
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/pagestacks.js"></script>
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/popovers.js"></script>
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/list.js"></script>
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/toolbars.js"></script>
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/tabs.js"></script>
    <script src="/usr/share/ubuntu-html5-ui-toolkit/0.1/ambiance/js/shape.js"></script>

</head>
<body>

    <div data-role="mainview">
      <header data-role="header">
      </header>

      <div data-role="content">
        <div data-role="pagestack">

          <div data-role="page" id="main" data-title="Weibo Status">

            <section data-role="list" id="statuslist"></section>

            <div id='results'></div>
          </div> <!-- main -->

        </div> <!-- pagestack -->
      </div> <!-- content -->
    </div> <!-- mainview -->

    <script src="js/app.js"></script>

  </body>
</html>

这里的UI非常简单,我只使用了一个list控件。我们大部分的代码将在“app.js”中实现:

var token = '';

window.onload = function () {
    console.log("this is so cool....!")

    var UI = new UbuntuUI();

    UI.init();
    UI.pagestack.push('main');
    var api = external.getUnityObject('1.0');
    var oa = api.OnlineAccounts;

    // UI.button('getstatus').click(auth);

    auth();

    function auth() {
        console.log("getstatus button is clicked!");

        var filters = {'provider': 'html5-weibo.ubuntu_account-plugin', 'service': ''};

        oa.api.getAccounts(filters, function(accounts) {
            console.log("total length: " + accounts.length);

            if (accounts.length < 1) {
                // setResults('No accounts available');
                oa.api.requestAccount(
                    "html5-weibo.ubuntu_html5weibo",
                    unescape("html5-weibo.ubuntu_account-plugin"),

                    function() {
                        console.log("requestAccount callback...");
                        auth();
                });
                return;
            } else {
                // setResults("Available accounts: " + accounts.length);
            }

            function authcallback(res) {
                token = res['data']['AccessToken'];
                console.log("AccessToken: " + token);
//                setResults("AccessToken: " + token);

                getWeiboStatus(token, function(statuses) {
                    console.log("the length: " + statuses.length);

                    if (statuses) {
                        // create the 'ul' element
                        var ul = document.createElement('ul');
//                        var results = document.getElementById('results');
                        var results = document.querySelector('#results');
                        var statuslist = document.querySelector('#statuslist');
                        var ul = document.createElement('ul');

                        for (var i = 0; i < statuses.length; i ++) {

                            var li = document.createElement('li');
                            ul.appendChild(li);

                            var aside = document.createElement('aside');
                            li.appendChild(aside);

                            var img = document.createElement('img');
                            img.setAttribute('src', statuses[i]['profile_image_url']);
                            img.setAttribute('width', "50");
                            img.setAttribute('height', "50");
                            aside.appendChild(img);

                            var a = document.createElement('a');
                            a.setAttribute('href', '#');
                            a.innerHTML = statuses[i]['text'];
                            li.appendChild(a);

                            var right = document.createElement('label');
                            right.innerHTML = ""
                            li.appendChild(right);

                        }

                        console.log("it is done6");
                        statuslist.appendChild(ul);
                    }
                    else {
                        setResults('<br><br>ERROR');
                    }
                });
            }

            accounts[0].authenticate(authcallback);
        }); //getAccounts
    } //auth

    function getWeiboStatus(accessToken, callback) {
        var http = new XMLHttpRequest()
        var url = "https://api.weibo.com/2/statuses/home_timeline.json?access_token=" + accessToken + "&page=" + 1

        console.log('url: \n' + url)

        http.open("GET", url, true);
        var statuses = [];
        http.onreadystatechange = function() {
            if (http.readyState === 4){
                if (http.status == 200) {
                    var response = JSON.parse(http.responseText);
                    console.log("it succeeds! lenght: " );

                    for (i = 0; i < response['statuses'].length; i++ ) {
                        var time = JSON.stringify(response['statuses'][i]['created_at']);
//                        console.log("time: " + time );

                        var text = JSON.stringify(response['statuses'][i]['text']);
//                        console.log("text: " + text);

                        var name = JSON.stringify(response['statuses'][i]['user']['name']);
//                        console.log("name: " + name);

                        var profile_image_url = JSON.stringify(response['statuses'][i]['user']['profile_image_url']);
                        profile_image_url = profile_image_url.slice(1, profile_image_url.length-1);
//                      console.log("profile_image_url: " + profile_image_url);

                        var id = JSON.stringify(response['statuses'][i]['idstr']);

                        statuses.push({'time': time,
                                       'text': text,
                                       'name': name,
                                       'id': id,
                                       'profile_image_url': profile_image_url});
                    }

                    callback(statuses);

                } else {
                    console.log("error: " + http.status)
                    callback(null);
                }
            }
        };
        http.send(null);
    }

    function setResults(data) {
        var results = document.getElementById('results');
        results.innerHTML = data;
    };

}; //onload

我们可以使用如下的句子来得到online account API的接口:

   var api = external.getUnityObject('1.0');
   var oa = api.OnlineAccounts;


在这里,我们使用HTML 5的online account API来检查是否有如下的provider:

var filters = {'provider': 'html5-weibo.ubuntu_account-plugin', 'service': ''};

如果没有,我们使用如下的代码来创建weibo账号:

           if (accounts.length < 1) {
                // setResults('No accounts available');
                oa.api.requestAccount(
                    "html5-weibo.ubuntu_html5weibo",
                    unescape("html5-weibo.ubuntu_account-plugin"),

                    function() {
                        console.log("requestAccount callback...");
                        auth();
                });
                return;
            }

等创建成功了,我们使用“auth()”来得到访问weibo的access token。进而我们可以得到我们所需要的任何的信息。

被创建的账号,可以在“系统设置”里的“账号”中可以看到:



另外我们可以在手机或模拟器中的如下的目录中看到我们所创建的文件:



整个项目的源码在:git clone https://gitcafe.com/ubuntu/html5weibo.git

作者:UbuntuTouch 发表于2015/3/16 10:07:57 原文链接
阅读:284 评论:0 查看评论

Read more