開發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。

相關文章
相關標籤/搜索