summaryrefslogtreecommitdiffstats
path: root/ui/arc/notification/arc_notification_item.cc
blob: e664e8cd586c360ca55705c0387d0a135502fdfc (plain)
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
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/arc/notification/arc_notification_item.h"

#include <algorithm>
#include <vector>

#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task_runner.h"
#include "base/task_runner_util.h"
#include "base/threading/worker_pool.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/image/image.h"
#include "ui/message_center/message_center_style.h"
#include "ui/message_center/notification.h"
#include "ui/message_center/notification_types.h"
#include "ui/message_center/notifier_settings.h"

namespace arc {

namespace {

static const char kNotifierId[] = "ARC_NOTIFICATION";

static const char kNotificationIdPrefix[] = "ARC_NOTIFICATION_";

SkBitmap DecodeImage(const std::vector<uint8_t>& data) {
  DCHECK(base::WorkerPool::RunsTasksOnCurrentThread());
  DCHECK(!data.empty());  // empty string should be handled in caller.

  // We may decode an image in the browser process since it has been generated
  // in NotificationListerService in Android and should be safe.
  SkBitmap bitmap;
  gfx::PNGCodec::Decode(&data[0], data.size(), &bitmap);
  return bitmap;
}

class ArcNotificationDelegate : public message_center::NotificationDelegate {
 public:
  explicit ArcNotificationDelegate(base::WeakPtr<ArcNotificationItem> item)
      : item_(item) {}

  void Close(bool by_user) override {
    if (item_)
      item_->Close(by_user);
  }

  // Indicates all notifications have a click handler. This changes the mouse
  // cursor on hover.
  // TODO(yoshiki): Return the correct value according to the content intent
  // and the flags.
  bool HasClickedListener() override { return true; }

  void Click() override {
    if (item_)
      item_->Click();
  }

  void ButtonClick(int button_index) override {
    if (item_)
      item_->ButtonClick(button_index);
  }

 private:
  // The destructor is private since this class is ref-counted.
  ~ArcNotificationDelegate() override {}

  base::WeakPtr<ArcNotificationItem> item_;

  DISALLOW_COPY_AND_ASSIGN(ArcNotificationDelegate);
};

}  // anonymous namespace

ArcNotificationItem::ArcNotificationItem(
    ArcNotificationManager* manager,
    message_center::MessageCenter* message_center,
    const std::string& notification_key,
    const AccountId& profile_id)
    : manager_(manager),
      message_center_(message_center),
      profile_id_(profile_id),
      notification_key_(notification_key),
      notification_id_(kNotificationIdPrefix + notification_key_),
      weak_ptr_factory_(this) {}

void ArcNotificationItem::UpdateWithArcNotificationData(
    const ArcNotificationData& data) {
  DCHECK(thread_checker_.CalledOnValidThread());
  DCHECK(notification_key_ == data.key);

  // Check if a decode task is on-going or not. If |notification_| is non-null,
  // a decode task is on-going asynchronously. Otherwise, there is no task.
  // TODO(yoshiki): Refactor and remove this check by omitting image decoding
  // from here.
  if (notification_) {
    // Store the latest data to the |newer_data_| property and returns, if the
    // previous decode is still in progress.
    // If old |newer_data_| has been stored, discard the old one.
    newer_data_ = data.Clone();
    return;
  }

  message_center::RichNotificationData rich_data;
  message_center::NotificationType type;

  switch (data.type) {
    case ArcNotificationType::BASIC:
      type = message_center::NOTIFICATION_TYPE_BASE_FORMAT;
      break;
    case ArcNotificationType::IMAGE:
      // TODO(yoshiki): Implement this types.
      type = message_center::NOTIFICATION_TYPE_BASE_FORMAT;
      LOG(ERROR) << "Unsupported notification type: image";
      break;
    case ArcNotificationType::PROGRESS:
      type = message_center::NOTIFICATION_TYPE_PROGRESS;
      rich_data.timestamp = base::Time::UnixEpoch() +
                            base::TimeDelta::FromMilliseconds(data.time);
      rich_data.progress = std::max(
          0, std::min(100, static_cast<int>(std::round(
                               static_cast<float>(data.progress_current) /
                               data.progress_max * 100))));
      break;
  }
  DCHECK(IsKnownEnumValue(data.type)) << "Unsupported notification type: "
                                      << data.type;

  for (size_t i = 0; i < data.buttons.size(); i++) {
    rich_data.buttons.push_back(message_center::ButtonInfo(
        base::UTF8ToUTF16(data.buttons.at(i)->label.get())));
  }

  // The identifier of the notifier, which is used to distinguish the notifiers
  // in the message center.
  message_center::NotifierId notifier_id(
      message_center::NotifierId::SYSTEM_COMPONENT, kNotifierId);
  notifier_id.profile_id = profile_id_.GetUserEmail();

  DCHECK(!data.title.is_null());
  DCHECK(!data.message.is_null());
  notification_.reset(new message_center::Notification(
      type, notification_id_, base::UTF8ToUTF16(data.title.get()),
      base::UTF8ToUTF16(data.message.get()),
      gfx::Image(),              // icon image: Will be overriden later.
      base::UTF8ToUTF16("arc"),  // display source
      GURL(),                    // empty origin url, for system component
      notifier_id, rich_data,
      new ArcNotificationDelegate(weak_ptr_factory_.GetWeakPtr())));

  DCHECK(!data.icon_data.is_null());
  if (data.icon_data.size() == 0) {
    OnImageDecoded(SkBitmap());  // Pass an empty bitmap.
    return;
  }

  // TODO(yoshiki): Remove decoding by passing a bitmap directly from Android.
  base::PostTaskAndReplyWithResult(
      base::WorkerPool::GetTaskRunner(true).get(), FROM_HERE,
      base::Bind(&DecodeImage, data.icon_data.storage()),
      base::Bind(&ArcNotificationItem::OnImageDecoded,
                 weak_ptr_factory_.GetWeakPtr()));
}

ArcNotificationItem::~ArcNotificationItem() {}

void ArcNotificationItem::OnClosedFromAndroid(bool by_user) {
  being_removed_by_manager_ = true;  // Closing is initiated by the manager.
  message_center_->RemoveNotification(notification_id_, by_user);
}

void ArcNotificationItem::Close(bool by_user) {
  if (being_removed_by_manager_) {
    // Closing is caused by the manager, so we don't need to nofify a close
    // event to the manager.
    return;
  }

  // Do not touch its any members afterwards, because this instance will be
  // destroyed in the following call
  manager_->SendNotificationRemovedFromChrome(notification_key_);
}

void ArcNotificationItem::Click() {
  manager_->SendNotificationClickedOnChrome(notification_key_);
}

void ArcNotificationItem::ButtonClick(int button_index) {
  manager_->SendNotificationButtonClickedOnChrome(
      notification_key_, button_index);
}

void ArcNotificationItem::OnImageDecoded(const SkBitmap& bitmap) {
  DCHECK(thread_checker_.CalledOnValidThread());

  gfx::Image image = gfx::Image::CreateFrom1xBitmap(bitmap);
  notification_->set_icon(image);

  DCHECK(notification_);
  message_center_->AddNotification(std::move(notification_));

  if (newer_data_) {
    // There is the newer data, so updates again.
    ArcNotificationDataPtr data(std::move(newer_data_));
    UpdateWithArcNotificationData(*data);
  }
}

}  // namespace arc