OLD | NEW |
---|---|
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" | |
17 #include "components/prefs/pref_registry_simple.h" | |
15 #include "jni/Clipboard_jni.h" | 18 #include "jni/Clipboard_jni.h" |
16 #include "third_party/skia/include/core/SkBitmap.h" | 19 #include "third_party/skia/include/core/SkBitmap.h" |
17 #include "ui/gfx/geometry/size.h" | 20 #include "ui/gfx/geometry/size.h" |
18 | 21 |
19 // TODO:(andrewhayden) Support additional formats in Android: Bitmap, URI, HTML, | 22 // TODO:(andrewhayden) Support additional formats in Android: Bitmap, URI, HTML, |
20 // HTML+text now that Android's clipboard system supports them, then nuke the | 23 // HTML+text now that Android's clipboard system supports them, then nuke the |
21 // legacy implementation note below. | 24 // legacy implementation note below. |
22 | 25 |
23 // Legacy implementation note: | 26 // Legacy implementation note: |
24 // The Android clipboard system used to only support text format. So we used the | 27 // 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 | 28 // 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 | 29 // 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 | 30 // variable protected by a lock. So the (non-text) clipboard will only work |
28 // within the same process. | 31 // within the same process. |
29 | 32 |
30 using base::android::AttachCurrentThread; | 33 using base::android::AttachCurrentThread; |
31 using base::android::ClearException; | 34 using base::android::ClearException; |
32 using base::android::ConvertJavaStringToUTF8; | 35 using base::android::ConvertJavaStringToUTF8; |
33 using base::android::ConvertUTF8ToJavaString; | 36 using base::android::ConvertUTF8ToJavaString; |
34 using base::android::ScopedJavaGlobalRef; | 37 using base::android::ScopedJavaGlobalRef; |
35 using base::android::ScopedJavaLocalRef; | 38 using base::android::ScopedJavaLocalRef; |
36 | 39 |
37 namespace ui { | 40 namespace ui { |
38 | 41 |
39 namespace { | 42 namespace { |
43 | |
44 const char kClipboardLastModifiedTimePref[] = "ui.clipboard.last_modified_time"; | |
45 | |
40 // Various formats we support. | 46 // Various formats we support. |
41 const char kURLFormat[] = "url"; | 47 const char kURLFormat[] = "url"; |
42 const char kPlainTextFormat[] = "text"; | 48 const char kPlainTextFormat[] = "text"; |
43 const char kHTMLFormat[] = "html"; | 49 const char kHTMLFormat[] = "html"; |
44 const char kRTFFormat[] = "rtf"; | 50 const char kRTFFormat[] = "rtf"; |
45 const char kBitmapFormat[] = "bitmap"; | 51 const char kBitmapFormat[] = "bitmap"; |
46 const char kWebKitSmartPasteFormat[] = "webkit_smart"; | 52 const char kWebKitSmartPasteFormat[] = "webkit_smart"; |
47 const char kBookmarkFormat[] = "bookmark"; | 53 const char kBookmarkFormat[] = "bookmark"; |
48 | 54 |
49 class ClipboardMap { | 55 class ClipboardMap { |
50 public: | 56 public: |
51 ClipboardMap(); | 57 ClipboardMap(); |
52 std::string Get(const std::string& format); | 58 std::string Get(const std::string& format); |
53 int64_t GetLastClipboardChangeTimeInMillis(); | 59 uint64_t GetSequenceNumber() const; |
60 base::Time GetClipboardLastModifiedTime() const; | |
61 void ClearClipboardLastModifiedTime(); | |
dcheng
2017/04/12 00:56:09
Ditto: omit Clipboard in the names here.
Mark P
2017/04/12 21:33:51
Done.
| |
54 bool HasFormat(const std::string& format); | 62 bool HasFormat(const std::string& format); |
63 void OnPrimaryClipboardChanged(); | |
55 void Set(const std::string& format, const std::string& data); | 64 void Set(const std::string& format, const std::string& data); |
56 void CommitToAndroidClipboard(); | 65 void CommitToAndroidClipboard(); |
57 void Clear(); | 66 void Clear(); |
58 | 67 |
59 private: | 68 private: |
60 void UpdateFromAndroidClipboard(); | 69 void UpdateFromAndroidClipboard(); |
70 void UpdateLastModifiedTimePref(); | |
61 std::map<std::string, std::string> map_; | 71 std::map<std::string, std::string> map_; |
62 base::Lock lock_; | 72 base::Lock lock_; |
63 | 73 |
64 int64_t last_clipboard_change_time_ms_; | 74 uint64_t sequence_number_; |
75 base::Time clipboard_last_modified_time_; | |
dcheng
2017/04/12 00:56:09
Nit: no clipboard_
Mark P
2017/04/12 21:33:51
Done.
| |
65 | 76 |
66 // Java class and methods for the Android ClipboardManager. | 77 // Java class and methods for the Android ClipboardManager. |
67 ScopedJavaGlobalRef<jobject> clipboard_manager_; | 78 ScopedJavaGlobalRef<jobject> clipboard_manager_; |
68 }; | 79 }; |
69 base::LazyInstance<ClipboardMap>::Leaky g_map = LAZY_INSTANCE_INITIALIZER; | 80 base::LazyInstance<ClipboardMap>::Leaky g_map = LAZY_INSTANCE_INITIALIZER; |
70 | 81 |
71 ClipboardMap::ClipboardMap() { | 82 ClipboardMap::ClipboardMap() { |
72 clipboard_manager_.Reset(Java_Clipboard_getInstance(AttachCurrentThread())); | 83 clipboard_manager_.Reset(Java_Clipboard_getInstance(AttachCurrentThread())); |
73 DCHECK(clipboard_manager_.obj()); | 84 DCHECK(clipboard_manager_.obj()); |
85 UpdateFromAndroidClipboard(); | |
86 // *** | |
87 // Read |clipboard_last_modified_time_| as a time (in seconds) from prefs | |
88 // under name |kClipboardLastModifiedTimePref|. Use 0 if the pref doesn't | |
89 // exist. | |
74 } | 90 } |
75 | 91 |
76 std::string ClipboardMap::Get(const std::string& format) { | 92 std::string ClipboardMap::Get(const std::string& format) { |
77 base::AutoLock lock(lock_); | 93 base::AutoLock lock(lock_); |
78 UpdateFromAndroidClipboard(); | |
79 std::map<std::string, std::string>::const_iterator it = map_.find(format); | 94 std::map<std::string, std::string>::const_iterator it = map_.find(format); |
80 return it == map_.end() ? std::string() : it->second; | 95 return it == map_.end() ? std::string() : it->second; |
81 } | 96 } |
82 | 97 |
83 int64_t ClipboardMap::GetLastClipboardChangeTimeInMillis() { | 98 uint64_t ClipboardMap::GetSequenceNumber() const { |
84 base::AutoLock lock(lock_); | 99 return sequence_number_; |
85 UpdateFromAndroidClipboard(); | 100 } |
86 return last_clipboard_change_time_ms_; | 101 |
102 base::Time ClipboardMap::GetClipboardLastModifiedTime() const { | |
103 return clipboard_last_modified_time_; | |
104 } | |
105 | |
106 void ClipboardMap::ClearClipboardLastModifiedTime() { | |
107 clipboard_last_modified_time_ = base::Time(); | |
108 UpdateLastModifiedTimePref(); | |
87 } | 109 } |
88 | 110 |
89 bool ClipboardMap::HasFormat(const std::string& format) { | 111 bool ClipboardMap::HasFormat(const std::string& format) { |
90 base::AutoLock lock(lock_); | 112 base::AutoLock lock(lock_); |
113 return base::ContainsKey(map_, format); | |
114 } | |
115 | |
116 void ClipboardMap::OnPrimaryClipboardChanged() { | |
117 sequence_number_++; | |
118 clipboard_last_modified_time_ = base::Time::Now(); | |
119 UpdateLastModifiedTimePref(); | |
91 UpdateFromAndroidClipboard(); | 120 UpdateFromAndroidClipboard(); |
dcheng
2017/04/12 00:56:09
I do wonder if we want to do this work up-front wh
Mark P
2017/04/12 04:21:11
Sure, I think I can do that pretty cleanly.
I'll m
Mark P
2017/04/12 21:33:51
Done.
| |
92 return base::ContainsKey(map_, format); | |
93 } | 121 } |
94 | 122 |
95 void ClipboardMap::Set(const std::string& format, const std::string& data) { | 123 void ClipboardMap::Set(const std::string& format, const std::string& data) { |
96 base::AutoLock lock(lock_); | 124 base::AutoLock lock(lock_); |
97 map_[format] = data; | 125 map_[format] = data; |
98 } | 126 } |
99 | 127 |
100 void ClipboardMap::CommitToAndroidClipboard() { | 128 void ClipboardMap::CommitToAndroidClipboard() { |
101 JNIEnv* env = AttachCurrentThread(); | 129 JNIEnv* env = AttachCurrentThread(); |
102 base::AutoLock lock(lock_); | 130 base::AutoLock lock(lock_); |
(...skipping 12 matching lines...) Expand all Loading... | |
115 Java_Clipboard_setHTMLText(env, clipboard_manager_, html, text); | 143 Java_Clipboard_setHTMLText(env, clipboard_manager_, html, text); |
116 } else if (base::ContainsKey(map_, kPlainTextFormat)) { | 144 } else if (base::ContainsKey(map_, kPlainTextFormat)) { |
117 ScopedJavaLocalRef<jstring> str = | 145 ScopedJavaLocalRef<jstring> str = |
118 ConvertUTF8ToJavaString(env, map_[kPlainTextFormat]); | 146 ConvertUTF8ToJavaString(env, map_[kPlainTextFormat]); |
119 DCHECK(str.obj()); | 147 DCHECK(str.obj()); |
120 Java_Clipboard_setText(env, clipboard_manager_, str); | 148 Java_Clipboard_setText(env, clipboard_manager_, str); |
121 } else { | 149 } else { |
122 Java_Clipboard_clear(env, clipboard_manager_); | 150 Java_Clipboard_clear(env, clipboard_manager_); |
123 NOTIMPLEMENTED(); | 151 NOTIMPLEMENTED(); |
124 } | 152 } |
153 sequence_number_++; | |
154 clipboard_last_modified_time_ = base::Time::Now(); | |
155 UpdateLastModifiedTimePref(); | |
125 } | 156 } |
126 | 157 |
127 void ClipboardMap::Clear() { | 158 void ClipboardMap::Clear() { |
128 JNIEnv* env = AttachCurrentThread(); | 159 JNIEnv* env = AttachCurrentThread(); |
129 base::AutoLock lock(lock_); | 160 base::AutoLock lock(lock_); |
130 map_.clear(); | 161 map_.clear(); |
131 Java_Clipboard_clear(env, clipboard_manager_); | 162 Java_Clipboard_clear(env, clipboard_manager_); |
163 sequence_number_++; | |
164 clipboard_last_modified_time_ = base::Time::Now(); | |
165 UpdateLastModifiedTimePref(); | |
132 } | 166 } |
133 | 167 |
134 // Add a key:jstr pair to map, but only if jstr is not null, and also | 168 // Add a key:jstr pair to map, but only if jstr is not null, and also |
135 // not empty. | 169 // not empty. |
136 void AddMapEntry(JNIEnv* env, | 170 void AddMapEntry(JNIEnv* env, |
137 std::map<std::string, std::string>* map, | 171 std::map<std::string, std::string>* map, |
138 const char* key, | 172 const char* key, |
139 const ScopedJavaLocalRef<jstring>& jstr) { | 173 const ScopedJavaLocalRef<jstring>& jstr) { |
140 if (!jstr.is_null()) { | 174 if (!jstr.is_null()) { |
141 std::string str = ConvertJavaStringToUTF8(env, jstr.obj()); | 175 std::string str = ConvertJavaStringToUTF8(env, jstr.obj()); |
142 if (!str.empty()) | 176 if (!str.empty()) |
143 (*map)[key] = str; | 177 (*map)[key] = str; |
144 } | 178 } |
145 } | 179 } |
146 | 180 |
147 // Return true if all the key-value pairs in map1 are also in map2. | |
148 bool MapIsSubset(const std::map<std::string, std::string>& map1, | |
149 const std::map<std::string, std::string>& map2) { | |
150 for (const auto& val : map1) { | |
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 | |
158 void ClipboardMap::UpdateFromAndroidClipboard() { | 181 void ClipboardMap::UpdateFromAndroidClipboard() { |
159 // Fetch the current Android clipboard state. Replace our state with | 182 // Fetch the current Android clipboard state. |
160 // the Android state if the Android state has been changed. | |
161 lock_.AssertAcquired(); | 183 lock_.AssertAcquired(); |
162 JNIEnv* env = AttachCurrentThread(); | 184 JNIEnv* env = AttachCurrentThread(); |
163 | 185 |
164 std::map<std::string, std::string> android_clipboard_state; | |
165 | |
166 ScopedJavaLocalRef<jstring> jtext = | 186 ScopedJavaLocalRef<jstring> jtext = |
167 Java_Clipboard_getCoercedText(env, clipboard_manager_); | 187 Java_Clipboard_getCoercedText(env, clipboard_manager_); |
168 ScopedJavaLocalRef<jstring> jhtml = | 188 ScopedJavaLocalRef<jstring> jhtml = |
169 Java_Clipboard_getHTMLText(env, clipboard_manager_); | 189 Java_Clipboard_getHTMLText(env, clipboard_manager_); |
170 | 190 |
171 AddMapEntry(env, &android_clipboard_state, kPlainTextFormat, jtext); | 191 AddMapEntry(env, &map_, kPlainTextFormat, jtext); |
172 AddMapEntry(env, &android_clipboard_state, kHTMLFormat, jhtml); | 192 AddMapEntry(env, &map_, kHTMLFormat, jhtml); |
173 last_clipboard_change_time_ms_ = | 193 } |
174 Java_Clipboard_getClipboardContentChangeTimeInMillis(env, | |
175 clipboard_manager_); | |
176 | 194 |
177 if (!MapIsSubset(android_clipboard_state, map_)) | 195 void ClipboardMap::UpdateLastModifiedTimePref() { |
178 android_clipboard_state.swap(map_); | 196 // *** |
197 // Record |clipboard_last_modified_time_| as a time (in seconds) to prefs | |
198 // under name |kClipboardLastModifiedTimePref|. Make sure that null times | |
199 // get recorded as 0. | |
179 } | 200 } |
180 | 201 |
181 } // namespace | 202 } // namespace |
182 | 203 |
183 // Clipboard::FormatType implementation. | 204 // Clipboard::FormatType implementation. |
184 Clipboard::FormatType::FormatType() { | 205 Clipboard::FormatType::FormatType() { |
185 } | 206 } |
186 | 207 |
187 Clipboard::FormatType::FormatType(const std::string& native_format) | 208 Clipboard::FormatType::FormatType(const std::string& native_format) |
188 : data_(native_format) { | 209 : data_(native_format) { |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
270 return type; | 291 return type; |
271 } | 292 } |
272 | 293 |
273 // Clipboard factory method. | 294 // Clipboard factory method. |
274 // static | 295 // static |
275 Clipboard* Clipboard::Create() { | 296 Clipboard* Clipboard::Create() { |
276 return new ClipboardAndroid; | 297 return new ClipboardAndroid; |
277 } | 298 } |
278 | 299 |
279 // ClipboardAndroid implementation. | 300 // ClipboardAndroid implementation. |
301 | |
302 // static | |
303 void ClipboardAndroid::RegisterPrefs(PrefRegistrySimple* registry) { | |
304 registry->RegisterUint64Pref(kClipboardLastModifiedTimePref, 0u); | |
305 } | |
306 | |
307 void ClipboardAndroid::OnPrimaryClipChanged( | |
308 JNIEnv* env, | |
309 const base::android::JavaParamRef<jobject>& obj) { | |
310 g_map.Get().OnPrimaryClipboardChanged(); | |
311 } | |
312 | |
280 ClipboardAndroid::ClipboardAndroid() { | 313 ClipboardAndroid::ClipboardAndroid() { |
281 DCHECK(CalledOnValidThread()); | 314 DCHECK(CalledOnValidThread()); |
282 } | 315 } |
283 | 316 |
284 ClipboardAndroid::~ClipboardAndroid() { | 317 ClipboardAndroid::~ClipboardAndroid() { |
285 DCHECK(CalledOnValidThread()); | 318 DCHECK(CalledOnValidThread()); |
286 } | 319 } |
287 | 320 |
288 void ClipboardAndroid::OnPreShutdown() {} | 321 void ClipboardAndroid::OnPreShutdown() {} |
289 | 322 |
290 uint64_t ClipboardAndroid::GetSequenceNumber(ClipboardType /* type */) const { | 323 uint64_t ClipboardAndroid::GetSequenceNumber(ClipboardType /* type */) const { |
291 DCHECK(CalledOnValidThread()); | 324 DCHECK(CalledOnValidThread()); |
292 // TODO: implement this. For now this interface will advertise | 325 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 } | 326 } |
297 | 327 |
298 bool ClipboardAndroid::IsFormatAvailable(const Clipboard::FormatType& format, | 328 bool ClipboardAndroid::IsFormatAvailable(const Clipboard::FormatType& format, |
299 ClipboardType type) const { | 329 ClipboardType type) const { |
300 DCHECK(CalledOnValidThread()); | 330 DCHECK(CalledOnValidThread()); |
301 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); | 331 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); |
302 return g_map.Get().HasFormat(format.ToString()); | 332 return g_map.Get().HasFormat(format.ToString()); |
303 } | 333 } |
304 | 334 |
305 void ClipboardAndroid::Clear(ClipboardType type) { | 335 void ClipboardAndroid::Clear(ClipboardType type) { |
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
409 } | 439 } |
410 | 440 |
411 void ClipboardAndroid::ReadData(const Clipboard::FormatType& format, | 441 void ClipboardAndroid::ReadData(const Clipboard::FormatType& format, |
412 std::string* result) const { | 442 std::string* result) const { |
413 DCHECK(CalledOnValidThread()); | 443 DCHECK(CalledOnValidThread()); |
414 *result = g_map.Get().Get(format.ToString()); | 444 *result = g_map.Get().Get(format.ToString()); |
415 } | 445 } |
416 | 446 |
417 base::Time ClipboardAndroid::GetClipboardLastModifiedTime() const { | 447 base::Time ClipboardAndroid::GetClipboardLastModifiedTime() const { |
418 DCHECK(CalledOnValidThread()); | 448 DCHECK(CalledOnValidThread()); |
419 return base::Time::FromJavaTime( | 449 return g_map.Get().GetClipboardLastModifiedTime(); |
420 g_map.Get().GetLastClipboardChangeTimeInMillis()); | 450 } |
451 | |
452 void ClipboardAndroid::ClearClipboardLastModifiedTime() { | |
453 DCHECK(CalledOnValidThread()); | |
454 g_map.Get().ClearClipboardLastModifiedTime(); | |
421 } | 455 } |
422 | 456 |
423 // Main entry point used to write several values in the clipboard. | 457 // Main entry point used to write several values in the clipboard. |
424 void ClipboardAndroid::WriteObjects(ClipboardType type, | 458 void ClipboardAndroid::WriteObjects(ClipboardType type, |
425 const ObjectMap& objects) { | 459 const ObjectMap& objects) { |
426 DCHECK(CalledOnValidThread()); | 460 DCHECK(CalledOnValidThread()); |
427 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); | 461 DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); |
428 g_map.Get().Clear(); | 462 g_map.Get().Clear(); |
429 | 463 |
430 for (ObjectMap::const_iterator iter = objects.begin(); iter != objects.end(); | 464 for (ObjectMap::const_iterator iter = objects.begin(); iter != objects.end(); |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
478 } | 512 } |
479 g_map.Get().Set(kBitmapFormat, packed); | 513 g_map.Get().Set(kBitmapFormat, packed); |
480 } | 514 } |
481 | 515 |
482 void ClipboardAndroid::WriteData(const Clipboard::FormatType& format, | 516 void ClipboardAndroid::WriteData(const Clipboard::FormatType& format, |
483 const char* data_data, | 517 const char* data_data, |
484 size_t data_len) { | 518 size_t data_len) { |
485 g_map.Get().Set(format.ToString(), std::string(data_data, data_len)); | 519 g_map.Get().Set(format.ToString(), std::string(data_data, data_len)); |
486 } | 520 } |
487 | 521 |
522 bool RegisterClipboardAndroid(JNIEnv* env) { | |
523 return RegisterNativesImpl(env); | |
524 } | |
525 | |
526 // Returns a pointer to the current ClipboardAndroid object. | |
527 static jlong Init(JNIEnv* env, | |
528 const base::android::JavaParamRef<jobject>& obj) { | |
529 return reinterpret_cast<intptr_t>(Clipboard::GetForCurrentThread()); | |
530 } | |
531 | |
488 } // namespace ui | 532 } // namespace ui |
OLD | NEW |