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 base::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 base::string16& tile_id,
125                             const base::string16& title_str,
126                             const base::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(base::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(
160      MakeHString(base::string16(L"file:///").append(logo_path.value())));
161  mswr::ComPtr<winfoundtn::IUriRuntimeClass> uri;
162  hr = uri_factory->CreateUri(logo_url.Get(), &uri);
163  CheckHR(hr, "Failed to create URI");
164
165  mswr::ComPtr<winui::StartScreen::ISecondaryTile> tile;
166  hr = tile_factory->CreateTile(id.Get(),
167                                title.Get(),
168                                title.Get(),
169                                args.Get(),
170                                options,
171                                uri.Get(),
172                                tile.GetAddressOf());
173  CheckHR(hr, "Failed to create tile");
174
175  hr = tile->put_ForegroundText(winui::StartScreen::ForegroundText_Light);
176  CheckHR(hr, "Failed to change foreground text color");
177
178  mswr::ComPtr<winfoundtn::IAsyncOperation<bool>> completion;
179  hr = tile->RequestCreateAsync(completion.GetAddressOf());
180  CheckHR(hr, "RequestCreateAsync failed");
181
182  if (FAILED(hr)) {
183    callback.Run(base::win::METRO_PIN_REQUEST_SHOW_ERROR);
184    return;
185  }
186
187  // Deleted in TileRequestCompleter::Respond when the async operation
188  // completes.
189  TileRequestCompleter* completer =
190      new TileRequestCompleter(TileRequestCompleter::PIN, callback);
191  completer->Complete(completion);
192}
193
194}  // namespace
195
196BOOL MetroIsPinnedToStartScreen(const base::string16& tile_id) {
197  mswr::ComPtr<winui::StartScreen::ISecondaryTileStatics> tile_statics;
198  HRESULT hr = winrt_utils::CreateActivationFactory(
199      RuntimeClass_Windows_UI_StartScreen_SecondaryTile,
200      tile_statics.GetAddressOf());
201  CheckHR(hr, "Failed to create instance of ISecondaryTileStatics");
202
203  boolean exists;
204  hr = tile_statics->Exists(MakeHString(tile_id), &exists);
205  CheckHR(hr, "ISecondaryTileStatics.Exists failed");
206  return exists;
207}
208
209void MetroUnPinFromStartScreen(const base::string16& tile_id,
210                               const MetroPinUmaResultCallback& callback) {
211  globals.appview_msg_loop->PostTask(
212      FROM_HERE, base::Bind(&DeleteTileFromStartScreen,
213                            tile_id,
214                            callback));
215}
216
217void MetroPinToStartScreen(const base::string16& tile_id,
218                           const base::string16& title,
219                           const base::string16& url,
220                           const base::FilePath& logo_path,
221                           const MetroPinUmaResultCallback& callback) {
222  globals.appview_msg_loop->PostTask(
223    FROM_HERE, base::Bind(&CreateTileOnStartScreen,
224                          tile_id,
225                          title,
226                          url,
227                          logo_path,
228                          callback));
229}
230