1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "stdafx.h"
6#include "secondary_tile.h"
7
8#include <windows.ui.startscreen.h>
9
10#include "base/bind.h"
11#include "base/logging.h"
12#include "base/strings/utf_string_conversions.h"
13#include "url/gurl.h"
14#include "win8/metro_driver/chrome_app_view.h"
15#include "win8/metro_driver/winrt_utils.h"
16
17namespace {
18
19using base::win::MetroPinUmaResultCallback;
20
21// Callback for asynchronous pin requests.
22class TileRequestCompleter {
23 public:
24  enum PinType {
25    PIN,
26    UNPIN
27  };
28  TileRequestCompleter(PinType type, const MetroPinUmaResultCallback& callback)
29      : type_(type), callback_(callback) {}
30
31  void Complete(mswr::ComPtr<winfoundtn::IAsyncOperation<bool>>& completion);
32
33 private:
34  // Callback that responds to user input on the pin request pop-up. This will
35  // run |callback_|, then delete |this| before returning.
36  HRESULT Respond(winfoundtn::IAsyncOperation<bool>* async,
37                  AsyncStatus status);
38
39  PinType type_;
40  MetroPinUmaResultCallback callback_;
41};
42
43void TileRequestCompleter::Complete(
44    mswr::ComPtr<winfoundtn::IAsyncOperation<bool>>& completion) {
45  typedef winfoundtn::IAsyncOperationCompletedHandler<bool> RequestDoneType;
46  mswr::ComPtr<RequestDoneType> handler(mswr::Callback<RequestDoneType>(
47      this, &TileRequestCompleter::Respond));
48  DCHECK(handler.Get() != NULL);
49  HRESULT hr = completion->put_Completed(handler.Get());
50  CheckHR(hr, "Failed to put_Completed");
51}
52
53HRESULT TileRequestCompleter::Respond(winfoundtn::IAsyncOperation<bool>* async,
54                                 AsyncStatus status) {
55  base::win::MetroSecondaryTilePinUmaResult pin_state =
56      base::win::METRO_PIN_STATE_NONE;
57
58  if (status == Completed) {
59    unsigned char result;
60    CheckHR(async->GetResults(&result));
61    LOG(INFO) << __FUNCTION__ << " result " << static_cast<int>(result);
62    switch (result) {
63      case 0:
64        pin_state = type_ == PIN ?
65            base::win::METRO_PIN_RESULT_CANCEL :
66            base::win::METRO_UNPIN_RESULT_CANCEL;
67        break;
68      case 1:
69        pin_state = type_ == PIN ?
70            base::win::METRO_PIN_RESULT_OK :
71            base::win::METRO_UNPIN_RESULT_OK;
72        break;
73      default:
74        pin_state = type_ == PIN ?
75            base::win::METRO_PIN_RESULT_OTHER :
76            base::win::METRO_UNPIN_RESULT_OTHER;
77        break;
78    }
79  } else {
80    LOG(ERROR) << __FUNCTION__ << " Unexpected async status "
81               << static_cast<int>(status);
82    pin_state = type_ == PIN ?
83        base::win::METRO_PIN_RESULT_ERROR :
84        base::win::METRO_UNPIN_RESULT_ERROR;
85  }
86  callback_.Run(pin_state);
87
88  delete this;
89  return S_OK;
90}
91
92void DeleteTileFromStartScreen(const string16& tile_id,
93                               const MetroPinUmaResultCallback& callback) {
94  DVLOG(1) << __FUNCTION__;
95  mswr::ComPtr<winui::StartScreen::ISecondaryTileFactory> tile_factory;
96  HRESULT hr = winrt_utils::CreateActivationFactory(
97      RuntimeClass_Windows_UI_StartScreen_SecondaryTile,
98      tile_factory.GetAddressOf());
99  CheckHR(hr, "Failed to create instance of ISecondaryTileFactory");
100
101  mswrw::HString id;
102  id.Attach(MakeHString(tile_id));
103
104  mswr::ComPtr<winui::StartScreen::ISecondaryTile> tile;
105  hr = tile_factory->CreateWithId(id.Get(), tile.GetAddressOf());
106  CheckHR(hr, "Failed to create tile");
107
108  mswr::ComPtr<winfoundtn::IAsyncOperation<bool>> completion;
109  hr = tile->RequestDeleteAsync(completion.GetAddressOf());
110  CheckHR(hr, "RequestDeleteAsync failed");
111
112  if (FAILED(hr)) {
113    callback.Run(base::win::METRO_UNPIN_REQUEST_SHOW_ERROR);
114    return;
115  }
116
117  // Deleted in TileRequestCompleter::Respond when the async operation
118  // completes.
119  TileRequestCompleter* completer =
120      new TileRequestCompleter(TileRequestCompleter::UNPIN, callback);
121  completer->Complete(completion);
122}
123
124void CreateTileOnStartScreen(const string16& tile_id,
125                             const string16& title_str,
126                             const string16& url_str,
127                             const base::FilePath& logo_path,
128                             const MetroPinUmaResultCallback& callback) {
129  VLOG(1) << __FUNCTION__;
130
131  mswr::ComPtr<winui::StartScreen::ISecondaryTileFactory> tile_factory;
132  HRESULT hr = winrt_utils::CreateActivationFactory(
133      RuntimeClass_Windows_UI_StartScreen_SecondaryTile,
134      tile_factory.GetAddressOf());
135  CheckHR(hr, "Failed to create instance of ISecondaryTileFactory");
136
137  winui::StartScreen::TileOptions options =
138      winui::StartScreen::TileOptions_ShowNameOnLogo;
139  mswrw::HString title;
140  title.Attach(MakeHString(title_str));
141
142  mswrw::HString id;
143  id.Attach(MakeHString(tile_id));
144
145  mswrw::HString args;
146  // The url is just passed into the tile agruments as is. Metro and desktop
147  // chrome will see the arguments as command line parameters.
148  // A GURL is used to ensure any spaces are properly escaped.
149  GURL url(url_str);
150  args.Attach(MakeHString(UTF8ToUTF16(url.spec())));
151
152  mswr::ComPtr<winfoundtn::IUriRuntimeClassFactory> uri_factory;
153  hr = winrt_utils::CreateActivationFactory(
154      RuntimeClass_Windows_Foundation_Uri,
155      uri_factory.GetAddressOf());
156  CheckHR(hr, "Failed to create URIFactory");
157
158  mswrw::HString logo_url;
159  logo_url.Attach(MakeHString(string16(L"file:///").append(logo_path.value())));
160  mswr::ComPtr<winfoundtn::IUriRuntimeClass> uri;
161  hr = uri_factory->CreateUri(logo_url.Get(), &uri);
162  CheckHR(hr, "Failed to create URI");
163
164  mswr::ComPtr<winui::StartScreen::ISecondaryTile> tile;
165  hr = tile_factory->CreateTile(id.Get(),
166                                title.Get(),
167                                title.Get(),
168                                args.Get(),
169                                options,
170                                uri.Get(),
171                                tile.GetAddressOf());
172  CheckHR(hr, "Failed to create tile");
173
174  hr = tile->put_ForegroundText(winui::StartScreen::ForegroundText_Light);
175  CheckHR(hr, "Failed to change foreground text color");
176
177  mswr::ComPtr<winfoundtn::IAsyncOperation<bool>> completion;
178  hr = tile->RequestCreateAsync(completion.GetAddressOf());
179  CheckHR(hr, "RequestCreateAsync failed");
180
181  if (FAILED(hr)) {
182    callback.Run(base::win::METRO_PIN_REQUEST_SHOW_ERROR);
183    return;
184  }
185
186  // Deleted in TileRequestCompleter::Respond when the async operation
187  // completes.
188  TileRequestCompleter* completer =
189      new TileRequestCompleter(TileRequestCompleter::PIN, callback);
190  completer->Complete(completion);
191}
192
193}  // namespace
194
195BOOL MetroIsPinnedToStartScreen(const string16& tile_id) {
196  mswr::ComPtr<winui::StartScreen::ISecondaryTileStatics> tile_statics;
197  HRESULT hr = winrt_utils::CreateActivationFactory(
198      RuntimeClass_Windows_UI_StartScreen_SecondaryTile,
199      tile_statics.GetAddressOf());
200  CheckHR(hr, "Failed to create instance of ISecondaryTileStatics");
201
202  boolean exists;
203  hr = tile_statics->Exists(MakeHString(tile_id), &exists);
204  CheckHR(hr, "ISecondaryTileStatics.Exists failed");
205  return exists;
206}
207
208void MetroUnPinFromStartScreen(const string16& tile_id,
209                               const MetroPinUmaResultCallback& callback) {
210  globals.appview_msg_loop->PostTask(
211      FROM_HERE, base::Bind(&DeleteTileFromStartScreen,
212                            tile_id,
213                            callback));
214}
215
216void MetroPinToStartScreen(const string16& tile_id,
217                           const string16& title,
218                           const string16& url,
219                           const base::FilePath& logo_path,
220                           const MetroPinUmaResultCallback& callback) {
221  globals.appview_msg_loop->PostTask(
222    FROM_HERE, base::Bind(&CreateTileOnStartScreen,
223                          tile_id,
224                          title,
225                          url,
226                          logo_path,
227                          callback));
228}
229