Add EdgeInsets, MediaQuery support for view insets (#13272)

* Add MediaQuery support for view insets

Also updates EdgeInsets documentation to reflect WindowPadding's use for
both padding and view insets.

See engine commits:
  flutter/engine#4403
  flutter/engine#4406
diff --git a/packages/flutter/lib/src/painting/edge_insets.dart b/packages/flutter/lib/src/painting/edge_insets.dart
index b9297f4..7da8363 100644
--- a/packages/flutter/lib/src/painting/edge_insets.dart
+++ b/packages/flutter/lib/src/painting/edge_insets.dart
@@ -354,9 +354,9 @@
 
   /// Creates insets that match the given window padding.
   ///
-  /// If you need the current system padding in the context of a widget,
-  /// consider using [MediaQuery.of] to obtain the current padding rather than
-  /// using the value from [dart:ui.window], so that you get notified when it
+  /// If you need the current system padding or view insets in the context of a
+  /// widget, consider using [MediaQuery.of] to obtain these values rather than
+  /// using the value from [dart:ui.window], so that you get notified of
   /// changes.
   EdgeInsets.fromWindowPadding(ui.WindowPadding padding, double devicePixelRatio)
     : left = padding.left / devicePixelRatio,
diff --git a/packages/flutter/lib/src/widgets/media_query.dart b/packages/flutter/lib/src/widgets/media_query.dart
index 35474b9..be94be4 100644
--- a/packages/flutter/lib/src/widgets/media_query.dart
+++ b/packages/flutter/lib/src/widgets/media_query.dart
@@ -41,6 +41,7 @@
     this.devicePixelRatio: 1.0,
     this.textScaleFactor: 1.0,
     this.padding: EdgeInsets.zero,
+    this.viewInsets: EdgeInsets.zero,
     this.alwaysUse24HourFormat: false,
   });
 
@@ -55,6 +56,7 @@
       devicePixelRatio = window.devicePixelRatio,
       textScaleFactor = window.textScaleFactor,
       padding = new EdgeInsets.fromWindowPadding(window.padding, window.devicePixelRatio),
+      viewInsets = new EdgeInsets.fromWindowPadding(window.viewInsets, window.devicePixelRatio),
       alwaysUse24HourFormat = window.alwaysUse24HourFormat;
 
   /// The size of the media in logical pixel (e.g, the size of the screen).
@@ -76,7 +78,17 @@
   /// the specified font size.
   final double textScaleFactor;
 
-  /// The padding around the edges of the media (e.g., the screen).
+  /// The number of physical pixels on each side of the display rectangle into
+  /// which the application can render, but over which the operating system
+  /// will likely place system UI, such as the keyboard, that fully obscures
+  /// any content.
+  final EdgeInsets viewInsets;
+
+  /// The number of physical pixels on each side of the display rectangle into
+  /// which the application can render, but which may be partially obscured by
+  /// system UI (such as the system notification area), or or physical
+  /// intrusions in the display (e.g. overscan regions on television screens or
+  /// phone sensor housings).
   final EdgeInsets padding;
 
   /// Whether to use 24-hour format when formatting time.
@@ -104,12 +116,14 @@
     double devicePixelRatio,
     double textScaleFactor,
     EdgeInsets padding,
+    EdgeInsets viewInsets,
   }) {
     return new MediaQueryData(
       size: size ?? this.size,
       devicePixelRatio: devicePixelRatio ?? this.devicePixelRatio,
       textScaleFactor: textScaleFactor ?? this.textScaleFactor,
       padding: padding ?? this.padding,
+      viewInsets: viewInsets ?? this.viewInsets,
     );
   }
 
@@ -144,6 +158,12 @@
         right: removeRight ? 0.0 : null,
         bottom: removeBottom ? 0.0 : null,
       ),
+      viewInsets: viewInsets.copyWith(
+        left: removeLeft ? 0.0 : null,
+        top: removeTop ? 0.0 : null,
+        right: removeRight ? 0.0 : null,
+        bottom: removeBottom ? 0.0 : null,
+      ),
     );
   }
 
@@ -155,16 +175,17 @@
     return typedOther.size == size
         && typedOther.devicePixelRatio == devicePixelRatio
         && typedOther.textScaleFactor == textScaleFactor
-        && typedOther.padding == padding;
+        && typedOther.padding == padding
+        && typedOther.viewInsets == viewInsets;
   }
 
   @override
-  int get hashCode => hashValues(size, devicePixelRatio, textScaleFactor, padding);
+  int get hashCode => hashValues(size, devicePixelRatio, textScaleFactor, padding, viewInsets);
 
   @override
   String toString() {
     return '$runtimeType(size: $size, devicePixelRatio: $devicePixelRatio, '
-           'textScaleFactor: $textScaleFactor, padding: $padding)';
+           'textScaleFactor: $textScaleFactor, padding: $padding, viewInsets: $viewInsets)';
   }
 }
 
diff --git a/packages/flutter/test/widgets/media_query_test.dart b/packages/flutter/test/widgets/media_query_test.dart
index 3441a23..4ac268b 100644
--- a/packages/flutter/test/widgets/media_query_test.dart
+++ b/packages/flutter/test/widgets/media_query_test.dart
@@ -44,4 +44,30 @@
     expect(data.hashCode, equals(data.copyWith().hashCode));
     expect(data.size, equals(ui.window.physicalSize / ui.window.devicePixelRatio));
   });
+
+  testWidgets('MediaQueryData.copyWith defaults to source', (WidgetTester tester) async {
+    final MediaQueryData data = new MediaQueryData.fromWindow(ui.window);
+    final MediaQueryData copied = data.copyWith();
+    expect(copied.size, data.size);
+    expect(copied.devicePixelRatio, data.devicePixelRatio);
+    expect(copied.textScaleFactor, data.textScaleFactor);
+    expect(copied.padding, data.padding);
+    expect(copied.viewInsets, data.viewInsets);
+  });
+
+  testWidgets('MediaQuery.copyWith copies specified values', (WidgetTester tester) async {
+    final MediaQueryData data = new MediaQueryData.fromWindow(ui.window);
+    final MediaQueryData copied = data.copyWith(
+      size: const Size(3.14, 2.72),
+      devicePixelRatio: 1.41,
+      textScaleFactor: 1.62,
+      padding: const EdgeInsets.all(9.10938),
+      viewInsets: const EdgeInsets.all(1.67262),
+    );
+    expect(copied.size, const Size(3.14, 2.72));
+    expect(copied.devicePixelRatio, 1.41);
+    expect(copied.textScaleFactor, 1.62);
+    expect(copied.padding, const EdgeInsets.all(9.10938));
+    expect(copied.viewInsets, const EdgeInsets.all(1.67262));
+  });
 }