Skip to content

Commit

Permalink
[Android] Introduce phone number detection.
Browse files Browse the repository at this point in the history
For more context see: https://chromiumcodereview.appspot.com/10187020/

BUG=125390
TEST=phone_number_detector_unittest.cc

Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=139019

Review URL: https://chromiumcodereview.appspot.com/10440021

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@139026 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
leandrogracia@chromium.org committed May 25, 2012
1 parent b63fee8 commit 975b42b
Show file tree
Hide file tree
Showing 7 changed files with 217 additions and 0 deletions.
7 changes: 7 additions & 0 deletions content/content_renderer.gypi
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
'renderer/android/content_detector.h',
'renderer/android/email_detector.cc',
'renderer/android/email_detector.h',
'renderer/android/phone_number_detector.cc',
'renderer/android/phone_number_detector.h',
'renderer/active_notification_tracker.cc',
'renderer/active_notification_tracker.h',
'renderer/device_orientation_dispatcher.cc',
Expand Down Expand Up @@ -268,6 +270,11 @@
'../base/allocator/allocator.gyp:allocator',
],
}],
['OS=="android"', {
'dependencies': [
'../third_party/libphonenumber/libphonenumber.gyp:libphonenumber',
],
}],
# TODO(jrg): remove the OS=="android" section?
# http://crbug.com/113172
# Understand better how media_stream_ is tied into Chromium.
Expand Down
1 change: 1 addition & 0 deletions content/content_tests.gypi
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@
'renderer/active_notification_tracker_unittest.cc',
'renderer/android/address_detector_unittest.cc',
'renderer/android/email_detector_unittest.cc',
'renderer/android/phone_number_detector_unittest.cc',
'renderer/gpu/input_event_filter_unittest.cc',
'renderer/media/audio_message_filter_unittest.cc',
'renderer/media/capture_video_decoder_unittest.cc',
Expand Down
3 changes: 3 additions & 0 deletions content/renderer/android/DEPS
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
include_rules = [
"+third_party/libphonenumber", # For phone number detection.
]
90 changes: 90 additions & 0 deletions content/renderer/android/phone_number_detector.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright (c) 2012 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 "content/renderer/android/phone_number_detector.h"

#include <algorithm>

#include "base/utf_string_conversions.h"
#include "net/base/escape.h"
#include "third_party/libphonenumber/src/phonenumber_api.h"
#include "third_party/libphonenumber/src/phonenumbers/phonenumbermatch.h"
#include "third_party/libphonenumber/src/phonenumbers/phonenumbermatcher.h"
#include "third_party/libphonenumber/src/phonenumbers/region_code.h"

using i18n::phonenumbers::PhoneNumberMatch;
using i18n::phonenumbers::PhoneNumberMatcher;
using i18n::phonenumbers::PhoneNumberUtil;
using i18n::phonenumbers::RegionCode;

namespace {

// Maximum number of characters to look around for phone number detection.
const size_t kMaximumTelephoneLength = 20;

// Prefix used for telephone number intent URIs.
const char kPhoneNumberSchemaPrefix[] = "tel:";

} // anonymous namespace

namespace content {

PhoneNumberDetector::PhoneNumberDetector()
: region_code_(RegionCode::GetUnknown()) {
}

// Region should be empty or an ISO 3166-1 alpha-2 country code.
PhoneNumberDetector::PhoneNumberDetector(const std::string& region)
: region_code_(region.empty() ? RegionCode::GetUnknown()
: StringToUpperASCII(region)) {
}

PhoneNumberDetector::~PhoneNumberDetector() {
}

size_t PhoneNumberDetector::GetMaximumContentLength() {
return kMaximumTelephoneLength;
}

GURL PhoneNumberDetector::GetIntentURL(const std::string& content_text) {
if (content_text.empty())
return GURL();

return GURL(kPhoneNumberSchemaPrefix +
net::EscapeQueryParamValue(content_text, true));
}

bool PhoneNumberDetector::FindContent(const string16::const_iterator& begin,
const string16::const_iterator& end,
size_t* start_pos,
size_t* end_pos,
std::string* content_text) {
string16 utf16_input = string16(begin, end);
std::string utf8_input = UTF16ToUTF8(utf16_input);

PhoneNumberMatcher matcher(utf8_input, region_code_);
if (matcher.HasNext()) {
PhoneNumberMatch match;
matcher.Next(&match);

PhoneNumberUtil* phone_util = PhoneNumberUtil::GetInstance();
phone_util->FormatNumberForMobileDialing(match.number(), region_code_,
false, /* with_formatting */
content_text);
// If the number can't be dialed from the current region, the formatted
// string will be empty.
if (content_text->empty())
return false;

// Need to return start_pos and end_pos relative to a UTF16 encoding.
*start_pos = UTF8ToUTF16(utf8_input.substr(0, match.start())).length();
*end_pos = *start_pos + UTF8ToUTF16(match.raw_string()).length();

return true;
}

return false;
}

} // namespace content
41 changes: 41 additions & 0 deletions content/renderer/android/phone_number_detector.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) 2012 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.

#ifndef CONTENT_RENDERER_ANDROID_PHONE_NUMBER_DETECTOR_H_
#define CONTENT_RENDERER_ANDROID_PHONE_NUMBER_DETECTOR_H_
#pragma once

#include "content/renderer/android/content_detector.h"

class PhoneNumberDetectorTest;

namespace content {

// Finds a telephone number in the given content text string.
class PhoneNumberDetector : public ContentDetector {
public:
PhoneNumberDetector();
explicit PhoneNumberDetector(const std::string& region);
virtual ~PhoneNumberDetector();

private:
friend class ::PhoneNumberDetectorTest;

// Implementation of ContentDetector.
virtual bool FindContent(const string16::const_iterator& begin,
const string16::const_iterator& end,
size_t* start_pos,
size_t* end_pos,
std::string* content_text) OVERRIDE;
virtual GURL GetIntentURL(const std::string& content_text) OVERRIDE;
virtual size_t GetMaximumContentLength() OVERRIDE;

const std::string region_code_;

DISALLOW_COPY_AND_ASSIGN(PhoneNumberDetector);
};

} // namespace content

#endif // CONTENT_RENDERER_ANDROID_PHONE_NUMBER_DETECTOR_H_
74 changes: 74 additions & 0 deletions content/renderer/android/phone_number_detector_unittest.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright (c) 2012 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 "content/renderer/android/phone_number_detector.h"

#include "base/utf_string_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"

using content::PhoneNumberDetector;

class PhoneNumberDetectorTest : public testing::Test {
public:
static std::string FindNumber(const std::string& content,
const std::string& region) {
string16 content_16 = UTF8ToUTF16(content);
string16 result_16;
size_t start, end;
PhoneNumberDetector detector(region);
std::string content_text;
if (detector.FindContent(content_16.begin(), content_16.end(),
&start, &end, &content_text))
result_16 = content_16.substr(start, end - start);
return UTF16ToUTF8(result_16);
}

static std::string FindAndFormatNumber(const std::string& content,
const std::string& region) {
string16 content_16 = UTF8ToUTF16(content);
string16 result_16;
size_t start, end;
PhoneNumberDetector detector(region);
std::string content_text;
detector.FindContent(content_16.begin(), content_16.end(),
&start, &end, &content_text);
return content_text;
}
};

TEST_F(PhoneNumberDetectorTest, FindNumber) {
// Tests cases with valid home numbers.
EXPECT_EQ("617-426-3000", FindNumber("hello 617-426-3000 blah", "us"));
EXPECT_EQ("", FindNumber("hello 617-426-3000 blah", "gb"));
EXPECT_EQ("020-7617-4426", FindNumber("<div>020-7617-4426</div>", "gb"));
EXPECT_EQ("", FindNumber("<div>020-7617-4426</div>", "fr"));
EXPECT_EQ("02.38.96.68.88", FindNumber("Tel:02.38.96.68.88", "fr"));
EXPECT_EQ("", FindNumber("Tel:02.38.96.68.88", "gb"));
EXPECT_EQ("1-800-866-2453",
FindNumber("You can call this number:1-800-866-2453 for more "
"information", "us"));
EXPECT_EQ("+1 203-925-4602", FindNumber("+1 203-925-4602", "us"));
}

TEST_F(PhoneNumberDetectorTest, FindAndFormatNumber) {
EXPECT_EQ("+16174263000",
FindAndFormatNumber("hello 617-426-3000 blah", "us"));
EXPECT_EQ("", FindAndFormatNumber("hello 617-426-3000 blah", "gb"));
EXPECT_EQ("+442076174426",
FindAndFormatNumber("<div>020-7617-4426</div>", "gb"));
EXPECT_EQ("", FindAndFormatNumber("<div>020-7617-4426</div>", "fr"));
EXPECT_EQ("+33238966888", FindAndFormatNumber("Tel:02.38.96.68.88", "fr"));
EXPECT_EQ("+18008662453",
FindAndFormatNumber("You can call this number:1-800-866-2453 for"
"more information", "us"));
EXPECT_EQ("+12039254602", FindAndFormatNumber("+1 203-925-4602", "us"));

// "+1 (650) 333-6000" using fullwidth UTF-8 characters.
EXPECT_EQ("+16503336000", FindAndFormatNumber(
"\xEF\xBC\x8B\xEF\xBC\x91\xE3\x80\x80\xEF\xBC\x88"
"\xEF\xBC\x96\xEF\xBC\x95\xEF\xBC\x90\xEF\xBC\x89"
"\xE3\x80\x80\xEF\xBC\x93\xEF\xBC\x93\xEF\xBC\x93"
"\xE3\x83\xBC\xEF\xBC\x96\xEF\xBC\x90\xEF\xBC\x90"
"\xEF\xBC\x90", "us"));
}
1 change: 1 addition & 0 deletions third_party/libphonenumber/libphonenumber.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
'direct_dependent_settings': {
'include_dirs': [
'<(SHARED_INTERMEDIATE_DIR)/protoc_out/third_party/libphonenumber',
'src',
],
},
'variables': {
Expand Down

0 comments on commit 975b42b

Please sign in to comment.