aboutsummaryrefslogtreecommitdiff
path: root/ui
diff options
context:
space:
mode:
authorTiem Song <tiem@google.com>2017-12-06 12:26:48 -0800
committerTiem Song <tiem@google.com>2017-12-11 10:04:43 -0800
commitd951aa5a3208b9a929624d10b22eb4a0879d9bac (patch)
tree76381e5dcb8b55223f34c37ef31426fdd817d5d8 /ui
parent44f2c83565522aff4be31c8ce33b483f6db5f05d (diff)
downloadandroid-d951aa5a3208b9a929624d10b22eb4a0879d9bac.tar.gz
Create Kotlin version of MultiWindowPlayground.
Bug: 64766094 Test: Manually verified no regression vs Java version. Change-Id: I65b5517d2ff3bb9a3140c3c9171c16e53ef9b097
Diffstat (limited to 'ui')
-rw-r--r--ui/window/MultiWindowPlayground/README.md6
-rw-r--r--ui/window/MultiWindowPlayground/kotlinApp/Application/build.gradle25
-rw-r--r--ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/AndroidManifest.xml86
-rw-r--r--ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/MainActivity.kt118
-rw-r--r--ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/AdjacentActivity.kt37
-rw-r--r--ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/BasicActivity.kt37
-rw-r--r--ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/CustomConfigurationChangeActivity.kt38
-rw-r--r--ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/LaunchBoundsActivity.kt37
-rw-r--r--ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/LoggingActivity.kt158
-rw-r--r--ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/MinimumSizeActivity.kt36
-rw-r--r--ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/UnresizableActivity.kt38
-rwxr-xr-xui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/logger/Log.kt59
-rwxr-xr-xui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/logger/LogFragment.kt90
-rwxr-xr-xui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/logger/LogNode.kt41
-rwxr-xr-xui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/logger/LogView.kt90
-rwxr-xr-xui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/logger/LogWrapper.kt52
-rwxr-xr-xui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/logger/MessageOnlyLogFilter.kt32
-rw-r--r--ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/layout/activity_logging.xml51
-rw-r--r--ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/layout/activity_main.xml123
-rw-r--r--ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/layout/logging.xml22
-rwxr-xr-xui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/mipmap-hdpi/ic_launcher.pngbin0 -> 2633 bytes
-rwxr-xr-xui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/mipmap-mdpi/ic_launcher.pngbin0 -> 1741 bytes
-rwxr-xr-xui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/mipmap-xhdpi/ic_launcher.pngbin0 -> 3241 bytes
-rwxr-xr-xui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/mipmap-xxhdpi/ic_launcher.pngbin0 -> 5345 bytes
-rwxr-xr-xui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/mipmap-xxxhdpi/ic_launcher.pngbin0 -> 7787 bytes
-rw-r--r--ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/values-w820dp/dimens.xml20
-rw-r--r--ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/values/colors.xml32
-rw-r--r--ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/values/dimens.xml24
-rw-r--r--ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/values/strings.xml69
-rw-r--r--ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/values/styles.xml37
-rw-r--r--ui/window/MultiWindowPlayground/kotlinApp/README.md94
-rw-r--r--ui/window/MultiWindowPlayground/kotlinApp/build.gradle22
-rw-r--r--ui/window/MultiWindowPlayground/kotlinApp/gradle/wrapper/gradle-wrapper.jarbin0 -> 49896 bytes
-rw-r--r--ui/window/MultiWindowPlayground/kotlinApp/gradle/wrapper/gradle-wrapper.properties6
-rwxr-xr-xui/window/MultiWindowPlayground/kotlinApp/gradlew164
-rw-r--r--ui/window/MultiWindowPlayground/kotlinApp/gradlew.bat90
-rwxr-xr-xui/window/MultiWindowPlayground/kotlinApp/screenshots/icon-web.pngbin0 -> 29390 bytes
-rw-r--r--ui/window/MultiWindowPlayground/kotlinApp/screenshots/main.pngbin0 -> 257056 bytes
-rw-r--r--ui/window/MultiWindowPlayground/kotlinApp/settings.gradle1
39 files changed, 1732 insertions, 3 deletions
diff --git a/ui/window/MultiWindowPlayground/README.md b/ui/window/MultiWindowPlayground/README.md
index dbe200ab..b58b2857 100644
--- a/ui/window/MultiWindowPlayground/README.md
+++ b/ui/window/MultiWindowPlayground/README.md
@@ -49,13 +49,13 @@ Pre-requisites
--------------
- Android SDK 24
-- Android Build Tools v24.0.2
+- Android Build Tools v26.0.1
- Android Support Repository
Screenshots
-------------
-<img src="screenshots/main.png" height="400" alt="Screenshot"/>
+<img src="screenshots/main.png" height="400" alt="Screenshot"/>
Getting Started
---------------
@@ -78,7 +78,7 @@ submitting a pull request through GitHub. Please see CONTRIBUTING.md for more de
License
-------
-Copyright 2016 The Android Open Source Project, Inc.
+Copyright 2017 The Android Open Source Project, Inc.
Licensed to the Apache Software Foundation (ASF) under one or more contributor
license agreements. See the NOTICE file distributed with this work for
diff --git a/ui/window/MultiWindowPlayground/kotlinApp/Application/build.gradle b/ui/window/MultiWindowPlayground/kotlinApp/Application/build.gradle
new file mode 100644
index 00000000..c5b64a93
--- /dev/null
+++ b/ui/window/MultiWindowPlayground/kotlinApp/Application/build.gradle
@@ -0,0 +1,25 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+
+android {
+ compileSdkVersion 27
+ defaultConfig {
+ applicationId "com.android.multiwindowplayground"
+ minSdkVersion 24
+ targetSdkVersion 27
+ versionCode 1
+ versionName "1.0"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ implementation "com.android.support:appcompat-v7:27.0.2"
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
+}
diff --git a/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/AndroidManifest.xml b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..10a81740
--- /dev/null
+++ b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/AndroidManifest.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<manifest package="com.android.multiwindowplayground"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <application
+ android:allowBackup="false"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:supportsRtl="true"
+ android:theme="@style/MultiWindowSampleTheme">
+ <!-- The launcher Activity that is started when the application is first started.
+ Note that we are setting the task affinity to "" to ensure each activity is launched
+ into a separate task stack. -->
+ <activity
+ android:name="com.android.multiwindowplayground.MainActivity"
+ android:taskAffinity="">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <!-- This Activity cannot be resized and is always displayed full screen. -->
+ <activity
+ android:name="com.android.multiwindowplayground.activities.UnresizableActivity"
+ android:resizeableActivity="false"
+ android:taskAffinity="" />
+
+ <!-- This Activity has a default size (750x500dp) with a minimum size
+ (500dp at its shortest side). It is launched in the top/end (top/right) corner by default.
+ These attributes are defined in the 'layout' tag within an Activity definition. -->
+ <activity
+ android:name="com.android.multiwindowplayground.activities.MinimumSizeActivity"
+ android:launchMode="singleInstance"
+ android:taskAffinity="">
+ <layout
+ android:defaultHeight="500dp"
+ android:defaultWidth="750dp"
+ android:gravity="top|end"
+ android:minWidth="500dp"
+ android:minHeight="500dp" />
+ </activity>
+
+ <!-- In split-screen mode, this Activity is launched adjacent to another Activity. This is
+ controlled via a flag set in the intent that launches this Activity. -->
+ <activity
+ android:name="com.android.multiwindowplayground.activities.AdjacentActivity"
+ android:taskAffinity="" />
+
+ <!-- This Activity is launched within an area defined in its launch intent. -->
+ <activity
+ android:name="com.android.multiwindowplayground.activities.LaunchBoundsActivity"
+ android:taskAffinity="" />
+
+ <!-- This activity handles all configuration changes itself.
+ Callbacks for configuration changes are received in 'onConfigurationChanged'. -->
+ <activity
+ android:name="com.android.multiwindowplayground.activities.CustomConfigurationChangeActivity"
+ android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
+ android:launchMode="singleInstance"
+ android:taskAffinity="" />
+
+ <!-- This Activity is launched in a new task without any special flags or settings. -->
+ <activity
+ android:name="com.android.multiwindowplayground.activities.BasicActivity"
+ android:launchMode="singleInstance"
+ android:taskAffinity="" />
+
+ </application>
+
+</manifest>
diff --git a/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/MainActivity.kt b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/MainActivity.kt
new file mode 100644
index 00000000..0c371a28
--- /dev/null
+++ b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/MainActivity.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.multiwindowplayground
+
+import android.app.ActivityOptions
+import android.content.Intent
+import android.graphics.Rect
+import android.os.Bundle
+import android.util.Log
+import android.view.View
+import com.android.multiwindowplayground.activities.AdjacentActivity
+import com.android.multiwindowplayground.activities.BasicActivity
+import com.android.multiwindowplayground.activities.CustomConfigurationChangeActivity
+import com.android.multiwindowplayground.activities.LOG_TAG
+import com.android.multiwindowplayground.activities.LaunchBoundsActivity
+import com.android.multiwindowplayground.activities.LoggingActivity
+import com.android.multiwindowplayground.activities.MinimumSizeActivity
+import com.android.multiwindowplayground.activities.UnresizableActivity
+
+class MainActivity : LoggingActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_main)
+
+ // Display an additional message if the app is not in multiwindow mode.
+ findViewById<View>(R.id.warning_multiwindow_disabled).visibility =
+ if (!isInMultiWindowMode) View.VISIBLE else View.GONE
+ }
+
+ @Suppress("UNUSED_PARAMETER")
+ fun onStartUnresizableClick(view: View) {
+ Log.d(LOG_TAG, "** starting UnresizableActivity")
+
+ /*
+ * This activity is marked as 'unresizable' in the AndroidManifest. We need to specify the
+ * FLAG_ACTIVITY_NEW_TASK flag here to launch it into a new task stack, otherwise the
+ * properties from the root activity would have been inherited (which was here marked as
+ * resizable by default).
+ */
+ val intent = Intent(this, UnresizableActivity::class.java).apply {
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK
+ }
+ startActivity(intent)
+ }
+
+ @Suppress("UNUSED_PARAMETER")
+ fun onStartMinimumSizeActivity(view: View) {
+ Log.d(LOG_TAG, "** starting MinimumSizeActivity")
+ startActivity(Intent(this, MinimumSizeActivity::class.java))
+ }
+
+ @Suppress("UNUSED_PARAMETER")
+ fun onStartAdjacentActivity(view: View) {
+ Log.d(LOG_TAG, "** starting AdjacentActivity")
+
+ /*
+ * Start this activity adjacent to the focused activity (ie. this activity) if possible.
+ * Note that this flag is just a hint to the system and may be ignored. For example,
+ * if the activity is launched within the same task, it will be launched on top of the
+ * previous activity that started the Intent. That's why the Intent.FLAG_ACTIVITY_NEW_TASK
+ * flag is specified here in the intent - this will start the activity in a new task.
+ */
+ val intent = Intent(this, AdjacentActivity::class.java).apply {
+ addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT or Intent.FLAG_ACTIVITY_NEW_TASK)
+ }
+ startActivity(intent)
+ }
+
+ @Suppress("UNUSED_PARAMETER")
+ fun onStartLaunchBoundsActivity(view: View) {
+ Log.d(LOG_TAG, "** starting LaunchBoundsActivity")
+
+ // Define the bounds in which the Activity will be launched into.
+ val bounds = Rect(500, 300, 100, 0)
+
+ // Set the bounds as an activity option.
+ val options = ActivityOptions.makeBasic().apply {
+ launchBounds = bounds
+ }
+
+ // Start the LaunchBoundsActivity with the specified options
+ val intent = Intent(this, LaunchBoundsActivity::class.java)
+ startActivity(intent, options.toBundle())
+ }
+
+ @Suppress("UNUSED_PARAMETER")
+ fun onStartBasicActivity(view: View) {
+ Log.d(LOG_TAG, "** starting BasicActivity")
+
+ // Start an Activity with the default options in the 'singleTask' launch mode as defined in
+ // the AndroidManifest.xml.
+ startActivity(Intent(this, BasicActivity::class.java))
+ }
+
+ @Suppress("UNUSED_PARAMETER")
+ fun onStartCustomConfigurationActivity(view: View) {
+ Log.d(LOG_TAG, "** starting CustomConfigurationChangeActivity")
+
+ // Start an Activity that handles all configuration changes itself.
+ startActivity(Intent(this, CustomConfigurationChangeActivity::class.java))
+ }
+
+}
diff --git a/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/AdjacentActivity.kt b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/AdjacentActivity.kt
new file mode 100644
index 00000000..fb8206ed
--- /dev/null
+++ b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/AdjacentActivity.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.multiwindowplayground.activities
+
+import android.os.Bundle
+import com.android.multiwindowplayground.R
+
+/**
+ * This Activity is to be launched adjacent to another Activity using the
+ * [android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT] flag.
+ *
+ * @see [com.android.multiwindowplayground.MainActivity.onStartAdjacentActivity]
+ */
+class AdjacentActivity : LoggingActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_logging)
+ setBackgroundColor(R.color.teal)
+ setDescription(R.string.activity_adjacent_description)
+ }
+
+}
diff --git a/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/BasicActivity.kt b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/BasicActivity.kt
new file mode 100644
index 00000000..cc7821a3
--- /dev/null
+++ b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/BasicActivity.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.multiwindowplayground.activities
+
+import android.os.Bundle
+import com.android.multiwindowplayground.R
+
+/**
+ * This activity is the most basic, simple use case and is to be launched without any special
+ * flags or settings.
+ *
+ * @see [com.android.multiwindowplayground.MainActivity.onStartBasicActivity]
+ */
+class BasicActivity : LoggingActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_logging)
+ setDescription(R.string.activity_description_basic)
+ setBackgroundColor(R.color.gray)
+ }
+
+}
diff --git a/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/CustomConfigurationChangeActivity.kt b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/CustomConfigurationChangeActivity.kt
new file mode 100644
index 00000000..5e48f2e9
--- /dev/null
+++ b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/CustomConfigurationChangeActivity.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.multiwindowplayground.activities
+
+import android.os.Bundle
+import com.android.multiwindowplayground.R
+
+/**
+ * This activity handles configuration changes itself. The list of configuration changes that are
+ * supported is defined in its AndroidManifest definition. Each configuration change triggers a
+ * call to [onConfigurationChanged], which is logged in the [LoggingActivity].
+ *
+ * @see [com.android.multiwindowplayground.MainActivity.onStartCustomConfigurationActivity]
+ */
+class CustomConfigurationChangeActivity : LoggingActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_logging)
+ setBackgroundColor(R.color.cyan)
+ setDescription(R.string.activity_custom_description)
+ }
+
+}
diff --git a/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/LaunchBoundsActivity.kt b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/LaunchBoundsActivity.kt
new file mode 100644
index 00000000..50838c6f
--- /dev/null
+++ b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/LaunchBoundsActivity.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.multiwindowplayground.activities
+
+import android.os.Bundle
+import com.android.multiwindowplayground.R
+
+/**
+ * In free-form mode, this activity is to be launched within a defined bounds on screen.
+ * This property is set as part of the Intent that starts this activity.
+ *
+ * @see [com.android.multiwindowplayground.MainActivity.onStartLaunchBoundsActivity]
+ */
+class LaunchBoundsActivity : LoggingActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_logging)
+ setBackgroundColor(R.color.lime)
+ setDescription(R.string.activity_bounds_description)
+ }
+
+}
diff --git a/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/LoggingActivity.kt b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/LoggingActivity.kt
new file mode 100644
index 00000000..313bb109
--- /dev/null
+++ b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/LoggingActivity.kt
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.multiwindowplayground.activities
+
+import android.content.res.Configuration
+import android.os.Bundle
+import android.os.PersistableBundle
+import android.support.annotation.ColorRes
+import android.support.annotation.StringRes
+import android.support.v7.app.AppCompatActivity
+import android.view.View
+import android.widget.TextView
+import com.android.multiwindowplayground.R
+import com.android.multiwindowplayground.logger.Log
+import com.android.multiwindowplayground.logger.LogFragment
+import com.android.multiwindowplayground.logger.LogWrapper
+import com.android.multiwindowplayground.logger.MessageOnlyLogFilter
+
+const val LOG_TAG = "LoggingActivity"
+
+/**
+ * Activity that logs all key lifecycle callbacks to [Log].
+ * Output is also logged to the UI into a [LogFragment] through [initializeLogging] and
+ * [stopLogging].
+ */
+abstract class LoggingActivity : AppCompatActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ Log.d(LOG_TAG, "onCreate")
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
+ super.onCreate(savedInstanceState, persistentState)
+ Log.d(LOG_TAG, "onCreatePersistable")
+ }
+
+ override fun onStart() {
+ super.onStart()
+ // Start logging to UI.
+ initializeLogging()
+
+ Log.d(LOG_TAG, "onStart")
+ }
+
+ override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
+ super.onRestoreInstanceState(savedInstanceState)
+ Log.d(LOG_TAG, "onRestoreInstanceState")
+ }
+
+ override fun onRestoreInstanceState(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
+ super.onRestoreInstanceState(savedInstanceState, persistentState)
+ Log.d(LOG_TAG, "onRestoreInstanceStatePersistable")
+ }
+
+ override fun onResume() {
+ super.onResume()
+ Log.d(LOG_TAG, "onResume")
+ }
+
+ override fun onPostCreate(savedInstanceState: Bundle?) {
+ super.onPostCreate(savedInstanceState)
+ Log.d(LOG_TAG, "onPostCreate")
+ }
+
+ override fun onPostCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
+ super.onPostCreate(savedInstanceState, persistentState)
+ Log.d(LOG_TAG, "onPostCreate")
+ }
+
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ super.onConfigurationChanged(newConfig)
+ Log.d(LOG_TAG, "onConfigurationChanged: $newConfig")
+ }
+
+ override fun onMultiWindowModeChanged(isInMultiWindowMode: Boolean, newConfig: Configuration?) {
+ super.onMultiWindowModeChanged(isInMultiWindowMode, newConfig)
+ Log.d(LOG_TAG, "onMultiWindowModeChanged: $isInMultiWindowMode")
+ }
+
+ override fun onSaveInstanceState(outState: Bundle?) {
+ super.onSaveInstanceState(outState)
+ Log.d(LOG_TAG, "onSaveInstanceState")
+ }
+
+ override fun onSaveInstanceState(outState: Bundle?, outPersistentState: PersistableBundle?) {
+ super.onSaveInstanceState(outState, outPersistentState)
+ Log.d(LOG_TAG, "onSaveInstanceStatePersistable")
+ }
+
+ override fun onPause() {
+ super.onPause()
+ Log.d(LOG_TAG, "onPause")
+ }
+
+ override fun onStop() {
+ super.onStop()
+ // Stop logging to UI when this activity is stopped.
+ stopLogging()
+
+ Log.d(LOG_TAG, "onStop")
+ }
+
+ override fun onRestart() {
+ super.onRestart()
+ Log.d(LOG_TAG, "onRestart")
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ Log.d(LOG_TAG, "onDestroy")
+ }
+
+ /**
+ * Set up targets to receive log data
+ */
+ private fun initializeLogging() {
+ // Using Log, front-end to the logging chain, emulates android.util.log method signatures.
+ // Wraps Android's native log framework
+ val logWrapper = LogWrapper()
+ Log.logNode = logWrapper
+
+ // Filter strips out everything except the message text.
+ val msgFilter = MessageOnlyLogFilter()
+ logWrapper.next = msgFilter
+
+ // On screen logging via a fragment with a TextView.
+ val logFragment = supportFragmentManager.findFragmentById(R.id.log_fragment) as LogFragment
+ msgFilter.next = logFragment.logView
+ }
+
+ private fun stopLogging() {
+ Log.logNode = null
+ }
+
+ protected fun setDescription(@StringRes textId: Int) {
+ findViewById<TextView>(R.id.description).setText(textId)
+ }
+
+ protected fun setBackgroundColor(@ColorRes colorId: Int) {
+ findViewById<View>(R.id.scrollview).setBackgroundResource(colorId)
+ }
+
+}
diff --git a/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/MinimumSizeActivity.kt b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/MinimumSizeActivity.kt
new file mode 100644
index 00000000..d91cd473
--- /dev/null
+++ b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/MinimumSizeActivity.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.multiwindowplayground.activities
+
+import android.os.Bundle
+import com.android.multiwindowplayground.R
+
+/**
+ * This Activity has a minimum size defined in the AndroidManifest.
+ *
+ * @see [com.android.multiwindowplayground.MainActivity.onStartMinimumSizeActivity]
+ */
+class MinimumSizeActivity : LoggingActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_logging)
+ setBackgroundColor(R.color.pink)
+ setDescription(R.string.activity_minimum_description)
+ }
+
+}
diff --git a/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/UnresizableActivity.kt b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/UnresizableActivity.kt
new file mode 100644
index 00000000..285fb821
--- /dev/null
+++ b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/UnresizableActivity.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.multiwindowplayground.activities
+
+import android.os.Bundle
+import com.android.multiwindowplayground.R
+
+/**
+ * This Activity is defined as unresizable in the AndroidManifest.
+ * This means that this activity is always launched full screen and will not be resized by the
+ * system.
+ *
+ * @see [com.android.multiwindowplayground.MainActivity.onStartUnresizableClick]
+ */
+class UnresizableActivity : LoggingActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_logging)
+ setBackgroundColor(R.color.purple)
+ setDescription(R.string.activity_description_unresizable)
+ }
+
+}
diff --git a/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/logger/Log.kt b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/logger/Log.kt
new file mode 100755
index 00000000..2bce5c87
--- /dev/null
+++ b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/logger/Log.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.multiwindowplayground.logger
+
+/**
+ * Helper class for a list (or tree) of LoggerNodes.
+ *
+ *
+ * When this is set as the head of the list,
+ * an instance of it can function as a drop-in replacement for [android.util.Log].
+ * Most of the methods in this class serve only to map a method call in Log to its equivalent
+ * in LogNode.
+ */
+object Log {
+
+ internal val NONE = -1
+
+ // Use the native value from Android's native logging facilities for easy migration and interop.
+ private val DEBUG = android.util.Log.DEBUG
+
+ // Stores the beginning of the LogNode topology.
+ internal var logNode: LogNode? = null
+
+ /**
+ * Instructs the LogNode to print the log data provided. Other LogNodes can
+ * be chained to the end of the LogNode as desired.
+ *
+ * @param priority Log level of the data being logged. Verbose, Error, etc.
+ * @param tag Tag for for the log data. Can be used to organize log statements.
+ * @param msg The actual message to be logged. The actual message to be logged.
+ */
+ private fun println(priority: Int, tag: String, msg: String) {
+ logNode?.println(priority, tag, msg, null)
+ }
+
+ /**
+ * Prints a message at DEBUG priority.
+ *
+ * @param tag Tag for for the log data. Can be used to organize log statements.
+ * @param msg The actual message to be logged.
+ */
+ fun d(tag: String, msg: String) {
+ println(DEBUG, tag, msg)
+ }
+}
diff --git a/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/logger/LogFragment.kt b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/logger/LogFragment.kt
new file mode 100755
index 00000000..2014d0ef
--- /dev/null
+++ b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/logger/LogFragment.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.multiwindowplayground.logger
+
+import android.graphics.Typeface
+import android.os.Bundle
+import android.support.v4.app.Fragment
+import android.support.v4.app.FragmentActivity
+import android.text.Editable
+import android.text.TextWatcher
+import android.view.Gravity
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.view.ViewGroup.LayoutParams.MATCH_PARENT
+import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
+import android.widget.ScrollView
+
+/**
+ * Simple fragment which contains a [LogView] and is used to output log data it receives through the
+ * [LogNode] interface.
+ */
+class LogFragment : Fragment() {
+
+ internal lateinit var logView: LogView
+ private lateinit var scrollView: ScrollView
+
+ private fun inflateViews(): View {
+ val scrollParams = ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)
+ val logParams = ViewGroup.LayoutParams(scrollParams).apply {
+ height = WRAP_CONTENT
+ }
+
+ // Want to set padding as 16 dips, setPadding takes pixels. Hooray math!
+ val paddingDips = 16
+ val scale = resources.displayMetrics.density.toDouble()
+ val paddingPixels = (paddingDips * scale + .5).toInt()
+
+ logView = LogView(activity as FragmentActivity).apply {
+ setTextAppearance(android.R.style.TextAppearance_Material_Medium)
+ layoutParams = logParams
+ isClickable = true
+ isFocusable = true
+ typeface = Typeface.create("monospace", Typeface.NORMAL)
+ setPadding(paddingPixels, paddingPixels, paddingPixels, paddingPixels)
+ compoundDrawablePadding = paddingPixels
+ gravity = Gravity.BOTTOM
+ }
+
+ scrollView = ScrollView(activity).apply {
+ layoutParams = scrollParams
+ addView(logView)
+ }
+ return scrollView
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ val result = inflateViews()
+
+ logView.addTextChangedListener(object : TextWatcher {
+ override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
+
+ override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
+
+ override fun afterTextChanged(s: Editable) {
+ scrollView.run { post { smoothScrollTo(0, scrollView.bottom + logView.height) }}
+ }
+ })
+ return result
+ }
+
+} \ No newline at end of file
diff --git a/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/logger/LogNode.kt b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/logger/LogNode.kt
new file mode 100755
index 00000000..a9c23f91
--- /dev/null
+++ b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/logger/LogNode.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.multiwindowplayground.logger
+
+/**
+ * Basic interface for a logging system that can output to one or more targets.
+ * Note that in addition to classes that will output these logs in some format,
+ * one can also implement this interface over a filter and insert that in the chain,
+ * such that no targets further down see certain data, or see manipulated forms of the data.
+ * You could, for instance, write a "ToHtmlLoggerNode" that just converted all the log data
+ * it received to HTML and sent it along to the next node in the chain, without printing it
+ * anywhere.
+ */
+interface LogNode {
+
+ /**
+ * Instructs first LogNode in the list to print the log data provided.
+ *
+ * @param priority Log level of the data being logged. Verbose, Error, etc.
+ * @param tag Tag for for the log data. Can be used to organize log statements.
+ * @param msg The actual message to be logged. The actual message to be logged.
+ * @param tr If an exception was thrown, this can be sent along for the logging
+ * facilities to extract and print useful information.
+ */
+ fun println(priority: Int, tag: String?, msg: String, tr: Throwable?)
+
+}
diff --git a/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/logger/LogView.kt b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/logger/LogView.kt
new file mode 100755
index 00000000..22488dce
--- /dev/null
+++ b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/logger/LogView.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.multiwindowplayground.logger
+
+import android.app.Activity
+import android.content.Context
+
+/**
+ * Simple TextView which is used to output log data received through the LogNode interface.
+ */
+class LogView(context: Context) : android.support.v7.widget.AppCompatTextView(context), LogNode {
+
+ // The next LogNode in the chain.
+ private var next: LogNode? = null
+
+ /**
+ * Formats the log data and prints it out to the LogView.
+ *
+ * @param priority Log level of the data being logged. Verbose, Error, etc.
+ * @param tag Tag for for the log data. Can be used to organize log statements.
+ * @param msg The actual message to be logged. The actual message to be logged.
+ * @param tr If an exception was thrown, this can be sent along for the logging
+ * facilities to extract and print useful information.
+ */
+ override fun println(priority: Int, tag: String?, msg: String, tr: Throwable?) {
+
+ // For the purposes of this View, we want to print the priority as readable text.
+ val priorityStr = when (priority) {
+ android.util.Log.VERBOSE -> "VERBOSE"
+ android.util.Log.DEBUG -> "DEBUG"
+ android.util.Log.INFO -> "INFO"
+ android.util.Log.WARN -> "WARN"
+ android.util.Log.ERROR -> "ERROR"
+ android.util.Log.ASSERT -> "ASSERT"
+ else -> "VERBOSE"
+ }
+
+ // Handily, the Log class has a facility for converting a stack trace into a usable string.
+ var exceptionStr: String? = null
+ if (tr != null) exceptionStr = android.util.Log.getStackTraceString(tr)
+
+ // Take the priority, tag, message, and exception, and concatenate as necessary
+ // into one usable line of text.
+ val delimiter = "\t"
+ val outputBuilder = StringBuilder().also {
+ appendIfNotNullOrEmpty(it, priorityStr, delimiter)
+ appendIfNotNullOrEmpty(it, tag, delimiter)
+ appendIfNotNullOrEmpty(it, msg, delimiter)
+ appendIfNotNullOrEmpty(it, exceptionStr, delimiter)
+ }
+
+ // In case this was originally called from an AsyncTask or some other off-UI thread,
+ // make sure the update occurs within the UI thread.
+ (context as Activity).runOnUiThread(Thread(Runnable {
+ // Display the text we just generated within the LogView as a new line of log data.
+ append("\n$outputBuilder")
+ }))
+
+ next?.println(priority, tag, msg, tr)
+ }
+
+ /**
+ * Takes a string and adds to it, with a separator, if the bit to be added isn't null. Since
+ * the logger takes so many arguments that might be null, this method helps cut out some of the
+ * agonizing tedium of writing the same statement over and over.
+ *
+ * @param source StringBuilder containing the text to append to.
+ * @param addStr The String to append
+ * @param delimiter The String to separate the source and appended strings.
+ */
+ private fun appendIfNotNullOrEmpty(source: StringBuilder, addStr: String?, delimiter: String) {
+ if (addStr.isNullOrEmpty()) return
+ source.append(addStr).append(delimiter)
+ }
+
+}
diff --git a/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/logger/LogWrapper.kt b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/logger/LogWrapper.kt
new file mode 100755
index 00000000..046b743d
--- /dev/null
+++ b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/logger/LogWrapper.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.multiwindowplayground.logger
+
+import android.util.Log
+
+/**
+ * Helper class which wraps Android's native Log utility in the Logger interface. This way
+ * normal DDMS output can be one of the many targets receiving and outputting logs simultaneously.
+ */
+class LogWrapper : LogNode {
+
+ // For piping: The next node to receive Log data after this one has done its work.
+ internal var next: LogNode? = null
+
+ /**
+ * Prints data out to the console using Android's native log mechanism.
+ *
+ * @param priority Log level of the data being logged. Verbose, Error, etc.
+ * @param tag Tag for for the log data. Can be used to organize log statements.
+ * @param msg The actual message to be logged. The actual message to be logged.
+ * @param tr If an exception was thrown, this can be sent along for the logging
+ * facilities to extract and print useful information.
+ */
+ override fun println(priority: Int, tag: String?, msg: String, tr: Throwable?) {
+
+ // If an exception was provided, convert that exception to a usable string and attach
+ // it to the end of the msg method.
+ if (tr != null) msg.plus("\n${Log.getStackTraceString(tr)}")
+
+ // This is functionally identical to Log.x(tag, useMsg);
+ // For instance, if priority were Log.VERBOSE, this would be the same as Log.v(tag, useMsg)
+ Log.println(priority, tag, msg)
+
+ // If this isn't the last node in the chain, move things along.
+ next?.println(priority, tag, msg, tr)
+ }
+}
diff --git a/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/logger/MessageOnlyLogFilter.kt b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/logger/MessageOnlyLogFilter.kt
new file mode 100755
index 00000000..a3f1e627
--- /dev/null
+++ b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/logger/MessageOnlyLogFilter.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.multiwindowplayground.logger
+
+/**
+ * Simple [LogNode] filter, removes everything except the message.
+ * Useful for situations like on-screen log output where you don't want a lot of metadata
+ * displayed, just easy-to-read message updates as they're happening.
+ */
+class MessageOnlyLogFilter : LogNode {
+
+ var next: LogNode? = null
+
+ override fun println(priority: Int, tag: String?, msg: String, tr: Throwable?) {
+ next?.println(Log.NONE, null, msg, null)
+ }
+
+}
diff --git a/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/layout/activity_logging.xml b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/layout/activity_logging.xml
new file mode 100644
index 00000000..2389e121
--- /dev/null
+++ b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/layout/activity_logging.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<!-- This layout contains a TextView and a LogFragment that logs some text to the screen. -->
+<LinearLayout android:id="@+id/layout"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/white"
+ android:orientation="vertical">
+
+ <ScrollView
+ android:id="@+id/scrollview"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_gravity="top"
+ android:layout_weight="0.75">
+
+ <TextView
+ android:id="@+id/description"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin"
+ android:textColor="@color/white" />
+ </ScrollView>
+
+ <include
+ layout="@layout/logging"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_gravity="bottom"
+ android:layout_weight="0.25" />
+</LinearLayout> \ No newline at end of file
diff --git a/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/layout/activity_main.xml b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/layout/activity_main.xml
new file mode 100644
index 00000000..7c405a38
--- /dev/null
+++ b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/layout/activity_main.xml
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:orientation="vertical"
+ android:background="@color/lightgray">
+
+ <ScrollView xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="0.75"
+ android:background="@color/white"
+ android:layout_gravity="top"
+ android:id="@+id/scrollview"
+ tools:context="com.android.multiwindowplayground.MainActivity">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin"
+ android:orientation="vertical">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/introduction_title"
+ android:textSize="@dimen/text_view_text_size" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/warning_multiwindow_disabled"
+ android:visibility="gone"
+ tools:visibility="visible"
+ style="@style/TextWarning"
+ android:paddingTop="@dimen/content_vertical_dividing_padding"
+ android:paddingBottom="@dimen/content_vertical_dividing_padding"
+ android:text="@string/multi_window_sample_action" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/description"
+ android:text="@string/sample_introduction" />
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/button_start_basic"
+ android:onClick="onStartBasicActivity"
+ android:text="@string/start_default" />
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/start_unresizable"
+ android:onClick="onStartUnresizableClick"
+ android:text="@string/start_unresizable" />
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/start_adjacent"
+ android:onClick="onStartAdjacentActivity"
+ android:text="@string/start_adjacent" />
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/start_customconfiguration"
+ android:onClick="onStartCustomConfigurationActivity"
+ android:text="@string/start_custom_activity" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingTop="@dimen/content_vertical_dividing_padding"
+ android:text="@string/sample_freeform_introduction" />
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/start_minimumsize"
+ android:onClick="onStartMinimumSizeActivity"
+ android:text="@string/start_minimum" />
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/start_launchbounds"
+ android:onClick="onStartLaunchBoundsActivity"
+ android:text="@string/start_bounds" />
+
+ </LinearLayout>
+ </ScrollView>
+
+ <include
+ layout="@layout/logging"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_gravity="bottom"
+ android:layout_weight="0.25" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/layout/logging.xml b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/layout/logging.xml
new file mode 100644
index 00000000..abd9018c
--- /dev/null
+++ b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/layout/logging.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<fragment xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/log_fragment"
+ android:name="com.android.multiwindowplayground.logger.LogFragment" />
diff --git a/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/mipmap-hdpi/ic_launcher.png b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100755
index 00000000..a150a5c4
--- /dev/null
+++ b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/mipmap-mdpi/ic_launcher.png b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100755
index 00000000..1a482dde
--- /dev/null
+++ b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/mipmap-xhdpi/ic_launcher.png b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100755
index 00000000..148db37f
--- /dev/null
+++ b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/mipmap-xxhdpi/ic_launcher.png b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100755
index 00000000..7c5c6a46
--- /dev/null
+++ b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100755
index 00000000..944fc543
--- /dev/null
+++ b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/values-w820dp/dimens.xml b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 00000000..e2ec1175
--- /dev/null
+++ b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+ <dimen name="activity_horizontal_margin">64dp</dimen>
+</resources>
diff --git a/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/values/colors.xml b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/values/colors.xml
new file mode 100644
index 00000000..18da4e19
--- /dev/null
+++ b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/values/colors.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+ <color name="colorPrimary">#3F51B5</color>
+ <color name="colorPrimaryDark">#303F9F</color>
+ <color name="colorAccent">#FF4081</color>
+
+ <color name="purple">#512DA8</color>
+ <color name="pink">#C2185B</color>
+ <color name="teal">#00695C</color>
+ <color name="lime">#9E9D24</color>
+ <color name="gray">#424242</color>
+ <color name="lightgray">#F5F5F5</color>
+ <color name="cyan">#00838F</color>
+
+ <color name="white">#FFFFFF</color>
+</resources>
diff --git a/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/values/dimens.xml b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/values/dimens.xml
new file mode 100644
index 00000000..a349e62a
--- /dev/null
+++ b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/values/dimens.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+ <dimen name="content_vertical_dividing_padding">16dp</dimen>
+ <dimen name="text_view_text_size">30sp</dimen>
+</resources>
diff --git a/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/values/strings.xml b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/values/strings.xml
new file mode 100644
index 00000000..7bd6fbde
--- /dev/null
+++ b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/values/strings.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+ <string name="app_name">MultiWindow Playground</string>
+ <string name="introduction_title">Multiwindow Playground</string>
+ <string name="sample_introduction">This sample demonstrates the use of the multi-window API
+ available in Android N.\nFirst, switch this app into
+ <b>split-screen mode</b>
+ (for example by long-pressing the recents button). Each button below starts a new activity
+ with special flags.\n<b>See the files MainActivity.kt and AndroidManifest.xml for
+ implementation details.</b>
+ </string>
+ <string name="sample_freeform_introduction">The buttons below demonstrate features only
+ available in <b>free-form multi-window mode</b>.</string>
+ <string name="start_default">Start basic, default Activity</string>
+ <string name="start_unresizable">Start unresizable Activity</string>
+ <string name="start_adjacent">Start Activity adjacent</string>
+ <string name="start_minimum">Start Activity with minimum size</string>
+ <string name="start_bounds">Start Activity with launch bounds</string>
+ <string name="start_custom_activity">Start activity that handles configuration changes.</string>
+
+ <string name="activity_description_basic">This Activity was launched in a new task without any
+ additional flags or options.
+ </string>
+ <string name="activity_description_unresizable">This activity is set as unresizable in the
+ AndroidManifest. This is done by setting the <i>resizeableActivity</i> property to
+ <i>false</i> for this activity.
+ </string>
+ <string name="activity_adjacent_description">This activity was launched with the flag
+ <b>Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT</b>.\n\nIf possible, it has been launched into the
+ adjacent area from the activity that started it.\nThis is only a hint to the system. For
+ example - if the application is not in split-screen mode, it will be launched full-screen.
+ If it is launched in the same task as the initial Activity, it will retain its activity
+ properties and its location.
+ </string>
+ <string name="activity_custom_description">This activity handles configuration changes
+ itself.\n\nIn the AndroidManifest, this activity has been configured to receive callbacks
+ for <b>screenSize|smallestScreenSize|screenLayout|orientation</b>
+ changes.\nTry resizing this activity to different sizes to see which configuration
+ properties change.
+ </string>
+ <string name="activity_bounds_description">This activity has been launched with a launch bounds
+ set in its intent. The bounds define the area into which the activity should be launched.
+ \n\nNote that this flag only applies in free-form mode.
+ </string>
+ <string name="activity_minimum_description">This activity has a minimum size.\nIt was launched
+ into the top/end corner with a a default size of 750dp by 500dp, with a minimum size of
+ 750dp as defined in its <b>layout attribute in the AndroidManifest definition</b>.
+ \n\nNote that this Activity was launched in a different task, otherwise the properties from
+ the Activity that launched this one would have been applied.
+ </string>
+ <string name="multi_window_sample_action">Enable multi-window mode to see this sample in
+ action!</string>
+</resources>
diff --git a/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/values/styles.xml b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/values/styles.xml
new file mode 100644
index 00000000..e62ba347
--- /dev/null
+++ b/ui/window/MultiWindowPlayground/kotlinApp/Application/src/main/res/values/styles.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+
+ <!-- Base application theme. -->
+ <style name="MultiWindowSampleTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+ <!-- Customize your theme here. -->
+ <item name="colorPrimary">@color/colorPrimary</item>
+ <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
+ <item name="colorAccent">@color/colorAccent</item>
+
+ <!-- Drawable to use in the background while the window is resizing on Android N. -->
+ <item name="android:windowBackgroundFallback">@color/colorAccent</item>
+ <item name="android:windowBackground">@color/colorAccent</item>
+ </style>
+
+ <style name="TextWarning" parent="TextAppearance.AppCompat.Medium">
+
+ </style>
+
+
+</resources>
diff --git a/ui/window/MultiWindowPlayground/kotlinApp/README.md b/ui/window/MultiWindowPlayground/kotlinApp/README.md
new file mode 100644
index 00000000..89b6e2fe
--- /dev/null
+++ b/ui/window/MultiWindowPlayground/kotlinApp/README.md
@@ -0,0 +1,94 @@
+Android MultiWindowPlayground Sample (Kotlin)
+=============================================
+
+This sample demonstrates the use of the multi-window API available
+in Android N. It shows the use of new Intent flags and
+AndroidManifest properties to define the multi-window behavior.
+Switch the sample app into multi-window mode to see how it affects
+the lifecycle and behavior of the app.
+
+Introduction
+------------
+
+Android N introduces new APIs to support multiple activities
+to be displayed at the same time.
+
+Activities that are started within the same task stack
+inherit their multiwindow properties from the activity that fired
+off the intent. The following features are available when an activity
+has been launched into a new task stack.
+
+An activity can be set as not resizable through the
+`android:resizableActivity` property in the AndroidManifest. All
+applications targeting Android N or above are resizable by default.
+
+In split-screen mode, an activity can be started adjacent to the
+launching activity by setting the
+`Intent.FLAG_ACTIVITY_LAUNCH_TO_ADJACENT` flag in its intent.
+
+Sometimes activities may choose to handle configuration changes
+themselves (for example for games or OpenGL-based applications). In this
+case, setting
+`android:configChanges=screenSize|smallestScreenSize|screenLayout|orientation`
+in the AndroidManifest definition of the activity enables callbacks for
+all configuration changes that may occur during multi-window use for the
+Activity. See [Handling Runtime Changes][1].
+
+In freeform mode (where applications can be freely resized), activities
+can be started within a certain area of the screen using the
+`ActivityOptions#setLaunchBounds` call.
+
+Alternatively, the preferred and minimum sizes can be set in a new
+`layout` property in the AndroidManifest.
+
+
+[1]: https://developer.android.com/guide/topics/resources/runtime-changes.html
+
+Pre-requisites
+--------------
+
+- Android SDK 27
+- Android Support Repository
+
+Screenshots
+-------------
+
+<img src="screenshots/main.png" height="400" alt="Screenshot"/>
+
+Getting Started
+---------------
+
+This sample uses the Gradle build system. To build this project, use the
+"gradlew build" command or use "Import Project" in Android Studio.
+
+Support
+-------
+
+- Google+ Community: https://plus.google.com/communities/105153134372062985968
+- Stack Overflow: http://stackoverflow.com/questions/tagged/android
+
+If you've found an error in this sample, please file an issue:
+https://github.com/googlesamples/android-MultiWindowPlayground
+
+Patches are encouraged, and may be submitted by forking this project and
+submitting a pull request through GitHub. Please see CONTRIBUTING.md for more details.
+
+License
+-------
+
+Copyright 2017 The Android Open Source Project, Inc.
+
+Licensed to the Apache Software Foundation (ASF) under one or more contributor
+license agreements. See the NOTICE file distributed with this work for
+additional information regarding copyright ownership. The ASF licenses this
+file to you under the Apache License, Version 2.0 (the "License"); you may not
+use this file except in compliance with the License. You may obtain a copy of
+the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+License for the specific language governing permissions and limitations under
+the License.
diff --git a/ui/window/MultiWindowPlayground/kotlinApp/build.gradle b/ui/window/MultiWindowPlayground/kotlinApp/build.gradle
new file mode 100644
index 00000000..6b3af00a
--- /dev/null
+++ b/ui/window/MultiWindowPlayground/kotlinApp/build.gradle
@@ -0,0 +1,22 @@
+buildscript {
+ ext.kotlin_version = '1.2.0'
+ repositories {
+ google()
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.0.1'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/ui/window/MultiWindowPlayground/kotlinApp/gradle/wrapper/gradle-wrapper.jar b/ui/window/MultiWindowPlayground/kotlinApp/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 00000000..8c0fb64a
--- /dev/null
+++ b/ui/window/MultiWindowPlayground/kotlinApp/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/ui/window/MultiWindowPlayground/kotlinApp/gradle/wrapper/gradle-wrapper.properties b/ui/window/MultiWindowPlayground/kotlinApp/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000..71a62b59
--- /dev/null
+++ b/ui/window/MultiWindowPlayground/kotlinApp/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Dec 28 10:00:20 PST 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip \ No newline at end of file
diff --git a/ui/window/MultiWindowPlayground/kotlinApp/gradlew b/ui/window/MultiWindowPlayground/kotlinApp/gradlew
new file mode 100755
index 00000000..91a7e269
--- /dev/null
+++ b/ui/window/MultiWindowPlayground/kotlinApp/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/ui/window/MultiWindowPlayground/kotlinApp/gradlew.bat b/ui/window/MultiWindowPlayground/kotlinApp/gradlew.bat
new file mode 100644
index 00000000..8a0b282a
--- /dev/null
+++ b/ui/window/MultiWindowPlayground/kotlinApp/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/ui/window/MultiWindowPlayground/kotlinApp/screenshots/icon-web.png b/ui/window/MultiWindowPlayground/kotlinApp/screenshots/icon-web.png
new file mode 100755
index 00000000..8d99c127
--- /dev/null
+++ b/ui/window/MultiWindowPlayground/kotlinApp/screenshots/icon-web.png
Binary files differ
diff --git a/ui/window/MultiWindowPlayground/kotlinApp/screenshots/main.png b/ui/window/MultiWindowPlayground/kotlinApp/screenshots/main.png
new file mode 100644
index 00000000..d9222003
--- /dev/null
+++ b/ui/window/MultiWindowPlayground/kotlinApp/screenshots/main.png
Binary files differ
diff --git a/ui/window/MultiWindowPlayground/kotlinApp/settings.gradle b/ui/window/MultiWindowPlayground/kotlinApp/settings.gradle
new file mode 100644
index 00000000..9464a359
--- /dev/null
+++ b/ui/window/MultiWindowPlayground/kotlinApp/settings.gradle
@@ -0,0 +1 @@
+include 'Application'