Add support for structured values (#249)

diff --git a/packages/android_intent/README.md b/packages/android_intent/README.md
index afa88ce..144ffbb 100644
--- a/packages/android_intent/README.md
+++ b/packages/android_intent/README.md
@@ -29,6 +29,12 @@
 
 Feel free to add support for additional Android intents.
 
+The Dart values supported for the arguments parameter, and their corresponding
+Android values, are listed [here](https://flutter.io/platform-channels/#codec).
+On the Android side, the arguments are used to populate an Android `Bundle`
+instance. This process currently restricts the use of lists to homogeneous lists
+of integers or strings.
+
 > Note that a similar method does not currently exist for iOS. Instead, the
 [url_launcher](https://pub.dartlang.org/packages/url_launcher) plugin
 can be used for deep linking. Url launcher can also be used for creating
diff --git a/packages/android_intent/android/src/main/java/io/flutter/plugins/androidintent/AndroidIntentPlugin.java b/packages/android_intent/android/src/main/java/io/flutter/plugins/androidintent/AndroidIntentPlugin.java
index f757b47..5c35f7a 100644
--- a/packages/android_intent/android/src/main/java/io/flutter/plugins/androidintent/AndroidIntentPlugin.java
+++ b/packages/android_intent/android/src/main/java/io/flutter/plugins/androidintent/AndroidIntentPlugin.java
@@ -15,6 +15,7 @@
 import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
 import io.flutter.plugin.common.MethodChannel.Result;
 import io.flutter.plugin.common.PluginRegistry.Registrar;
+import java.util.ArrayList;
 import java.util.Map;
 
 /** AndroidIntentPlugin */
@@ -58,6 +59,20 @@
         bundle.putDouble(key, (Double) value);
       } else if (value instanceof Long) {
         bundle.putLong(key, (Long) value);
+      } else if (value instanceof byte[]) {
+        bundle.putByteArray(key, (byte[]) value);
+      } else if (value instanceof int[]) {
+        bundle.putIntArray(key, (int[]) value);
+      } else if (value instanceof long[]) {
+        bundle.putLongArray(key, (long[]) value);
+      } else if (value instanceof double[]) {
+        bundle.putDoubleArray(key, (double[]) value);
+      } else if (isTypedArrayList(value, Integer.class)) {
+        bundle.putIntegerArrayList(key, (ArrayList<Integer>) value);
+      } else if (isTypedArrayList(value, String.class)) {
+        bundle.putStringArrayList(key, (ArrayList<String>) value);
+      } else if (isStringKeyedMap(value)) {
+        bundle.putBundle(key, convertArguments((Map<String, ?>) value));
       } else {
         throw new UnsupportedOperationException("Unsupported type " + value);
       }
@@ -65,6 +80,32 @@
     return bundle;
   }
 
+  private boolean isTypedArrayList(Object value, Class<?> type) {
+    if (!(value instanceof ArrayList)) {
+      return false;
+    }
+    ArrayList list = (ArrayList) value;
+    for (Object o : list) {
+      if (!(o == null || type.isInstance(o))) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  private boolean isStringKeyedMap(Object value) {
+    if (!(value instanceof Map)) {
+      return false;
+    }
+    Map map = (Map) value;
+    for (Object key : map.keySet()) {
+      if (!(key == null || key instanceof String)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
   @Override
   public void onMethodCall(MethodCall call, Result result) {
     String action = convertAction((String) call.argument("action"));
diff --git a/packages/android_intent/example/android/app/src/main/AndroidManifest.xml b/packages/android_intent/example/android/app/src/main/AndroidManifest.xml
index 81c36a7..ac963f7 100644
--- a/packages/android_intent/example/android/app/src/main/AndroidManifest.xml
+++ b/packages/android_intent/example/android/app/src/main/AndroidManifest.xml
@@ -10,6 +10,7 @@
          to allow setting breakpoints, to provide hot reload, etc.
     -->
     <uses-permission android:name="android.permission.INTERNET"/>
+    <uses-permission android:name="com.android.alarm.permission.SET_ALARM"/>
 
     <!-- io.flutter.app.FlutterApplication is an android.app.Application that
          calls FlutterMain.startInitialization(this); in its onCreate method.
diff --git a/packages/android_intent/example/lib/main.dart b/packages/android_intent/example/lib/main.dart
index 48a67b6..f2e2a8e 100644
--- a/packages/android_intent/example/lib/main.dart
+++ b/packages/android_intent/example/lib/main.dart
@@ -25,30 +25,36 @@
 }
 
 class MyHomePage extends StatelessWidget {
+  void _createAlarm() {
+    final AndroidIntent intent = const AndroidIntent(
+      action: 'android.intent.action.SET_ALARM',
+      arguments: const <String, dynamic>{
+        'android.intent.extra.alarm.DAYS': const <int>[2, 3, 4, 5, 6],
+        'android.intent.extra.alarm.HOUR': 21,
+        'android.intent.extra.alarm.MINUTES': 30,
+        'android.intent.extra.alarm.SKIP_UI': true,
+        'android.intent.extra.alarm.MESSAGE': 'Create a Flutter app',
+      },
+    );
+    intent.launch();
+  }
+
   @override
   Widget build(BuildContext context) {
     Widget body;
     if (const LocalPlatform().isAndroid) {
-      body = new GestureDetector(
-          child: const Center(
-              child: const Text(
-                  'Click here to launch play store with New York Times app.')),
-          onTap: () {
-            final AndroidIntent intent = const AndroidIntent(
-                action: 'action_view',
-                data:
-                    'https://play.google.com/store/apps/details?id=com.nytimes.android');
-            intent.launch();
-          });
+      body = new InkWell(
+        child: const Text('Tap here to set an alarm\non weekdays at 9:30pm.'),
+        onTap: _createAlarm,
+      );
     } else {
-      body = const Center(
-          child: const Text('This plugin only works with Android'));
+      body = const Text('This plugin only works with Android');
     }
     return new Scaffold(
       appBar: new AppBar(
         title: const Text('Plugin example app'),
       ),
-      body: body,
+      body: new Center(child: body),
     );
   }
 }