blob: 7d0f1dcc62621131f5d65eb1a934d4ac29f804e3 [file] [log] [blame]
// Copyright 2018 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/translate/android/translate_bridge.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "chrome/android/chrome_jni_headers/TranslateBridge_jni.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/language/language_model_manager_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/translate/chrome_translate_client.h"
#include "chrome/browser/translate/translate_service.h"
#include "components/language/core/browser/language_model.h"
#include "components/language/core/browser/language_model_manager.h"
#include "components/language/core/browser/pref_names.h"
#include "components/language/core/common/language_experiments.h"
#include "components/language/core/common/language_util.h"
#include "components/prefs/pref_service.h"
#include "components/strings/grit/components_locale_settings.h"
#include "components/translate/core/browser/translate_manager.h"
#include "components/translate/core/browser/translate_pref_names.h"
#include "components/translate/core/browser/translate_prefs.h"
#include "content/public/browser/web_contents.h"
#include "third_party/icu/source/common/unicode/uloc.h"
#include "ui/base/l10n/l10n_util.h"
using base::android::ConvertJavaStringToUTF8;
using base::android::ConvertUTF8ToJavaString;
using base::android::JavaParamRef;
using base::android::ToJavaArrayOfStrings;
namespace {
PrefService* GetPrefService() {
return ProfileManager::GetActiveUserProfile()
->GetOriginalProfile()
->GetPrefs();
}
} // namespace
static ChromeTranslateClient* GetTranslateClient(
const base::android::JavaParamRef<jobject>& j_web_contents) {
content::WebContents* web_contents =
content::WebContents::FromJavaWebContents(j_web_contents);
ChromeTranslateClient* client =
ChromeTranslateClient::FromWebContents(web_contents);
DCHECK(client);
return client;
}
static void JNI_TranslateBridge_ManualTranslateWhenReady(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& j_web_contents) {
content::WebContents* web_contents =
content::WebContents::FromJavaWebContents(j_web_contents);
ChromeTranslateClient* client =
ChromeTranslateClient::FromWebContents(web_contents);
DCHECK(client);
client->ManualTranslateWhenReady();
}
static jboolean JNI_TranslateBridge_CanManuallyTranslate(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& j_web_contents) {
ChromeTranslateClient* client = GetTranslateClient(j_web_contents);
translate::TranslateManager* manager = client->GetTranslateManager();
DCHECK(manager);
return manager->CanManuallyTranslate();
}
static jboolean JNI_TranslateBridge_ShouldShowManualTranslateIPH(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& j_web_contents) {
ChromeTranslateClient* client = GetTranslateClient(j_web_contents);
translate::TranslateManager* manager = client->GetTranslateManager();
DCHECK(manager);
const std::string page_lang = manager->GetLanguageState().original_language();
std::unique_ptr<translate::TranslatePrefs> translate_prefs(
client->GetTranslatePrefs());
return base::StartsWith(page_lang, "en",
base::CompareCase::INSENSITIVE_ASCII) &&
!language::ShouldForceTriggerTranslateOnEnglishPages(
translate_prefs->GetForceTriggerOnEnglishPagesCount()) &&
!manager->GetLanguageState().translate_enabled();
}
static void JNI_TranslateBridge_SetPredefinedTargetLanguage(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& j_web_contents,
const base::android::JavaParamRef<jstring>& j_translate_language) {
content::WebContents* web_contents =
content::WebContents::FromJavaWebContents(j_web_contents);
const std::string translate_language(
ConvertJavaStringToUTF8(env, j_translate_language));
ChromeTranslateClient* client =
ChromeTranslateClient::FromWebContents(web_contents);
DCHECK(client);
client->SetPredefinedTargetLanguage(translate_language);
}
// Returns the preferred target language to translate into for this user.
static base::android::ScopedJavaLocalRef<jstring>
JNI_TranslateBridge_GetTargetLanguage(JNIEnv* env) {
Profile* profile = ProfileManager::GetActiveUserProfile();
language::LanguageModel* language_model =
LanguageModelManagerFactory::GetForBrowserContext(profile)
->GetPrimaryModel();
DCHECK(language_model);
PrefService* pref_service = profile->GetPrefs();
std::string target_language =
TranslateService::GetTargetLanguage(pref_service, language_model);
DCHECK(!target_language.empty());
base::android::ScopedJavaLocalRef<jstring> j_target_language =
base::android::ConvertUTF8ToJavaString(env, target_language);
return j_target_language;
}
// Determines whether the given language is blocked for translation.
static jboolean JNI_TranslateBridge_IsBlockedLanguage(
JNIEnv* env,
const base::android::JavaParamRef<jstring>& j_language_string) {
std::string language_we_might_block =
ConvertJavaStringToUTF8(env, j_language_string);
Profile* profile = ProfileManager::GetActiveUserProfile();
PrefService* pref_service = profile->GetPrefs();
std::unique_ptr<translate::TranslatePrefs> translate_prefs =
ChromeTranslateClient::CreateTranslatePrefs(pref_service);
DCHECK(translate_prefs);
return translate_prefs->IsBlockedLanguage(language_we_might_block);
}
// Gets all the model languages and calls back to the Java
// TranslateBridge#addModelLanguageToSet once for each language.
static void JNI_TranslateBridge_GetModelLanguages(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& set) {
Profile* profile = ProfileManager::GetActiveUserProfile();
language::LanguageModel* language_model =
LanguageModelManagerFactory::GetForBrowserContext(profile)
->GetPrimaryModel();
DCHECK(language_model);
std::string model_languages;
std::vector<language::LanguageModel::LanguageDetails> languageDetails =
language_model->GetLanguages();
DCHECK(!languageDetails.empty());
for (const auto& details : languageDetails) {
Java_TranslateBridge_addModelLanguageToSet(
env, set,
base::android::ConvertUTF8ToJavaString(env, details.lang_code));
}
}
// static
// This logic should be kept in sync with prependToAcceptLanguagesIfNecessary in
// chrome/android/java/src/org/chromium/chrome/browser/
// physicalweb/PwsClientImpl.java
// Input |locales| is a comma separated locale representation that consists of
// language tags (BCP47 compliant format). Each language tag contains a language
// code and a country code or a language code only.
void TranslateBridge::PrependToAcceptLanguagesIfNecessary(
const std::string& locales,
std::string* accept_languages) {
std::vector<std::string> locale_list =
base::SplitString(locales + "," + *accept_languages, ",",
base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
std::set<std::string> seen_tags;
std::vector<std::pair<std::string, std::string>> unique_locale_list;
for (const std::string& locale_str : locale_list) {
char locale_ID[ULOC_FULLNAME_CAPACITY] = {};
char language_code_buffer[ULOC_LANG_CAPACITY] = {};
char country_code_buffer[ULOC_COUNTRY_CAPACITY] = {};
UErrorCode error = U_ZERO_ERROR;
uloc_forLanguageTag(locale_str.c_str(), locale_ID, ULOC_FULLNAME_CAPACITY,
nullptr, &error);
if (U_FAILURE(error)) {
LOG(ERROR) << "Ignoring invalid locale representation " << locale_str;
continue;
}
error = U_ZERO_ERROR;
uloc_getLanguage(locale_ID, language_code_buffer, ULOC_LANG_CAPACITY,
&error);
if (U_FAILURE(error)) {
LOG(ERROR) << "Ignoring invalid locale representation " << locale_str;
continue;
}
error = U_ZERO_ERROR;
uloc_getCountry(locale_ID, country_code_buffer, ULOC_COUNTRY_CAPACITY,
&error);
if (U_FAILURE(error)) {
LOG(ERROR) << "Ignoring invalid locale representation " << locale_str;
continue;
}
std::string language_code(language_code_buffer);
std::string country_code(country_code_buffer);
std::string language_tag(language_code + "-" + country_code);
if (seen_tags.find(language_tag) != seen_tags.end())
continue;
seen_tags.insert(language_tag);
unique_locale_list.push_back(std::make_pair(language_code, country_code));
}
// If language is not in the accept languages list, also add language
// code. A language code should only be inserted after the last
// languageTag that contains that language.
// This will work with the IDS_ACCEPT_LANGUAGE localized strings bundled
// with Chrome but may fail on arbitrary lists of language tags due to
// differences in case and whitespace.
std::set<std::string> seen_languages;
std::vector<std::string> output_list;
for (auto it = unique_locale_list.rbegin(); it != unique_locale_list.rend();
++it) {
if (seen_languages.find(it->first) == seen_languages.end()) {
output_list.push_back(it->first);
seen_languages.insert(it->first);
}
if (!it->second.empty())
output_list.push_back(it->first + "-" + it->second);
}
std::reverse(output_list.begin(), output_list.end());
*accept_languages = base::JoinString(output_list, ",");
}
static void JNI_TranslateBridge_ResetAcceptLanguages(
JNIEnv* env,
const JavaParamRef<jstring>& default_locale) {
std::string accept_languages(l10n_util::GetStringUTF8(IDS_ACCEPT_LANGUAGES));
std::string locale_string(ConvertJavaStringToUTF8(env, default_locale));
TranslateBridge::PrependToAcceptLanguagesIfNecessary(locale_string,
&accept_languages);
GetPrefService()->SetString(language::prefs::kAcceptLanguages,
accept_languages);
}
static void JNI_TranslateBridge_GetChromeAcceptLanguages(
JNIEnv* env,
const JavaParamRef<jobject>& list) {
std::unique_ptr<translate::TranslatePrefs> translate_prefs =
ChromeTranslateClient::CreateTranslatePrefs(GetPrefService());
std::vector<translate::TranslateLanguageInfo> languages;
std::string app_locale = g_browser_process->GetApplicationLocale();
translate_prefs->GetLanguageInfoList(
app_locale, translate_prefs->IsTranslateAllowedByPolicy(), &languages);
language::ToTranslateLanguageSynonym(&app_locale);
for (const auto& info : languages) {
// If the language comes from the same language family as the app locale,
// translate for this language won't be supported on this device.
std::string lang_code = info.code;
language::ToTranslateLanguageSynonym(&lang_code);
bool supports_translate =
info.supports_translate && lang_code != app_locale;
Java_TranslateBridge_addNewLanguageItemToList(
env, list, ConvertUTF8ToJavaString(env, info.code),
ConvertUTF8ToJavaString(env, info.display_name),
ConvertUTF8ToJavaString(env, info.native_display_name),
supports_translate);
}
}
static void JNI_TranslateBridge_GetUserAcceptLanguages(
JNIEnv* env,
const JavaParamRef<jobject>& list) {
std::unique_ptr<translate::TranslatePrefs> translate_prefs =
ChromeTranslateClient::CreateTranslatePrefs(GetPrefService());
std::vector<std::string> languages;
translate_prefs->GetLanguageList(&languages);
Java_TranslateBridge_copyStringArrayToList(
env, list, ToJavaArrayOfStrings(env, languages));
}
static void JNI_TranslateBridge_SetLanguageOrder(
JNIEnv* env,
const JavaParamRef<jobjectArray>& j_order) {
std::unique_ptr<translate::TranslatePrefs> translate_prefs =
ChromeTranslateClient::CreateTranslatePrefs(GetPrefService());
std::vector<std::string> order;
const int num_langs = (*env).GetArrayLength(j_order);
for (int i = 0; i < num_langs; i++) {
jstring string = (jstring)(*env).GetObjectArrayElement(j_order, i);
order.push_back((*env).GetStringUTFChars(string, nullptr));
}
translate_prefs->SetLanguageOrder(order);
}
static void JNI_TranslateBridge_UpdateUserAcceptLanguages(
JNIEnv* env,
const JavaParamRef<jstring>& language,
jboolean is_add) {
std::unique_ptr<translate::TranslatePrefs> translate_prefs =
ChromeTranslateClient::CreateTranslatePrefs(GetPrefService());
std::string language_code(ConvertJavaStringToUTF8(env, language));
if (is_add) {
translate_prefs->AddToLanguageList(language_code, false /*force_blocked=*/);
} else {
translate_prefs->RemoveFromLanguageList(language_code);
}
}
static void JNI_TranslateBridge_MoveAcceptLanguage(
JNIEnv* env,
const JavaParamRef<jstring>& language,
jint offset) {
std::unique_ptr<translate::TranslatePrefs> translate_prefs =
ChromeTranslateClient::CreateTranslatePrefs(GetPrefService());
std::vector<std::string> languages;
translate_prefs->GetLanguageList(&languages);
std::string language_code(ConvertJavaStringToUTF8(env, language));
translate::TranslatePrefs::RearrangeSpecifier where =
translate::TranslatePrefs::kNone;
if (offset > 0) {
where = translate::TranslatePrefs::kDown;
} else {
offset = -offset;
where = translate::TranslatePrefs::kUp;
}
translate_prefs->RearrangeLanguage(language_code, where, offset, languages);
}
static jboolean JNI_TranslateBridge_IsBlockedLanguage2(
JNIEnv* env,
const JavaParamRef<jstring>& language) {
std::unique_ptr<translate::TranslatePrefs> translate_prefs =
ChromeTranslateClient::CreateTranslatePrefs(GetPrefService());
std::string language_code(ConvertJavaStringToUTF8(env, language));
language::ToTranslateLanguageSynonym(&language_code);
// Application language is always blocked.
std::string app_locale = g_browser_process->GetApplicationLocale();
language::ToTranslateLanguageSynonym(&app_locale);
if (app_locale == language_code)
return true;
return translate_prefs->IsBlockedLanguage(language_code);
}
static void JNI_TranslateBridge_SetLanguageBlockedState(
JNIEnv* env,
const JavaParamRef<jstring>& language,
jboolean blocked) {
std::unique_ptr<translate::TranslatePrefs> translate_prefs =
ChromeTranslateClient::CreateTranslatePrefs(GetPrefService());
std::string language_code(ConvertJavaStringToUTF8(env, language));
if (blocked) {
translate_prefs->BlockLanguage(language_code);
} else {
translate_prefs->UnblockLanguage(language_code);
}
}
static jboolean JNI_TranslateBridge_GetExplicitLanguageAskPromptShown(
JNIEnv* env) {
std::unique_ptr<translate::TranslatePrefs> translate_prefs =
ChromeTranslateClient::CreateTranslatePrefs(GetPrefService());
return translate_prefs->GetExplicitLanguageAskPromptShown();
}
static void JNI_TranslateBridge_SetExplicitLanguageAskPromptShown(
JNIEnv* env,
jboolean shown) {
std::unique_ptr<translate::TranslatePrefs> translate_prefs =
ChromeTranslateClient::CreateTranslatePrefs(GetPrefService());
translate_prefs->SetExplicitLanguageAskPromptShown(shown);
}