5. 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 Paradigm
A user experience where content is front and center
6. 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)
7. Surface you content on the homescreen
Default 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
9. ● 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?
11. Aggregated scope
A scope can call a scope, and a scope can aggregate data from any data
sources, including other scopes
12. Process life cycle
● scoperegistry started by upstart, runs
permanently
● scoperunners started by registry on
demand
● scoperunners exit after 40 seconds of idle
time
13. 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
15. 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
17. 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)
20. 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 reference
http://goo.gl/1VXa0l -a good tutorial for customization and branding of your scope
Building blocks | front-end
24. 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/
26. 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();
}
27. ● 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
28. 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
29. Query - department scope with filter
● A way for users to provide multiple options to select
30. 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();
}
32. 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;
};
33. 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
34. ● 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
35. 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);
}
36. 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
37. 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.
38. 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:
39. 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
40. 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 News
DisplayName[de] = Sportnachrichten
Description = Breaking news from the world of sports
Author = Not Me
Icon = URL for icon file
Art = URL for screen shot of the
41. 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/
42. 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
43. 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 = string
defaultValue = London
displayName = Location
[distanceUnit]
type = list
defaultValue = 1
displayName = Distance Unit
displayName[de] = Entfernungseinheit
displayValues = Kilometers;Miles
displayValues[de] = Kilometer;Meilen
[color]
type = string
displayName = Color
44. 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.
}
如
45. 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/