diff --git a/gradle.properties b/gradle.properties index 389e1c25c..3c0ea0fe2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,2 @@ group = com.mixpanel.android -version = 4.5.0-SNAPSHOT +version = 4.5.0 diff --git a/src/androidTest/java/com/mixpanel/android/viewcrawler/EditProtocolTest.java b/src/androidTest/java/com/mixpanel/android/viewcrawler/EditProtocolTest.java index a9b4024d0..73aa42f4a 100644 --- a/src/androidTest/java/com/mixpanel/android/viewcrawler/EditProtocolTest.java +++ b/src/androidTest/java/com/mixpanel/android/viewcrawler/EditProtocolTest.java @@ -21,7 +21,7 @@ public class EditProtocolTest extends AndroidTestCase { public void setUp() throws JSONException { mProtocol = new EditProtocol(getContext()); mSnapshotConfig = new JSONObject( - "{\"config\": {\"classes\":[{\"name\":\"android.view.View\",\"properties\":[{\"name\":\"importantForAccessibility\",\"get\":{\"selector\":\"isImportantForAccessibility\",\"parameters\":[],\"result\":{\"type\":\"java.lang.Boolean\"}}},{\"name\":\"backgroundColor\",\"get\":{\"selector\":\"getBackgroundColor\",\"parameters\":[],\"result\":{\"type\":\"java.lang.Integer\"}},\"set\":{\"selector\":\"setBackgroundColor\",\"parameters\":[{\"type\":\"java.lang.Integer\"}]},\"editor\":\"hexstring\"}]},{\"name\":\"android.widget.TextView\",\"properties\":[{\"name\":\"text\",\"get\":{\"selector\":\"getText\",\"parameters\":[],\"result\":{\"type\":\"java.lang.CharSequence\"}},\"set\":{\"selector\":\"setText\",\"parameters\":[{\"type\":\"java.lang.CharSequence\"}]}}]},{\"name\":\"android.widget.ImageView\",\"properties\":[{\"name\":\"image\",\"set\":{\"selector\":\"setImageBitmap\",\"parameters\":[{\"type\":\"android.graphics.Bitmap\"}]}}]}]}}" + "{\"config\": {\"classes\":[{\"name\":\"android.view.View\",\"properties\":[{\"name\":\"importantForAccessibility\",\"get\":{\"selector\":\"isImportantForAccessibility\",\"parameters\":[],\"result\":{\"type\":\"java.lang.Boolean\"}}}]},{\"name\":\"android.widget.TextView\",\"properties\":[{\"name\":\"text\",\"get\":{\"selector\":\"getText\",\"parameters\":[],\"result\":{\"type\":\"java.lang.CharSequence\"}},\"set\":{\"selector\":\"setText\",\"parameters\":[{\"type\":\"java.lang.CharSequence\"}]}}]},{\"name\":\"android.widget.ImageView\",\"properties\":[{\"name\":\"image\",\"set\":{\"selector\":\"setImageBitmap\",\"parameters\":[{\"type\":\"android.graphics.Bitmap\"}]}}]}]}}" ); mPropertyEdit = new JSONObject( "{\"path\":[{\"view_class\":\"com.mixpanel.android.viewcrawler.TestView\",\"index\":0},{\"view_class\":\"android.widget.LinearLayout\",\"index\":0},{\"view_class\":\"android.widget.LinearLayout\",\"index\":0},{\"view_class\":\"android.widget.Button\",\"index\":1}],\"property\":{\"name\":\"text\",\"get\":{\"selector\":\"getText\",\"parameters\":[],\"result\":{\"type\":\"java.lang.CharSequence\"}},\"set\":{\"selector\":\"setText\",\"parameters\":[{\"type\":\"java.lang.CharSequence\"}]}},\"args\":[[\"Ground Control to Major Tom\",\"java.lang.CharSequence\"]]}" @@ -56,21 +56,18 @@ public void testSnapshotConfig() throws EditProtocol.BadInstructionsException { final ViewSnapshot snapshot = mProtocol.readSnapshotConfig(mSnapshotConfig); final List properties = snapshot.getProperties(); - assertEquals(properties.size(), 4); + assertEquals(properties.size(), 3); final PropertyDescription prop1 = properties.get(0); final PropertyDescription prop2 = properties.get(1); final PropertyDescription prop3 = properties.get(2); - final PropertyDescription prop4 = properties.get(3); assertEquals(prop1.name, "importantForAccessibility"); - assertEquals(prop2.name, "backgroundColor"); - assertEquals(prop3.name, "text"); - assertEquals(prop4.name, "image"); + assertEquals(prop2.name, "text"); + assertEquals(prop3.name, "image"); assertEquals(prop1.targetClass, View.class); - assertEquals(prop2.targetClass, View.class); - assertEquals(prop3.targetClass, TextView.class); - assertEquals(prop4.targetClass, ImageView.class); + assertEquals(prop2.targetClass, TextView.class); + assertEquals(prop3.targetClass, ImageView.class); } public void testReadPaths() throws JSONException { diff --git a/src/androidTest/java/com/mixpanel/android/viewcrawler/TweaksTest.java b/src/androidTest/java/com/mixpanel/android/viewcrawler/TweaksTest.java deleted file mode 100644 index 1229d4a3c..000000000 --- a/src/androidTest/java/com/mixpanel/android/viewcrawler/TweaksTest.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.mixpanel.android.viewcrawler; - -import android.test.AndroidTestCase; - -import com.mixpanel.android.mpmetrics.Tweaks; - -import java.util.HashMap; -import java.util.concurrent.CountDownLatch; - -public class TweaksTest extends AndroidTestCase { - - @Override - public void setUp() throws Exception { - super.setUp(); - mTweaks = new Tweaks(); - } - - public void testGet() throws Exception { - String val = (String) mTweaks.get("test", "testval"); - mTweaks.set("test", "testval2"); - String val2 = (String) mTweaks.get("test", "testval"); - assertEquals("testval", val); - assertEquals("testval2", val2); - } - - public void testInitialValueCallback() throws Exception { - final CountDownLatch latch = new CountDownLatch(1); - - final HashMap vals = new HashMap(); - mTweaks.bind("callbacktest", "first val", new Tweaks.TweakChangeCallback() { - @Override - public void onChange(Object value) { - vals.put((String) value, value); - latch.countDown(); - } - }); - - latch.await(); - assertTrue(vals.containsKey("first val")); - } - - public void testCallback() throws Exception { - final CountDownLatch latch = new CountDownLatch(2); - - final HashMap vals = new HashMap(); - mTweaks.bind("callbacktest", "first val", new Tweaks.TweakChangeCallback() { - @Override - public void onChange(Object value) { - vals.put((String) value, value); - latch.countDown(); - } - }); - - mTweaks.set("callbacktest", "new value"); - - latch.await(); - assertTrue(vals.containsKey("new value")); - } - - private Tweaks mTweaks; -} diff --git a/src/androidTest/java/com/mixpanel/android/viewcrawler/ViewSnapshotTest.java b/src/androidTest/java/com/mixpanel/android/viewcrawler/ViewSnapshotTest.java index 1019a75da..91585d4c4 100644 --- a/src/androidTest/java/com/mixpanel/android/viewcrawler/ViewSnapshotTest.java +++ b/src/androidTest/java/com/mixpanel/android/viewcrawler/ViewSnapshotTest.java @@ -24,16 +24,16 @@ import java.util.Set; public class ViewSnapshotTest extends AndroidTestCase { - public void setUp() { + public void setUp() throws NoSuchMethodException { mRootView = new TestView(this.getContext()); final List props = new ArrayList(); - final Caller textGetter = new Caller("getText", new Object[0], CharSequence.class); + final Caller textGetter = new Caller(TextView.class, "getText", new Object[0], CharSequence.class); final PropertyDescription text = new PropertyDescription("text", TextView.class, textGetter, "setText"); props.add(text); - final Caller customPropGetter = new Caller("getCustomProperty", new Object[0], CharSequence.class); + final Caller customPropGetter = new Caller(TestView.CustomPropButton.class, "getCustomProperty", new Object[0], CharSequence.class); final PropertyDescription custom = new PropertyDescription( "custom", TestView.CustomPropButton.class, @@ -42,24 +42,6 @@ public void setUp() { ); props.add(custom); - final Caller crazyGetter = new Caller("CRAZY GETTER", new Object[0], Void.TYPE); - final PropertyDescription crazy = new PropertyDescription( - "crazy", - View.class, - crazyGetter, - "CRAZY SETTER" - ); - props.add(crazy); - - final Caller badTypesGetter = new Caller("getText", new Object[0], Integer.class); - final PropertyDescription badTypes = new PropertyDescription( - "badTypes", - TextView.class, - badTypesGetter, - "setText" - ); - props.add(badTypes); - final SparseArray idNamesById = new SparseArray(); idNamesById.put(TestView.ROOT_ID, "ROOT_ID"); idNamesById.put(TestView.TEXT_VIEW_ID, "TEXT_VIEW_ID"); @@ -69,6 +51,22 @@ public void setUp() { mSnapshot = new ViewSnapshot(props, idNamesById); } + public void testBadMethods() { + try { + final Caller crazyGetter = new Caller(View.class, "CRAZY GETTER", new Object[0], Void.TYPE); + fail("Exception was not thrown when constructing a bad caller"); + } catch (NoSuchMethodException e) { + // OK! + } + + try { + final Caller badTypesGetter = new Caller(TextView.class, "getText", new Object[0], Integer.class); + fail("Exception was not thrown when constructing a caller with bad types"); + } catch (NoSuchMethodException e) { + // OK! + } + } + public void testViewSnapshot() throws IOException, JSONException { int width = View.MeasureSpec.makeMeasureSpec(768, View.MeasureSpec.EXACTLY); int height = View.MeasureSpec.makeMeasureSpec(1280, View.MeasureSpec.EXACTLY); diff --git a/src/androidTest/java/com/mixpanel/android/viewcrawler/ViewVisitorTest.java b/src/androidTest/java/com/mixpanel/android/viewcrawler/ViewVisitorTest.java index 4e0f6b015..636a75a45 100644 --- a/src/androidTest/java/com/mixpanel/android/viewcrawler/ViewVisitorTest.java +++ b/src/androidTest/java/com/mixpanel/android/viewcrawler/ViewVisitorTest.java @@ -304,7 +304,7 @@ public void testResetSameEventOnClick() { mTrackListener.events.clear(); } - public void testDuplicateBitmapSet() { + public void testDuplicateBitmapSet() throws NoSuchMethodException { final Paint paint = new Paint(); paint.setColor(Color.BLUE); @@ -319,10 +319,10 @@ public void testDuplicateBitmapSet() { final Canvas canvas2 = new Canvas(bitmap2); canvas2.drawCircle(6, 6, 4, paint); - final Caller mutateBitmap1a = new Caller("setCountingProperty", new Object[] { bitmap1a }, Void.TYPE); - final Caller mutateBitmap1b = new Caller("setCountingProperty", new Object[] { bitmap1b }, Void.TYPE); - final Caller mutateBitmap2 = new Caller("setCountingProperty", new Object[] { bitmap2 }, Void.TYPE); - final Caller accessBitmap = new Caller("getCountingProperty", new Object[]{}, Object.class); + final Caller mutateBitmap1a = new Caller(TestView.AdHocButton2.class, "setCountingProperty", new Object[] { bitmap1a }, Void.TYPE); + final Caller mutateBitmap1b = new Caller(TestView.AdHocButton2.class, "setCountingProperty", new Object[] { bitmap1b }, Void.TYPE); + final Caller mutateBitmap2 = new Caller(TestView.AdHocButton2.class, "setCountingProperty", new Object[] { bitmap2 }, Void.TYPE); + final Caller accessBitmap = new Caller(TestView.AdHocButton2.class, "getCountingProperty", new Object[]{}, Object.class); { final ViewVisitor propertySetVisitor1_1 = @@ -354,11 +354,11 @@ public void testDuplicateBitmapSet() { } } - public void testDuplicateTextSet() { - final Caller mutateCountingProperty1a = new Caller("setCountingProperty", new Object[]{"Set String1"}, Void.TYPE); - final Caller mutateCountingProperty1b = new Caller("setCountingProperty", new Object[]{"Set String1"}, Void.TYPE); - final Caller mutateCountingProperty2 = new Caller("setCountingProperty", new Object[]{"Set String2"}, Void.TYPE); - final Caller accessCountingProperty = new Caller("getCountingProperty", new Object[]{}, Object.class); + public void testDuplicateTextSet() throws NoSuchMethodException { + final Caller mutateCountingProperty1a = new Caller(TestView.AdHocButton2.class, "setCountingProperty", new Object[]{"Set String1"}, Void.TYPE); + final Caller mutateCountingProperty1b = new Caller(TestView.AdHocButton2.class, "setCountingProperty", new Object[]{"Set String1"}, Void.TYPE); + final Caller mutateCountingProperty2 = new Caller(TestView.AdHocButton2.class, "setCountingProperty", new Object[]{"Set String2"}, Void.TYPE); + final Caller accessCountingProperty = new Caller(TestView.AdHocButton2.class, "getCountingProperty", new Object[]{}, Object.class); { final ViewVisitor propertySetVisitor1_1 = diff --git a/src/main/java/com/mixpanel/android/mpmetrics/AnalyticsMessages.java b/src/main/java/com/mixpanel/android/mpmetrics/AnalyticsMessages.java index a8d135e29..a59344bd8 100644 --- a/src/main/java/com/mixpanel/android/mpmetrics/AnalyticsMessages.java +++ b/src/main/java/com/mixpanel/android/mpmetrics/AnalyticsMessages.java @@ -317,13 +317,18 @@ private void runGCMRegistration(String senderID) { // Consider adding a transitive dependency on the latest // Google Play Services version and requiring Java 1.7 // in the next major library release. - - final int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(mContext); - if (resultCode != ConnectionResult.SUCCESS) { - Log.i(LOGTAG, "Can't register for push notifications, Google Play Services are not installed."); + try { + final int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(mContext); + if (resultCode != ConnectionResult.SUCCESS) { + Log.i(LOGTAG, "Can't register for push notifications, Google Play Services are not installed."); + return; + } + } catch (RuntimeException e) { + Log.i(LOGTAG, "Can't register for push notifications, Google Play services are not configured."); return; } + final GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(mContext); registrationId = gcm.register(senderID); } catch (IOException e) { @@ -439,24 +444,31 @@ private JSONObject getDefaultEventProperties() ret.put("$model", Build.MODEL == null ? "UNKNOWN" : Build.MODEL); try { - final int servicesAvailable = GooglePlayServicesUtil.isGooglePlayServicesAvailable(mContext); - switch (servicesAvailable) { - case ConnectionResult.SUCCESS: - ret.put("$google_play_services", "available"); - break; - case ConnectionResult.SERVICE_MISSING: - ret.put("$google_play_services", "missing"); - break; - case ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED: - ret.put("$google_play_services", "out of date"); - break; - case ConnectionResult.SERVICE_DISABLED: - ret.put("$google_play_services", "disabled"); - break; - case ConnectionResult.SERVICE_INVALID: - ret.put("$google_play_services", "invalid"); - break; + try { + final int servicesAvailable = GooglePlayServicesUtil.isGooglePlayServicesAvailable(mContext); + switch (servicesAvailable) { + case ConnectionResult.SUCCESS: + ret.put("$google_play_services", "available"); + break; + case ConnectionResult.SERVICE_MISSING: + ret.put("$google_play_services", "missing"); + break; + case ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED: + ret.put("$google_play_services", "out of date"); + break; + case ConnectionResult.SERVICE_DISABLED: + ret.put("$google_play_services", "disabled"); + break; + case ConnectionResult.SERVICE_INVALID: + ret.put("$google_play_services", "invalid"); + break; + } + } catch (RuntimeException e) { + // Turns out even checking for the service will cause explosions + // unless we've set up meta-data + ret.put("$google_play_services", "not configured"); } + } catch (NoClassDefFoundError e) { ret.put("$google_play_services", "not included"); }