博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
开发Ubuntu Touch可用的Scope界面-快速入门
阅读量:6395 次
发布时间:2019-06-23

本文共 12588 字,大约阅读时间需要 41 分钟。

hot3.png

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

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

    注意:本教程也适用于Ubuntu 14.04及更高版本。如果您希望使用Scope布局工具,则至少要使用Ubuntu 14.10。

SDK设置

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

如果您需要获得更多有关SDK入门指南的帮助,请查看。

注意:即使您要使用平台的,您还需要了解有关Scope的另一件事:如果您在某个时刻需要使用网络,您将无法访问用户数据。这是一项合理的隐私政策,以避免在未得到明确许可的情况下提取用户数据。

测试您的Scope

在本教程的任一点,您都可按下SDK侧栏上的Play按钮来测试的Scope。等待几秒,项目的生成并上传到设备后,项目应会自动打开。

关键源文件

您可通过运行以下项目获得本教程的源代码

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

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

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 {
""
};

其他URL参数稍后将通过net-cpp库添加。

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

我们的C++标头的其余部分。如下所示,更改标头,以匹配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

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

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

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

注意:您可能会发现在相应的标头文件:include/scope/scope.h中检查ScopeBase类声明(其API)很有用。该标头文件是了解C++类的绝佳方法,因为它们的API无需其他任何实行代码即可声明,理解非常容易。

提示:如果您希望深入了解各种特定类,请在本教程期间查看。

src/scope/query.cpp

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

该文件定义一个类型类。

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

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

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

  • 发送查询到API客户端

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

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

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

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

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

src/scope/preview.cpp

该关键文件定义一个类型类。

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

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

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

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

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

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

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

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

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

查询字符串

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

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

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);

        
}

(...)

生成搜索结果

让我们移至,获取来自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,您将需要在中注册自己的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对象有两个涉及直接兴趣的字段:模板和组件。

修改上的类别,使其类似与以下类别:

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中使用的类别样式,与很多不同的内容类型都能兼容。您可查看中的所有选项。

现在,在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中的显示位置和方式(必要)

  • 标头:曲目的名称

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

  • 视觉:专辑/曲目封面

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

还是在中,在try{}部分/我们的Query::run方法中,我们需要迭代我们的曲目列表,为每个曲目创建一个。将教程文件的类容粘贴到您自己的方法中,或复制以下行:

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的标头小工具(在的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文档()。

我们不必具体了解如何客户端如何使用这些布局。但是,一般的预期是,单列布局与窄屏情况相配(比如素描模式),双列布局可能与宽屏情况相配(比如景观模式)。

现在,如您在教程文件中所见,我们需要定义三个小工具在每个布局中的放置位置。

自然情况下,在单列布局中,所有小工具必须放入单列中:

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

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

如果您调整类别布局和颜色,您可得到差异非常大的样式。左侧的布局是使用上述代码片段生成的:

请查看所有可用的并尝试让您的Scope美观起来!

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

概述

  • 我们已看到如何创建可查询web API的Scope

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

  • 客户端显示搜索结果

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

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

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

进一步了解

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

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

发布Scope与发布其他应用程序完全一致,请查看,以用数分钟的时间在店内发布您的Scope。

转载于:https://my.oschina.net/u/2306127/blog/402741

你可能感兴趣的文章
ubuntu apt-get install 出现无法定位软件包...
查看>>
centos7 下 基于docker搭建java/tomcat (方式一)
查看>>
全世界最好的编辑器VIM之Windows配置(gvim)[未测试]
查看>>
2018年你需要知道的13个JavaScript工具库
查看>>
当你点击按钮的时候如何设置其他按钮不可点击
查看>>
spring 高级装配
查看>>
【合集】parasoft Jtest 从安装到使用教程合集,收藏推荐!
查看>>
Python Pygame库的学习之路(1)
查看>>
信息安全与Linux系统
查看>>
Ubuntu安装mysql
查看>>
SpringCloud 微服务 (十四) 服务网关 Zuul 过滤器(Pre&Post)
查看>>
代理设计模式
查看>>
初识Shiro
查看>>
在Developerkit开发板上运行blink例程
查看>>
企业级性能、安全可靠 阿里云发布企业级大数据平台开发者版
查看>>
Spring Boot使用过程小记(一)--加载自定义的Spring XML
查看>>
Git分支关联远程GitHub分支出错
查看>>
设计模式--桥接模式
查看>>
linux笔记之进程信息查看工具
查看>>
django 自定义分页器
查看>>