Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(142)

Side by Side Diff: ui/base/clipboard/clipboard_android.cc

Issue 2812773002: Refactor Clipboard Last Modified Time Storage (Closed)
Patch Set: itri-state enum Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « ui/base/clipboard/clipboard_android.h ('k') | ui/base/test/test_clipboard.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "ui/base/clipboard/clipboard_android.h" 5 #include "ui/base/clipboard/clipboard_android.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "base/android/context_utils.h" 9 #include "base/android/context_utils.h"
10 #include "base/android/jni_string.h" 10 #include "base/android/jni_string.h"
11 #include "base/android/scoped_java_ref.h"
11 #include "base/lazy_instance.h" 12 #include "base/lazy_instance.h"
12 #include "base/stl_util.h" 13 #include "base/stl_util.h"
13 #include "base/strings/utf_string_conversions.h" 14 #include "base/strings/utf_string_conversions.h"
14 #include "base/synchronization/lock.h" 15 #include "base/synchronization/lock.h"
16 #include "base/time/time.h"
15 #include "jni/Clipboard_jni.h" 17 #include "jni/Clipboard_jni.h"
16 #include "third_party/skia/include/core/SkBitmap.h" 18 #include "third_party/skia/include/core/SkBitmap.h"
17 #include "ui/gfx/geometry/size.h" 19 #include "ui/gfx/geometry/size.h"
18 20
19 // TODO:(andrewhayden) Support additional formats in Android: Bitmap, URI, HTML, 21 // TODO:(andrewhayden) Support additional formats in Android: Bitmap, URI, HTML,
20 // HTML+text now that Android's clipboard system supports them, then nuke the 22 // HTML+text now that Android's clipboard system supports them, then nuke the
21 // legacy implementation note below. 23 // legacy implementation note below.
22 24
23 // Legacy implementation note: 25 // Legacy implementation note:
24 // The Android clipboard system used to only support text format. So we used the 26 // The Android clipboard system used to only support text format. So we used the
25 // Android system when some text was added or retrieved from the system. For 27 // Android system when some text was added or retrieved from the system. For
26 // anything else, we STILL store the value in some process wide static 28 // anything else, we STILL store the value in some process wide static
27 // variable protected by a lock. So the (non-text) clipboard will only work 29 // variable protected by a lock. So the (non-text) clipboard will only work
28 // within the same process. 30 // within the same process.
29 31
30 using base::android::AttachCurrentThread; 32 using base::android::AttachCurrentThread;
31 using base::android::ClearException; 33 using base::android::ClearException;
32 using base::android::ConvertJavaStringToUTF8; 34 using base::android::ConvertJavaStringToUTF8;
33 using base::android::ConvertUTF8ToJavaString; 35 using base::android::ConvertUTF8ToJavaString;
34 using base::android::ScopedJavaGlobalRef; 36 using base::android::ScopedJavaGlobalRef;
35 using base::android::ScopedJavaLocalRef; 37 using base::android::ScopedJavaLocalRef;
36 38
37 namespace ui { 39 namespace ui {
38 40
39 namespace { 41 namespace {
42
40 // Various formats we support. 43 // Various formats we support.
41 const char kURLFormat[] = "url"; 44 const char kURLFormat[] = "url";
42 const char kPlainTextFormat[] = "text"; 45 const char kPlainTextFormat[] = "text";
43 const char kHTMLFormat[] = "html"; 46 const char kHTMLFormat[] = "html";
44 const char kRTFFormat[] = "rtf"; 47 const char kRTFFormat[] = "rtf";
45 const char kBitmapFormat[] = "bitmap"; 48 const char kBitmapFormat[] = "bitmap";
46 const char kWebKitSmartPasteFormat[] = "webkit_smart"; 49 const char kWebKitSmartPasteFormat[] = "webkit_smart";
47 const char kBookmarkFormat[] = "bookmark"; 50 const char kBookmarkFormat[] = "bookmark";
48 51
49 class ClipboardMap { 52 class ClipboardMap {
50 public: 53 public:
51 ClipboardMap(); 54 ClipboardMap();
52 std::string Get(const std::string& format); 55 std::string Get(const std::string& format);
53 int64_t GetLastClipboardChangeTimeInMillis(); 56 uint64_t GetSequenceNumber() const;
57 base::Time GetLastModifiedTime() const;
58 void ClearLastModifiedTime();
54 bool HasFormat(const std::string& format); 59 bool HasFormat(const std::string& format);
60 void OnPrimaryClipboardChanged();
55 void Set(const std::string& format, const std::string& data); 61 void Set(const std::string& format, const std::string& data);
56 void CommitToAndroidClipboard(); 62 void CommitToAndroidClipboard();
57 void Clear(); 63 void Clear();
58 64
59 private: 65 private:
66 enum class MapState {
67 kOutOfDate,
68 kUpToDate,
69 kPreparingCommit,
70 };
71
60 void UpdateFromAndroidClipboard(); 72 void UpdateFromAndroidClipboard();
61 std::map<std::string, std::string> map_; 73 std::map<std::string, std::string> map_;
74 MapState map_state_;
62 base::Lock lock_; 75 base::Lock lock_;
63 76
64 int64_t last_clipboard_change_time_ms_; 77 uint64_t sequence_number_;
78 base::Time last_modified_time_;
65 79
66 // Java class and methods for the Android ClipboardManager. 80 // Java class and methods for the Android ClipboardManager.
67 ScopedJavaGlobalRef<jobject> clipboard_manager_; 81 ScopedJavaGlobalRef<jobject> clipboard_manager_;
68 }; 82 };
69 base::LazyInstance<ClipboardMap>::Leaky g_map = LAZY_INSTANCE_INITIALIZER; 83 base::LazyInstance<ClipboardMap>::Leaky g_map = LAZY_INSTANCE_INITIALIZER;
70 84
71 ClipboardMap::ClipboardMap() { 85 ClipboardMap::ClipboardMap() : map_state_(MapState::kOutOfDate) {
72 clipboard_manager_.Reset(Java_Clipboard_getInstance(AttachCurrentThread())); 86 clipboard_manager_.Reset(Java_Clipboard_getInstance(AttachCurrentThread()));
73 DCHECK(clipboard_manager_.obj()); 87 DCHECK(clipboard_manager_.obj());
74 } 88 }
75 89
76 std::string ClipboardMap::Get(const std::string& format) { 90 std::string ClipboardMap::Get(const std::string& format) {
77 base::AutoLock lock(lock_); 91 base::AutoLock lock(lock_);
78 UpdateFromAndroidClipboard(); 92 UpdateFromAndroidClipboard();
79 std::map<std::string, std::string>::const_iterator it = map_.find(format); 93 std::map<std::string, std::string>::const_iterator it = map_.find(format);
80 return it == map_.end() ? std::string() : it->second; 94 return it == map_.end() ? std::string() : it->second;
81 } 95 }
82 96
83 int64_t ClipboardMap::GetLastClipboardChangeTimeInMillis() { 97 uint64_t ClipboardMap::GetSequenceNumber() const {
84 base::AutoLock lock(lock_); 98 return sequence_number_;
85 UpdateFromAndroidClipboard(); 99 }
86 return last_clipboard_change_time_ms_; 100
101 base::Time ClipboardMap::GetLastModifiedTime() const {
102 return last_modified_time_;
103 }
104
105 void ClipboardMap::ClearLastModifiedTime() {
106 last_modified_time_ = base::Time();
87 } 107 }
88 108
89 bool ClipboardMap::HasFormat(const std::string& format) { 109 bool ClipboardMap::HasFormat(const std::string& format) {
90 base::AutoLock lock(lock_); 110 base::AutoLock lock(lock_);
91 UpdateFromAndroidClipboard(); 111 UpdateFromAndroidClipboard();
92 return base::ContainsKey(map_, format); 112 return base::ContainsKey(map_, format);
93 } 113 }
94 114
115 void ClipboardMap::OnPrimaryClipboardChanged() {
116 sequence_number_++;
117 last_modified_time_ = base::Time::Now();
118 map_state_ = MapState::kOutOfDate;
119 }
120
95 void ClipboardMap::Set(const std::string& format, const std::string& data) { 121 void ClipboardMap::Set(const std::string& format, const std::string& data) {
96 base::AutoLock lock(lock_); 122 base::AutoLock lock(lock_);
97 map_[format] = data; 123 map_[format] = data;
124 map_state_ = MapState::kPreparingCommit;
98 } 125 }
99 126
100 void ClipboardMap::CommitToAndroidClipboard() { 127 void ClipboardMap::CommitToAndroidClipboard() {
101 JNIEnv* env = AttachCurrentThread(); 128 JNIEnv* env = AttachCurrentThread();
102 base::AutoLock lock(lock_); 129 base::AutoLock lock(lock_);
103 if (base::ContainsKey(map_, kHTMLFormat)) { 130 if (base::ContainsKey(map_, kHTMLFormat)) {
104 // Android's API for storing HTML content on the clipboard requires a plain- 131 // Android's API for storing HTML content on the clipboard requires a plain-
105 // text representation to be available as well. 132 // text representation to be available as well.
106 if (!base::ContainsKey(map_, kPlainTextFormat)) 133 if (!base::ContainsKey(map_, kPlainTextFormat))
107 return; 134 return;
108 135
109 ScopedJavaLocalRef<jstring> html = 136 ScopedJavaLocalRef<jstring> html =
110 ConvertUTF8ToJavaString(env, map_[kHTMLFormat]); 137 ConvertUTF8ToJavaString(env, map_[kHTMLFormat]);
111 ScopedJavaLocalRef<jstring> text = 138 ScopedJavaLocalRef<jstring> text =
112 ConvertUTF8ToJavaString(env, map_[kPlainTextFormat]); 139 ConvertUTF8ToJavaString(env, map_[kPlainTextFormat]);
113 140
114 DCHECK(html.obj() && text.obj()); 141 DCHECK(html.obj() && text.obj());
115 Java_Clipboard_setHTMLText(env, clipboard_manager_, html, text); 142 Java_Clipboard_setHTMLText(env, clipboard_manager_, html, text);
116 } else if (base::ContainsKey(map_, kPlainTextFormat)) { 143 } else if (base::ContainsKey(map_, kPlainTextFormat)) {
117 ScopedJavaLocalRef<jstring> str = 144 ScopedJavaLocalRef<jstring> str =
118 ConvertUTF8ToJavaString(env, map_[kPlainTextFormat]); 145 ConvertUTF8ToJavaString(env, map_[kPlainTextFormat]);
119 DCHECK(str.obj()); 146 DCHECK(str.obj());
120 Java_Clipboard_setText(env, clipboard_manager_, str); 147 Java_Clipboard_setText(env, clipboard_manager_, str);
121 } else { 148 } else {
122 Java_Clipboard_clear(env, clipboard_manager_); 149 Java_Clipboard_clear(env, clipboard_manager_);
123 NOTIMPLEMENTED(); 150 NOTIMPLEMENTED();
124 } 151 }
152 map_state_ = MapState::kUpToDate;
153 sequence_number_++;
154 last_modified_time_ = base::Time::Now();
125 } 155 }
126 156
127 void ClipboardMap::Clear() { 157 void ClipboardMap::Clear() {
128 JNIEnv* env = AttachCurrentThread(); 158 JNIEnv* env = AttachCurrentThread();
129 base::AutoLock lock(lock_); 159 base::AutoLock lock(lock_);
130 map_.clear(); 160 map_.clear();
131 Java_Clipboard_clear(env, clipboard_manager_); 161 Java_Clipboard_clear(env, clipboard_manager_);
162 map_state_ = MapState::kUpToDate;
163 sequence_number_++;
164 last_modified_time_ = base::Time::Now();
132 } 165 }
133 166
134 // Add a key:jstr pair to map, but only if jstr is not null, and also 167 // Add a key:jstr pair to map, but only if jstr is not null, and also
135 // not empty. 168 // not empty.
136 void AddMapEntry(JNIEnv* env, 169 void AddMapEntry(JNIEnv* env,
137 std::map<std::string, std::string>* map, 170 std::map<std::string, std::string>* map,
138 const char* key, 171 const char* key,
139 const ScopedJavaLocalRef<jstring>& jstr) { 172 const ScopedJavaLocalRef<jstring>& jstr) {
140 if (!jstr.is_null()) { 173 if (!jstr.is_null()) {
141 std::string str = ConvertJavaStringToUTF8(env, jstr.obj()); 174 std::string str = ConvertJavaStringToUTF8(env, jstr.obj());
142 if (!str.empty()) 175 if (!str.empty())
143 (*map)[key] = str; 176 (*map)[key] = str;
144 } 177 }
145 } 178 }
146 179
147 // Return true if all the key-value pairs in map1 are also in map2. 180 void ClipboardMap::UpdateFromAndroidClipboard() {
148 bool MapIsSubset(const std::map<std::string, std::string>& map1, 181 DCHECK_NE(MapState::kPreparingCommit, map_state_);
149 const std::map<std::string, std::string>& map2) { 182 if (map_state_ == MapState::kUpToDate)
150 for (const auto& val : map1) { 183 return;
151 auto iter = map2.find(val.first);
152 if (iter == map2.end() || iter->second != val.second)
153 return false;
154 }
155 return true;
156 }
157 184
158 void ClipboardMap::UpdateFromAndroidClipboard() { 185 // Fetch the current Android clipboard state.
159 // Fetch the current Android clipboard state. Replace our state with
160 // the Android state if the Android state has been changed.
161 lock_.AssertAcquired(); 186 lock_.AssertAcquired();
162 JNIEnv* env = AttachCurrentThread(); 187 JNIEnv* env = AttachCurrentThread();
163 188
164 std::map<std::string, std::string> android_clipboard_state;
165
166 ScopedJavaLocalRef<jstring> jtext = 189 ScopedJavaLocalRef<jstring> jtext =
167 Java_Clipboard_getCoercedText(env, clipboard_manager_); 190 Java_Clipboard_getCoercedText(env, clipboard_manager_);
168 ScopedJavaLocalRef<jstring> jhtml = 191 ScopedJavaLocalRef<jstring> jhtml =
169 Java_Clipboard_getHTMLText(env, clipboard_manager_); 192 Java_Clipboard_getHTMLText(env, clipboard_manager_);
170 193
171 AddMapEntry(env, &android_clipboard_state, kPlainTextFormat, jtext); 194 AddMapEntry(env, &map_, kPlainTextFormat, jtext);
172 AddMapEntry(env, &android_clipboard_state, kHTMLFormat, jhtml); 195 AddMapEntry(env, &map_, kHTMLFormat, jhtml);
173 last_clipboard_change_time_ms_ =
174 Java_Clipboard_getClipboardContentChangeTimeInMillis(env,
175 clipboard_manager_);
176 196
177 if (!MapIsSubset(android_clipboard_state, map_)) 197 map_state_ = MapState::kUpToDate;
178 android_clipboard_state.swap(map_);
179 } 198 }
180 199
181 } // namespace 200 } // namespace
182 201
183 // Clipboard::FormatType implementation. 202 // Clipboard::FormatType implementation.
184 Clipboard::FormatType::FormatType() { 203 Clipboard::FormatType::FormatType() {
185 } 204 }
186 205
187 Clipboard::FormatType::FormatType(const std::string& native_format) 206 Clipboard::FormatType::FormatType(const std::string& native_format)
188 : data_(native_format) { 207 : data_(native_format) {
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
270 return type; 289 return type;
271 } 290 }
272 291
273 // Clipboard factory method. 292 // Clipboard factory method.
274 // static 293 // static
275 Clipboard* Clipboard::Create() { 294 Clipboard* Clipboard::Create() {
276 return new ClipboardAndroid; 295 return new ClipboardAndroid;
277 } 296 }
278 297
279 // ClipboardAndroid implementation. 298 // ClipboardAndroid implementation.
299
300 void ClipboardAndroid::OnPrimaryClipChanged(
301 JNIEnv* env,
302 const base::android::JavaParamRef<jobject>& obj) {
303 g_map.Get().OnPrimaryClipboardChanged();
304 }
305
280 ClipboardAndroid::ClipboardAndroid() { 306 ClipboardAndroid::ClipboardAndroid() {
281 DCHECK(CalledOnValidThread()); 307 DCHECK(CalledOnValidThread());
282 } 308 }
283 309
284 ClipboardAndroid::~ClipboardAndroid() { 310 ClipboardAndroid::~ClipboardAndroid() {
285 DCHECK(CalledOnValidThread()); 311 DCHECK(CalledOnValidThread());
286 } 312 }
287 313
288 void ClipboardAndroid::OnPreShutdown() {} 314 void ClipboardAndroid::OnPreShutdown() {}
289 315
290 uint64_t ClipboardAndroid::GetSequenceNumber(ClipboardType /* type */) const { 316 uint64_t ClipboardAndroid::GetSequenceNumber(ClipboardType /* type */) const {
291 DCHECK(CalledOnValidThread()); 317 DCHECK(CalledOnValidThread());
292 // TODO: implement this. For now this interface will advertise 318 return g_map.Get().GetSequenceNumber();
293 // that the clipboard never changes. That's fine as long as we
294 // don't rely on this signal.
295 return 0;
296 } 319 }
297 320
298 bool ClipboardAndroid::IsFormatAvailable(const Clipboard::FormatType& format, 321 bool ClipboardAndroid::IsFormatAvailable(const Clipboard::FormatType& format,
299 ClipboardType type) const { 322 ClipboardType type) const {
300 DCHECK(CalledOnValidThread()); 323 DCHECK(CalledOnValidThread());
301 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); 324 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
302 return g_map.Get().HasFormat(format.ToString()); 325 return g_map.Get().HasFormat(format.ToString());
303 } 326 }
304 327
305 void ClipboardAndroid::Clear(ClipboardType type) { 328 void ClipboardAndroid::Clear(ClipboardType type) {
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after
407 DCHECK(CalledOnValidThread()); 430 DCHECK(CalledOnValidThread());
408 NOTIMPLEMENTED(); 431 NOTIMPLEMENTED();
409 } 432 }
410 433
411 void ClipboardAndroid::ReadData(const Clipboard::FormatType& format, 434 void ClipboardAndroid::ReadData(const Clipboard::FormatType& format,
412 std::string* result) const { 435 std::string* result) const {
413 DCHECK(CalledOnValidThread()); 436 DCHECK(CalledOnValidThread());
414 *result = g_map.Get().Get(format.ToString()); 437 *result = g_map.Get().Get(format.ToString());
415 } 438 }
416 439
417 base::Time ClipboardAndroid::GetClipboardLastModifiedTime() const { 440 base::Time ClipboardAndroid::GetLastModifiedTime() const {
418 DCHECK(CalledOnValidThread()); 441 DCHECK(CalledOnValidThread());
419 return base::Time::FromJavaTime( 442 return g_map.Get().GetLastModifiedTime();
420 g_map.Get().GetLastClipboardChangeTimeInMillis()); 443 }
444
445 void ClipboardAndroid::ClearLastModifiedTime() {
446 DCHECK(CalledOnValidThread());
447 g_map.Get().ClearLastModifiedTime();
421 } 448 }
422 449
423 // Main entry point used to write several values in the clipboard. 450 // Main entry point used to write several values in the clipboard.
424 void ClipboardAndroid::WriteObjects(ClipboardType type, 451 void ClipboardAndroid::WriteObjects(ClipboardType type,
425 const ObjectMap& objects) { 452 const ObjectMap& objects) {
426 DCHECK(CalledOnValidThread()); 453 DCHECK(CalledOnValidThread());
427 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); 454 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE);
428 g_map.Get().Clear(); 455 g_map.Get().Clear();
429 456
430 for (ObjectMap::const_iterator iter = objects.begin(); iter != objects.end(); 457 for (ObjectMap::const_iterator iter = objects.begin(); iter != objects.end();
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
478 } 505 }
479 g_map.Get().Set(kBitmapFormat, packed); 506 g_map.Get().Set(kBitmapFormat, packed);
480 } 507 }
481 508
482 void ClipboardAndroid::WriteData(const Clipboard::FormatType& format, 509 void ClipboardAndroid::WriteData(const Clipboard::FormatType& format,
483 const char* data_data, 510 const char* data_data,
484 size_t data_len) { 511 size_t data_len) {
485 g_map.Get().Set(format.ToString(), std::string(data_data, data_len)); 512 g_map.Get().Set(format.ToString(), std::string(data_data, data_len));
486 } 513 }
487 514
515 bool RegisterClipboardAndroid(JNIEnv* env) {
516 return RegisterNativesImpl(env);
517 }
518
519 // Returns a pointer to the current ClipboardAndroid object.
520 static jlong Init(JNIEnv* env,
521 const base::android::JavaParamRef<jobject>& obj) {
522 return reinterpret_cast<intptr_t>(Clipboard::GetForCurrentThread());
523 }
524
488 } // namespace ui 525 } // namespace ui
OLDNEW
« no previous file with comments | « ui/base/clipboard/clipboard_android.h ('k') | ui/base/test/test_clipboard.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698