Commit 4417ce64 authored by cco3's avatar cco3 Committed by Commit bot

Pass Physical Web metadata through a struct

Currently, we use DictionaryValues in the Physical Web data source.
The problem with this is that we cannot store store longs (timestamps).
This change introduces a metadata struct that can be requested from the
data source, deprecating the DictionaryValue option.

BUG=667722

Review-Url: https://codereview.chromium.org/2561493002
Cr-Commit-Position: refs/heads/master@{#443425}
parent c4bfb0e6
......@@ -283,7 +283,7 @@ class UrlManager {
PwsResult pwsResult = mPwsResultMap.get(requestUrl);
if (pwsResult != null) {
nativeAppendMetadataItem(nativePhysicalWebCollection, requestUrl,
urlInfo.getDistance(), (int) urlInfo.getScanTimestamp(), pwsResult.siteUrl,
urlInfo.getDistance(), urlInfo.getScanTimestamp(), pwsResult.siteUrl,
pwsResult.iconUrl, pwsResult.title, pwsResult.description,
pwsResult.groupId);
}
......@@ -825,7 +825,7 @@ class UrlManager {
private native long nativeInit();
private native void nativeAppendMetadataItem(long nativePhysicalWebCollection,
String requestUrl, double distanceEstimate, int scanTimestamp, String siteUrl,
String requestUrl, double distanceEstimate, long scanTimestamp, String siteUrl,
String iconUrl, String title, String description, String groupId);
private native void nativeOnFound(long nativePhysicalWebDataSourceAndroid, String url);
private native void nativeOnLost(long nativePhysicalWebDataSourceAndroid, String url);
......
......@@ -19,7 +19,8 @@ using base::android::JavaParamRef;
using base::android::ScopedJavaLocalRef;
PhysicalWebCollection::PhysicalWebCollection()
: metadata_list_(base::MakeUnique<base::ListValue>()),
: dictionary_value_list_(base::MakeUnique<base::ListValue>()),
metadata_list_(base::MakeUnique<physical_web::MetadataList>()),
accessed_once_(false) {}
PhysicalWebCollection::~PhysicalWebCollection() {}
......@@ -29,37 +30,56 @@ void PhysicalWebCollection::AppendMetadataItem(
const JavaParamRef<jobject>& obj,
const JavaParamRef<jstring>& j_request_url,
jdouble distance_estimate,
jint scan_timestamp,
jlong scan_timestamp,
const JavaParamRef<jstring>& j_site_url,
const JavaParamRef<jstring>& j_icon_url,
const JavaParamRef<jstring>& j_title,
const JavaParamRef<jstring>& j_description,
const JavaParamRef<jstring>& j_group_id) {
auto metadata_item = new base::DictionaryValue();
metadata_item->SetString(physical_web::kScannedUrlKey,
// Create a DictionaryValue. Notice that we no longer set the timestamp
// since it is a long and the DictionaryValue can only store ints.
auto dictionary_value = base::MakeUnique<base::DictionaryValue>();
dictionary_value->SetString(physical_web::kScannedUrlKey,
ConvertJavaStringToUTF8(j_request_url));
metadata_item->SetDouble(physical_web::kDistanceEstimateKey,
dictionary_value->SetDouble(physical_web::kDistanceEstimateKey,
distance_estimate);
metadata_item->SetInteger(physical_web::kScanTimestampKey, scan_timestamp);
metadata_item->SetString(physical_web::kResolvedUrlKey,
dictionary_value->SetString(physical_web::kResolvedUrlKey,
ConvertJavaStringToUTF8(j_site_url));
metadata_item->SetString(physical_web::kIconUrlKey,
dictionary_value->SetString(physical_web::kIconUrlKey,
ConvertJavaStringToUTF8(j_icon_url));
metadata_item->SetString(physical_web::kTitleKey,
dictionary_value->SetString(physical_web::kTitleKey,
ConvertJavaStringToUTF8(j_title));
metadata_item->SetString(physical_web::kDescriptionKey,
dictionary_value->SetString(physical_web::kDescriptionKey,
ConvertJavaStringToUTF8(j_description));
metadata_item->SetString(physical_web::kGroupIdKey,
dictionary_value->SetString(physical_web::kGroupIdKey,
ConvertJavaStringToUTF8(j_group_id));
metadata_list_->Append(std::move(metadata_item));
dictionary_value_list_->Append(std::move(dictionary_value));
physical_web::Metadata metadata;
metadata.scanned_url = GURL(ConvertJavaStringToUTF8(j_request_url));
metadata.resolved_url = GURL(ConvertJavaStringToUTF8(j_site_url));
metadata.icon_url = GURL(ConvertJavaStringToUTF8(j_icon_url));
metadata.title = ConvertJavaStringToUTF8(j_title);
metadata.description = ConvertJavaStringToUTF8(j_description);
metadata.group_id = ConvertJavaStringToUTF8(j_group_id);
metadata.distance_estimate = distance_estimate;
metadata.scan_timestamp = base::Time::FromJavaTime(scan_timestamp);
metadata_list_->push_back(std::move(metadata));
}
std::unique_ptr<base::ListValue> PhysicalWebCollection::GetMetadataList() {
std::unique_ptr<physical_web::MetadataList>
PhysicalWebCollection::GetMetadataList() {
DCHECK(!accessed_once_);
accessed_once_ = true;
return std::move(metadata_list_);
}
std::unique_ptr<base::ListValue> PhysicalWebCollection::GetMetadata() {
DCHECK(!accessed_once_);
accessed_once_ = true;
return std::move(dictionary_value_list_);
}
PhysicalWebDataSourceAndroid::PhysicalWebDataSourceAndroid() {
Initialize();
}
......@@ -85,16 +105,28 @@ void PhysicalWebDataSourceAndroid::StopDiscovery() {
NOTREACHED();
}
std::unique_ptr<base::ListValue> PhysicalWebDataSourceAndroid::GetMetadata() {
std::unique_ptr<physical_web::MetadataList>
PhysicalWebDataSourceAndroid::GetMetadataList() {
JNIEnv* env = AttachCurrentThread();
auto pw_collection = base::MakeUnique<PhysicalWebCollection>();
Java_UrlManager_getPwCollection(env, url_manager_.obj(),
(long)pw_collection.get());
reinterpret_cast<long>(pw_collection.get()));
return pw_collection->GetMetadataList();
}
std::unique_ptr<base::ListValue>
PhysicalWebDataSourceAndroid::GetMetadata() {
JNIEnv* env = AttachCurrentThread();
auto pw_collection = base::MakeUnique<PhysicalWebCollection>();
Java_UrlManager_getPwCollection(env, url_manager_.obj(),
reinterpret_cast<long>(pw_collection.get()));
return pw_collection->GetMetadata();
}
bool PhysicalWebDataSourceAndroid::HasUnresolvedDiscoveries() {
NOTIMPLEMENTED();
return false;
......
......@@ -27,7 +27,7 @@ class PhysicalWebCollection {
const base::android::JavaParamRef<jobject>& obj,
const base::android::JavaParamRef<jstring>& j_request_url,
jdouble distance_estimate,
jint scan_timestamp,
jlong scan_timestamp,
const base::android::JavaParamRef<jstring>& j_site_url,
const base::android::JavaParamRef<jstring>& j_icon_url,
const base::android::JavaParamRef<jstring>& j_title,
......@@ -36,10 +36,17 @@ class PhysicalWebCollection {
// Returns the metadata list and transfers ownership of the list to the
// caller. Call only once.
std::unique_ptr<base::ListValue> GetMetadataList();
std::unique_ptr<physical_web::MetadataList> GetMetadataList();
// Returns the metadata list and transfers ownership of the list to the
// caller. Call only once.
// DEPRECATED
// TODO(cco3): Remove when we no longer rely on this.
std::unique_ptr<base::ListValue> GetMetadata();
private:
std::unique_ptr<base::ListValue> metadata_list_;
std::unique_ptr<base::ListValue> dictionary_value_list_;
std::unique_ptr<physical_web::MetadataList> metadata_list_;
bool accessed_once_;
DISALLOW_COPY_AND_ASSIGN(PhysicalWebCollection);
......@@ -59,6 +66,7 @@ class PhysicalWebDataSourceAndroid
void StopDiscovery() override;
std::unique_ptr<base::ListValue> GetMetadata() override;
std::unique_ptr<physical_web::MetadataList> GetMetadataList() override;
bool HasUnresolvedDiscoveries() override;
void OnFound(JNIEnv* env,
......
// 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 "chrome/browser/android/physical_web/physical_web_data_source_android.h"
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/memory/ptr_util.h"
#include "base/values.h"
#include "testing/gtest/include/gtest/gtest.h"
using base::android::JavaParamRef;
namespace {
const char kRequestUrl[] = "https://awesomesite.us/";
const double kDistanceEstimate = 42.0;
const int64_t kScanTimestamp = 10000000L; // 10M
const char kSiteUrl[] = "https://awesomesite.us/page.html";
const char kIconUrl[] = "https://awesomesite.us/favicon.ico";
const char kTitle[] = "AwesomeSite";
const char kDescription[] = "An awesome site";
const char kGroupId[] = "14a8ef122";
} // namespace
class PhysicalWebCollectionTest : public testing::Test {
public:
PhysicalWebCollectionTest() {}
~PhysicalWebCollectionTest() override {}
void SetUp() override {
collection_ = base::MakeUnique<PhysicalWebCollection>();
env_ = base::android::AttachCurrentThread();
}
void TearDown() override {
collection_.reset();
}
PhysicalWebCollection* Collection();
JNIEnv* Env();
jstring JavaString(const std::string& value);
private:
std::unique_ptr<PhysicalWebCollection> collection_;
JNIEnv* env_;
};
PhysicalWebCollection* PhysicalWebCollectionTest::Collection() {
DCHECK(collection_);
return collection_.get();
}
JNIEnv* PhysicalWebCollectionTest::Env() {
return env_;
}
jstring PhysicalWebCollectionTest::JavaString(const std::string& value) {
return base::android::ConvertUTF8ToJavaString(Env(), value).Release();
}
TEST_F(PhysicalWebCollectionTest, AppendMetadataItem_ListWork) {
Collection()->AppendMetadataItem(
Env(),
JavaParamRef<jobject>(NULL),
JavaParamRef<jstring>(Env(), JavaString(kRequestUrl)),
static_cast<jdouble>(kDistanceEstimate),
static_cast<jlong>(kScanTimestamp),
JavaParamRef<jstring>(Env(), JavaString(kSiteUrl)),
JavaParamRef<jstring>(Env(), JavaString(kIconUrl)),
JavaParamRef<jstring>(Env(), JavaString(kTitle)),
JavaParamRef<jstring>(Env(), JavaString(kDescription)),
JavaParamRef<jstring>(Env(), JavaString(kGroupId)));
std::unique_ptr<physical_web::MetadataList> metadata_list =
Collection()->GetMetadataList();
EXPECT_EQ(1U, metadata_list->size());
auto metadata = (*metadata_list.get())[0];
EXPECT_EQ(kRequestUrl, metadata.scanned_url.spec());
EXPECT_EQ(kDistanceEstimate, metadata.distance_estimate);
EXPECT_EQ(kScanTimestamp, metadata.scan_timestamp.ToJavaTime());
EXPECT_EQ(kSiteUrl, metadata.resolved_url.spec());
EXPECT_EQ(kIconUrl, metadata.icon_url.spec());
EXPECT_EQ(kTitle, metadata.title);
EXPECT_EQ(kDescription, metadata.description);
EXPECT_EQ(kGroupId, metadata.group_id);
}
TEST_F(PhysicalWebCollectionTest, AppendMetadataItem_DictionaryValueWorks) {
Collection()->AppendMetadataItem(
Env(),
JavaParamRef<jobject>(NULL),
JavaParamRef<jstring>(Env(), JavaString(kRequestUrl)),
static_cast<jdouble>(kDistanceEstimate),
static_cast<jlong>(kScanTimestamp),
JavaParamRef<jstring>(Env(), JavaString(kSiteUrl)),
JavaParamRef<jstring>(Env(), JavaString(kIconUrl)),
JavaParamRef<jstring>(Env(), JavaString(kTitle)),
JavaParamRef<jstring>(Env(), JavaString(kDescription)),
JavaParamRef<jstring>(Env(), JavaString(kGroupId)));
std::unique_ptr<base::ListValue> list_value = Collection()->GetMetadata();
EXPECT_EQ(1U, list_value->GetSize());
const base::Value* value;
EXPECT_TRUE(list_value->Get(0, &value));
const base::DictionaryValue* dictionary_value;
EXPECT_TRUE(value->GetAsDictionary(&dictionary_value));
std::string stored_scanned_url;
EXPECT_TRUE(dictionary_value->GetString(physical_web::kScannedUrlKey,
&stored_scanned_url));
EXPECT_EQ(kRequestUrl, stored_scanned_url);
double stored_distance_estimate;
EXPECT_TRUE(dictionary_value->GetDouble(physical_web::kDistanceEstimateKey,
&stored_distance_estimate));
EXPECT_EQ(kDistanceEstimate, stored_distance_estimate);
std::string stored_resolved_url;
EXPECT_TRUE(dictionary_value->GetString(physical_web::kResolvedUrlKey,
&stored_resolved_url));
EXPECT_EQ(kSiteUrl, stored_resolved_url);
std::string stored_icon_url;
EXPECT_TRUE(dictionary_value->GetString(physical_web::kIconUrlKey,
&stored_icon_url));
EXPECT_EQ(kIconUrl, stored_icon_url);
std::string stored_title;
EXPECT_TRUE(dictionary_value->GetString(physical_web::kTitleKey,
&stored_title));
EXPECT_EQ(kTitle, stored_title);
std::string stored_description;
EXPECT_TRUE(dictionary_value->GetString(physical_web::kDescriptionKey,
&stored_description));
EXPECT_EQ(kDescription, stored_description);
std::string stored_group_id;
EXPECT_TRUE(dictionary_value->GetString(physical_web::kGroupIdKey,
&stored_group_id));
EXPECT_EQ(kGroupId, stored_group_id);
}
......@@ -3042,6 +3042,7 @@ test("unit_tests") {
"../browser/android/mock_location_settings.cc",
"../browser/android/mock_location_settings.h",
"../browser/android/net/external_estimate_provider_android_unittest.cc",
"../browser/android/physical_web/physical_web_data_source_android_unittest.cc",
"../browser/android/preferences/pref_service_bridge_unittest.cc",
"../browser/android/shortcut_info_unittest.cc",
"../browser/android/thumbnail/scoped_ptr_expiring_cache_unittest.cc",
......
......@@ -11,8 +11,9 @@ source_set("data_source") {
"physical_web_listener.h",
]
deps = [
public_deps = [
"//base",
"//url",
]
}
......
......@@ -73,6 +73,10 @@ std::unique_ptr<base::ListValue> FakePhysicalWebDataSource::GetMetadata() {
return metadata_->CreateDeepCopy();
}
std::unique_ptr<MetadataList> FakePhysicalWebDataSource::GetMetadataList() {
return base::MakeUnique<MetadataList>(*metadata_list_.get());
}
bool FakePhysicalWebDataSource::HasUnresolvedDiscoveries() {
return false;
}
......@@ -92,6 +96,11 @@ void FakePhysicalWebDataSource::SetMetadata(
metadata_ = std::move(metadata);
}
void FakePhysicalWebDataSource::SetMetadataList(
std::unique_ptr<MetadataList> metadata_list) {
metadata_list_ = std::move(metadata_list);
}
void FakePhysicalWebDataSource::NotifyOnFound(const GURL& url) {
for (PhysicalWebListener& observer : observer_list_)
observer.OnFound(url);
......
......@@ -43,6 +43,7 @@ class FakePhysicalWebDataSource : public PhysicalWebDataSource {
void StopDiscovery() override;
std::unique_ptr<base::ListValue> GetMetadata() override;
std::unique_ptr<MetadataList> GetMetadataList() override;
bool HasUnresolvedDiscoveries() override;
......@@ -51,12 +52,14 @@ class FakePhysicalWebDataSource : public PhysicalWebDataSource {
// for testing
void SetMetadata(std::unique_ptr<base::ListValue> metadata);
void SetMetadataList(std::unique_ptr<MetadataList> metadata_list);
void NotifyOnFound(const GURL& url);
void NotifyOnLost(const GURL& url);
void NotifyOnDistanceChanged(const GURL& url, double distance_estimate);
private:
std::unique_ptr<base::ListValue> metadata_;
std::unique_ptr<MetadataList> metadata_list_;
base::ObserverList<PhysicalWebListener> observer_list_;
DISALLOW_COPY_AND_ASSIGN(FakePhysicalWebDataSource);
......
......@@ -15,4 +15,10 @@ const char kScanTimestampKey[] = "scanTimestamp";
const char kScannedUrlKey[] = "scannedUrl";
const char kTitleKey[] = "title";
Metadata::Metadata() {}
Metadata::Metadata(const Metadata& other) = default;
Metadata::~Metadata() {}
} // namespace physical_web
......@@ -6,6 +6,11 @@
#define COMPONENTS_PHYSICAL_WEB_DATA_SOURCE_PHYSICAL_WEB_DATA_SOURCE_H_
#include <memory>
#include <string>
#include <vector>
#include "base/time/time.h"
#include "url/gurl.h"
namespace base {
class ListValue;
......@@ -16,6 +21,7 @@ namespace physical_web {
class PhysicalWebListener;
// Dictionary keys for reading Physical Web URL metadata.
// TODO(cco3): Remove these when we are no longer dependent.
extern const char kDescriptionKey[];
extern const char kDistanceEstimateKey[];
extern const char kGroupIdKey[];
......@@ -25,6 +31,53 @@ extern const char kScanTimestampKey[];
extern const char kScannedUrlKey[];
extern const char kTitleKey[];
// Metadata struct for associating data with Physical Web URLs.
struct Metadata {
Metadata();
Metadata(const Metadata& other);
~Metadata();
// The URL broadcasted by the beacon and scanned by the client device.
// REQUIRED
GURL scanned_url;
// The URL that the scanned_url redirects to.
// This is the URL that users should be directed to.
// REQUIRED
GURL resolved_url;
// The favicon URL.
// OPTIONAL
GURL icon_url;
// The title of the web page.
// REQUIRED
std::string title;
// The description of the web page.
// OPTIONAL: When the website has not specified a description, the PWS
// generates one based on the initial text of the site, but this is not
// guaranteed behavior.
std::string description;
// An identifier that associates multiple resolved URLs. These URLs will
// most typically be associated because their metadata is near-identical
// (same icon, title, description, URL minus the fragment). e.g.,
// https://mymuseum/exhibits#e1
// https://mymuseum/exhibits#e2
// If two URLs have the same group id, only one should be shown (typically,
// the one with the smallest distance estimate).
// OPTIONAL: Treat the item as its own unique group if this is empty.
std::string group_id;
// The estimated distance between the user and the Physical Web device (e.g.,
// beacon) in meters.
// OPTIONAL: This will be a value <= 0 if no distance estimate has been
// calculated. The distance may not be calculated if we aren't able to
// receive an estimate from the underlying scanning service in time, or if
// (in the future) we begin sourcing Physical Web URLs from a non-BLE
// transport (e.g. mDNS).
double distance_estimate;
// The timestamp corresponding to when this URL was last scanned.
// REQUIRED
base::Time scan_timestamp;
};
using MetadataList = std::vector<Metadata>;
// Helper class for accessing Physical Web metadata and controlling the scanner.
class PhysicalWebDataSource {
public:
......@@ -41,6 +94,14 @@ class PhysicalWebDataSource {
// requests are disabled or if discovery is not active, the list will be
// empty. The method can be called at any time to receive the current metadata
// list.
virtual std::unique_ptr<MetadataList> GetMetadataList() = 0;
// Returns a list of resolved URLs and associated page metadata. If network
// requests are disabled or if discovery is not active, the list will be
// empty. The method can be called at any time to receive the current metadata
// list.
// DEPRECATED
// TODO(cco3): Remove this when we are no longer dependent on it.
virtual std::unique_ptr<base::ListValue> GetMetadata() = 0;
// Returns boolean |true| if network requests are disabled and there are one
......
......@@ -23,6 +23,7 @@ class TestPhysicalWebDataSource : public PhysicalWebDataSourceImpl {
void StartDiscovery(bool network_request_enabled) override;
void StopDiscovery() override;
std::unique_ptr<base::ListValue> GetMetadata() override;
std::unique_ptr<MetadataList> GetMetadataList() override;
bool HasUnresolvedDiscoveries() override;
};
void TestPhysicalWebDataSource::StartDiscovery(bool network_request_enabled) {}
......@@ -33,6 +34,10 @@ std::unique_ptr<base::ListValue> TestPhysicalWebDataSource::GetMetadata() {
return NULL;
}
std::unique_ptr<MetadataList> TestPhysicalWebDataSource::GetMetadataList() {
return NULL;
}
bool TestPhysicalWebDataSource::HasUnresolvedDiscoveries() {
return false;
}
......
......@@ -35,6 +35,10 @@ class IOSChromePhysicalWebDataSource
// requests are disabled, the list will be empty.
std::unique_ptr<base::ListValue> GetMetadata() override;
// Returns a list of resolved URLs and associated page metadata. If network
// requests are disabled, the list will be empty.
std::unique_ptr<physical_web::MetadataList> GetMetadataList() override;
// Returns boolean |true| if network requests are disabled and there are one
// or more discovered URLs that have not been sent to the resolution service.
bool HasUnresolvedDiscoveries() override;
......
......@@ -52,6 +52,14 @@ std::unique_ptr<base::ListValue> IOSChromePhysicalWebDataSource::GetMetadata() {
return [scanner_ metadata];
}
std::unique_ptr<physical_web::MetadataList>
IOSChromePhysicalWebDataSource::GetMetadataList() {
if (!scanner_) {
return base::MakeUnique<physical_web::MetadataList>();
}
return [scanner_ metadataList];
}
bool IOSChromePhysicalWebDataSource::HasUnresolvedDiscoveries() {
return [scanner_ unresolvedBeaconsCount] > 0;
}
......@@ -8,11 +8,17 @@
#import <Foundation/Foundation.h>
#include <memory>
#include <vector>
namespace base {
class ListValue;
}
namespace physical_web {
struct Metadata;
using MetadataList = std::vector<Metadata>;
}
@protocol PhysicalWebScannerDelegate;
// This class will scan for physical web devices.
......@@ -57,6 +63,11 @@ class ListValue;
// returned.
- (std::unique_ptr<base::ListValue>)metadata;
// Returns the metadata for all resolved physical web URLs. The returned value
// will never be nil; if no metadata has been received then an empty list is
// returned.
- (std::unique_ptr<physical_web::MetadataList>)metadataList;
@end
@protocol PhysicalWebScannerDelegate<NSObject>
......
......@@ -20,6 +20,7 @@
#import "ios/chrome/common/physical_web/physical_web_device.h"
#import "ios/chrome/common/physical_web/physical_web_request.h"
#import "ios/chrome/common/physical_web/physical_web_types.h"
#include "url/gurl.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
......@@ -185,7 +186,7 @@ enum BeaconType {
}
- (std::unique_ptr<base::ListValue>)metadata {
auto metadataList = base::MakeUnique<base::ListValue>();
auto metadataRet = base::MakeUnique<base::ListValue>();
for (PhysicalWebDevice* device in [self devices]) {
std::string scannedUrl =
......@@ -202,10 +203,34 @@ enum BeaconType {
metadataItem->SetString(physical_web::kIconUrlKey, icon);
metadataItem->SetString(physical_web::kTitleKey, title);
metadataItem->SetString(physical_web::kDescriptionKey, description);
metadataList->Append(std::move(metadataItem));
metadataRet->Append(std::move(metadataItem));
}
return metadataList;
return metadataRet;
}
- (std::unique_ptr<physical_web::MetadataList>)metadataList {
auto metadataRet = base::MakeUnique<physical_web::MetadataList>();
for (PhysicalWebDevice* device in [self devices]) {
std::string scannedUrl =
base::SysNSStringToUTF8([[device requestURL] absoluteString]);
std::string resolvedUrl =
base::SysNSStringToUTF8([[device url] absoluteString]);
std::string icon = base::SysNSStringToUTF8([[device icon] absoluteString]);
std::string title = base::SysNSStringToUTF8([device title]);
std::string description = base::SysNSStringToUTF8([device description]);
physical_web::Metadata metadataItem;
metadataItem.scanned_url = GURL(scannedUrl);
metadataItem.resolved_url = GURL(resolvedUrl);
metadataItem.icon_url = GURL(icon);
metadataItem.title = title;
metadataItem.description = description;
metadataRet->push_back(std::move(metadataItem));
}
return metadataRet;
}
- (void)setNetworkRequestEnabled:(BOOL)enabled {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment