开发Ubuntu Touch可用的Scope界面-快速入门

    Scope是一组数据的定制视图,可以使用定制的布局、显示和品牌建立选项。从RSS新闻推送到天气数据和搜索引擎结果,Scope的灵活性使您可以使用其他OS提供简单、明确且一致的体验。Scope也可与系统范围内的账户集成(电子邮件、社交网络…),将您的内容分为多个类别并在各个类别 中进行集合(例如,“shopping”Scope集合了多个商店Scope的结果)。web

    在本教程中,您将了解如何使用Ubuntu SDK编写SouldCloud的C++Scope。在本示例中,只须要很是少的C++知识,将其根据暴露JSON API其余服务来调整也很是简单。json

    注意:本教程也适用于Ubuntu 14.04及更高版本。若是您但愿使用Scope布局工具,则至少要使用Ubuntu 14.10。ubuntu

SDK设置

SDK提供适用于多种不一样应用程序类型的多种模板。C++Scope有本身的模板,这也是咱们将使用的模板。单击“New Project”按钮来建立新Scope项目。系统将要求您填入一些值来生成该项目。api

若是您须要得到更多有关SDK入门指南的帮助,请查看SDK设置文章安全

注意:即便您要使用平台的安全策略,您还须要了解有关Scope的另外一件事:若是您在某个时刻须要使用网络,您将没法访问用户数据。这是一项合理的隐私政策,以免在未获得明确许可的状况下提取用户数据。网络

测试您的Scope

在本教程的任一点,您均可按下SDK侧栏上的Play按钮来测试您手机上或仿真器上的Scope。等待几秒,项目的生成并上传到设备后,项目应会自动打开。数据结构

关键源文件

您可经过运行如下项目得到本教程的源代码app

$ bzr branch lp:~davidc3/ubuntu-sdk-tutorials/scope-tutorial-soundcloud-qjson编辑器

生成的项目包含至关多的文件,咱们将讨论其中最重要的一些文件。须要注意的一点是:模板已提供了一个正在使用的Scope:使用openweathermap.org的天气Scope。咱们将对其进行更改,使其从SoundCloud中提取结果。ide

manifest.json

其能容将由生成系统用于生成点击数据包,您可以在Ubuntu Store内安装和发布该点击数据包。大多数状况下,您可保留从开发人员环境中提取的默认值。

<scope>.apparmor

您的Scope使用的安全策略组。咱们的示例中为无,由于咱们使用的“ubuntu-scope-network”模板已经许可网络调用。了解更多安全策略组

data/<appid>.ini

链接到文件

一个很是重要的文件,将容许您自定义和推广您的Scope(图标、背景图像、颜色…)。咱们稍后将看到相关状况。

include/api/config.h

链接到文件

咱们的HTTP配置:用户代理和基础API URL。让咱们更改SoundCloud API URL的apiroot,完成首个更改。

15

std::string apiroot { "https://api.soundcloud.com" };

其余URL参数稍后将经过net-cpp库添加。

include/api/client.h, /scope/scope.h, /scope/query.h, /scope/preview.h

链接至文件夹

咱们的C++标头的其他部分。以下所示,更改client.h标头,以匹配SoundCloud API的数据结构。您可保留标头的其他部分不变。

这是个人Client类如今的外观。您可经过将教程文件的内容粘贴到您本身的文件中进行尝试:

22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95

class Client {

     public :

 

     /**

     * Our Artist object.

     */

     struct Artist {

         unsigned int id;

         std::string username;

         std::string avatar_url;

     };

 

     /**

     * Track info, including the artist.

     */

     struct Track {

         unsigned int id;

         std::string title;

         std::string uri;

         std::string artwork_url;

         std::string stream_url;

         std::string description;

         std::string genre;

         Artist artist;

     };

 

     /**

     * A list of Track objects.

     */

     typedef std::deque<Track> TrackList;

 

     /**

     * Track results.

     */

     struct TrackRes {

         TrackList tracks;

     };

 

     Client(Config::Ptr config);

 

     virtual ~Client() = default ;

 

     /**

      * Get the track list for a query

      */

     virtual TrackRes tracks( const std::string &query);

 

     /**

      * Cancel any pending queries (this method can be called from a different thread)

      */

     virtual void cancel();

 

     virtual Config::Ptr config();

 

protected :

     void get( const core::net::Uri::Path &path,

              const core::net::Uri::QueryParameters &parameters,

              QJsonDocument &root);

     /**

      * Progress callback that allows the query to cancel pending HTTP requests.

      */

     core::net::http::Request::Progress::Next progress_report(

             const core::net::http::Request::Progress& progress);

 

     /**

      * Hang onto the configuration information

      */

     Config::Ptr config_;

 

     /**

      * Thread-safe cancelled flag

      */

     std::atomic< bool > cancelled_;

};

src/api/client.cpp

链接到文件

咱们的API客户端。它提供Scope代码和HTTP API访问之间的隔离。其惟一的做用是检索SoundCloud中的数据。

src/scope/scope.cpp

链接到文件

该文件定义类型类unity::scopes::ScopeBase,该类型类提供客户端用于与Scope互动的输入点API。

  • 它实行启动和中止方法。不少Scope都保持这些方法处于不修改的状态,本示例也同样。

  • 它还实行两个关键方法:搜索和预览。这些方法通常不须要修改,本示例也未修改。可是,以下所述,它们调用每一个Scope须要实行的关键方法。

注意:您可能会发如今相应的标头文件:include/scope/scope.h中检查ScopeBase类声明(其API)颇有用。该标头文件是了解C++类的绝佳方法,由于它们的API无需其余任何实行代码便可声明,理解很是容易。

提示:若是您但愿深刻了解各类特定类,请在本教程期间查看Unity 8 Scope API参考文件

src/scope/query.cpp

链接到文件

咱们在该位置发送查询到API客户端,传输返回的结果到结果卡,声明将托管这些卡及其布局的类别。

该文件定义一个类型类unity::scopes::SearchQueryBase

该类从客户端提供的查询字符串生成搜索结果,并将其返回做为对客户端的回复:

  • 接收来自客户端的查询字符串

  • 接收来自客户端的回复对象

  • 发送查询到API客户端

  • 建立搜索结果类别(对于有不一样布局的示例:grid/carousel)

  • 将每一个搜索结果与其类别结合(建立CategorisedResult对象)

  • 推送分类结果到回复对象中,由客户端进行显示

在运行方法中完成了大量的编码规则,咱们在此处只需完成最少的变更。

检查相应标头文件:include/scope/query.h中的SearchQueryBase类声明(其API)。

src/scope/preview.cpp

链接到文件

该关键文件定义一个类型类unity::scopes::PreviewQueryBase

该类定义预览阶段每一个搜索结果使用的小工具和布局。它:

  • 定义预览中使用的小工具

  • 每一个结果中针对数据字段的Maps小工具字段

  • 定义有不一样列数的布局——取决于显示大小的不一样,仅由客户端在显示时间了解。

  • 分配小工具到每一个布局的各列

  • 接收回复对象,推送由客户端使用的小工具和布局到对象上

检查相应标头文件:include/scope/preview.h中的SearchPreviewBase类声明(其API)。

对于预览小工具列表和文档,请参阅本页

当咱们深刻了解咱们的示例Scope并详细说明一些代码,从查询开始。

查询字符串

src/scope/query.cpp中,您可轻松看到Scope的哪一个位置在接收用户查询。在Scope打开后,该查询为空白,您将为本示例提供一些数据。这是呈现特点类容或最新/流行项目的好机会。

在这里,我刚刚触发了一个有关字符串“blur cover”的搜索,该搜索将推送至API客户端,由于SoundCloud为本身的歌曲设置了美观的封面。您可能但愿看到更明确的解释,但就本示例来看,让咱们假设这是咱们用户的一个好起点。修改Query::run方法,使其如此处所示,或者只需将教程文件的内容粘贴到您本身的方法中:

60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76

void Query::run(sc::SearchReplyProxy const & reply) {

     try {

         // Start by getting information about the query

         const sc::CannedQuery &query(sc::SearchQueryBase::query());

 

         // Trim the query string of whitespace

         string query_string = alg::trim_copy(query.query_string());

 

         Client::TrackRes trackslist;

         if (query_string.empty()) {

             // If the string is empty, provide a specific one

             trackslist = client_.tracks( "blur cover" );

         } else {

             // otherwise, use the query string

             trackslist = client_.tracks(query_string);

         }

(...)

生成搜索结果

让咱们移至api/client.cpp,获取来自SoundCloud的一些结果…

net-cpp是一个咱们将用于查询API的简单联网库。可是,您能够用替换他,并使用其余任何知足您目的的联网库。模板已提供一个使用net- cpp处理HTTP标题和错误的get方法,解析回复并返回一个JSON对象,该操做很方便,将执行大多数JSON API的工做。只需粘贴教程文件中的内容到本身的方法中,或执行如下步骤,便可尝试该方法。

基础URL来自咱们的配置标头,咱们只需添加咱们的路径和参数其他部分便可:

60
61

get( { "tracks.json" }, { { "client_id" , "apigee" }, { "q" , query } }, root);

注释client_id:若是您但愿分发SoundCloudScope,您将须要在SoundCloud Developers中注册本身的API键(免费,只需花费 5分钟)。在上述例子中,我使用示例键。

而后,咱们须要迭代根JSON中显示的每一个结果,而后提取咱们须要的结果。如下是咱们的完整方法:

54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95

Client::TrackRes Client::tracks( const string& query) {

     QJsonDocument root;

 

     // Build a URI and get the contents.

     // The fist parameter forms the path part of the URI.

     // The second parameter forms the CGI parameters.

     get( { "tracks.json" }, { { "client_id" , "apigee" }, { "q" , query } }, root);

 

     // My “list of tracks” object (as seen in the corresponding header file)

     TrackRes result;

 

     QVariantList variant = root.toVariant().toList();

     for ( const QVariant &i : variant) {

         QVariantMap item = i.toMap();

         QVariantMap user = item[ "user" ].toMap();

         string art;

         // If the track artwork is empty, we use the artist picture

         if (item[ "artwork_url" ].toString().toStdString() == "" ) {

             art = user[ "avatar_url" ].toString().toStdString();

         } else {

             art = item[ "artwork_url" ].toString().toStdString();

         }

         cout << item[ "title" ].toString().toStdString();

         // We add each result to our list

         result.tracks.emplace_back(

             Track {

                 item[ "id" ].toUInt(), item[ "title" ].toString().toStdString(),

                 item[ "uri" ].toString().toStdString(), art,

                 item[ "stream_url" ].toString().toStdString(),

                 item[ "description" ].toString().toStdString(),

                 item[ "genre" ].toString().toStdString(),

                 Artist {

                     user[ "id" ].toUInt(),

                     user[ "username" ].toString().toStdString(),

                     user[ "avatar_url" ].toString().toStdString()

                 }

             }

         );

     }

     return result;

}

就是这样了。咱们已经得到所需的数据,下面将开始了解如何按照咱们喜欢的方式显示这些数据。

类别渲染器

每一个结果都须要在一个类别内显示。对于UI,一个类别可为一列结果提供一个标头标题和一个具体的布局,布局说明结果的放置方式和外观。经过粘贴教程文件的内容到本身的方法中,或执行如下步骤,尝试这一操做。

CategoryRenderer经过JSON对象建立。这些渲染器做为原始字符串建立。JSON对象有两个涉及直接兴趣的字段:模板和组件。

修改src/scope/query.cpp上的类别,使其相似与如下类别:

30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

const static string TRACKS_TEMPLATE =

     R"(

         {

             "schema-version" : 1,

             "template" : {

                 "category-layout" : "grid" ,

                 "card-layout" : "horizontal" ,

                 "card-size" : "large"

             },

             "components" : {

                 "title" : "title" ,

                 "art" : {

                     "field" : "art"

                 },

                 "subtitle" : "artist"

             }

         }

     )";

这将显示简单的结构列表,它是不少Scope中使用的类别样式,与不少不一样的内容类型都能兼容。您可查看unity::scopes::CategoryRenderer doc中的全部选项。

如今,在try{}部分/Query::run方法中,咱们能够在回复对象行登记咱们的类别:

77
78
79
80
81
82

// Register a category for tracks

auto tracks_cat = reply->register_category( "tracks" , "" , "" ,

     sc::CategoryRenderer(TRACKS_TEMPLATE));

// register_category(arbitrary category id, header title, header icon, template)

// In this case, since this is the only category used by our scope,

// it doesn’t need to display a header title, we leave it as a blank string.

结果

要使这个SoundCloudScope有用,咱们但愿每一个结果至少都拥有:

  • URI:曲目页面的连接(必要)

  • 类别:如上所示,它决定告终果在UI中的显示位置和方式(必要)

  • 标头:曲目的名称

  • 艺术家:品牌/艺术家的名称

  • 视觉:专辑/曲目封面

确保您已在类别模板组件中定义的每一个字段都在结果中显示,即便这些字段为空。无效结果将自动弃置。

仍是在src/scope/query.cpp中,在try{}部分/咱们的Query::run方法中,咱们须要迭代咱们的曲目列表,为每一个曲目建立一个unity::scope::CategorisedResult。将教程文件的类容粘贴到您本身的方法中,或复制如下行:

85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106

for ( const auto &;track : trackslist.tracks) {

 

     // Use the tracks category

     sc::CategorisedResult res(tracks_cat);

 

     // We must have a URI

     res.set_uri(track.uri);

 

     // Our result also needs a track title

     res.set_title(track.title);

 

     // Set the rest of the attributes, art, artist, etc.

     res.set_art(track.artwork_url);

     res[ "artist" ] = track.artist.username;

     res[ "stream" ] = track.stream_url;

 

     // Push the result

     if (!reply->push(res)) {

         // If we fail to push, it means the query has been cancelled.

         return ;

     }

}

如您所见,您可为某些字段使用特定方法(set_art、set_uri…),也可添加自定义字段(artist、stream、duration…)。

预览

该预览须要生成小工具,并链接其字段到CategorisedResult中的数据字段。

它还将生成处理不一样显示环境的布局。想法是仅由客户端了解布局上下文。客户端在思考布局上下文时要考虑可用的列数。对于有不一样列数的布局,Scope定义哪些列用于放入小工具。

首先,让咱们来了解一下小工具。

预览小工具

如下是一组预约义的预览小工具。每一个小工具都有一个您用于建立的输入字段。每一个小工具类型也有其余的字段,具体状况因小工具类型的不一样而变。

您可看到此处提供的预览小工具类型和字段列表。

本示例使用三种类型的预览小工具:

  • 标头:有一个标题和一个副标题字段

  • 图像:有用于检索艺术形式的源字段

  • 操做:用户单击预览时,用于提供按钮文本“Open”和已打开的URI

此处示范咱们的示例如何建立名为w_header的标头小工具(在src/scope/preview.cpp的Preview::run方法中):

40

sc::PreviewWidget w_header( "headerId" , "header" );

  • 首个参数为一个随意的ID。咱们使用这些ID分配小工具到不一样的布局,稍后将展现这一操做。

  • 第二个参数是预览小工具类型,预约义类型组中的一个类型。

在建立小工具后,小工具字段中将填入由客户端处理的CategorisedResult中的数据。咱们的w_header小工具的标准字段:标题和副标题已填充。

有两种可用的方法用于在小工具字段中填入数据:

  • add_attribute_value(FIELD, VALUE):您可以使用该方法将您手边的数据填充到小工具字段中

  • add_attribute_mapping(FIELD, CR_FIELD):使用该方法填充待处理的CategorisedResult中的数据到小工具字段中。

在咱们的示例中,小工具数据经过当前CategorisedResult提取,add_attribute_mapping也使用该方法。

首先,当咱们将w_header小工具的标题字段(第一个参数)映射到当前CategorisedResult的标题字段(第二个参数):

42

w_header.add_attribute_mapping( "title" , "title" );

接下来的示例会更有趣,由于咱们将从CategorisedResult(不属于CategoryRenderer)字段填充小工具字段。该字段为 艺术家。对于以前的每一个结果,咱们已经直接在CategorisedResult中添加艺术家键和值。所以,本示例说明当数据未在结果阶段显示并定制到 Scope时如何在预览中显示数据:

43

w_header.add_attribute_mapping( "subtitle" , "artist" );

回看查询,即建立CategorisedResults的位置,咱们再次了解艺术家数据如何在CategorisedResult提供:

84

res[ "artist" ] = track.artist.username;

所以,每一个CategorisedResult都有一个“艺术家”字段,该字段由搜索结果填充。在此预览阶段,咱们将艺术家数据推送到w_header小工具预约义的副标题字段。

教程文件的内容可粘贴到本身的方法中,以试用这些小工具。

如下是咱们的变动结果:

39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62

// Define the header section

sc::PreviewWidget w_header( "headerId" , "header" );

 

// It has title and a subtitle properties

w_header.add_attribute_mapping( "title" , "title" );

w_header.add_attribute_mapping( "subtitle" , "artist" );

 

// Define the image section

sc::PreviewWidget w_art( "imageId" , "image" );

 

// It has a single source property, mapped to the result's art property

w_art.add_attribute_mapping( "source" , "art" );

 

// Define the actions section

sc::PreviewWidget w_actions( "actionsId" , "actions" );

 

// Actions are built using tuples with an id, a label and a URI

sc::VariantBuilder builder;

builder.add_tuple({

     { "id" , sc::Variant( "open" )},

     { "label" , sc::Variant( "Open" )},

     { "uri" , result[ "uri" ]}

});

w_actions.add_attribute_value( "actions" , builder.end());

如今,它们可与回复对象一同推送到客户端:

61

reply->push( { w_art, w_header, w_actions });

小工具已建立、填充和推送。可是,客户端也须要了解放置小工具的位置,以及如何在不一样的上下文中以美观的方式安排小工具,例如,一个窄屏和一个宽屏,让咱们一块儿查看布局。

生成布局

咱们的示例定义了两个布局:一个有一列,另外一个有两列。这些布局以下所示进行声明:

27

sc::ColumnLayout layout1col(1), layout2col(2);

提示:查看ColumnLayout文档(此处)。

咱们没必要具体了解如何客户端如何使用这些布局。可是,通常的预期是,单列布局与窄屏状况相配(好比素描模式),双列布局可能与宽屏状况相配(好比景观模式)。

如今,如您在教程文件src/scope/preview.cpp中所见,咱们须要定义三个小工具在每一个布局中的放置位置。

天然状况下,在单列布局中,全部小工具必须放入单列中:

30

layout1col.add_column( { "imageId" , "headerId" , "actionsId" });

在双列布局中,咱们决定添加图像到第一列,标头和操做添加到第二列:

33
34

layout2col.add_column( { "imageId" });

layout2col.add_column( { "headerId" , "actionsId" });

如今,咱们须要在回复对象中注册布局,方法以下所示:

37

reply->;register_layout({layout1col, layout2col});

自定义和推广

默认状况下,您的Scope将以下所示:

不少显示选项均可在data/<appid>.ini中进行更改。如下是我为推广该Scope的最佳作法,大多数选项都是自明式选项:

1
2
3
4
5
6
7
8
9
10
11
12
13
14

[ScopeConfig]

DisplayName = SoundCloud

Description = This is a SoundCloud scope doing SoundCloud things

Art = screenshot.png

Author = Firstname Lastname

Icon = icon.png

 

[Appearance]

PageHeader.Logo = logo.png

PageHeader.background = color:///#FFFFFF

PageHeader.ForegroundColor = #F8500F

BackgroundColor = #FFFFFF

PageHeader.DividerColor = #F8500F

PreviewButtonColor = #F8500F

我也找到了这个SoundCloud徽标来替换模板中提供的徽标。下载它,将其保存为data/logo.png

若是您调整类别布局和颜色,您可获得差别很是大的样式。左侧的布局是使用上述代码片断生成的:

请查看全部可用的自定义选项并尝试让您的Scope美观起来!

这就是了,咱们的SoundCloudScope完成了。您可按下SDK侧栏的Start按钮启动该Scope,在编辑器的底部查看是否全部内容都已编写完成且正确启动,而后试用您的新Scope!

概述

  • 咱们已看到如何建立可查询web API的Scope

  • 查询结果将经过一个独特的渲染器放入一个类别

  • 客户端显示搜索结果

  • 对于预览阶段,使用了四个预约义的小工具类型

  • 多个布局已建立,在布局中小工具采用不一样的布置方法,以在数个外观设置中获得美观的显示效果

  • 一些仅与此Scope相配的自定义数据(例如艺术家)在预览和结果中显示

进一步了解

Scope是强大的工具,可帮助用户访问信息和选中的内容。Ubuntu已提供大量默认的Scope,但咱们老是能够建立更多的Scope!

新API的收藏夹源(书籍、电影等)转换到Scope中为ProgrammableWeb API目录,但还有其余多种不一样的源。请随意实践不一样的布局和卡,以包含不一样类型的数据!

发布Scope与发布其余应用程序彻底一致,请查看咱们的发布指南,以用数分钟的时间在店内发布您的Scope。

相关文章
相关标签/搜索