Skip to content

Push Translation #29

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 18, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 51 additions & 7 deletions Parse/src/main/java/com/parse/ParseInstallation.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.text.TextUtils;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;

import bolts.Continuation;
Expand All @@ -36,12 +38,13 @@ public class ParseInstallation extends ParseObject {
private static final String KEY_DEVICE_TOKEN = "deviceToken";
private static final String KEY_PUSH_TYPE = "pushType";
private static final String KEY_TIME_ZONE = "timeZone";
private static final String KEY_LOCALE = "localeIdentifier";
private static final String KEY_APP_VERSION = "appVersion";
/* package */ static final String KEY_CHANNELS = "channels";

private static final List<String> READ_ONLY_FIELDS = Collections.unmodifiableList(
Arrays.asList(KEY_DEVICE_TYPE, KEY_INSTALLATION_ID, KEY_DEVICE_TOKEN, KEY_PUSH_TYPE,
KEY_TIME_ZONE, KEY_APP_VERSION, KEY_APP_NAME, KEY_PARSE_VERSION,
KEY_TIME_ZONE, KEY_LOCALE, KEY_APP_VERSION, KEY_APP_NAME, KEY_PARSE_VERSION,
KEY_APP_IDENTIFIER));

// TODO(mengyan): Inject into ParseInstallationInstanceController
Expand Down Expand Up @@ -108,6 +111,7 @@ public String getInstallationId() {
updateTimezone();
updateVersionInfo();
updateDeviceInfo();
updateLocaleIdentifier();
}
}

Expand Down Expand Up @@ -165,8 +169,8 @@ public Task<Void> then(Task<Void> task) throws Exception {
// time zones from devices reporting other formats.
private void updateTimezone() {
String zone = TimeZone.getDefault().getID();
if ((zone.indexOf('/') > 0 || zone.equals("GMT")) && !zone.equals(get("timeZone"))) {
performPut("timeZone", zone);
if ((zone.indexOf('/') > 0 || zone.equals("GMT")) && !zone.equals(get(KEY_TIME_ZONE))) {
performPut(KEY_TIME_ZONE, zone);
}
}

Expand All @@ -180,25 +184,65 @@ private void updateVersionInfo() {
String appVersion = pkgInfo.versionName;
String appName = pm.getApplicationLabel(pm.getApplicationInfo(packageName, 0)).toString();

if (packageName != null && !packageName.equals(get("appIdentifier"))) {
if (packageName != null && !packageName.equals(get(KEY_APP_IDENTIFIER))) {
performPut(KEY_APP_IDENTIFIER, packageName);
}
if (appName != null && !appName.equals(get("appName"))) {
if (appName != null && !appName.equals(get(KEY_APP_NAME))) {
performPut(KEY_APP_NAME, appName);
}
if (appVersion != null && !appVersion.equals(get("appVersion"))) {
if (appVersion != null && !appVersion.equals(get(KEY_APP_VERSION))) {
performPut(KEY_APP_VERSION, appVersion);
}
} catch (PackageManager.NameNotFoundException e) {
PLog.w(TAG, "Cannot load package info; will not be saved to installation");
}

if (!VERSION_NAME.equals(get("parseVersion"))) {
if (!VERSION_NAME.equals(get(KEY_PARSE_VERSION))) {
performPut(KEY_PARSE_VERSION, VERSION_NAME);
}
}
}

/*
* Save locale in the following format:
* [language code]-[country code]
*
* The language codes are two-letter lowercase ISO language codes (such as "en") as defined by
* <a href="http://en.wikipedia.org/wiki/ISO_639-1">ISO 639-1</a>.
* The country codes are two-letter uppercase ISO country codes (such as "US") as defined by
* <a href="http://en.wikipedia.org/wiki/ISO_3166-1_alpha-3">ISO 3166-1</a>.
*
* Note that Java uses several deprecated two-letter codes. The Hebrew ("he") language
* code is rewritten as "iw", Indonesian ("id") as "in", and Yiddish ("yi") as "ji". This
* rewriting happens even if you construct your own {@code Locale} object, not just for
* instances returned by the various lookup methods.
*/
private void updateLocaleIdentifier() {
final Locale locale = Locale.getDefault();

String language = locale.getLanguage();
String country = locale.getCountry();

if (TextUtils.isEmpty(language)) {
return;
}

// rewrite depreciated two-letter codes
if (language.equals("iw")) language = "he"; // Hebrew
if (language.equals("in")) language = "id"; // Indonesian
if (language.equals("ji")) language = "yi"; // Yiddish
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add a unit test to verify that we're correctly persisting these 3 locales.


String localeString = language;

if (!TextUtils.isEmpty(country)) {
localeString = String.format(Locale.US, "%s-%s", language, country);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add a unit test for this use case.


if (!localeString.equals(get(KEY_LOCALE))) {
performPut(KEY_LOCALE, localeString);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's also verify that we update the locale if it's already set.

}

// TODO(mengyan): Move to ParseInstallationInstanceController
/* package */ void updateDeviceInfo() {
updateDeviceInfo(ParsePlugins.get().installationId());
Expand Down
83 changes: 64 additions & 19 deletions Parse/src/test/java/com/parse/ParseInstallationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.robolectric.res.builder.RobolectricPackageManager;

import java.util.Arrays;
import java.util.Locale;
import java.util.TimeZone;

import bolts.Task;
Expand All @@ -46,17 +47,25 @@ public class ParseInstallationTest {
private static final String KEY_APP_NAME = "appName";
private static final String KEY_APP_IDENTIFIER = "appIdentifier";
private static final String KEY_TIME_ZONE = "timeZone";
private static final String KEY_LOCALE_IDENTIFIER = "localeIdentifier";
private static final String KEY_APP_VERSION = "appVersion";

private Locale defaultLocale;

@Before
public void setUp() {
ParseObject.registerSubclass(ParseInstallation.class);

defaultLocale = Locale.getDefault();
}

@After
public void tearDown() {
ParseObject.unregisterSubclass(ParseInstallation.class);
ParseCorePlugins.getInstance().reset();
ParsePlugins.reset();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Set default Locale back to whatever it was before


Locale.setDefault(defaultLocale);
}

@Test
Expand All @@ -71,6 +80,7 @@ public void testImmutableKeys() {
"deviceTokenLastModified",
"pushType",
"timeZone",
"localeIdentifier",
"appVersion"
};

Expand Down Expand Up @@ -146,24 +156,9 @@ public void testHandleFetchResultAsync() throws Exception {

@Test
public void testUpdateBeforeSave() throws Exception {
// Mock currentInstallationController to make setAsync work
ParseCurrentInstallationController controller =
mock(ParseCurrentInstallationController.class);
when(controller.isCurrent(any(ParseInstallation.class))).thenReturn(true);
ParseCorePlugins.getInstance().registerCurrentInstallationController(controller);
// Mock package manager
RobolectricPackageManager packageManager =
spy(RuntimeEnvironment.getRobolectricPackageManager());
doReturn("parseTest").when(packageManager).getApplicationLabel(any(ApplicationInfo.class));
RuntimeEnvironment.setRobolectricPackageManager(packageManager);
ParsePlugins.Android plugins = mock(ParsePlugins.Android.class);
// Mock installationId
InstallationId installationId = mock(InstallationId.class);
when(installationId.get()).thenReturn("installationId");
when(plugins.installationId()).thenReturn(installationId);
// Mock application context
when(plugins.applicationContext()).thenReturn(RuntimeEnvironment.application);
ParsePlugins.set(plugins);
mocksForUpdateBeforeSave();

Locale.setDefault(new Locale("en", "US"));

ParseInstallation installation = new ParseInstallation();
installation.updateBeforeSave();
Expand All @@ -183,7 +178,9 @@ public void testUpdateBeforeSave() throws Exception {
assertEquals(appVersion, installation.getString(KEY_APP_VERSION));
// Make sure we update device info
assertEquals("android", installation.getString(KEY_DEVICE_TYPE));
assertEquals(installationId.get(), installation.getString(KEY_INSTALLATION_ID));
assertEquals("installationId", installation.getString(KEY_INSTALLATION_ID));
// Make sure we update the locale identifier
assertEquals("en-US", installation.getString(KEY_LOCALE_IDENTIFIER));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's specify a locale with Locale.setDefaultLocale(Locale) since we might not always be running on a en-US device.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did this in setUp(). Do you prefer doing it in the test?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah missed that! yeah let's do that so it's a bit more contextual.

}

// TODO(mengyan): Add other testUpdateBeforeSave cases to cover all branches
Expand Down Expand Up @@ -255,6 +252,54 @@ public void testGetCurrentInstallation() throws Exception {
verify(controller, times(1)).getAsync();
}

@Test
public void testLocaleIdentifierSpecialCases() throws Exception {
mocksForUpdateBeforeSave();

ParseInstallation installation = new ParseInstallation();

// Deprecated two-letter codes (Java issue).
Locale.setDefault(new Locale("iw", "US"));
installation.updateBeforeSave();
assertEquals("he-US", installation.getString(KEY_LOCALE_IDENTIFIER));

Locale.setDefault(new Locale("in", "US"));
installation.updateBeforeSave();
assertEquals("id-US", installation.getString(KEY_LOCALE_IDENTIFIER));

Locale.setDefault(new Locale("ji", "US"));
installation.updateBeforeSave();
assertEquals("yi-US", installation.getString(KEY_LOCALE_IDENTIFIER));

// No country code.
Locale.setDefault(new Locale("en"));
installation.updateBeforeSave();
assertEquals("en", installation.getString(KEY_LOCALE_IDENTIFIER));
}



// TODO(mengyan): Add testFetchAsync, right now we can not test super methods inside
// testFetchAsync

private static void mocksForUpdateBeforeSave() {
// Mock currentInstallationController to make setAsync work
ParseCurrentInstallationController controller =
mock(ParseCurrentInstallationController.class);
when(controller.isCurrent(any(ParseInstallation.class))).thenReturn(true);
ParseCorePlugins.getInstance().registerCurrentInstallationController(controller);
// Mock package manager
RobolectricPackageManager packageManager =
spy(RuntimeEnvironment.getRobolectricPackageManager());
doReturn("parseTest").when(packageManager).getApplicationLabel(any(ApplicationInfo.class));
RuntimeEnvironment.setRobolectricPackageManager(packageManager);
ParsePlugins.Android plugins = mock(ParsePlugins.Android.class);
// Mock installationId
InstallationId installationId = mock(InstallationId.class);
when(installationId.get()).thenReturn("installationId");
when(plugins.installationId()).thenReturn(installationId);
// Mock application context
when(plugins.applicationContext()).thenReturn(RuntimeEnvironment.application);
ParsePlugins.set(plugins);
}
}