ubuntu scope development

Post on 20-Feb-2017

418 Views

Category:

Engineering

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Scope development :)

Agenda

1 What are scopes? Terminology 5 Building blocks (back-end)

2 Scope in detail

3 Tools

4 Building blocks (front-end)

6 Hands-on

What are scopes? Terminology

What do scopes look like?

Scopes are a a UI toolkit to present local or remote content and services in the home screen

Make you content and services easy to discover:

● Scopes are easy to find and favourite as a new home screen

● Scopes dont fight for user attention, related content :○ Search for an artist in Music to see songs

in your phone, concerts near you or buy their new album

Scopes are very easy to build, the only thing you need to get started is any kind of web api.

Scopes | A New UI ParadigmA user experience where content is front and center

Scopes are fast and engaging Scopes are a fast and engaging way to embed your services in Ubuntu

Indian Today(Neaby)

Time of Indian(News)

Indian Video content(Video)

Surface you content on the homescreenDefault aggregating scopes can put your content where it matters

Relevant sources displayed together

Users can discover your service via search

The user can easily configure what to see

Scopes in detail

● A scope is a dedicated search engine that responds to queries, or surfacing about content

● A scope is passive, runs on demand only

● Produces query results● Responds to preview and/or

activation requests on results● A scope can customize visual

appearance or results (order, layout, etc.)

What is a scope really about technically?

Scopes data flow

Aggregated scope

A scope can call a scope, and a scope can aggregate data from any data sources, including other scopes

Process life cycle

● scoperegistry started by upstart, runs permanently

● scoperunners started by registry on demand

● scoperunners exit after 40 seconds of idle time

Registry

● White pages lookup service○ Provides list of all scopes (local and

remote)○ Provides metadata for each scope

(name, description, author, etc.)○ Provides invocation handle for each

scope● Starts and stops scoperunners● Monitors installation files for

added/deleted scopes

Tools

IDE● Ubuntu SDK to cross-build a scope to armhf and click package it

CLI● cmake & make● unity-scope-tool● phablet-screenshot● http://blog.csdn.net/ubuntutouch/article/details/40651093

Environment/host● Utopic 14.10, develop against latest APIs● click chroot or sbuild to cross build on your work machine● armhf device (i.e. Nexus 4, Nexus 7, Chromebook)● http://blog.csdn.net/ubuntutouch/article/details/38395635

Tools

Building blocks (front-end)

Categories

● Every result has a category● Categories group results into named groups● The shell renders categories according to a JSON

definition that controls aspects of the display● Results are rendered in the order in which they are

pushed, grouped by category. The order in which results are pushed determines the order of the categories. Every time a result with a new category is pushed, the shell creates a new group (in top-to-bottom or left-to-right order)

● The shell may choose to show categories collapsed if they contain more than one row of results (depending on the renderer)

Categories vertical templateconst static string CAT_RENDERER { R"( { "schema_version" : 1, "template" : { "category-layout" : "grid", "card-layout": "vertical", "card-size" : "large", "card-background": "#00FF00" }, "components" : { "title" : "title", "art" : "art", "subtitle": "subtitle", "mascot": "mascot", "emblem": "emblem", "summary": "summary", "overlay-color": "overlay-color", "attributes": { "field": "attributes", "max-count": 2 } } } )" };

Categories horizontal templateconst static string CAT_RENDERER { R"( { "schema_version" : 1, "template" : { "category-layout" : "grid", "card-layout": "horizontal", "card-size" : "large", "card-background": "#00FF00" }, "components" : { "title" : "title", "art" : "art", "subtitle": "subtitle", "mascot": "mascot", "emblem": "emblem", "summary": "summary", "overlay-color": "overlay-color", "attributes": { "field": "attributes", "max-count": 2 } } } )" };

Cards

Sizes● small● medium● large

Card-Layout● vertical● horizontal

Overlay

Layouts● grid● carousel● v-journal

http://goo.gl/75s3Ln - should not be treated as documentation, but a nice referencehttp://goo.gl/1VXa0l -a good tutorial for customization and branding of your scope

Building blocks | front-end

Category layouts

Widgets● audio

● video

● image

● gallery

● header

● actions

● progress

● text

● rating-input

● reviews

● table

Building blocks | front-end

http://developer.ubuntu.com/api/scopes/sdk-14.04/preview_20widget_20types/

Building blocks (back-end)

Scope source constitutes of the following classes

● scope - extends unity::scopes::ScopeBase● query - extends unity::scopes::SearchQueryBase● preview - extends unity::scopes::PreviewQueryBase

Most important classes

● CannedQuery - contains query, department id, filter state● PreviewReplyProxy - feeds back the results

Building blocks | back-end

http://developer.ubuntu.com/api/scopes/sdk-14.10/

Scope - directories

● Scope directorystd::string unity::scopes::ScopeBase::scope_directory()/opt/click.ubuntu.com/com.ubuntu.developer.liu-xiao-guo.dianping/0.1/dianping

● Scope cachestd::string unity::scopes::ScopeBase::cache_directory()/home/phablet/.local/share/unity-scopes/leaf-net/com.ubuntu.developer.liu-xiao-guo.dianping

● Scope tmp directorystd::string unity::scopes::ScopeBase::tmp_directory()/run/user/32011/scopes/leaf-net/com.ubuntu.developer.liu-xiao-guo.dianping_dianping

Scope - search metadata

class SearchMetadata : public QueryMetadata {

public:

int cardinality() const;

Location location() const;

bool has_location() const;

...

};

class QueryMetadata {

public:

std::string locale() const; // "zh_CN"

std::string form_factor() const; // "phone"

...

}

void Query::run(sc::SearchReplyProxy const& reply) { auto metadata = search_metadata();

}

● The query string may be the empty string when scope is started

● UI is asking your scope to produce default results that are shown in what is known as surfacing mode

● Show something when scope is started○ Music scope could be “Popular songs”○ Weather scope could “current location”

weather○ A developer has to decide what to show

Query - surfacing mode

Query - department scope

● A way for users to navigate the data sources/channels exposed by the scope

● A department may have optional sub-department

● Each department has an unique id to identify it

● Department id info is used to generate the request uri

● Display will be matched with the returned results

Query - department scope with filter

● A way for users to provide multiple options to select

Query - CannedQuery

class CannedQuery final {

public:

string scope_id() const;

string department_id() const;

string query_string() const;

string to_uri() const;

static CannedQuery from_uri(string const& uri);

// …

};

● CannedQuery() provides accessors for query details and to/from URI conversion (scope:// schema)

● Also provides constructor and modifiers, so you can create a query

void Query::run( sc::SearchReplyProxy const& reply ) { CannedQuery cannedQuery = query();}

Query - register categories

class SearchReply : public virtual Reply

{

public:

virtual Category::SCPtr register_category(

std::string const& id, //must be unique!

std::string const& title,

std::string const& icon,

CategoryRenderer const& renderer_template = CategoryRenderer()) = 0;

virtual bool push(CategorisedResult const& result) = 0;

// ...

};

CategoryRenderer rdrCarousel(CR_CAROUSEL_TEMPLATE);

auto carousel = reply->register_category

("dianpingcarousel", title.toStdString(), "",

rdrCarousel);

Query - push results 1/2

class Result { // abstract base class

public:

void set_uri(std::string const& uri); // mandatory

void set_title(std::string const& title);

void set_art(std::string const& image);

Variant& operator[](std::string const& key);

Variant const& operator[](std::string const& key) const;

bool contains(std::string const& key) const;

// ...

};

class CategorisedResult: public Result{public: explicit CategorisedResult(Category::SCPtr category); void set_category(Category::SCPtr category); Category::SCPtr category() const;};

Query - push results 2/2

CategorisedResult catres(carousel);

catres.set_uri(business_url.toStdString());

catres.set_dnd_uri(business_url.toStdString()); catres.set_title(name.toStdString());

catres["subtitle"] = address.toStdString();

catres["summary"] = summary.toStdString();

catres.set_art(photo_url.toStdString());

catres["address"] = Variant(address.toStdString());

catres["telephone"] = Variant(telephone.toStdString());

//push the categorized result to the client

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

break; // false from push() means search waas cancelled

}

The category order depends on the order of the pushing CategorisedResult

● Previews support 1-, 2-, and 3- column layout● The dash picks what is appropriate for the device● Each column contains one or more display widgets● Widgets are displayed in the order in which they are added to columns.● Widgets come in a number of types:

- audio, video, image, gallery, header, actions, progress, test, rating-input, reviews, expandable

- Each widget type has a number of attributes that depend on the widget type

- E.g., a text widget has a mandatory string attribute named “text” and an optional string attribute named “title”

- Valid widget types and attributes are described in the doc

Preview - layout

Preview layout (cont.)void Preview::run(unity::scopes::PreviewReplyProxy const& reply){ ColumnLayout layout1col(1), layout2col(2);

layout1col.add_column({"headerId", "artId", "infoId", "telId", "actionsId"}); layout2col.add_column({"artId", "headerId"}); layout2col.add_column({"infoId", "telId", "actionsId"});

// Push the layouts into the PreviewReplyProxy intance, thus making them // available for use in Preview diplay reply->register_layout({layout1col, layout2col});

//Create some widgets PreviewWidget w_header("headerId", "header"); w_header.add_attribute_mapping("title", "title"); … PreviewWidget w_tel("telId", "text"); w_tel.add_attribute_mapping("text", "telephone");

Result result = PreviewQueryBase::result(); QString urlString(result["uri"].get_string().c_str()); // Bundle out widgets as required into a PreviewWidgetList PreviewWidgetList widgets({w_header, w_art, w_info, w_tel, w_actions}); // And push them to the PreviewReplyProxy as needed for use in the preview reply->push(widgets);}

Preview - widget initialization

● Four ways to initialize widget attributes:○ Set attribute directly and push widget○ Push value to widget (new value

overrides any previous value and updates display)

○ Map result attributes to widget attributes○ Construct entire widget from a JSON

string

Preview - widget direct attr initialization

// Widget for “summary_wgt” of type text

PreviewWidget description(“summary_wgt”, “text”);

Variant v(“This is a description”);

Description.add_attribute_value(v);

reply->push({ description });

● Variant is a thin wrapper around boost::variant with much the same functionality

● You can update values, pushing the same attribute several times renders the last-pushed value.

Preview - widget attribute mapping // Widget of type image

PreviewWidget image(“image_wgt”, “image”);

// “art” field of result becomes “source” attribute.

image.add_attribute_mapping(“source”, “art”);

// Widget type header

PreviewWidget header(“header_wgt”, “header”);

// “title” field of result goes into title,

// “subtitle” field of result goes into subtitle.

header.add_attribute_mapping(“title”, “title”);

header.add_attribute_mapping(“subtitle”, “subtitle”);

reply->push({ image, header}); // Pushes both widgets

The first constructor argument links the widget to its column. The second constructor argument sets the type of widget (text, audio, etc.) add_attribute_mapping arranges for the specified widget attribute to be filled by the specified attribute in the result: add_attribute_mapping(widget_attr, result_attr); To pass the corresponding value to the shell for display, call push() on the PreviewReplyProxy:

Preview - pushing into mapped attribute

PreviewWidget description(“summary_wgt”, “text”);\

description.add_attribute_mapping(“title”,

“description_heading”);

description.add_attribute_mapping(“text”, “description”);

reply->push({ description });

reply->push(“description”, Variant(“replacement description”);

● Pushing an attribute directly pushes the attribute into the result, as if the attribute had been set in the result to begin with

● The attribute mapping then maps the result attribute to the widget attribute

Scope .ini file

● The .ini file for a scope must contain a [ScopeConfig] group with at least the following attributes:○ DisplayName○ Description○ Author○ DisplayName and Description can (and should) be

localized.

[ScopeConfig]DisplayName = Sports NewsDisplayName[de] = SportnachrichtenDescription = Breaking news from the world of sportsAuthor = Not MeIcon = URL for icon fileArt = URL for screen shot of the

Scope .ini file (cont.)

● The [Appearance] group of the scope’s .ini file controls basic visual settings:

● [Appearance]● ForegroundColor =● BackgroundColor =● ShapeImages = true or false● CategoryHeaderBackground = URL● PreviewButtonColor =● LogoOverlayColor =● PageHeader.Logo = URL● PageHeader.ForegroundColor =● PageHeader.Background = URL● PageHeader.DividerColor =● PageHeader.NavigationBackground = URL● Color and background specifications can be color names (white)

or URLs

http://developer.ubuntu.com/scopes/guides/scopes-customization-branding/

Scope setting

● Scopes can use persistent settings● Scope author ships a .ini settings definition

that defines which settings exist● The shell constructs a UI for the scope’s

settings● User enters/changes setting values● ScopeBase and QueryBase provide access

to currently-established settings● Very simple types: bool, string, number (int

or float), single-choice pick list

Scope setting definitions

● Setting name: “scope ini file with no extension”-settting.ini data/com.ubuntu.developer.liu-xiao-guo.dianping_dianping-settings.ini

[location]type = stringdefaultValue = LondondisplayName = Location

[distanceUnit]type = listdefaultValue = 1displayName = Distance UnitdisplayName[de] = EntfernungseinheitdisplayValues = Kilometers;MilesdisplayValues[de] = Kilometer;Meilen

[color]type = stringdisplayName = Color

Scope setting access

The settings() method on QueryBase and ScopeBase returns a dictionary of string-Variant pairs.

// In your ScopeBase or QueryBase implementation:

// (The settings method is provided by the base class.)

unity::scopes::VariantMap s = settings();

// Prints "London" unless the user changed the value

cout << s.at("location”).get_string();

try {

cout << s.at("color").get_string() << endl;

} catch (std::out_of_range) {

// Color not set, no default value defined.

}

Adding keyword to your scopes (1/2)

Adding keywords to your scope in order to improve its discoverability in these searches- Users searching for scopes on the store- Aggregators looking for child scopes

[ScopeConfig]

DisplayName = scopename

Description = description of scope

Author = author

Keywords = videos;

https://developer.ubuntu.com/en/scopes/tutorials/scope-keywords/http://summit.ubuntu.com/uos-1505/meeting/22460/scopes-keywords/

Adding keyword to your scopes (2/2)

● Ubuntu Developer http://developer.ubuntu.com/- Scope http://developer.ubuntu.com/scopes/overview/

● Touch - Ubuntu Wiki https://wiki.ubuntu.com/Touch- Touch/Porting https://wiki.ubuntu.com/Touch/Porting- Touch/Deploying https://wiki.ubuntu.com/Touch/Deploying

● Ubuntu Phone - https://lists.launchpad.net/ubuntu-phone/● Tech support - http://askubuntu.com/● Publish your app: http://developer.ubuntu.com/publish/● Department scope example: http://goo.gl/o5XZxf● Scope tutorials: http://developer.ubuntu.com/scopes/tutorials/● Scopes:https://developer.ubuntu.com/api/scopes/cpp/development/

Hands-on

Questions?

IRC: liuxg | mail: xiaoguo.liu@canonical.com

top related