/usr/share/doc/unity-scopes-api/index.html is in libunity-scopes-doc 0.4.2+14.04.20140408-0ubuntu1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 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 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen 1.8.6"/>
<title>Unity Scopes API: Main Page</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="dynsections.js"></script>
<link href="navtree.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="resize.js"></script>
<script type="text/javascript" src="navtree.js"></script>
<script type="text/javascript">
$(document).ready(initResizable);
$(window).load(resizeHeight);
</script>
<link href="search/search.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="search/search.js"></script>
<script type="text/javascript">
$(document).ready(function() { searchBox.OnSelectItem(0); });
</script>
<link href="doxygen.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 56px;">
<td style="padding-left: 0.5em;">
<div id="projectname">Unity Scopes API
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- end header part -->
<!-- Generated by Doxygen 1.8.6 -->
<script type="text/javascript">
var searchBox = new SearchBox("searchBox", "search",false,'Search');
</script>
<div id="navrow1" class="tabs">
<ul class="tablist">
<li class="current"><a href="index.html"><span>Main Page</span></a></li>
<li><a href="pages.html"><span>Related Pages</span></a></li>
<li><a href="namespaces.html"><span>Namespaces</span></a></li>
<li><a href="annotated.html"><span>Classes</span></a></li>
<li><a href="files.html"><span>Files</span></a></li>
<li>
<div id="MSearchBox" class="MSearchBoxInactive">
<span class="left">
<img id="MSearchSelect" src="search/mag_sel.png"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
alt=""/>
<input type="text" id="MSearchField" value="Search" accesskey="S"
onfocus="searchBox.OnSearchFieldFocus(true)"
onblur="searchBox.OnSearchFieldFocus(false)"
onkeyup="searchBox.OnSearchFieldChange(event)"/>
</span><span class="right">
<a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
</span>
</div>
</li>
</ul>
</div>
</div><!-- top -->
<div id="side-nav" class="ui-resizable side-nav-resizable">
<div id="nav-tree">
<div id="nav-tree-contents">
<div id="nav-sync" class="sync"></div>
</div>
</div>
<div id="splitbar" style="-moz-user-select:none;"
class="ui-resizable-handle">
</div>
</div>
<script type="text/javascript">
$(document).ready(function(){initNavTree('index.html','');});
</script>
<div id="doc-content">
<!-- window showing the filter options -->
<div id="MSearchSelectWindow"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
onkeydown="return searchBox.OnSearchSelectKey(event)">
<a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(0)"><span class="SelectionMark"> </span>All</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(1)"><span class="SelectionMark"> </span>Classes</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(2)"><span class="SelectionMark"> </span>Namespaces</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(3)"><span class="SelectionMark"> </span>Files</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(4)"><span class="SelectionMark"> </span>Functions</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(5)"><span class="SelectionMark"> </span>Variables</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(6)"><span class="SelectionMark"> </span>Typedefs</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(7)"><span class="SelectionMark"> </span>Enumerations</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(8)"><span class="SelectionMark"> </span>Enumerator</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(9)"><span class="SelectionMark"> </span>Macros</a><a class="SelectItem" href="javascript:void(0)" onclick="searchBox.OnSelectItem(10)"><span class="SelectionMark"> </span>Pages</a></div>
<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<iframe src="javascript:void(0)" frameborder="0"
name="MSearchResults" id="MSearchResults">
</iframe>
</div>
<div class="contents">
<div class="textblock"><h1><a class="anchor" id="overview"></a>
What are scopes</h1>
<h2><a class="anchor" id="intro"></a>
Introduction</h2>
<p>One of Unity’s core features on the desktop is the Dash. The Dash allows users to search for and discover virtually anything from local files and applications, to web content and other online data. The Dash achieves this by interfacing with one or more search plug-ins called “scopes” (e.g. “Apps”, “Music”, “Videos”, or “Amazon”, “Wikipedia”, “Youtube”).</p>
<p>On the phone and tablet, scopes make up the central user interface, as they provide everything a user needs from an operating system. Scopes enable users to locate and launch applications, access local files, play music and videos, search the web, manage their favourite social network, keep up with the latest news, and much more.</p>
<p>Each scope is a dedicated search engine for the category / data source it represents. The data source could be a local database, a web service, or even an aggregation of other scopes (e.g. the “Music” scope aggregates “Local Music” and “Online Music” scopes). A scope is primarily responsible for performing the actual search logic and returning the best possible results for each query it receives.</p>
<p>This document describes how to implement, test and package your own scope using the Unity Scopes C++ API (unity-scopes-api).</p>
<h2><a class="anchor" id="LvsR"></a>
Local vs remote scopes</h2>
<p>Local scopes are scopes that are located and run on the user’s device, while remote scopes (or “Smart Scopes”) are scopes that are located and run remotely on the Ubuntu Smart Scopes Server (or “SSS”). (Note: Although local scopes execute as local processes, they may still query online services in order to retrieve search results). A local scope usually requires local data, and therefore, can only be run locally, while a remote scope can effectively be run both locally and remotely.</p>
<p>When deciding on whether to write a local or remote scope, keep the user’s privacy in mind. For security reasons, a scope should not access the user’s personal data unless absolutely necessary (i.e. The scope requires account information or local data to perform searches). It is only in these situations that a scope should be written to run locally. <b>By default, a scope should be written with the intention of running remotely on the Smart Scopes Server.</b></p>
<p>(For more information on how to deploy your scope to the Smart Scopes Server, or how to implement a native remote scope using the SSS REST API see: <em>link_not_yet_available</em>)</p>
<h1><a class="anchor" id="develop"></a>
Developing scopes</h1>
<h2><a class="anchor" id="starting"></a>
Getting started</h2>
<p>A simple C++ scope template with cmake build system is currently available on Launchpad, to use it install the packages required for scope development:</p>
<pre class="fragment">sudo apt-get install libunity-scopes-dev
</pre><p>Clone the bazaar branch with the scope template and build the scope:</p>
<pre class="fragment">bzr branch lp:~jpakkane/+junk/scopetemplate
cd scopetemplate
mkdir build
cd build
cmake ..
make
</pre><p>Now you're ready to explore and modify the sample code in the src/ directory.</p>
<h2><a class="anchor" id="impl"></a>
Implementing scope</h2>
<p>This short tutorial covers the basic steps and building blocks needed for implementing your own scope with unity-scopes-api, using C++. For complete examples of various scopes see demo/scopes subdirectory of the unity-scopes-api source project.</p>
<p>A typical scope implementation needs to implement interfaces of the following classes from the Scopes API: </p>
<ul>
<li>
<a class="el" href="classunity_1_1scopes_1_1_scope_base.html" title="Base class for a scope implementation. ">unity::scopes::ScopeBase</a> - the main scope class and an entry point for all incoming requests. </li>
<li>
<a class="el" href="classunity_1_1scopes_1_1_search_query_base.html" title="Abstract base class to represent a particular query. ">unity::scopes::SearchQueryBase</a> - the handler of search request. </li>
<li>
<a class="el" href="classunity_1_1scopes_1_1_preview_query_base.html" title="Abstract base class to represent a particular preview. ">unity::scopes::PreviewQueryBase</a> - the handler of preview requests (only if handling previews). </li>
<li>
<a class="el" href="classunity_1_1scopes_1_1_activation_query_base.html" title="Base class for an activation request that is executed inside a scope. ">unity::scopes::ActivationQueryBase</a> - the handler of activation and preview action requests (only if handling previews and activation) </li>
<li>
<a class="el" href="classunity_1_1scopes_1_1_search_listener_base.html" title="Abstract base interface that a scope specializes to receive the results of a query. ">unity::scopes::SearchListenerBase</a> - the handler of search replies (only in aggreagator scopes, to pull results from other scopes) </li>
</ul>
<p>The following sections show them in more detail.</p>
<h3><a class="anchor" id="simplescope"></a>
Case 1: A simple scope which doesn't query other scopes.</h3>
<p>This is the typical case: a scope that connects to a remote or local backend, database etc. and provides results in response to search queries coming from a client (i.e. Unity Dash or another scope).</p>
<h4><a class="anchor" id="scopebase"></a>
Create a scope class that implements ScopeBase inteface.</h4>
<p>There are a few pure virtual methods that need to be implemented; at the very minimum you need to provide a non-empty implementation of <a class="el" href="classunity_1_1scopes_1_1_scope_base.html#add7f4b1811774a63cd474ccab702acdf">start</a> and <a class="el" href="classunity_1_1scopes_1_1_scope_base.html#a0e4969ff26dc1d396d74c56d896fd564" title="Called by the scopes run time when a scope needs to instantiate a query. ">unity::scopes::ScopeBase::search()</a> and <a class="el" href="classunity_1_1scopes_1_1_scope_base.html#a154b9b4cfc0f40572cfec60dd819396f" title="Invoked when a scope is requested to create a preview for a particular result. ">unity::scopes::ScopeBase::preview()</a> methods.</p>
<div class="fragment"><div class="line"><span class="keyword">class </span>MyScope: <span class="keyword">public</span> <a class="code" href="classunity_1_1scopes_1_1_scope_base.html">unity::scopes::ScopeBase</a></div>
<div class="line">{</div>
<div class="line"><span class="keyword">public</span>:</div>
<div class="line"> <span class="keyword">virtual</span> <span class="keywordtype">int</span> <a class="code" href="classunity_1_1scopes_1_1_scope_base.html#add7f4b1811774a63cd474ccab702acdf">start</a>(std::string <span class="keyword">const</span>&, <a class="code" href="namespaceunity_1_1scopes.html#a45babc254d3548863d79ee54f266e84d">unity::scopes::RegistryProxy</a> <span class="keyword">const</span>&) <span class="keyword">override</span>;</div>
<div class="line"> <span class="keyword">virtual</span> <span class="keywordtype">void</span> <a class="code" href="classunity_1_1scopes_1_1_scope_base.html#ad55d5cb31003751b49783799d4d80c09">stop</a>() <span class="keyword">override</span>;</div>
<div class="line"> <span class="keyword">virtual</span> <span class="keywordtype">void</span> <a class="code" href="classunity_1_1scopes_1_1_scope_base.html#a386e99b98318a70f25db84bbe11c0292">run</a>() <span class="keyword">override</span>;</div>
<div class="line"> <span class="keyword">virtual</span> unity::scopes::SearchQueryBase::UPtr <a class="code" href="classunity_1_1scopes_1_1_scope_base.html#a0e4969ff26dc1d396d74c56d896fd564">search</a>(CannedQuery <span class="keyword">const</span>& query, SearchMetadata <span class="keyword">const</span>& metadata) <span class="keyword">override</span>;</div>
<div class="line"> <span class="keyword">virtual</span> unity::scopes::PreviewQueryBase::UPtr <a class="code" href="classunity_1_1scopes_1_1_scope_base.html#a154b9b4cfc0f40572cfec60dd819396f">preview</a>(<a class="code" href="classunity_1_1scopes_1_1_result.html">unity::scopes::Result</a> <span class="keyword">const</span>& result, <a class="code" href="classunity_1_1scopes_1_1_action_metadata.html">unity::scopes::ActionMetadata</a> <span class="keyword">const</span>& metadata) <span class="keyword">override</span>;</div>
<div class="line">}</div>
</div><!-- fragment --><p>The <a class="el" href="classunity_1_1scopes_1_1_scope_base.html#add7f4b1811774a63cd474ccab702acdf">start</a> method must, at the very least return <a class="el" href="classunity_1_1scopes_1_1_scope_base.html#a7ef008b8ec7ae3bdab9a1b5dbc2a0dd0" title="This value must be returned from the start() method. ">unity::scopes::ScopeBase::VERSION</a>, e.g.</p>
<div class="fragment"><div class="line"><span class="keywordtype">int</span> MyScope::start(<span class="keywordtype">string</span> <span class="keyword">const</span>&, <a class="code" href="namespaceunity_1_1scopes.html#a45babc254d3548863d79ee54f266e84d">unity::scopes::RegistryProxy</a> <span class="keyword">const</span>&)</div>
<div class="line">{</div>
<div class="line"> <span class="keywordflow">return</span> ScopeBase::VERSION;</div>
<div class="line">}</div>
</div><!-- fragment --><p>The <a class="el" href="classunity_1_1scopes_1_1_scope_base.html#ad55d5cb31003751b49783799d4d80c09">stop</a> method should release any resources, such as network connections where applicable. See the documentation of <a class="el" href="classunity_1_1scopes_1_1_scope_base.html">ScopeBase</a> for an explanation of when <a class="el" href="classunity_1_1scopes_1_1_scope_base.html#a386e99b98318a70f25db84bbe11c0292">ScopeBase::run</a>; is useful; for typical and simple cases the implementation of run can be an empty function.</p>
<h4><a class="anchor" id="handlingsearch"></a>
Handling search</h4>
<p>The <a class="el" href="classunity_1_1scopes_1_1_scope_base.html#a0e4969ff26dc1d396d74c56d896fd564" title="Called by the scopes run time when a scope needs to instantiate a query. ">unity::scopes::ScopeBase::search()</a> method of scope implementation is the entry point of every search - it receives search queries from the Dash or other scopes. This method must return an instance of an object that implements <a class="el" href="classunity_1_1scopes_1_1_search_query_base.html" title="Abstract base class to represent a particular query. ">unity::scopes::SearchQueryBase</a> interface, e.g:</p>
<div class="fragment"><div class="line">SearchQueryBase::UPtr MyScope::search(CannedQuery <span class="keyword">const</span>& query, SearchMetadata <span class="keyword">const</span>& metadata)</div>
<div class="line">{</div>
<div class="line"> SearchQueryBase::UPtr q(<span class="keyword">new</span> MyQuery(query));</div>
<div class="line"> <span class="keywordflow">return</span> q;</div>
<div class="line">}</div>
</div><!-- fragment --><p>The search() method receives two arguments: a <a class="el" href="classunity_1_1scopes_1_1_canned_query.html" title="Parameters of a search query. ">unity::scopes::CannedQuery</a> query object that carries actual query string (among other information) and additional parameters of the search request, stored in <a class="el" href="classunity_1_1scopes_1_1_search_metadata.html" title="Metadata passed with search requests. ">unity::scopes::SearchMetadata</a> - such as locale string, form factor string and cardinality. Cardinality is the maximum number of results expected from the scope (the value of 0 should be treated as if no limit was set). For optimal performance scopes should provide no more results than requested; if they however fail to handle cardinality constraint, any excessive results will be ignored by scopes API.</p>
<h4><a class="anchor" id="querybase"></a>
Create a query class that implements SearchQueryBase interface.</h4>
<p>The central and most important method that needs to be implemented in this interface is <a class="el" href="classunity_1_1scopes_1_1_search_query_base.html#afc4f15b2266838d7da75b05ea37d504b" title="Called by scopes run time to start the query. ">unity::scopes::SearchQueryBase::run()</a>. This is where actual processing of current search query takes place, and this is the spot where you may want to query local or remote data source for results matching the query.</p>
<p>The <a class="el" href="classunity_1_1scopes_1_1_search_query_base.html#afc4f15b2266838d7da75b05ea37d504b" title="Called by scopes run time to start the query. ">unity::scopes::SearchQueryBase::run()</a> method gets passed an instance of SearchReplyProxy, which represents a receiver of query results. Please note that SearchReplyProxy is just a shared pointer for <a class="el" href="classunity_1_1scopes_1_1_search_reply.html">SearchReply</a> object. The two most important methods of SearchReply object that every scope have to use are <a class="el" href="classunity_1_1scopes_1_1_search_reply.html#aaa061806a96f50ff66abc6184135ea66">register_category</a> and <a class="el" href="classunity_1_1scopes_1_1_search_reply.html#a63d6de93152b3a972901c2d406ef5760">push</a>.</p>
<p>The <a class="el" href="classunity_1_1scopes_1_1_search_reply.html#aaa061806a96f50ff66abc6184135ea66">register_category</a> method is a factory method for creating new categories (see <a class="el" href="classunity_1_1scopes_1_1_category.html">unity::scopes::Category</a>). Categories can be created at any point during query processing inside run method, but it's recommended to create them as soon as possible (ideally as soon as they are known to the scope).</p>
<p>When creating a category, one of its parameters is a <a class="el" href="classunity_1_1scopes_1_1_category_renderer.html">unity::scopes::CategoryRenderer</a> instance, which specifies how will a particular category be rendered. See the <a class="el" href="classunity_1_1scopes_1_1_category_renderer.html" title="A category renderer template in JSON format. ">unity::scopes::CategoryRenderer</a> documentation for more on that subject.</p>
<p>The actual search results have to be wrapped inside <a class="el" href="classunity_1_1scopes_1_1_categorised_result.html">CategorisedResult</a> objects and passed to <a class="el" href="classunity_1_1scopes_1_1_search_reply.html#a63d6de93152b3a972901c2d406ef5760">push</a>.</p>
<p>A typical implementation of run may look like this:</p>
<div class="fragment"><div class="line"><span class="keywordtype">void</span> MyQuery::run(SearchReplyProxy <span class="keyword">const</span>& reply)</div>
<div class="line">{</div>
<div class="line"> <span class="keyword">auto</span> category = reply->register_category(<span class="stringliteral">"recommended"</span>, <span class="stringliteral">"Recommended"</span>, icon);</div>
<div class="line"> <span class="comment">//... query a local or remote backend</span></div>
<div class="line"></div>
<div class="line"> <span class="keywordflow">for</span> (<span class="keyword">auto</span> res: backend.get_results(search_query)) <span class="comment">// for every result returned by a backend</span></div>
<div class="line"> {</div>
<div class="line"> ...</div>
<div class="line"> CategorisedResult result(category); <span class="comment">// create a result item in "recommended" category</span></div>
<div class="line"> result.set_uri(...);</div>
<div class="line"> result.set_title(...);</div>
<div class="line"> result.set_art(...);</div>
<div class="line"> result.set_dnd_uri(...);</div>
<div class="line"> result[<span class="stringliteral">"my-custom-attribute"</span>] = Variant(...); <span class="comment">// add arbitrary data as needed</span></div>
<div class="line"></div>
<div class="line"> <span class="keywordflow">if</span> (!reply->push(result)) <span class="comment">// send result to the client</span></div>
<div class="line"> {</div>
<div class="line"> <span class="keywordflow">break</span>; <span class="comment">// false from push() means search was cancelled</span></div>
<div class="line"> }</div>
<div class="line"> }</div>
<div class="line">}</div>
</div><!-- fragment --><h4><a class="anchor" id="handlingpreview"></a>
Handling previews</h4>
<p>Scopes are responsible for handling preview requests for results they created; this needs to be implemented by overriding <a class="el" href="classunity_1_1scopes_1_1_scope_base.html#a154b9b4cfc0f40572cfec60dd819396f" title="Invoked when a scope is requested to create a preview for a particular result. ">unity::scopes::ScopeBase::preview()</a> method:</p>
<div class="fragment"><div class="line"><span class="keyword">class </span>MyScope: <span class="keyword">public</span> <a class="code" href="classunity_1_1scopes_1_1_scope_base.html">unity::scopes::ScopeBase</a></div>
<div class="line">{</div>
<div class="line"><span class="keyword">public</span>:</div>
<div class="line"> ...</div>
<div class="line"> <span class="keyword">virtual</span> unity::scopes::PreviewQueryBase::UPtr <a class="code" href="classunity_1_1scopes_1_1_scope_base.html#a154b9b4cfc0f40572cfec60dd819396f">preview</a>(unitu::scopes::Result <span class="keyword">const</span>& result, <a class="code" href="classunity_1_1scopes_1_1_action_metadata.html">unity::scopes::ActionMetadata</a> <span class="keyword">const</span>& metadata) <span class="keyword">override</span>;</div>
<div class="line"> ...</div>
<div class="line">}</div>
</div><!-- fragment --><p>This method must return an instance derived from <a class="el" href="classunity_1_1scopes_1_1_preview_query_base.html" title="Abstract base class to represent a particular preview. ">unity::scopes::PreviewQueryBase</a>. The implementation of <a class="el" href="classunity_1_1scopes_1_1_preview_query_base.html" title="Abstract base class to represent a particular preview. ">unity::scopes::PreviewQueryBase</a> interface is similar to <a class="el" href="classunity_1_1scopes_1_1_search_query_base.html" title="Abstract base class to represent a particular query. ">unity::scopes::SearchQueryBase</a> in that its central method is <a class="el" href="classunity_1_1scopes_1_1_preview_query_base.html#a81b89daf29cd1ada55286f2a3a871347" title="Called by scopes run time to start the preview. ">unity::scopes::PreviewQueryBase::run()</a>. This method is responsible for gathering preview data (from local or remote sources) and passing it along with the definition of preview look to <a class="el" href="namespaceunity_1_1scopes.html#a7b46ef0e880da4c75314fe60bdd55754" title="Convenience type definition. ">unity::scopes::PreviewReplyProxy</a> (this is a pointer to unity::scopes::PreviewReplyBasel; the run() method receives a pointer to an instance of <a class="el" href="classunity_1_1scopes_1_1_preview_reply.html" title="Allows the results of a preview to be sent to the preview requester. ">unity::scopes::PreviewReply</a>).</p>
<p>A preview consists of one or more preview widgets - these are the basic building blocks for previews, such as a header with a title and subtitle, an image, a gallery with multiple images, a list of audio tracks etc.; see <a class="el" href="classunity_1_1scopes_1_1_preview_widget.html" title="A widget for a preview. ">unity::scopes::PreviewWidget</a> for a detailed documentation and a list of supported widget types. So, the implementation of <a class="el" href="classunity_1_1scopes_1_1_preview_query_base.html#a81b89daf29cd1ada55286f2a3a871347" title="Called by scopes run time to start the preview. ">unity::scopes::PreviewQueryBase::run()</a> needs to create and populate one or more instances of <a class="el" href="classunity_1_1scopes_1_1_preview_widget.html" title="A widget for a preview. ">unity::scopes::PreviewWidget</a> and push them to the client with <a class="el" href="classunity_1_1scopes_1_1_preview_reply.html#a9fc593618b83ec444fb6c9b2b298764a" title="Sends widget definitions to the sender of the preview query. ">unity::scopes::PreviewReply::push()</a>.</p>
<p>Every <a class="el" href="classunity_1_1scopes_1_1_preview_widget.html" title="A widget for a preview. ">unity::scopes::PreviewWidget</a> has a unique identifier, a type name and a set of attributes determined by its type. For example, a widget of "image" type expects two attributes: "source", which should point to an image (an uri) and "zoomable" boolean flag, which determines if the image should be zoomable. Values of such attributes can either be specified directly, or they can reference values present already in the <a class="el" href="classunity_1_1scopes_1_1_result.html" title="The attributes of a result returned by a Scope. ">unity::scopes::Result</a> instance, or pushed spearately during the execution of <a class="el" href="classunity_1_1scopes_1_1_preview_query_base.html#a81b89daf29cd1ada55286f2a3a871347" title="Called by scopes run time to start the preview. ">unity::scopes::PreviewQueryBase::run()</a>.</p>
<p>Attributes can be specified directly with <a class="el" href="classunity_1_1scopes_1_1_preview_widget.html#a42dd64704890d72bcc6ecbd7bccbfcd9" title="Adds an attribute definition and its value. ">unity::scopes::PreviewWidget::add_attribute_value()</a> method, e.g:</p>
<div class="fragment"><div class="line">PreviewWidget image_widget(<span class="stringliteral">"myimage"</span>, <span class="stringliteral">"image"</span>);</div>
<div class="line"></div>
<div class="line">image_widget.add_attribute_value(<span class="stringliteral">"source"</span>, Variant(<span class="stringliteral">"file:///tmp/image.jpg"</span>));</div>
<div class="line">image_widget.add_attribute_value(<span class="stringliteral">"zoomable"</span>, Variant(<span class="keyword">false</span>));</div>
</div><!-- fragment --><p>To reference values from results or arbitrary values pushed separately, use <a class="el" href="classunity_1_1scopes_1_1_preview_widget.html#a8bb890267a69dd6bb5ca70b663c75e74" title="Adds an attribute definition using a component mapping. ">unity::scopes::PreviewWidget::add_attribute_mapping()</a> method:</p>
<div class="fragment"><div class="line">PreviewWidget image_widget(<span class="stringliteral">"myimage"</span>, <span class="stringliteral">"image"</span>);</div>
<div class="line">image_widget.add_attribute_mapping(<span class="stringliteral">"source"</span>, <span class="stringliteral">"art"</span>); <span class="comment">// use 'art' attribute from the result</span></div>
<div class="line">image_widget.add_attribute_mapping(<span class="stringliteral">"zoomable"</span>, <span class="stringliteral">"myzoomable"</span>); <span class="comment">// 'myzoomable' not specified, but pushed below</span></div>
<div class="line">reply->push(<span class="stringliteral">"myzoomable"</span>, Variant(<span class="keyword">true</span>));</div>
</div><!-- fragment --><p>To push preview widgets to the client, use <a class="el" href="classunity_1_1scopes_1_1_preview_reply.html#a9fc593618b83ec444fb6c9b2b298764a" title="Sends widget definitions to the sender of the preview query. ">unity::scopes::PreviewReply::push()</a>:</p>
<div class="fragment"><div class="line">PreviewWidget image_widget(<span class="stringliteral">"myimage"</span>, <span class="stringliteral">"image"</span>);</div>
<div class="line">PreviewWidget header_widget(<span class="stringliteral">"myheader"</span>, <span class="stringliteral">"header"</span>);</div>
<div class="line"><span class="comment">// fill in widget attributes</span></div>
<div class="line">...</div>
<div class="line">PreviewWidgetList widgets { image_widget, header_widget };</div>
<div class="line">reply->push(widgets);</div>
</div><!-- fragment --><h4><a class="anchor" id="previewactions"></a>
Preview actions</h4>
<p>Previews can have actions (i.e. buttons) that user can activate - they are supported by <a class="el" href="classunity_1_1scopes_1_1_preview_widget.html" title="A widget for a preview. ">unity::scopes::PreviewWidget</a> of "actions" type. This type of widget takes one or more action button definitions, where every button is constituted by an unique identifier, a label and an optional icon. For example, a widget with two buttons: "Open" and "Download" can be defined as follows (using <a class="el" href="classunity_1_1scopes_1_1_variant_builder.html" title="Helper class for creating and populating Variant containers. ">unity::scopes::VariantBuilder</a> helper class):</p>
<div class="fragment"><div class="line">PreviewWidget buttons(<span class="stringliteral">"mybuttons"</span>, <span class="stringliteral">"actions"</span>);</div>
<div class="line"></div>
<div class="line">VariantBuilder builder;</div>
<div class="line">builder.add_tuple({</div>
<div class="line"> {<span class="stringliteral">"id"</span>, Variant(<span class="stringliteral">"open"</span>)},</div>
<div class="line"> {<span class="stringliteral">"label"</span>, Variant(<span class="stringliteral">"Open"</span>)}</div>
<div class="line">});</div>
<div class="line">builder.add_tuple({</div>
<div class="line"> {<span class="stringliteral">"id"</span>, Variant(<span class="stringliteral">"download"</span>)},</div>
<div class="line"> {<span class="stringliteral">"label"</span>, Variant(<span class="stringliteral">"Download"</span>)}</div>
<div class="line">});</div>
<div class="line"></div>
<div class="line">buttons.add_attribute_value(<span class="stringliteral">"actions"</span>, builder.end());</div>
</div><!-- fragment --><p>To handle activation of preview actions, scope needs to implement the following method of <a class="el" href="classunity_1_1scopes_1_1_scope_base.html" title="Base class for a scope implementation. ">unity::scopes::ScopeBase</a>:</p>
<div class="fragment"><div class="line"><span class="keyword">class </span>MyScope: <span class="keyword">public</span> <a class="code" href="classunity_1_1scopes_1_1_scope_base.html">unity::scopes::ScopeBase</a></div>
<div class="line">{</div>
<div class="line"> <span class="keyword">virtual</span> ActivationQueryBase::UPtr <a class="code" href="classunity_1_1scopes_1_1_scope_base.html#a2f4d476fa790349c9a7de52be3232d11">perform_action</a>(Result <span class="keyword">const</span>& result, ActionMetadata <span class="keyword">const</span>& metadata, std::string <span class="keyword">const</span>& widget_id, std::string <span class="keyword">const</span>& action_id) <span class="keyword">override</span></div>
<div class="line"> ...</div>
<div class="line">}</div>
</div><!-- fragment --><p>This method receives a widget identifier and action identifier that was activated. This method needs to return an instance derived from <a class="el" href="classunity_1_1scopes_1_1_activation_query_base.html" title="Base class for an activation request that is executed inside a scope. ">unity::scopes::ActivationQueryBase</a>. The derived class needs to reimplement <a class="el" href="classunity_1_1scopes_1_1_activation_query_base.html#a61ed49d8bc56e677ff2eb1f30e6a6b6b" title="Return response to the activation request. ">unity::scopes::ActivationQueryBase::activate()</a> method and put any activation logic in there. This method needs to respond with an instance of <a class="el" href="classunity_1_1scopes_1_1_activation_response.html" title="Response to a result activation. ">unity::scopes::ActivationResponse</a>, which informs the shell about status of activation and the expected behaviour of the UI. For example, activate() may request a new search query to be executed as follows:</p>
<div class="fragment"><div class="line"><span class="keyword">class </span>MyActivation : <span class="keyword">public</span> <a class="code" href="classunity_1_1scopes_1_1_activation_query_base.html">unity::scopes::ActivationQueryBase</a></div>
<div class="line">{</div>
<div class="line"> <span class="keyword">virtual</span> <a class="code" href="classunity_1_1scopes_1_1_activation_response.html">unity::scopes::ActivationResponse</a> <a class="code" href="classunity_1_1scopes_1_1_activation_query_base.html#a61ed49d8bc56e677ff2eb1f30e6a6b6b">activate</a>()<span class="keyword"> override</span></div>
<div class="line"><span class="keyword"> </span>{</div>
<div class="line"> ...</div>
<div class="line"> <span class="keywordflow">if</span> (action_id == <span class="stringliteral">"search-grooveshark"</span>)</div>
<div class="line"> {</div>
<div class="line"> CannedQuery query(<span class="stringliteral">"com.canonical.scopes.grooveshark"</span>);</div>
<div class="line"> query.set_query_string(<span class="stringliteral">"metal"</span>);</div>
<div class="line"> <span class="keywordflow">return</span> <a class="code" href="classunity_1_1scopes_1_1_activation_response.html">unity::scopes::ActivationResponse</a>(query);</div>
<div class="line"> }</div>
<div class="line"> ...</div>
<div class="line"> }</div>
<div class="line">}</div>
</div><!-- fragment --><h4><a class="anchor" id="handlingactivation"></a>
Handling result activation</h4>
<p>In many cases search results can be activated (i.e. when user taps or clicks them) directly by the shell - as long as a desktop schema (such as "http://") of result's uri has a handler in the system. If this is the case, then there is nothing to do in terms of activation handling in the scope code. If however a scope relies on a schema handler that's not present in the system, the offending result will be ignored by Unity shell and nothing will happen on activation.</p>
<p>In cases where scope wants to intercept and handle activation request (e.g. when no handler for specifc type of uri exists, or to do some extra work on activation), it has to reimplement <a class="el" href="classunity_1_1scopes_1_1_scope_base.html#a49a0b9ada0eeb4c71e6a2181c3d8c9e7" title="Called by the scopes run time when a scope needs to respond to a result activation request...">unity::scopes::ScopeBase::activate()</a> method:</p>
<div class="fragment"><div class="line"><span class="keyword">class </span>MyScope : <span class="keyword">public</span> ScopeBase</div>
<div class="line">{</div>
<div class="line"> <span class="keyword">virtual</span> ActivationQueryBase::UPtr activate(Result <span class="keyword">const</span>& result, ActionMetadata <span class="keyword">const</span>& metadata) <span class="keyword">override</span>;</div>
<div class="line"> ...</div>
<div class="line">}</div>
</div><!-- fragment --><p>and also call Result::set_intercept_activation() for all results that should trigger <a class="el" href="classunity_1_1scopes_1_1_scope_base.html#a49a0b9ada0eeb4c71e6a2181c3d8c9e7" title="Called by the scopes run time when a scope needs to respond to a result activation request...">unity::scopes::ScopeBase::activate()</a> on activation. The implementation of <a class="el" href="classunity_1_1scopes_1_1_scope_base.html#a49a0b9ada0eeb4c71e6a2181c3d8c9e7" title="Called by the scopes run time when a scope needs to respond to a result activation request...">unity::scopes::ScopeBase::activate()</a> should follow the same guidelines as <a class="el" href="classunity_1_1scopes_1_1_scope_base.html#a2f4d476fa790349c9a7de52be3232d11" title="Invoked when a scope is requested to handle a preview action. ">unity::scopes::ScopeBase::perform_action()</a>, the only difference with result activation being the lack of widget or action identifiers, as those are specific to preview widgets.</p>
<h4><a class="anchor" id="export"></a>
Exporting the scope</h4>
<p>The scope needs to be compiled into a .so shared library and to be succesfully loaded at runtime it must provide two C functions to create and destroy it - a typical code snippet to do this looks as follows:</p>
<div class="fragment"><div class="line"><span class="keyword">extern</span> <span class="stringliteral">"C"</span> {</div>
<div class="line"> EXPORT <a class="code" href="classunity_1_1scopes_1_1_scope_base.html">unity::scopes::ScopeBase</a>* <a class="code" href="_scope_base_8h.html#a21674629dcda399390c80d469fcb1d6d">UNITY_SCOPE_CREATE_FUNCTION</a>()</div>
<div class="line"> {</div>
<div class="line"> <span class="keywordflow">return</span> <span class="keyword">new</span> MyScope();</div>
<div class="line"> }</div>
<div class="line"></div>
<div class="line"> EXPORT <span class="keywordtype">void</span> <a class="code" href="_scope_base_8h.html#ac01d346e43462c07535811fd24c468f7">UNITY_SCOPE_DESTROY_FUNCTION</a>(<a class="code" href="classunity_1_1scopes_1_1_scope_base.html">unity::scopes::ScopeBase</a>* scope_base)</div>
<div class="line"> {</div>
<div class="line"> <span class="keyword">delete</span> scope_base;</div>
<div class="line"> }</div>
<div class="line">}</div>
</div><!-- fragment --><h3><a class="anchor" id="aggscope"></a>
Case 2: A simple aggregator scope.</h3>
<p>Aggregator scope is not much different from regular scopes, except for its data sources can include any other scope(s). The main difference is in the implementation of run method of <a class="el" href="classunity_1_1scopes_1_1_search_query_base.html" title="Abstract base class to represent a particular query. ">unity::scopes::SearchQueryBase</a> and in the new class that has to implement <a class="el" href="classunity_1_1scopes_1_1_search_listener_base.html">SearchListenerBase</a> interface, which receives result from other scope(s).</p>
<h4><a class="anchor" id="subquery"></a>
Query another scopes via SearchQueryBase::subsearch()</h4>
<p>To send search query to another scope, use one of the <code>subsearch()</code> overloads of <a class="el" href="classunity_1_1scopes_1_1_search_query_base.html" title="Abstract base class to represent a particular query. ">unity::scopes::SearchQueryBase</a> inside your implementation of <a class="el" href="classunity_1_1scopes_1_1_search_query_base.html" title="Abstract base class to represent a particular query. ">unity::scopes::SearchQueryBase</a>. This method requires - among search query string - an instance of <a class="el" href="namespaceunity_1_1scopes.html#a94db15da410f8419e4da711db842aaae">ScopeProxy</a> that points to the target scope and an instance of class that implements <a class="el" href="classunity_1_1scopes_1_1_search_listener_base.html">SearchListenerBase</a> interface. ScopeProxy can be obtained from <a class="el" href="namespaceunity_1_1scopes.html#a45babc254d3548863d79ee54f266e84d" title="Convenience type definition. ">unity::scopes::RegistryProxy</a> and the right place to do this is in the implementation of <a class="el" href="classunity_1_1scopes_1_1_scope_base.html#add7f4b1811774a63cd474ccab702acdf">start()</a> method of <a class="el" href="classunity_1_1scopes_1_1_scope_base.html">ScopeBase</a> interface.</p>
<div class="fragment"><div class="line"><span class="keywordtype">int</span> MyScope::start(std::string <span class="keyword">const</span>&, <a class="code" href="namespaceunity_1_1scopes.html#a45babc254d3548863d79ee54f266e84d">unity::scopes::RegistryProxy</a> <span class="keyword">const</span>& registry)</div>
<div class="line">{</div>
<div class="line"> <span class="keywordflow">try</span></div>
<div class="line"> {</div>
<div class="line"> <span class="keyword">auto</span> meta = registry->get_metadata(<span class="stringliteral">"scope-A"</span>);</div>
<div class="line"> scope_to_query_ = meta.proxy(); <span class="comment">// store the proxy for passing it further in search </span></div>
<div class="line"> }</div>
<div class="line"> <span class="keywordflow">catch</span> (NotFoundException <span class="keyword">const</span>& e)</div>
<div class="line"> {</div>
<div class="line"> ...</div>
<div class="line"> }</div>
<div class="line"> <span class="keywordflow">return</span> VERSION;</div>
<div class="line">}</div>
<div class="line"></div>
<div class="line">unity::scopes::QueryBase::UPtr MyScope::search(CannedQuery <span class="keyword">const</span>& query, <a class="code" href="classunity_1_1scopes_1_1_search_metadata.html">unity::scopes::SearchMetadata</a> <span class="keyword">const</span>&)</div>
<div class="line">{</div>
<div class="line"> SearchQueryBase::UPtr q(<span class="keyword">new</span> MyQuery(query, scope_to_query_));</div>
<div class="line"> <span class="keywordflow">return</span> q;</div>
<div class="line">}</div>
<div class="line"></div>
<div class="line">...</div>
<div class="line"></div>
<div class="line">void MyQuery::run(<a class="code" href="namespaceunity_1_1scopes.html#a9cd604d9b842ac3b2b8636c2165dec1f">unity::scopes::SearchReplyProxy</a> <span class="keyword">const</span>& upstream_reply) </div>
<div class="line">{</div>
<div class="line"> <span class="keyword">auto</span> category = reply->register_category(<span class="stringliteral">"recommended"</span>, <span class="stringliteral">"Recommended"</span>, icon, <span class="stringliteral">""</span>);</div>
<div class="line"> SearchListenerBase::SPtr reply(<span class="keyword">new</span> MyReceiver(upstream_reply, category));</div>
<div class="line"> subsearch(scope_to_query_, query_, reply);</div>
<div class="line"> ...</div>
<div class="line">}</div>
</div><!-- fragment --><h4><a class="anchor" id="receiver"></a>
Create a class that implements SearchListenerBase interface</h4>
<p>The <a class="el" href="classunity_1_1scopes_1_1_search_listener_base.html">SearchListenerBase</a> is an abstract class to receive the results of a query sent to a scope. Its virtual push methods let the implementation receive <a class="el" href="classunity_1_1scopes_1_1_search_listener_base.html#af3f2815ec080baffefd9099273b81a5e">result items and categories</a> returned by that query. A simple implementation of an aggregator scope may just register all categories it receives and push all received results upstream to the query originator, e.g.</p>
<div class="fragment"><div class="line"><span class="keywordtype">void</span> push(Category::SCPtr category)</div>
<div class="line">{</div>
<div class="line"> upstream_->register_category(category);</div>
<div class="line">}</div>
<div class="line"></div>
<div class="line"><span class="keywordtype">void</span> MyReceiver::push(CategorisedResult result)</div>
<div class="line">{</div>
<div class="line"> upstream_->push(std::move(result));</div>
<div class="line">}</div>
</div><!-- fragment --><p>A more sophisticated aggregator scope can rearrange results it receives into a different set of categories, alter or enrich the results before pushing them upstream etc.</p>
<h4><a class="anchor" id="aggactiv"></a>
Activation and previews of results processed by aggregator scopes</h4>
<p>If an aggregator scope just forwards results it receives from other scopes, possibly only changing their category assignment, then there is nothing to do in terms of handling previews, preview actions and result activation: preview and perform_action requests will trigger respective methods of <a class="el" href="classunity_1_1scopes_1_1_scope_base.html" title="Base class for a scope implementation. ">unity::scopes::ScopeBase</a> for the scope that created results. Result activation will trigger <a class="el" href="classunity_1_1scopes_1_1_scope_base.html#a49a0b9ada0eeb4c71e6a2181c3d8c9e7" title="Called by the scopes run time when a scope needs to respond to a result activation request...">unity::scopes::ScopeBase::activate()</a> method for the scope that produced the result as long as it set interception flag for it. In other words, when aggreagor scope just forwards results (and makes only minor adjustements to them, such as category assignment), it is not involved in preview or activation handling at all.</p>
<p>If, however, aggregator scope changes attributes of results (or creates completely new results that "replace" received results), then some extra care needs to be taken: </p>
<ul>
<li>
<p class="startli">if original scope should still handle preview (and activation) requests, then <b>aggregator has to store a copy of original result</b> in the modified (or brand new) result. This can be done with <a class="el" href="classunity_1_1scopes_1_1_result.html#a744776333a9748ba41dace7c6943ca4d" title="Stores a Result inside this Result instance. ">unity::scopes::Result::store</a> method. Preview request for such result will automatically trigger a scope that created the most inner stored result, and that scope will receive the stored result. It will also do the same for activation as long as the original scope set interception flag on that result.</p>
<dl class="section note"><dt>Note</dt><dd>Making substantial changes to received results and failing to store original results with them may result in unexpected behavior: a scope will suddenly receive a modified version of it and depending on the level of changes, it may or may not be able to correctly handle it.</dd></dl>
</li>
<li>
if aggregator scope creates a completly new result that replaces original one, but doesn't store a copy of the original result, it is expected to handle preview (and potentially activation requests - if interception activation flag is set) - this is no different than for normal scopes, see <a class="el" href="index.html#handlingpreview">Handling previews</a> and <a class="el" href="index.html#handlingactivation">Handling result activation</a> . </li>
</ul>
<p>Consider the following example of implementation of <a class="el" href="classunity_1_1scopes_1_1_search_listener_base.html" title="Abstract base interface that a scope specializes to receive the results of a query. ">unity::scopes::SearchListenerBase</a> interface that modifies results and stores their copies, so that original scope can handle previews and activation for them:</p>
<div class="fragment"><div class="line"><span class="keywordtype">void</span> MyReceiver::push(CategorisedResult original_result)</div>
<div class="line">{</div>
<div class="line"> CategorisedResult result(agg_category); <span class="comment">// agg_category is a category that aggregates all results from other scopes</span></div>
<div class="line"> result.set_uri(original_result.uri());</div>
<div class="line"> result.set_title(original_result.title() + <span class="stringliteral">"(aggregated)"</span>);</div>
<div class="line"> result.set_art(original_result.art());</div>
<div class="line"> result.store(original_result);</div>
<div class="line"></div>
<div class="line"> upstream_->push(std::move(result));</div>
<div class="line">}</div>
</div><!-- fragment --><h2><a class="anchor" id="scopetesting"></a>
Testing</h2>
<p>Unity Scopes API provides testing helpers based on well-known and established testing frameworks: <a href="https://code.google.com/p/googletest/">googletest</a> and <a href="https://code.google.com/p/googlemock/">googlemock</a>. Please see respective documentation of those projects for general information about how to use Google C++ Testing Framework.</p>
<p>All the helper classes provided by Scopes API are located in unity::scopes::testing namespace. The most important ones are: </p>
<ul>
<li>
unity::scopes::testing::TypedScopeFixture - template class that takes your scope class name as a template argument and creates a test fixture that can be used in tests. </li>
<li>
unity::scopes::testing::MockSearchReply - a mock of <a class="el" href="classunity_1_1scopes_1_1_search_reply.html" title="Allows the results of a search query to be sent to the query source. ">unity::scopes::SearchReply</a> that makes it possible to intercept responses to search request sent from the scope to a client, making it easy to test if your scope returns all expected data. </li>
<li>
unity::scopes::testing::MockPreviewReply - a mock of <a class="el" href="classunity_1_1scopes_1_1_preview_reply.html" title="Allows the results of a preview to be sent to the preview requester. ">unity::scopes::PreviewReply</a> that makes is possible to intercept and test responses to preview request sent from the scope to a client. </li>
<li>
<a class="el" href="classunity_1_1scopes_1_1testing_1_1_result.html" title="A simple class implementation of unity::scopes::Result that provides a default constructor. ">unity::scopes::testing::Result</a> - a simple class defined on top of <a class="el" href="classunity_1_1scopes_1_1_result.html" title="The attributes of a result returned by a Scope. ">unity::scopes::Result</a> that provides a default constructor, making it possible to create dummy results (with no attributes) for testing purposes. </li>
<li>
unity::scopes::testing::category - a simple class defined on top of <a class="el" href="classunity_1_1scopes_1_1_category.html" title="A set of related results returned by a scope and displayed within a single pane in the Unity dash...">unity::scopes::Category</a> that makes it possible to create dummy categories (which would otherwise require an instance of <a class="el" href="classunity_1_1scopes_1_1_search_reply.html" title="Allows the results of a search query to be sent to the query source. ">unity::scopes::SearchReply</a> and a call to <a class="el" href="classunity_1_1scopes_1_1_search_reply.html#aaa061806a96f50ff66abc6184135ea66" title="Register an existing category instance and send it to the source of the query. ">unity::scopes::SearchReply::register_category()</a>). </li>
</ul>
<p>With the above classes a test case that checks if MyScope calls appropriate methods of <a class="el" href="classunity_1_1scopes_1_1_search_reply.html" title="Allows the results of a search query to be sent to the query source. ">unity::scopes::SearchReply</a> may look like this (note that it just checks if proper methods get called and uses _ matchers that match any values; put actual values in there for stricts checks): </p>
<div class="fragment"><div class="line"><span class="keyword">typedef</span> unity::scopes::testing::TypedScopeFixture<MyScope> TestScopeFixutre;</div>
<div class="line"><span class="keyword">using namespace </span>::testing;</div>
<div class="line"></div>
<div class="line">TEST_F(TestScopeFixutre, search_results)</div>
<div class="line">{</div>
<div class="line"> <span class="keyword">const</span> <a class="code" href="classunity_1_1scopes_1_1_category_renderer.html">unity::scopes::CategoryRenderer</a> renderer;</div>
<div class="line"></div>
<div class="line"> NiceMock<unity::scopes::testing::MockSearchReply> reply;</div>
<div class="line"> EXPECT_CALL(reply, register_departments(_, _)).Times(1);</div>
<div class="line"> EXPECT_CALL(reply, register_category(_, _, _, _))</div>
<div class="line"> .Times(1)</div>
<div class="line"> .WillOnce(</div>
<div class="line"> Return(</div>
<div class="line"> unity::scopes::Category::SCPtr(<span class="keyword">new</span> <a class="code" href="classunity_1_1scopes_1_1testing_1_1_category.html">unity::scopes::testing::Category</a>(<span class="stringliteral">"id"</span>, <span class="stringliteral">"title"</span>, <span class="stringliteral">"icon"</span>, renderer))</div>
<div class="line"> )</div>
<div class="line"> );</div>
<div class="line"> EXPECT_CALL(reply, register_annotation(_))</div>
<div class="line"> .Times(1)</div>
<div class="line"> .WillOnce(Return(<span class="keyword">true</span>));</div>
<div class="line"> EXPECT_CALL(reply, push(_))</div>
<div class="line"> .Times(1)</div>
<div class="line"> .WillOnce(Return(<span class="keyword">true</span>));</div>
<div class="line"></div>
<div class="line"> <a class="code" href="namespaceunity_1_1scopes.html#a9cd604d9b842ac3b2b8636c2165dec1f">unity::scopes::SearchReplyProxy</a> reply_proxy(&reply, [](unity::scopes::SearchReplyBase*) {}); <span class="comment">// note: this is a std::shared_ptr with empty deleter</span></div>
<div class="line"></div>
<div class="line"> <a class="code" href="classunity_1_1scopes_1_1_canned_query.html">unity::scopes::CannedQuery</a> query(scope_id, <span class="stringliteral">""</span>, <span class="stringliteral">""</span>);</div>
<div class="line"> <a class="code" href="classunity_1_1scopes_1_1_search_metadata.html">unity::scopes::SearchMetadata</a> meta_data(<span class="stringliteral">"en_EN"</span>, <span class="stringliteral">"phone"</span>);</div>
<div class="line"></div>
<div class="line"> <span class="keyword">auto</span> search_query = scope->search(query, meta_data);</div>
<div class="line"> ASSERT_NE(<span class="keyword">nullptr</span>, search_query);</div>
<div class="line"> search_query->run(reply_proxy);</div>
<div class="line">}</div>
</div><!-- fragment --><h2><a class="anchor" id="deployment"></a>
Deployment</h2>
<p>Installing a scope is as simple as running <code>make install</code> when using the scope template. You might need to restart the global scope registry when a new scope is installed by running:</p>
<pre class="fragment">restart scope-registry
</pre><p>The scope will be installed under one of the "scopes directories" scanned by the scope registry. Currently these default to:</p>
<ul>
<li>
/usr/lib/${arch}/unity-scopes </li>
<li>
<p class="startli">/custom/lib/${arch}/unity-scopes</p>
<p class="endli"></p>
</li>
</ul>
<p>Individual scopes are installed into a subdirectory matching the scope's name. At a minimum, the directory structure should contain the following: </p>
<pre class="fragment">-+- ${scopesdir}
`-+- scopename
|--- scopename.ini
`--- libscopename.so
</pre><p>That is, a scope metadata file and a shared library containing the scope code. The scope author is free to ship additional data in this directory (e.g. icons and screenshots).</p>
<p>The scope metadata file uses the standard ini file format, with the following keys: </p>
<pre class="fragment">[ScopeConfig]
DisplayName = human readable name of scope
Description = description of scope
Author = Author
Icon = path to icon representing the scope
Art = path to screenshot of the scope
SearchHint = hint text displayed to user when viewing scope
HotKey =
</pre><p>In addition to allowing the registry to make the scope available, this information controls how the scope appears in the "Scopes" scope.</p>
<h2><a class="anchor" id="scopetool"></a>
Previewing scope</h2>
<p>To help with the development of a scope and to be able to see how will the dash render the dynamically-specified categories (see <a class="el" href="classunity_1_1scopes_1_1_category_renderer.html" title="A category renderer template in JSON format. ">unity::scopes::CategoryRenderer</a>), a specialized tool to preview a scope is provided - the "Unity Scope Tool".</p>
<p>You can install it from the Ubuntu archive using: </p>
<pre class="fragment">sudo apt-get install unity-scope-tool
</pre><p>After installation, you can run the scope-tool with a parameter specifying path to your scope configuration file (for example <code>unity-scope-tool ~/dev/myscope/build/myscope.ini</code>). If a binary for your scope can be found in the same directory (ie there's <code>~/dev/myscope/build/libmyscope.so</code>), the scope-tool will display surfacing and search results provided by your scope, and allow you to perform searches, invoke previews and actions within previews.</p>
<p>Note that the scope-tool is using the same rendering mechanism as Unity itself, and therefore what you see in the scope-tool is what you get in Unity. It can also be used to fine-tune the category definitions, as it allows you to manipulate the definitions on the fly, and once you're happy with the result you can just copy the JSON definition back into your scope (see <a class="el" href="classunity_1_1scopes_1_1_category_renderer.html#a046414ae2092968686ee4ee00629054a" title="Creates a CategoryRenderer from a JSON string. ">unity::scopes::CategoryRenderer::CategoryRenderer()</a>).</p>
<p>The scope-tool supports a few command line arguments: </p>
<ul>
<li>by default (without any arguments) it will communicate with all scopes installed on the system and available on the smart scopes server. </li>
<li>When a path to a scope configuration file is provided, only that scope is initialized, but you can either pass multiple configuration files or the <code>--include-system-scopes</code> / <code>--include-server-scopes</code> option to allow development of aggregating scopes. </li>
</ul>
</div></div><!-- contents -->
</div><!-- doc-content -->
<!-- start footer part -->
<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
<ul>
<li class="footer">Generated on Tue Apr 8 2014 14:04:48 for Unity Scopes API by
<a href="http://www.doxygen.org/index.html">
<img class="footer" src="doxygen.png" alt="doxygen"/></a> 1.8.6 </li>
</ul>
</div>
</body>
</html>
|