Canonical Voices

What Ubuntu Touch Development in CSDN (Chinese) talks about

UbuntuTouch

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

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


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

作者:UbuntuTouch 发表于2015/3/11 13:26:59 原文链接
阅读:304 评论: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 原文链接
阅读:1881 评论: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 原文链接
阅读:606 评论:0 查看评论

Read more
UbuntuTouch

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


  



作者:UbuntuTouch 发表于2015/1/13 15:08:31 原文链接
阅读:529 评论: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 原文链接
阅读:687 评论:0 查看评论

Read more
UbuntuTouch

在这篇文章中,我们将介绍如何使用Ubuntu系统提供的online account API来实现我们的微博。通过这篇文章,开发者可以使用同样的方法来实现对其它Web Service的访问,比如淘宝,优酷等网站的访问。我们实现的新浪微博的Scope的截图如下。本篇文章的重点是介绍online account API的使用,所以我不会像以前的那些教程一样从最基本的Scope开发介绍。更多关于online account的介绍在网址找到。


   


1)创建一个最基本的weibo Scope

我们通过使用Ubuntu SDK来创建一个最基本的Scope。在这里我们使用如下的Scope template:


     


    

按照如上的步骤,我们可以创建一个我们自己的一个最基本的weibo Scope,虽然里面显示的还是天气。


2)为online account添加必要的文件


我们可以参考文章,也可以参照视频,来对online account API有一个更深的了解。为了方便我们的设计,我们在项目的根目录下创建如下的"click"目录:



我们同时也把在根目录下的“manifest.json.in”及“weibo.apparmor”移到“click”目录下,当然,我们也需要对根目录下的“CMakeLists.txt”做必要的修改。修改后的CMakeLists.txt文件如下:

project(weibo CXX)
cmake_minimum_required(VERSION 2.8.10)
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake" "${CMAKE_MODULE_PATH}")

# We require g++ 4.9, to avoid ABI breakage with earlier version.
set(cxx_version_required 4.9)
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
    if (NOT CMAKE_CXX_COMPILER_VERSION MATCHES "^${cxx_version_required}")
        message(FATAL_ERROR "g++ version must be ${cxx_version_required}!")
    endif()
endif()

# Set strict and naggy C++ compiler flags, and enable C++11
add_definitions(
  -fno-permissive
  -std=c++11
  -pedantic
  -Wall
  -Wextra
  -fPIC
  -DQT_NO_KEYWORDS
)

include(GNUInstallDirs)
find_package(PkgConfig)
find_package(Intltool)

# We depend on Boost for string trimming
find_package(
  Boost
  REQUIRED
)

# Search for our dependencies
pkg_check_modules(
  SCOPE
  libunity-scopes>=0.6.0
  net-cpp>=1.1.0
  REQUIRED
)

find_package(Qt5Core REQUIRED)
include_directories(${Qt5Core_INCLUDE_DIRS})

# Add our dependencies to the include paths
include_directories(
  "${CMAKE_SOURCE_DIR}/include"
  ${Boost_INCLUDE_DIRS}
  ${SCOPE_INCLUDE_DIRS}
)

# Do not remove this line, its required for the correct functionality of the Ubuntu-SDK
set(UBUNTU_MANIFEST_PATH "click/manifest.json.in" CACHE INTERNAL "Tells QtCreator location and name of the manifest file")
set(UBUNTU_PROJECT_TYPE "Scope" CACHE INTERNAL "Tells QtCreator this is a Scope project")

# Important project paths
set(CMAKE_INSTALL_PREFIX /)
set(SCOPE_INSTALL_DIR "/weibo")
set(GETTEXT_PACKAGE "weibo")
set(PACKAGE_NAME "weibo.ubuntu")
set(SCOPE_NAME "${PACKAGE_NAME}_weibo")

set(SCOPE_INSTALL_NAME "${PACKAGE_NAME}_weibo")
set(SCOPE_ACCOUNTS_NAME "${PACKAGE_NAME}_accounts")
set(SYMBOL_MAP "${CMAKE_SOURCE_DIR}/data/${PACKAGE_NAME}.map")

add_definitions(-DGETTEXT_PACKAGE="${GETTEXT_PACKAGE}")
add_definitions(-DSCOPE_NAME="${SCOPE_NAME}")
add_definitions(-DSCOPE_INSTALL_NAME="${SCOPE_INSTALL_NAME}")
add_definitions(-DSCOPE_ACCOUNTS_NAME="${SCOPE_ACCOUNTS_NAME}")
add_definitions(-DPACKAGE_NAME="${PACKAGE_NAME}")

# If we need to refer to the scope's name or package in code, these definitions will help

#This command figures out the target architecture and puts it into the manifest file
execute_process(
  COMMAND dpkg-architecture -qDEB_HOST_ARCH
  OUTPUT_VARIABLE CLICK_ARCH
  OUTPUT_STRIP_TRAILING_WHITESPACE
)

#configure_file(manifest.json.in ${CMAKE_CURRENT_BINARY_DIR}/manifest.json)

# Install the click manifest
#install(FILES ${CMAKE_CURRENT_BINARY_DIR}/manifest.json DESTINATION "/")
#install(FILES "weibo.apparmor" DESTINATION "/")

# Make these files show up in QtCreator
file(GLOB_RECURSE
  _PO_FILES
  "po/*.po" 
)
add_custom_target(hidden_files
  ALL
  SOURCES
#    manifest.json.in
#    weibo.apparmor
    data/${SCOPE_NAME}.ini.in
    po/POTFILES.in
    po/${GETTEXT_PACKAGE}.pot
    ${_PO_FILES}
)

add_subdirectory(click)

# Add our main directories
add_subdirectory(src)
add_subdirectory(data)
add_subdirectory(po)

# Set up the tests
enable_testing()
add_subdirectory(tests)
add_custom_target(
  check
  ${CMAKE_CTEST_COMMAND} --force-new-ctest-process --output-on-failure
)

这里主要的修改是影藏显示“manifest.json.in”及“weibo.apparmor”文件的显示,应为这两个文件已经移到“click”目录中了。同时也注释掉如下的语句,应为这项工作在“click”目录中的“CMakeLists.txt”中已经做了。

#configure_file(manifest.json.in ${CMAKE_CURRENT_BINARY_DIR}/manifest.json)

# Install the click manifest
#install(FILES ${CMAKE_CURRENT_BINARY_DIR}/manifest.json DESTINATION "/")
#install(FILES "weibo.apparmor" DESTINATION "/")

另外,我们为了能够正确地显示项目,我们也对如下的句子做了修改:

set(UBUNTU_MANIFEST_PATH "click/manifest.json.in" CACHE INTERNAL "Tells QtCreator location and name of the manifest file")

这里我们加上了“click”路径。同时我们也加入了如下的定义:

set(SCOPE_INSTALL_NAME "${PACKAGE_NAME}_weibo")
set(SCOPE_ACCOUNTS_NAME "${PACKAGE_NAME}_accounts")
set(SYMBOL_MAP "${CMAKE_SOURCE_DIR}/data/${PACKAGE_NAME}.map")

add_definitions(-DGETTEXT_PACKAGE="${GETTEXT_PACKAGE}")
add_definitions(-DSCOPE_NAME="${SCOPE_NAME}")
add_definitions(-DSCOPE_INSTALL_NAME="${SCOPE_INSTALL_NAME}")
add_definitions(-DSCOPE_ACCOUNTS_NAME="${SCOPE_ACCOUNTS_NAME}")
add_definitions(-DPACKAGE_NAME="${PACKAGE_NAME}")

这样这些变量及定义可以在项目的其它文件中被正确地引用了。

下面,我们来看看“click”目录下的文件:

apparmor.json


{
    "template": "ubuntu-scope-network",
    "policy_groups": [
        "accounts"
    ],
    "policy_version": 1.2
}


注意这里在“policy_groups”里我们多加入了一个称作为“accounts”的policy。这个是必须的,否则不可以正常工作。


manifest.json.in

这个文件的显示如下:

{
    "description": "This is a tutorial for weibo scope",
    "framework": "ubuntu-sdk-14.10",
    "architecture": "@CLICK_ARCH@",
    "hooks": {
        "weibo": {
            "scope": "weibo",
            "apparmor": "apparmor.json",
            "account-application": "weibo.application",
            "account-service": "weibo.service"
        },
        "accounts": {
            "account-provider": "weibo.provider",
            "account-qml-plugin": "qml-plugin"
        }
    },
    "maintainer": "XiaoGuo, Liu <xiaoguo.liu@canonical.com>",
    "name": "@PACKAGE_NAME@",
    "title": "weibo scope",
    "version": "0.1"
}

这个文件和我们template所产生的文件略有不同。这里的“hooks”中加入了account的一些东西。在每个用户登陆时都会生产相应的配置文件。

weibo.application.in


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


这个是微博Scope的描述文件。它定义了Scope的service id及描述

weibo.service.in


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

这个service文件描述了微博Scope的service信息。

weibo.provider.in

这是一个微博的account provider信息。这是一个重要的文件。如果我们不能正确地设置这个文件,可能我们不能正确地访问微博account。

<?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>

我们需要在“微博开放平台”去申请开发者key及secret。同时,我们一定要记得在微博的设置中做如下的设置,否则我们不能得到正确的数据。



“click”目录下的CMakeLists.txt文件内容如下:

function(configure_scope_files)
  foreach(_file ${ARGV})
    configure_file(
      "${_file}.in"
      "${CMAKE_CURRENT_BINARY_DIR}/${_file}"
      @ONLY
    )
  endforeach()
endfunction()

configure_scope_files(
  manifest.json
  weibo.provider
  weibo.service
  weibo.application
)

add_custom_target(hidden_files1
  ALL
  SOURCES
    apparmor.json
)

install(
  FILES
    "${CMAKE_CURRENT_BINARY_DIR}/manifest.json"
    "apparmor.json"
    "${CMAKE_CURRENT_BINARY_DIR}/weibo.provider"
    "${CMAKE_CURRENT_BINARY_DIR}/weibo.service"
    "${CMAKE_CURRENT_BINARY_DIR}/weibo.application"
  DESTINATION ${CMAKE_INSTALL_PREFIX}
)

install(
  DIRECTORY
    "qml-plugin"
  DESTINATION ${CMAKE_INSTALL_PREFIX}
)

这里它对该目录下的文件进行了安装。

为了能够对Scope的设置文件“weibo.ubuntu_weibo.ini.in”进行本地化的支持,我们对“data\CMakeListx.txt”加入如下的句子:

intltool_merge_translations(
  "${CMAKE_CURRENT_SOURCE_DIR}/${SCOPE_NAME}.ini.in"
  "${CMAKE_CURRENT_BINARY_DIR}/${SCOPE_INSTALL_NAME}.ini"
  ALL
  UTF8
)

这样它可以提取里面相应的英文,并在“po”目录中的.pot文件中进行翻译。

经过我们这样的修改后,我们的最基本的具有online account的设置文件已经基本已经做好了。我们可以在我们的手机或模拟器中运行我们的Scope了。在运行时,



我们选择“yes”继续运行。我们可以看到如下的画面:




者和我们所创建的最基本的Scope没有任何的差别,我们可以在手机的如下地址查看该目录的内容:



这个是我们所创建weibo provider的地方。


3)对代码进行修改

我们在上面已经对online account的配置文件做了修改。下面我们来对我们的cpp代码进行修改,为了能够使得我们使用online account,我们在query.cpp文件中加入如下的代码:

const static string SEARCH_CATEGORY_LOGIN_NAG = R"(
{
        "schema-version": 1,
        "template": {
        "category-layout": "grid",
        "card-size": "large",
        "card-background": "color:///#1ab7ea"
        },
        "components": {
        "title": "title",
        "background": "background",
        "art" : {
        "aspect-ratio": 100.0
        }
        }
        }
        )";


void Query::run(sc::SearchReplyProxy const& reply) {
    add_login_nag(reply);
 ...
}

void Query::add_login_nag(const sc::SearchReplyProxy &reply) {
    //    if (getenv("VIMEO_SCOPE_IGNORE_ACCOUNTS")) {
    //        return;
    //    }
    qDebug() << "SCOPE_INSTALL_NAME: " << SCOPE_INSTALL_NAME;
    qDebug() << "SCOPE_ACCOUNTS_NAME: " << SCOPE_ACCOUNTS_NAME;

    sc::OnlineAccountClient oa_client(SCOPE_INSTALL_NAME, "sharing", SCOPE_ACCOUNTS_NAME);

    // Check if our service is authenticated
    bool service_authenticated = false;

    int count = oa_client.get_service_statuses().size();
    qDebug() << "count: " << count;

    for ( sc::OnlineAccountClient::ServiceStatus const& status :
          oa_client.get_service_statuses())
    {
        if (status.service_authenticated)
        {
            service_authenticated = true;
            qDebug() << "Sevice is authenticated!";
            qDebug() << "account id: "  << status.account_id;
            qDebug() << "client id: " << QString::fromStdString(status.client_id);
            qDebug() << "service enabled: " << status.service_enabled;
            qDebug() << "secret: " << QString::fromStdString(status.client_secret);
            qDebug() << "access token: " << QString::fromStdString(status.access_token);
            accessToken_ = QString::fromStdString(status.access_token);

            // pass the access token to the client so that http request can be made
            client_.setAccessToken(accessToken_);
            break;
        }
    }

    if (!service_authenticated)
    {
        qDebug() << "Service is not authenicated!";

        sc::CategoryRenderer rdr(SEARCH_CATEGORY_LOGIN_NAG);
        auto cat = reply->register_category("weibo_login_nag", "", "", rdr);

        sc::CategorisedResult res(cat);
        res.set_title(_("Log-in to Weibo"));

        oa_client.register_account_login_item(res,
                                              query(),
                                              sc::OnlineAccountClient::InvalidateResults,
                                              sc::OnlineAccountClient::DoNothing);

        reply->push(res);
    }
}

重新运行我们的Scope,我们可以在手机上看到如下的画面:


我们点击“Log-in to Weibo”按钮:



我们使用我们自己的微博的账号进行登陆,在代码中,我们可以看到:



我们打开手机中的“系统设置”,再打开“账号”,当我们完成我们自己的账号登陆微博后,可以看到:



这说明我们的online account设置等已经是成功的。我们更进一步对我们的client代码进行修改,我们可以看到如下的画面:




整个项目的source code可以在如下的地址下载:

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

在使用代码时,一定要在“click/weibo.provider.in”中填入自己的开发者key及secret。
作者:UbuntuTouch 发表于2015/2/3 11:51:37 原文链接
阅读:776 评论:7 查看评论

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 原文链接
阅读:240 评论:0 查看评论

Read more
UbuntuTouch

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


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

作者:UbuntuTouch 发表于2015/2/10 19:26:33 原文链接
阅读:492 评论: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 原文链接
阅读:483 评论: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 原文链接
阅读:236 评论: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”信号可以得到下载完成的事件而进行分别的处理!

作者:UbuntuTouch 发表于2015/3/12 14:55:15 原文链接
阅读:191 评论: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 原文链接
阅读:276 评论:0 查看评论

Read more
UbuntuTouch

在这篇文章中,我们来介绍如何使用在线(online)的Webapp生成器来生产在Ubuntu手机或模拟器中可以安装的click安装包。Webapp生成器的地址:https://developer.ubuntu.com/webapp-generator/


1)打开生成器网页,并填入相应的信息


我们在浏览器中打开网页:https://developer.ubuntu.com/webapp-generator/, 并填入应用所需要的信息。请注意:如果你还没有Ubuntu One账号,请注册一个,否则你将不能进入。Ubuntu One账号在上传应用到商店时也是必须有的账号。






等填完我们所需要的信息后,我们点击按钮“Submit”,就可以在屏幕的做下方出现下载的一个叫做类似“wqqcom-uii.liu-xiao-guo_0.1_all.click”的Click安装包。这个文件通常应该在:

/home/HOST_NAME/Downloads

文件夹中(这里“HOSE_NAME”)根据自己的电脑名字的不同而不同。

2)安装应用到我们的手机或模拟器中


我们通过如下的命令来把我们生产的click安装包安装到手机或模拟器中。注意我们上面所生产的click包的名字中含有“all",说明它是对手机和模拟器都适用的安装包。我们打开一个Terminal,并在其中打入如下的命令:

liuxg@liuxg:~/Downloads$ adb push wqqcom-uii.liu-xiao-guo_0.1_all.click /home/phablet



紧接着,我们打入如下的命令:

liuxg@liuxg:~/Downloads$ adb shell
phablet@ubuntu-phablet:~$ pkcon --allow-untrusted install-local wqqcom-uii.liu-xiao-guo_0.1_all.click 





这样我们就把应用安装到手机或模拟器中。如果在手机桌面上没有找到该安装的应用,可以尝试搜索“qq”(对我们的这个例子而言,它的名字中含有qq)。

  


在模拟器中的安装和运行,显示如下:

     

欢迎大家生产自己喜欢的Webapps,并把它上传到我们的商店中。另外值得指出的是:我们也可以适用SDK中的“Web app”模版来生产我们喜欢的在线Webapps。




更多关于在线Webapp的开发的信息可以在地址https://developer.ubuntu.com/zh-cn/web/找到。

作者:UbuntuTouch 发表于2015/3/19 9:31:27 原文链接
阅读:215 评论:0 查看评论

Read more
UbuntuTouch

目前在Ubuntu提供了丰富的开发途径供开发者进行开发。它覆盖了从本地开发到Web的应用开发。下面我来介绍一下有那些开发的语言及途径可以用来开发:





1)Scope 开发

Scope 这个词对很多的开发者来说可能比较陌生,到目前为止也没有一个相应的中文词来精确地描述它到底是什么。简单地说Scope是Ubuntu OS区别于其他系统的一个独特的UI显示及应用框架。它很容易使得应用的内容得以显示及按照不同的要求进行呈现。使用Scope,可以使得运营商,OEM及应用开发着可以很方便地把一些Web Service的内容方便简洁地呈现,并使得内容很容易被搜索和发现。常见的Scope有应用商店,音乐商店,视频商店等。我们也可以包Amazon及淘宝商店也用Scope进行实现。下面我们通过一些图片看一些Scope到底长的是什么样子:










就其实质,就是利用一些现有的Web Service API来通过返回的json或xml数据,Scope利用其自身已经建立起的显示框架(有许多不同的布局供选择)来呈现数据,同时利用Scope的框架很方便地进行搜寻。

Scope的开发可以使用Qt C++, C++11及Scope 相应的C++API进行开发。

更多关于Scope的资料可以在Ubuntu的网站上http://developer.ubuntu.com/scopes/查看到。在那里可以看到相应的Tutorials

2)利用Qt/QML (QtQuick)进行开发

Ubuntu的应用基本上都是使用Qt C++/QML进行开发的。这也是Ubuntu的本地开发语言。使用Qt 语言可以开发出很炫很酷的应用。目前BlackBerry的平台也是使用Qt/QML来进行开发的。具体关于Qt/QML的更多的介绍,开发者可以在网站http://qt-project.org/找到。在Ubuntu 网站上,我们也可以看到关于Qt/QML的一些介绍,请查阅网站http://developer.ubuntu.com/apps/qml/tutorial/

3)利用HTML 5开发Web应用

Ubuntu上面也可以使用HTML 5进行应用开发。通过使用ApacheCordova & Phonegap及HTML 5,Javascript, CSS,开发者可以开发出像本地应用一样界面的应用。通过Cordova API接口,应用可以访问系统的很多的应用接口,比如Camera, battery, geolocation, media-capture等。

更多的关于Web的应用开发可以在Ubuntu网站http://developer.ubuntu.com/apps/html-5/tutorials/


作者:UbuntuTouch 发表于2014-8-6 9:58:16 原文链接
阅读:154 评论:0 查看评论

Read more
UbuntuTouch

[原]怎么在Virtualbox下安装Ubuntu OS

在这篇文章中,我们来介绍如何安装VirtualBox及在VirtualBox下面安装最新的Ubuntu操作系统。


1)下载VirtualBox文件

首先我们到网站 https://www.virtualbox.org/wiki/Downloads下载最新的virtualbox。



根据自己的操作系统,在该网站上下载两样东西:

  • VirtualBox platform packages
  • VirtualBox 4.3.16 Oracle VM VirtualBox Extension Pack 
你可以永远下载最新的版本。

2)安装VirtualBox

我们在Ubuntu OS或Windows下,双击刚刚下载的VirtualBox文件,系统会自动帮我们调用软件帮我们安装。在Ubuntu OS下的界面如下:



至此,我们已经完成了对VirtualBox的安装。我们可以在Ubuntu中打开dash,找到VirtualBox的图标。在Windows上,我们也可以通过类似的方法找到相应的图标:



点击VirtualBox图标,我们可以看到如下的画面:



3) 下载Ubuntu Desktop Image

由于在Ubuntu for phone的开发中,我们将使用Utopic (14.10)来进行开发。我们在网址http://cdimage.ubuntu.com/daily-live/current/下载适合自己平台的image。这里我们选择utopic-desktop-amd64.iso。我们把image存放于我们电脑中的一个目录中。

4) 在VirtualBox中安装Ubuntu OS

打开"Oracle VM VirtualBox Manager",并点击“New”。可以看到如下的画面:



紧接着,我们根据我们电脑的实际情况给予一定的内存大小。



对于我们首次创建Ubuntu OS来说,我们选择“Create a virtual hard drive now”。如果你曾经创建过,并存有一个virtual hard drive的文件,你可以直接选择“Using an existing virtual hard drive file”。这样创建起来更块。



接下来:



再接下来,我们选择“Dynamically allocated”。



紧接着,我们选择至少有20G的硬盘容量以装载整个OS及SDK的安装:



这样就完成了我们初步的设置。下面我们来真正地来安装我们的Ubuntu OS系统。

点击VirtualBox中的“Start




我们选择我们刚才下载好的image:



再选择“Install Ubuntu



如果网络的情况好,可以在安装的过程中同时下载更新及第三方的软件。











紧接下来,就开始安装Ubuntu OS了:



最终,我们把Ubuntu系统安装好了。




5) 安装VirtualBox Extensions

我们发现当我们把Ubuntu OS安装好后,界面的分辨率是不对的。我们没法把它的分辨率调到我们的主屏幕的分辨率。这时我们需要安装Extensions。



首先,我们关掉Ubuntu OS。打开VirtualBox,并点击菜单中的“Preferences”:



点击"Extensions", 



重新启动我们创建的VM。



然后选择:



如果弹出一个对话框,就点击“运行”(Run)即可。如果没有对话框出现的话,寻找“Terminal



打开“Terminal”应用,并输入如下的命令:



等安装完后,我们重新启动VM。我们就可以看到全屏的Ubuntu系统了。它的分辨率和Host的分辨率应该是一样的。另外,如果在任何时候我们在VM中的系统升级而造成的分辨率下降或共享文件夹不可以工作,我们都可以通过上面的方法重新编译,并使得显示正常。

6) 设置VirtualBox


点击VirtualBox的“Settings”,选择“Display”进行设置。




点击“Shared Folders”,来设置可以在host及guest OS之间进行拷贝文件。



同时,我们在VM中的Ubuntu OS中的“Terminal”键入如下的命令:

$sudo usermod -a -G vboxsf username

这里“username"是用户名。具体操作如下:

 

在VM中打开文件浏览器,即可看见Host中被分享的文件夹。文件名通常会有一个“sf”开始,表示shared folder。




选择“General”,我们可以设置可以在host及guest系统之间进行拷贝。这个对我们有时非常有用。



这样我们基本上对VirtualBox的设置已经完成。当然,你们如果有兴趣的话,可以对更多的选项进行设置。


7) 安装中文输入法

我们知道中文输入法对开发中文的应用是非常有用的。我们可以参照文章“怎么在Ubuntu OS上面安装搜狗输入法及对Qt Creator的支持”进行输入法的设置。

8) 安装Ubuntu SDK

我们可以按照“Ubuntu SDK 安装”来安装我们的SDK来进行我们的Ubuntu应用开发了。


作者:UbuntuTouch 发表于2014-9-24 13:34:18 原文链接
阅读:322 评论:0 查看评论

Read more
UbuntuTouch

当我们刷最新的Ubuntu手机软件时,我们必须打开开发者模式。否则,当我们使用"adb"命令时,会出现如下的情况:




当手机和电脑连接后,不能查看到任何的device。当我们打开开发者模式后,我们可以看到如下的画面:



这里我们可以看到,我们通过"adb"命令可以看到连接到的device。


为了打开开发者模式,我们可以通过如下的步骤来操作:

1)打开“系统设置”应用




2) 选中“关于此手机”




3)点击“开发者模式”




4) 打开开关




一旦完成开发者模式的设置,我们就可以对手机进行部署了。

我们也可以通过“adb shell"命令对手机的文件系统进行操作




作者:UbuntuTouch 发表于2014-9-23 10:06:47 原文链接
阅读:388 评论:0 查看评论

Read more
UbuntuTouch

[原]在Ubuntu上的C++及QML混合编程

在这篇文章中,我讲述如果使用QML来调用C++来扩展我们的应用。我们知道,在许多的场景中,QML虽然很强大,在UI方面是非常突出的,但是,有的时候,我们可能会想到应用我们的C++代码。这有一下方面的原因:

1)我们已经有成熟的C++引擎设计或协议等。比如我们已经用C++设计好了我们的Fetion协议代码。我们没要再用另外一个低性能的语言来重新写一边

2)有些功能我们没有办法使用Javascript来完成,必须使用系统或专用的一些功能包来完成。这时我们可以使用C++语言来完成我们需要的功能

3)有些代码对计算的要求非常高,需要使用大量的CPU时间,这时,我们可以考虑使用C++来完成这部分的工作


事实上,在Ubuntu OS上,我们可以可以使用QML/Javascript来创建非常炫的UI,同时我们也可以使用C++来完成我们特有的一些功能(比如需要大量计算)。两者之间的结合是非常自然的和密切的。你可以想像C++是可以用来拓展QML的功能的。


在这里,我们来创建一个应用读取Linux的一些环境变量。通过这个练习,我们可以掌握怎么在QML文件中调用C++的代码。

1)创建一个基本的应用

  • 启动Ubuntu IDE,选定"App with QML extension Library"模版应用



  • 设定应用的设置


  • 选择所需要运行的架构。这里我们选择"Desktop"及在手机上能运行的“armhf"架构


至此,我们基本上已经产生了一个可以在手机和电脑上运行的应用。选择合适的架构,点击IDE左下角的运行键



如果你们在运行到这里,还有什么问题的话,请参照我先前的文章正确安装你的环境。

2)添加C++功能模块


首先我们找到项目的子目录“ReadEnv/backend/modules/ReadEnv”,在这里我们创建两个文件“readenv.cpp”及“readenv.h”。他们的内容如下:


#ifndef READ_ENV_H
#define READ_ENV_H

#include <QObject>

class ReadEnv : public QObject
{
    Q_OBJECT
public:
    explicit ReadEnv(QObject *parent = 0);
    Q_INVOKABLE QString getenv(const QString envVarName) const;

private:
};

#endif // READ_ENV_H

#include "readenv.h"
#include <QDebug>

ReadEnv::ReadEnv(QObject *parent) : QObject(parent)
{
}

QString ReadEnv::getenv(const QString envVarName) const
{
    QByteArray result = qgetenv(envVarName.toStdString().c_str());
    QString output = QString::fromLocal8Bit(result);
    qDebug() << envVarName << " value is: "  << output;
    return output;
}

这是一个非常简单的C++类。这个类必须是从QObject类继承,这样可以使得这个类可以使用Qt的signal-slot机制。如果大家对这个还不熟的话,可以参阅一些资料。对一个这样的类来说,如果它的函数或方法可以被QML调用的话,必须定义为“Q_INVOKABLE”。另外所有被定义为"slot"的函数或方法也可以被QML中的JS直接调用。我们可以通过更多的例子来说明这个问题。“getenv"方法很简单。它直接读取所需要的环境变量的值,并通过转换返回。

我们也同时打开该目录下的另外一个文件"backend.cpp"。经过修改的文件显示如下:

#include <QtQml>
#include <QtQml/QQmlContext>
#include "backend.h"
#include "mytype.h"
#include "readenv.h"


void BackendPlugin::registerTypes(const char *uri)
{
    Q_ASSERT(uri == QLatin1String("ReadEnv"));

    qmlRegisterType<MyType>(uri, 1, 0, "MyType");
    qmlRegisterType<ReadEnv>(uri, 1, 0, "ReadEnv");
}

void BackendPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
{
    QQmlExtensionPlugin::initializeEngine(engine, uri);
}

这里其实也没有做什么。加入了一句

 qmlRegisterType<ReadEnv>(uri, 1, 0, "ReadEnv");

这里的第一个“ReadEnv"对应于我们所定义的类的名称,而不是项目的名称。第二个“ReadEnv"究其实是在QML使用的来实例化一个这样的类。它的名称可以和实际的类不同名字,也可以是任何一个你喜欢的名称。不过你必须也要在QML文件中做相应的修改。通过这样的注册,该类就可以在QML中进行实例化,并引用!等我们已经把源码已经做好,我们必须把它加入到我们的项目中进行编译。我们打开位于"backend"目录下的”CMakeLists.txt"文件中,加入如下的语句:

set(
<pre style="margin-top: 0px; margin-bottom: 0px;"><!--StartFragment-->    ReadEnvbackend_SRCS<!--EndFragment-->

<span style="font-family: Arial, Helvetica, sans-serif; color: rgb(192, 192, 192);">        </span><span style="font-family: Arial, Helvetica, sans-serif;">modules/ReadEnv/backend.cpp</span>
    modules/ReadEnv/mytype.cpp
    modules/ReadEnv/readenv.cpp
)

我们可以通过关掉项目再重新打开应用的方式,使得项目中的文件目录得到更新。也可以使用点击右键的方式使新加入的文件得到正确的显示:
这样,我们的C++部分的代码基本上就已经完成了。

3)修改QML部分的代码

上面我们已经完成了基本的C++部分的代码,我们在这里继续来完成QML部分的界面。首先我们来熟悉一下QML的layout。我们知道layout可以很方便地帮我们管理我们的控件,并在适当的时候自动适配合适的屏幕尺寸及手机方向的改变。QML中有Column及Row来帮我们管理我们的控件:
        Row {
            anchors.horizontalCenter: parent.horizontalCenter
            spacing: 10

            Label {
                text: "Value:  "
                fontSize: "large"
            }

            TextArea {
                id:value
                readOnly: true
                contentWidth: units.gu(40)
                contentHeight: units.gu(80)
            }

        }


这里我们并排放上两个控件,一个是Label,另外一个是"TextArea"。通过这样的组合,这整个又可以看做是一个复合的控件,里面有两个控件。它整个又可以被放到它外面的布局管理器(Row, Grid, Flow, Column等)中。通过我的修改,我们创建了如下的界面及界面。




我们通过修改“ReadEnv.qml” 来实现界面。具体的代码如下:

import QtQuick 2.0
import Ubuntu.Components 0.1
import ReadEnv 1.0
import "ui"

MainView {
    // 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.ReadEnv"

    anchorToKeyboard: true

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

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

    ReadEnv {
        id: readEnv
    }

    Column {
        // anchors.fill: parent
        //        anchors.verticalCenter: parent.verticalCenter
        //        anchors.horizontalCenter: parent.horizontalCenter
        anchors.centerIn: parent
        spacing: 20

        Row {
            anchors.horizontalCenter: parent.horizontalCenter
            spacing: 10
            Label {
                text: "Variable:"
                fontSize: "large"
            }

            TextField {
                id:input
                text:"PWD"
                focus: true

                placeholderText: "Please input a env variable"

                Keys.onPressed: {
                    if (event.key === Qt.Key_Return) {
                        print("Enter is pressed!")
                        value.text = readEnv.getenv(input.text)
                    }
                }
            }
        }

        Row {
            anchors.horizontalCenter: parent.horizontalCenter
            spacing: 10

            Label {
                text: "Value:  "
                fontSize: "large"
            }

            TextArea {
                id:value
                readOnly: true
                contentWidth: units.gu(40)
                contentHeight: units.gu(80)
            }

        }

        Button {
            anchors.horizontalCenter: parent.horizontalCenter
            text: "Get"
            onClicked: {
                print("Button is clicked!")
                value.text = readEnv.getenv(input.text)
            }
        }
    }
}



特别注意的是:
  • 在调用backend的库时,我们必须使用如下的动作来完成。
	import ReadEnv 1.0
这里的“ReadEnv",实际上对应于在“backend.cpp"中的如下语句中的“ReadEnv"。如果该处发生变化,import语句也得变化

    Q_ASSERT(uri == QLatin1String("ReadEnv"));
我们也可以在手机的安装环境中可以看出。"ReadEnv"同时也是在“lib"下的一个目录名称。其实这个理解是非常重要的。所有的import中的文字其实就是表示的是路经。它标示了库所在的位置




  • 我们通过如下的语句来对"ReadEnv"类进行实例化。我们在实例化的同时,也给了一个ID,这样就可以在其他的地方引用了。在本应用中,我们在Button中调用
      ReadEnv {
          id: readEnv
      }

        Button {
            anchors.horizontalCenter: parent.horizontalCenter
            text: "Get"
            onClicked: {
                print("Button is clicked!")
                value.text = readEnv.getenv(input.text)
            }
        }


这个应用的运行效果为:


好了今天我们已经完成了一个练习。它实现了从QML中调用C++代码。我们以后还可以添加更多的功能到这个应用中。本例程的代码可以在如下网站找到:

bzr branch lp:~liu-xiao-guo/debiantrial/readenv

作者:UbuntuTouch 发表于2014-8-14 13:28:23 原文链接
阅读:70 评论:0 查看评论

Read more
UbuntuTouch

在Ubuntu平台上面,我们可以使用History API来读取电话本及短信的内容,并使用listview来显示出来。下面我们来展示该怎么做。


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

我们使用Qt Creator来创建一个最基本的QML应用。我们选择使用“App with Simple UI"模版来创建我们的应用。





为了使用History API, 我们必须引入

import Ubuntu.History 0.1

为了能够使我们读取不同的history,我们先来做一个ComboButton。它的设计如下:

            ComboButton {
                id: type
                expanded: false

                text: "Voice"
                ListView {
                    model: typemodel
                    delegate: ListItem.Standard {
                        text: modelData

                        onClicked: {
                            console.log("item is clicked!" + index + " " + name);
                            type.expanded = false;
                            type.text = text;

                            console.log("type is: " + type.text);

                            if ( name === "Voice") {
                                historyEventModel.type = HistoryThreadModel.EventTypeVoice;
                                listView.model = historyEventModel
                            } else if ( name === "Text" ) {
                                historyEventModel.type = HistoryThreadModel.EventTypeText;
                                listView.model = historyEventModel
                            } else if ( name === "Thread") {
                                listView.model = historyThreadModel
                            }

                        }
                    }
                }
            }
这里我们可以参考ComboButton来更加多了解如何使用这个控件。这里,我们提供了三个选项"Voice", "Text" 及“Thread"。同时我们也创建了俩个不同的history model。

    HistoryEventModel {
        id: historyEventModel
        filter: HistoryFilter {}
        type: HistoryThreadModel.EventTypeVoice
        sort: HistorySort {
            sortField: "timestamp"
            sortOrder: HistorySort.DescendingOrder
        }
    }

    SortProxyModel {
        id: sortProxy
        sortRole: HistoryEventModel.TimestampRole
        sourceModel: historyEventModel
        ascending: false
    }

    HistoryThreadModel {
        id: historyThreadModel        
	filter: HistoryFilter {}
 sort: HistorySort { sortField: "lastEventTimestamp" sortOrder: HistorySort.DescendingOrder 
        } 


由于一些性能方面的原因的考虑,目前我们必须把filter设置为空,即:
filter: HistoryFilter {}

我们同时也把上面定义的model和我们的ListView一起连接起来:

            ListView {
                id: listView

                width: parent.width
                height: parent.height - type.height

                Component {
                    id: sectionDelegate

                    Text {
                        text: section
                    }
                }

                model: historyEventModel

               delegate:
               ...
            }

目前似乎一切都已经好了。接下来我们来运行我们的应用到手机中。我们会发现在手机中没有任何的history的东西显示。问题出现在哪里呢?



我们开启一个terminal,然后打入如下的命令:

$adb shell

然后再在手机中的命令行输入:

root@ubuntu-phablet:~# grep "DENIED" /var/log/syslog 

我们可以看到如下的图片:



显然,我们遇到了一个安全的问题。为了接的这个问题,我们必须在应用中加入它所需要的policy。



我们再重新运行程序。在手机上,我们可以看到如下的画面:




我们再也没有security的问题了。整个工程的源码在如下的地址可以下载:

bzr branch lp:~liu-xiao-guo/debiantrial/history





作者:UbuntuTouch 发表于2014-9-4 12:05:55 原文链接
阅读:148 评论:0 查看评论

Read more
UbuntuTouch

QML中的Loader是用来动态地载入一个QML的Component。它可以用来载入一个QML文件(使用它的source属性)。它也可以载入一个Component(使用它的sourceComponent属性)。它适合在需要载入一个Component时才载入它,这样避免资源的浪费。它可以动态地载入按需求在需要的时候创建我们需要的Component。更多阅读,可以参照:http://qt-project.org/doc/qt-4.8/qml-loader.html


1)动态载入一个Component


我们首先来创建我们一个基本的应用。这里我们使用一个"App with Simple UI"的模版。我们首先创建一个称作为"filearray.js"的javascript文件,如下:

var filearray = [
                    ["images/fall1.jpg", "First image"],
                    ["images/fall2.jpg", "Second image"],
                    ["images/fall3.jpg", "Three image"],
                    ["images/fall4.jpg", "Fourth image"]
                ];

这里创建了一个二维的数组。为了我们能够在应用中使用。同时,我们在"main.qml"中也创建一个Component。这样它可以在我们的应用中动态地产生。代码如下:

        Component {
            id: imageText

            Rectangle {
                id:top
                anchors.fill: parent
                Image {
                    id:innerImage
                    anchors.top: parent.top
                    anchors.topMargin:30
                    anchors.horizontalCenter: parent.horizontalCenter
                    width:parent.width*0.8;
                    height: parent.height*0.8
                    source:root.currentImage

                }
                Text{
                    id:answer
                    anchors.top:innerImage.bottom
                    anchors.topMargin:30
                    horizontalAlignment: Text.AlignHCenter
                    width:parent.width;
                    text:root.currentText
                }
            }

       
这个Component非常简单。上面使用了一个Image,下面是一个Text。这两个item的内容是从root控件中的两个property中获得。我们希望这root中的这两个property改变时他们的内容也可以改变。

   Page {
        id: root;
        title: i18n.tr("QML Loader")
        property int index: 0
        property string currentImage
        property string currentText:" "

同时,为了说明问题,我们也设计了一个timer。当这个timer每次timeout时,我们希望我们的loader:

        Loader {
            id: loader
            anchors.fill: parent
            anchors.centerIn:parent
        }

的"sourceComponent"每次都能发生改变,以使得UI得到变化。这里有趣的是,我们也可以为Loader定义它的大小:

  • 如果没有定义Loader的大小的话,Loader将会自动地适配到Component的大小尺寸(当component完成装载以后)
  • 如果Loader的大小已经被定义的话,当component完成装载后,component的尺寸将自动被适配到Loader的尺寸
    function timeout() {
        console.log("root.index" + root.index);
        console.log(FileExt.filearray[root.index][0]);
        root.currentImage = FileExt.filearray[root.index][0];
        root.currentText = FileExt.filearray[root.index][1];
        loader.sourceComponent = imageText;

        root.index++;
        if ( root.index === FileExt.filearray.length) {
            root.index = 0;
        }
    }

        Timer {
            id: timer
            interval: 3000
            onTriggered: {
                timeout();
            }
            repeat: true
            triggeredOnStart: true
        }

运行的效果图如下:



我们可以看到画面中的每三次改变一次。每一次都是一个新的Component,而不是同一个Component不变,只是其中的属性发生改变。Loader很适用于在不同的场景中装载不同的component,比如在不同的菜单中,装载不同的component,以显示不同的UI。Qt SDK很多的例程就是这样写的!

整个例程的代码在如下地址找到:

bzr branch lp:~liu-xiao-guo/debiantrial/loaderqml

2)使用Loader载入qml文件

在下面的例程中,我们来完成一个使用Loader来载入qml文件。我们来做一个类似wizard的东西。当第一个页面显示完后,紧接着按下一个按钮,进入下一个页面。我们首先来创建一个简单的"App with Simple UI"的模版应用,然后,修改main.qml文件:

import QtQuick 2.0
import Ubuntu.Components 1.1
import "components"

MainView {
    // 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.wizard"


    //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 {
        id: root
        title: i18n.tr("Wizard")

        Loader {
            z: 1
            id: main
            anchors.fill: parent
        }

        Image {
            source: "images/fall1.jpg"
            anchors.fill: parent
        }

        Button {
            anchors.right: parent.right
            anchors.bottom: parent.bottom
            anchors.bottomMargin: 20
            anchors.rightMargin: 20


            text: "Go to Page 1"
            onClicked: {
                main.source = "Page1.qml"
                console.log("Clicked in main")
            }
        }

        Connections {
            target:main.item
            onHandlerLoader:{
                console.log("Something happened!")
            }
        }
    }
}


这里我们定义了一个叫做main的Loader。当我们点击按钮”Go to Page 1"时,我们使用它来装载另外一个页面“Page1.qml"。注意我们在这里设置它的"z" order值为"1”。同时,我们也可以通过如下的Connections来接受来自main Loader的一些signal来做我们所需要的一些处理。
        Connections {
            target:main.item
            onHandlerLoader:{
                console.log("Something happened!")
            }
        }

在我们的Page1.qml文件中,我们设计如下:

import QtQuick 2.0
import Ubuntu.Components 1.1

Rectangle {
    id:page1
    anchors.fill: parent

    signal handlerLoader;

    Loader{
        z: 2
        id:loader
        anchors.fill: parent
    }
    
    Image {
        source: "images/fall2.jpg"
        anchors.fill: parent
    }

    Button {
        anchors.right: parent.right
        anchors.bottom: parent.bottom
        anchors.bottomMargin: 20
        anchors.rightMargin: 20

        text: "Go to Page 2"
        onClicked: {
            loader.source = "Page2.qml"
            handlerLoader();
        }
    }
}

在这里,我们定义了另外一个Loader, 并且设置它的“z” order值为2,使一个画面得它可以覆盖以前的页面。我们也尝试定义了一个signal "handlerLoader"。这样我们可以使得前一个面可以的得到一个响应。我们可以把我们想要的信号通过这样的方式发送出去,让需要对它感兴趣的代码利用它。

运行我们的程序,结果如下:

       

代码在如下的地址可以找到:

bzr branch lp:~liu-xiao-guo/debiantrial/wizard

作者:UbuntuTouch 发表于2014-9-9 14:07:55 原文链接
阅读:171 评论:0 查看评论

Read more
UbuntuTouch

在这篇文章里,我来介绍如何在Ubuntu  OS上上面读取电话本的信息。


1)首先我们来创建一个最基本的应用

打开我们的Qt Creator, 我们来创建一个称作为“contact1"的项目。在本项目中,我们使用"App with Simple UI"的模版。我们修改我们的“main.qml"代码如下:


import QtQuick 2.0
import Ubuntu.Components 0.1
import "components"
import QtContacts 5.0
import Ubuntu.Components.ListItems 0.1 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: "com.ubuntu.developer.liu-xiao-guo.contact1"

    /*
     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("Contact1")

        ContactModel {
            id: contactModel

            manager: "galera"
        }

        ListView {
            id: contactView

            anchors {
                left: parent.left
                right: parent.right
                top: parent.top
                bottom: buttons.bottom
            }

            model: contactModel

            delegate: ListItem.Subtitled {
                text: contact.name.firstName
                subText: contact.phoneNumber.number
            }
        }
    }
}

这里我们首先输入我们的QtContact 5.0库。在代码中我们定义了如下的ContactModel:

        ContactModel {
            id: contactModel
            manager: "galera"
        }

一定要记住使用"galera" manager。最后,我们使用一个ListView来展示我们的内容。代码显示如上所示。但是我们现在马上在手机上运行时,可能看到如下的信息:

Fail to connect with service: QDBusError("org.freedesktop.DBus.Error.AccessDenied", "An AppArmor policy prevents this sender from sending this message to this recipient, 0 matched rules; type="method_call", sender=":1.119" (uid=32011 pid=20604 comm="/usr/lib/arm-linux-gnueabihf/qt5/bin/qmlscene $@ m") interface="org.freedesktop.DBus.Introspectable" member="Introspect" error name="(unset)" destination="com.canonical.pim" (uid=32011 pid=3057 comm="/usr/lib/arm-linux-gnueabihf/address-book-service/")")

这说明,我们的程序遇到了安全的问题。我们必须在我们应用的apparmor文件中加入相应的policy以来使得我们的读取是可行的。

==ou

加入"Contacts"policy后,我们再在手机中运行,可以看到如下的画面:



我们可以在如下的地址下载我们的代码:

bzr branch lp:~liu-xiao-guo/debiantrial/contact1

2)读取favorite contact信息


我们在我们的应用中定义如下的以个favorite model

        ContactModel {
            id: favouritesContactsModel

            manager: "galera"
            sortOrders: [
                SortOrder {
                    id: sortOrder

                    detail: ContactDetail.Name
                    field: Name.FirstName
                    direction: Qt.AscendingOrder
                }
            ]

            fetchHint: FetchHint {
                detailTypesHint: [ContactDetail.Avatar,
                    ContactDetail.Name,
                    ContactDetail.PhoneNumber]
            }

            filter: DetailFilter {
                id: favouritesFilter

                detail: ContactDetail.Favorite
                field: Favorite.Favorite
                value: true
                matchFlags: DetailFilter.MatchExactly
            }

            onErrorChanged: {
                if (error) {
                    busyIndicator.busy = false
                    contactListView.error(error)
                }
            }
        }

我们在手机上使一个contact成为favorite。这时在我们的ListView中使用我们的favorite Model。代码如下:

        ListView {
            id: contactView
            anchors.fill:parent

            model: root.showFavourites ? favouritesContactsModel : contactModel

            delegate: ListItem.Subtitled {
                text: contact.name.firstName
                subText: contact.phoneNumber.number
            }
        }
我们可以看到我们的一个被设为favorite的contact被列举出来了。



代码在如下的地址可以下载:

bzr branch lp:~liu-xiao-guo/debiantrial/contact2





作者:UbuntuTouch 发表于2014-9-9 9:01:03 原文链接
阅读:136 评论:0 查看评论

Read more