diff --git a/MultiWindowPlayground/.google/packaging.yaml b/MultiWindowPlayground/.google/packaging.yaml
new file mode 100644
index 00000000..21fd83dd
--- /dev/null
+++ b/MultiWindowPlayground/.google/packaging.yaml
@@ -0,0 +1,18 @@
+
+# GOOGLE SAMPLE PACKAGING DATA
+#
+# This file is used by Google as part of our samples packaging process.
+# End users may safely ignore this file. It has no relevance to other systems.
+---
+status: PUBLISHED
+technologies: [Android]
+categories: [UI]
+languages: [Java]
+solutions: [Mobile]
+github: android/views-widgets
+level: INTERMEDIATE
+icon: screenshots/icon-web.png
+apiRefs:
+ - android:android.content.Intent
+ - android:android.app.ActivityOptions
+license: apache2
diff --git a/MultiWindowPlayground/Application/build.gradle b/MultiWindowPlayground/Application/build.gradle
new file mode 100644
index 00000000..c2a60ee7
--- /dev/null
+++ b/MultiWindowPlayground/Application/build.gradle
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+buildscript {
+ repositories {
+ google()
+ jcenter()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.3.0'
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ }
+}
+
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 27
+
+ defaultConfig {
+ applicationId "com.android.multiwindowplayground"
+ minSdkVersion 24
+ targetSdkVersion 27
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_7
+ targetCompatibility JavaVersion.VERSION_1_7
+ }
+}
+
+dependencies {
+ compile fileTree(include: ['*.jar'], dir: 'libs')
+ testCompile 'junit:junit:4.12'
+ compile 'com.android.support:appcompat-v7:27.0.0'
+ androidTestCompile 'com.android.support:support-annotations:27.0.0'
+ androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.1'
+}
diff --git a/MultiWindowPlayground/Application/src/main/AndroidManifest.xml b/MultiWindowPlayground/Application/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..25b21bdf
--- /dev/null
+++ b/MultiWindowPlayground/Application/src/main/AndroidManifest.xml
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MultiWindowPlayground/Application/src/main/java/com/android/multiwindowplayground/MainActivity.java b/MultiWindowPlayground/Application/src/main/java/com/android/multiwindowplayground/MainActivity.java
new file mode 100644
index 00000000..bd7b5529
--- /dev/null
+++ b/MultiWindowPlayground/Application/src/main/java/com/android/multiwindowplayground/MainActivity.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2016 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 com.android.multiwindowplayground.activities.AdjacentActivity;
+import com.android.multiwindowplayground.activities.BasicActivity;
+import com.android.multiwindowplayground.activities.CustomConfigurationChangeActivity;
+import com.android.multiwindowplayground.activities.LaunchBoundsActivity;
+import com.android.multiwindowplayground.activities.LoggingActivity;
+import com.android.multiwindowplayground.activities.MinimumSizeActivity;
+import com.android.multiwindowplayground.activities.UnresizableActivity;
+
+import android.app.ActivityOptions;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+
+public class MainActivity extends LoggingActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ View multiDisabledMessage = findViewById(R.id.warning_multiwindow_disabled);
+ // Display an additional message if the app is not in multiwindow mode.
+ if (!isInMultiWindowMode()) {
+ multiDisabledMessage.setVisibility(View.VISIBLE);
+ } else {
+ multiDisabledMessage.setVisibility(View.GONE);
+ }
+ }
+
+ public void onStartUnresizableClick(View view) {
+ Log.d(mLogTag, "** 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).
+ */
+ Intent intent = new Intent(this, UnresizableActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
+ }
+
+ public void onStartMinimumSizeActivity(View view) {
+ Log.d(mLogTag, "** starting MinimumSizeActivity");
+
+ startActivity(new Intent(this, MinimumSizeActivity.class));
+ }
+
+ public void onStartAdjacentActivity(View view) {
+ Log.d(mLogTag, "** 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.
+ */
+ Intent intent = new Intent(this, AdjacentActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT | Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
+ }
+
+ public void onStartLaunchBoundsActivity(View view) {
+ Log.d(mLogTag, "** starting LaunchBoundsActivity");
+
+ // Define the bounds in which the Activity will be launched into.
+ Rect bounds = new Rect(500, 300, 100, 0);
+
+ // Set the bounds as an activity option.
+ ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchBounds(bounds);
+
+ // Start the LaunchBoundsActivity with the specified options
+ Intent intent = new Intent(this, LaunchBoundsActivity.class);
+ startActivity(intent, options.toBundle());
+
+ }
+
+ public void onStartBasicActivity(View view) {
+ Log.d(mLogTag, "** starting BasicActivity");
+
+ // Start an Activity with the default options in the 'singleTask' launch mode as defined in
+ // the AndroidManifest.xml.
+ startActivity(new Intent(this, BasicActivity.class));
+
+ }
+
+ public void onStartCustomConfigurationActivity(View view) {
+ Log.d(mLogTag, "** starting CustomConfigurationChangeActivity");
+
+ // Start an Activity that handles all configuration changes itself.
+ startActivity(new Intent(this, CustomConfigurationChangeActivity.class));
+
+ }
+}
diff --git a/MultiWindowPlayground/Application/src/main/java/com/android/multiwindowplayground/activities/AdjacentActivity.java b/MultiWindowPlayground/Application/src/main/java/com/android/multiwindowplayground/activities/AdjacentActivity.java
new file mode 100644
index 00000000..cfb915b6
--- /dev/null
+++ b/MultiWindowPlayground/Application/src/main/java/com/android/multiwindowplayground/activities/AdjacentActivity.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 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 com.android.multiwindowplayground.R;
+
+import android.os.Bundle;
+import android.view.View;
+
+/**
+ * This Activity is to be launched adjacent to another Activity using the {@link
+ * android.content.Intent#FLAG_ACTIVITY_LAUNCH_ADJACENT}.
+ *
+ * @see com.android.multiwindowplayground.MainActivity#onStartAdjacentActivity(View)
+ */
+public class AdjacentActivity extends LoggingActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_logging);
+
+ setBackgroundColor(R.color.teal);
+ setDescription(R.string.activity_adjacent_description);
+ }
+
+}
diff --git a/MultiWindowPlayground/Application/src/main/java/com/android/multiwindowplayground/activities/BasicActivity.java b/MultiWindowPlayground/Application/src/main/java/com/android/multiwindowplayground/activities/BasicActivity.java
new file mode 100644
index 00000000..c40dee22
--- /dev/null
+++ b/MultiWindowPlayground/Application/src/main/java/com/android/multiwindowplayground/activities/BasicActivity.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2016 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 com.android.multiwindowplayground.R;
+
+import android.os.Bundle;
+import android.view.View;
+
+/**
+ * This activity is the most basic, simeple use case and is to be launched without any special
+ * flags
+ * or settings.
+ *
+ * @see com.android.multiwindowplayground.MainActivity#onStartBasicActivity(View)
+ */
+public class BasicActivity extends LoggingActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_logging);
+
+ // Set the color and description
+ setDescription(R.string.activity_description_basic);
+ setBackgroundColor(R.color.gray);
+
+ }
+}
diff --git a/MultiWindowPlayground/Application/src/main/java/com/android/multiwindowplayground/activities/CustomConfigurationChangeActivity.java b/MultiWindowPlayground/Application/src/main/java/com/android/multiwindowplayground/activities/CustomConfigurationChangeActivity.java
new file mode 100644
index 00000000..32e5233e
--- /dev/null
+++ b/MultiWindowPlayground/Application/src/main/java/com/android/multiwindowplayground/activities/CustomConfigurationChangeActivity.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 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 com.android.multiwindowplayground.R;
+
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.view.View;
+
+/**
+ * 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 {@link #onConfigurationChanged(Configuration)}, which is logged in the {@link
+ * LoggingActivity}.
+ *
+ * @see com.android.multiwindowplayground.MainActivity#onStartCustomConfigurationActivity(View)
+ */
+public class CustomConfigurationChangeActivity extends LoggingActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_logging);
+
+ setBackgroundColor(R.color.cyan);
+ setDescription(R.string.activity_custom_description);
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+
+ /*
+ Note: The implementation in LoggingActivity logs the output o the new configuration.
+ This callback is received whenever the configuration is updated, for example when the
+ size of this Activity is changed.
+ */
+ }
+}
diff --git a/MultiWindowPlayground/Application/src/main/java/com/android/multiwindowplayground/activities/LaunchBoundsActivity.java b/MultiWindowPlayground/Application/src/main/java/com/android/multiwindowplayground/activities/LaunchBoundsActivity.java
new file mode 100644
index 00000000..80a98f2c
--- /dev/null
+++ b/MultiWindowPlayground/Application/src/main/java/com/android/multiwindowplayground/activities/LaunchBoundsActivity.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 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 com.android.multiwindowplayground.R;
+
+import android.os.Bundle;
+import android.view.View;
+
+/**
+ * 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(View)
+ */
+public class LaunchBoundsActivity extends LoggingActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_logging);
+
+ setBackgroundColor(R.color.lime);
+ setDescription(R.string.activity_bounds_description);
+ }
+
+}
diff --git a/MultiWindowPlayground/Application/src/main/java/com/android/multiwindowplayground/activities/LoggingActivity.java b/MultiWindowPlayground/Application/src/main/java/com/android/multiwindowplayground/activities/LoggingActivity.java
new file mode 100644
index 00000000..988a3195
--- /dev/null
+++ b/MultiWindowPlayground/Application/src/main/java/com/android/multiwindowplayground/activities/LoggingActivity.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2016 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 com.android.multiwindowplayground.R;
+import com.example.android.common.logger.Log;
+import com.example.android.common.logger.LogFragment;
+import com.example.android.common.logger.LogWrapper;
+import com.example.android.common.logger.MessageOnlyLogFilter;
+
+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;
+
+/**
+ * Activity that logs all key lifecycle callbacks to {@link Log}.
+ * Output is also logged to the UI into a {@link LogFragment} through {@link #initializeLogging()}
+ * and {@link #stopLogging()}.
+ */
+public abstract class LoggingActivity extends AppCompatActivity {
+
+ protected String mLogTag = getClass().getSimpleName();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.d(mLogTag, "onCreate");
+
+
+ }
+
+ @Override
+ public void onPostCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
+ super.onPostCreate(savedInstanceState, persistentState);
+ Log.d(mLogTag, "onPostCreate");
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ Log.d(mLogTag, "onPause");
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ Log.d(mLogTag, "onDestroy");
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ Log.d(mLogTag, "onResume");
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ Log.d(mLogTag, "onConfigurationChanged: " + newConfig.toString());
+ }
+
+ @Override
+ protected void onPostCreate(Bundle savedInstanceState) {
+ super.onPostCreate(savedInstanceState);
+ Log.d(mLogTag, "onPostCreate");
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ // Start logging to UI.
+ initializeLogging();
+
+ Log.d(mLogTag, "onStart");
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ // Stop logging to UI when this activity is stopped.
+ stopLogging();
+
+ Log.d(mLogTag, "onStop");
+ }
+
+ @Override
+ public void onMultiWindowModeChanged(boolean isInMultiWindowMode) {
+ super.onMultiWindowModeChanged(isInMultiWindowMode);
+
+ Log.d(mLogTag, "onMultiWindowModeChanged: " + isInMultiWindowMode);
+ }
+
+ // Logging and UI methods below.
+
+ /** Set up targets to receive log data */
+ public void initializeLogging() {
+ // Using Log, front-end to the logging chain, emulates android.util.log method signatures.
+ // Wraps Android's native log framework
+ LogWrapper logWrapper = new LogWrapper();
+ Log.setLogNode(logWrapper);
+
+ // Filter strips out everything except the message text.
+ MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter();
+ logWrapper.setNext(msgFilter);
+
+ // On screen logging via a fragment with a TextView.
+ LogFragment logFragment = (LogFragment) getSupportFragmentManager()
+ .findFragmentById(R.id.log_fragment);
+ msgFilter.setNext(logFragment.getLogView());
+ }
+
+ public void stopLogging() {
+ Log.setLogNode(null);
+ }
+
+ /**
+ * Set the description text if a TextView with the id description is available.
+ */
+ protected void setDescription(@StringRes int textId) {
+ // Set the text and background color
+ TextView description = (TextView) findViewById(R.id.description);
+ if (description != null) {
+ description.setText(textId);
+ }
+ }
+
+ /**
+ * Set the background color for the description text.
+ */
+ protected void setBackgroundColor(@ColorRes int colorId) {
+ View scrollView = findViewById(R.id.scrollview);
+ if (scrollView != null) {
+ scrollView.setBackgroundResource(colorId);
+ }
+ }
+
+}
diff --git a/MultiWindowPlayground/Application/src/main/java/com/android/multiwindowplayground/activities/MinimumSizeActivity.java b/MultiWindowPlayground/Application/src/main/java/com/android/multiwindowplayground/activities/MinimumSizeActivity.java
new file mode 100644
index 00000000..a42c379e
--- /dev/null
+++ b/MultiWindowPlayground/Application/src/main/java/com/android/multiwindowplayground/activities/MinimumSizeActivity.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 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 com.android.multiwindowplayground.R;
+
+import android.os.Bundle;
+import android.view.View;
+
+/**
+ * This Activity has a minimum size defined in the AndroidManifeset.
+ *
+ * @see com.android.multiwindowplayground.MainActivity#onStartMinimumSizeActivity(View)
+ */
+public class MinimumSizeActivity extends LoggingActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_logging);
+
+ setBackgroundColor(R.color.pink);
+ setDescription(R.string.activity_minimum_description);
+ }
+
+}
diff --git a/MultiWindowPlayground/Application/src/main/java/com/android/multiwindowplayground/activities/UnresizableActivity.java b/MultiWindowPlayground/Application/src/main/java/com/android/multiwindowplayground/activities/UnresizableActivity.java
new file mode 100644
index 00000000..eb2523e9
--- /dev/null
+++ b/MultiWindowPlayground/Application/src/main/java/com/android/multiwindowplayground/activities/UnresizableActivity.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2016 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 com.android.multiwindowplayground.R;
+
+import android.os.Bundle;
+import android.view.View;
+
+/**
+ * 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(View)
+ */
+public class UnresizableActivity extends LoggingActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_logging);
+
+ setBackgroundColor(R.color.purple);
+ setDescription(R.string.activity_description_unresizable);
+ }
+
+}
diff --git a/MultiWindowPlayground/Application/src/main/java/com/example/android/common/logger/Log.java b/MultiWindowPlayground/Application/src/main/java/com/example/android/common/logger/Log.java
new file mode 100755
index 00000000..7c112230
--- /dev/null
+++ b/MultiWindowPlayground/Application/src/main/java/com/example/android/common/logger/Log.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2016 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.example.android.common.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 {@link android.util.Log}.
+ * Most of the methods in this class server only to map a method call in Log to its equivalent
+ * in LogNode.
+ */
+public class Log {
+
+ // Grabbing the native values from Android's native logging facilities,
+ // to make for easy migration and interop.
+ public static final int NONE = -1;
+ public static final int VERBOSE = android.util.Log.VERBOSE;
+ public static final int DEBUG = android.util.Log.DEBUG;
+ public static final int INFO = android.util.Log.INFO;
+ public static final int WARN = android.util.Log.WARN;
+ public static final int ERROR = android.util.Log.ERROR;
+ public static final int ASSERT = android.util.Log.ASSERT;
+
+ // Stores the beginning of the LogNode topology.
+ private static LogNode mLogNode;
+
+ /**
+ * Returns the next LogNode in the linked list.
+ */
+ public static LogNode getLogNode() {
+ return mLogNode;
+ }
+
+ /**
+ * Sets the LogNode data will be sent to.
+ */
+ public static void setLogNode(LogNode node) {
+ mLogNode = node;
+ }
+
+ /**
+ * 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.
+ * @param tr If an exception was thrown, this can be sent along for the logging
+ * facilities
+ * to extract and print useful information.
+ */
+ public static void println(int priority, String tag, String msg, Throwable tr) {
+ if (mLogNode != null) {
+ mLogNode.println(priority, tag, msg, tr);
+ }
+ }
+
+ /**
+ * 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.
+ */
+ public static void println(int priority, String tag, String msg) {
+ println(priority, tag, msg, null);
+ }
+
+ /**
+ * Prints a message at VERBOSE priority.
+ *
+ * @param tag Tag for for the log data. Can be used to organize log statements.
+ * @param msg 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.
+ */
+ public static void v(String tag, String msg, Throwable tr) {
+ println(VERBOSE, tag, msg, tr);
+ }
+
+ /**
+ * Prints a message at VERBOSE priority.
+ *
+ * @param tag Tag for for the log data. Can be used to organize log statements.
+ * @param msg The actual message to be logged.
+ */
+ public static void v(String tag, String msg) {
+ v(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.
+ * @param tr If an exception was thrown, this can be sent along for the logging facilities
+ * to extract and print useful information.
+ */
+ public static void d(String tag, String msg, Throwable tr) {
+ println(DEBUG, tag, msg, tr);
+ }
+
+ /**
+ * 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.
+ */
+ public static void d(String tag, String msg) {
+ d(tag, msg, null);
+ }
+
+ /**
+ * Prints a message at INFO priority.
+ *
+ * @param tag Tag for for the log data. Can be used to organize log statements.
+ * @param msg 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.
+ */
+ public static void i(String tag, String msg, Throwable tr) {
+ println(INFO, tag, msg, tr);
+ }
+
+ /**
+ * Prints a message at INFO priority.
+ *
+ * @param tag Tag for for the log data. Can be used to organize log statements.
+ * @param msg The actual message to be logged.
+ */
+ public static void i(String tag, String msg) {
+ i(tag, msg, null);
+ }
+
+ /**
+ * Prints a message at WARN priority.
+ *
+ * @param tag Tag for for the log data. Can be used to organize log statements.
+ * @param msg 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.
+ */
+ public static void w(String tag, String msg, Throwable tr) {
+ println(WARN, tag, msg, tr);
+ }
+
+ /**
+ * Prints a message at WARN priority.
+ *
+ * @param tag Tag for for the log data. Can be used to organize log statements.
+ * @param msg The actual message to be logged.
+ */
+ public static void w(String tag, String msg) {
+ w(tag, msg, null);
+ }
+
+ /**
+ * Prints a message at WARN priority.
+ *
+ * @param tag Tag for for the log data. Can be used to organize log statements.
+ * @param tr If an exception was thrown, this can be sent along for the logging facilities
+ * to extract and print useful information.
+ */
+ public static void w(String tag, Throwable tr) {
+ w(tag, null, tr);
+ }
+
+ /**
+ * Prints a message at ERROR priority.
+ *
+ * @param tag Tag for for the log data. Can be used to organize log statements.
+ * @param msg 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.
+ */
+ public static void e(String tag, String msg, Throwable tr) {
+ println(ERROR, tag, msg, tr);
+ }
+
+ /**
+ * Prints a message at ERROR priority.
+ *
+ * @param tag Tag for for the log data. Can be used to organize log statements.
+ * @param msg The actual message to be logged.
+ */
+ public static void e(String tag, String msg) {
+ e(tag, msg, null);
+ }
+
+ /**
+ * Prints a message at ASSERT priority.
+ *
+ * @param tag Tag for for the log data. Can be used to organize log statements.
+ * @param msg 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.
+ */
+ public static void wtf(String tag, String msg, Throwable tr) {
+ println(ASSERT, tag, msg, tr);
+ }
+
+ /**
+ * Prints a message at ASSERT priority.
+ *
+ * @param tag Tag for for the log data. Can be used to organize log statements.
+ * @param msg The actual message to be logged.
+ */
+ public static void wtf(String tag, String msg) {
+ wtf(tag, msg, null);
+ }
+
+ /**
+ * Prints a message at ASSERT priority.
+ *
+ * @param tag Tag for for the log data. Can be used to organize log statements.
+ * @param tr If an exception was thrown, this can be sent along for the logging facilities
+ * to extract and print useful information.
+ */
+ public static void wtf(String tag, Throwable tr) {
+ wtf(tag, null, tr);
+ }
+}
diff --git a/MultiWindowPlayground/Application/src/main/java/com/example/android/common/logger/LogFragment.java b/MultiWindowPlayground/Application/src/main/java/com/example/android/common/logger/LogFragment.java
new file mode 100755
index 00000000..0e9ea9e4
--- /dev/null
+++ b/MultiWindowPlayground/Application/src/main/java/com/example/android/common/logger/LogFragment.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2016 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.example.android.common.logger;
+
+import android.graphics.Typeface;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+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.widget.ScrollView;
+
+/**
+ * Simple fraggment which contains a LogView and uses is to output log data it receives
+ * through the LogNode interface.
+ */
+public class LogFragment extends Fragment {
+
+ private LogView mLogView;
+ private ScrollView mScrollView;
+
+ public LogFragment() {
+ }
+
+ public View inflateViews() {
+ mScrollView = new ScrollView(getActivity());
+ ViewGroup.LayoutParams scrollParams = new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT);
+ mScrollView.setLayoutParams(scrollParams);
+
+ mLogView = new LogView(getActivity());
+ ViewGroup.LayoutParams logParams = new ViewGroup.LayoutParams(scrollParams);
+ logParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
+ mLogView.setTextAppearance(android.R.style.TextAppearance_Material_Medium);
+ mLogView.setLayoutParams(logParams);
+ mLogView.setClickable(true);
+ mLogView.setFocusable(true);
+ mLogView.setTypeface(Typeface.create("monospace", Typeface.NORMAL));
+
+ // Want to set padding as 16 dips, setPadding takes pixels. Hooray math!
+ int paddingDips = 16;
+ double scale = getResources().getDisplayMetrics().density;
+ int paddingPixels = (int) ((paddingDips * (scale)) + .5);
+ mLogView.setPadding(paddingPixels, paddingPixels, paddingPixels, paddingPixels);
+ mLogView.setCompoundDrawablePadding(paddingPixels);
+
+ mLogView.setGravity(Gravity.BOTTOM);
+
+ mScrollView.addView(mLogView);
+ return mScrollView;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+
+ View result = inflateViews();
+
+ mLogView.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+
+ mScrollView.post(new Runnable() {
+ @Override
+ public void run() {
+ mScrollView
+ .smoothScrollTo(0, mScrollView.getBottom() + mLogView.getHeight());
+ }
+ });
+ }
+ });
+ return result;
+ }
+
+ public LogView getLogView() {
+ return mLogView;
+ }
+}
\ No newline at end of file
diff --git a/MultiWindowPlayground/Application/src/main/java/com/example/android/common/logger/LogNode.java b/MultiWindowPlayground/Application/src/main/java/com/example/android/common/logger/LogNode.java
new file mode 100755
index 00000000..7620ca6c
--- /dev/null
+++ b/MultiWindowPlayground/Application/src/main/java/com/example/android/common/logger/LogNode.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 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.example.android.common.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.
+ */
+public 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.
+ */
+ public void println(int priority, String tag, String msg, Throwable tr);
+
+}
diff --git a/MultiWindowPlayground/Application/src/main/java/com/example/android/common/logger/LogView.java b/MultiWindowPlayground/Application/src/main/java/com/example/android/common/logger/LogView.java
new file mode 100755
index 00000000..b1fb2054
--- /dev/null
+++ b/MultiWindowPlayground/Application/src/main/java/com/example/android/common/logger/LogView.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2016 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.example.android.common.logger;
+
+import android.app.Activity;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+/**
+ * Simple TextView which is used to output log data received through the LogNode interface.
+ */
+public class LogView extends TextView implements LogNode {
+
+ public LogView(Context context) {
+ super(context);
+ }
+
+ public LogView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public LogView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ /**
+ * 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
+ public void println(int priority, String tag, String msg, Throwable tr) {
+
+ String priorityStr = null;
+
+ // For the purposes of this View, we want to print the priority as readable text.
+ switch (priority) {
+ case android.util.Log.VERBOSE:
+ priorityStr = "VERBOSE";
+ break;
+ case android.util.Log.DEBUG:
+ priorityStr = "DEBUG";
+ break;
+ case android.util.Log.INFO:
+ priorityStr = "INFO";
+ break;
+ case android.util.Log.WARN:
+ priorityStr = "WARN";
+ break;
+ case android.util.Log.ERROR:
+ priorityStr = "ERROR";
+ break;
+ case android.util.Log.ASSERT:
+ priorityStr = "ASSERT";
+ break;
+ default:
+ break;
+ }
+
+ // Handily, the Log class has a facility for converting a stack trace into a usable string.
+ String exceptionStr = 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.
+ final StringBuilder outputBuilder = new StringBuilder();
+
+ String delimiter = "\t";
+ appendIfNotNull(outputBuilder, priorityStr, delimiter);
+ appendIfNotNull(outputBuilder, tag, delimiter);
+ appendIfNotNull(outputBuilder, msg, delimiter);
+ appendIfNotNull(outputBuilder, 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.
+ ((Activity) getContext()).runOnUiThread((new Thread(new Runnable() {
+ @Override
+ public void run() {
+ // Display the text we just generated within the LogView.
+ appendToLog(outputBuilder.toString());
+ }
+ })));
+
+ if (mNext != null) {
+ mNext.println(priority, tag, msg, tr);
+ }
+ }
+
+ public LogNode getNext() {
+ return mNext;
+ }
+
+ public void setNext(LogNode node) {
+ mNext = node;
+ }
+
+ /**
+ * 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 3 lines 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. A tab or comma,
+ * for instance.
+ * @return The fully concatenated String as a StringBuilder
+ */
+ private StringBuilder appendIfNotNull(StringBuilder source, String addStr, String delimiter) {
+ if (addStr != null) {
+ if (addStr.length() == 0) {
+ delimiter = "";
+ }
+
+ return source.append(addStr).append(delimiter);
+ }
+ return source;
+ }
+
+ // The next LogNode in the chain.
+ LogNode mNext;
+
+ /** Outputs the string as a new line of log data in the LogView. */
+ public void appendToLog(String s) {
+ append("\n" + s);
+ }
+
+
+}
diff --git a/MultiWindowPlayground/Application/src/main/java/com/example/android/common/logger/LogWrapper.java b/MultiWindowPlayground/Application/src/main/java/com/example/android/common/logger/LogWrapper.java
new file mode 100755
index 00000000..8594bc47
--- /dev/null
+++ b/MultiWindowPlayground/Application/src/main/java/com/example/android/common/logger/LogWrapper.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 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.example.android.common.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.
+ */
+public class LogWrapper implements LogNode {
+
+ // For piping: The next node to receive Log data after this one has done its work.
+ private LogNode mNext;
+
+ /**
+ * Returns the next LogNode in the linked list.
+ */
+ public LogNode getNext() {
+ return mNext;
+ }
+
+ /**
+ * Sets the LogNode data will be sent to..
+ */
+ public void setNext(LogNode node) {
+ mNext = node;
+ }
+
+ /**
+ * 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
+ public void println(int priority, String tag, String msg, Throwable tr) {
+ // There actually are log methods that don't take a msg parameter. For now,
+ // if that's the case, just convert null to the empty string and move on.
+ String useMsg = msg;
+ if (useMsg == null) {
+ useMsg = "";
+ }
+
+ // If an exeption was provided, convert that exception to a usable string and attach
+ // it to the end of the msg method.
+ if (tr != null) {
+ msg += "\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, useMsg);
+
+ // If this isn't the last node in the chain, move things along.
+ if (mNext != null) {
+ mNext.println(priority, tag, msg, tr);
+ }
+ }
+}
diff --git a/MultiWindowPlayground/Application/src/main/java/com/example/android/common/logger/MessageOnlyLogFilter.java b/MultiWindowPlayground/Application/src/main/java/com/example/android/common/logger/MessageOnlyLogFilter.java
new file mode 100755
index 00000000..b86e1aef
--- /dev/null
+++ b/MultiWindowPlayground/Application/src/main/java/com/example/android/common/logger/MessageOnlyLogFilter.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2016 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.example.android.common.logger;
+
+/**
+ * Simple {@link 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.
+ */
+public class MessageOnlyLogFilter implements LogNode {
+
+ LogNode mNext;
+
+ /**
+ * Takes the "next" LogNode as a parameter, to simplify chaining.
+ *
+ * @param next The next LogNode in the pipeline.
+ */
+ public MessageOnlyLogFilter(LogNode next) {
+ mNext = next;
+ }
+
+ public MessageOnlyLogFilter() {
+ }
+
+ @Override
+ public void println(int priority, String tag, String msg, Throwable tr) {
+ if (mNext != null) {
+ getNext().println(Log.NONE, null, msg, null);
+ }
+ }
+
+ /**
+ * Returns the next LogNode in the chain.
+ */
+ public LogNode getNext() {
+ return mNext;
+ }
+
+ /**
+ * Sets the LogNode data will be sent to..
+ */
+ public void setNext(LogNode node) {
+ mNext = node;
+ }
+
+}
diff --git a/MultiWindowPlayground/Application/src/main/res/layout/activity_logging.xml b/MultiWindowPlayground/Application/src/main/res/layout/activity_logging.xml
new file mode 100644
index 00000000..6c6d79b2
--- /dev/null
+++ b/MultiWindowPlayground/Application/src/main/res/layout/activity_logging.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MultiWindowPlayground/Application/src/main/res/layout/activity_main.xml b/MultiWindowPlayground/Application/src/main/res/layout/activity_main.xml
new file mode 100644
index 00000000..640d9cc9
--- /dev/null
+++ b/MultiWindowPlayground/Application/src/main/res/layout/activity_main.xml
@@ -0,0 +1,124 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MultiWindowPlayground/Application/src/main/res/layout/logging.xml b/MultiWindowPlayground/Application/src/main/res/layout/logging.xml
new file mode 100644
index 00000000..b2da5f1b
--- /dev/null
+++ b/MultiWindowPlayground/Application/src/main/res/layout/logging.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
diff --git a/MultiWindowPlayground/Application/src/main/res/mipmap-hdpi/ic_launcher.png b/MultiWindowPlayground/Application/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100755
index 00000000..a150a5c4
Binary files /dev/null and b/MultiWindowPlayground/Application/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/MultiWindowPlayground/Application/src/main/res/mipmap-mdpi/ic_launcher.png b/MultiWindowPlayground/Application/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100755
index 00000000..1a482dde
Binary files /dev/null and b/MultiWindowPlayground/Application/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/MultiWindowPlayground/Application/src/main/res/mipmap-xhdpi/ic_launcher.png b/MultiWindowPlayground/Application/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100755
index 00000000..148db37f
Binary files /dev/null and b/MultiWindowPlayground/Application/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/MultiWindowPlayground/Application/src/main/res/mipmap-xxhdpi/ic_launcher.png b/MultiWindowPlayground/Application/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100755
index 00000000..7c5c6a46
Binary files /dev/null and b/MultiWindowPlayground/Application/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/MultiWindowPlayground/Application/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/MultiWindowPlayground/Application/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100755
index 00000000..944fc543
Binary files /dev/null and b/MultiWindowPlayground/Application/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/MultiWindowPlayground/Application/src/main/res/values-w820dp/dimens.xml b/MultiWindowPlayground/Application/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 00000000..e8b7e246
--- /dev/null
+++ b/MultiWindowPlayground/Application/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,19 @@
+
+
+
+ 64dp
+
diff --git a/MultiWindowPlayground/Application/src/main/res/values/colors.xml b/MultiWindowPlayground/Application/src/main/res/values/colors.xml
new file mode 100644
index 00000000..e18e61e2
--- /dev/null
+++ b/MultiWindowPlayground/Application/src/main/res/values/colors.xml
@@ -0,0 +1,31 @@
+
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
+ #512DA8
+ #C2185B
+ #00695C
+ #9E9D24
+ #424242
+ #F5F5F5
+ #00838F
+
+ #FFFFFF
+
diff --git a/MultiWindowPlayground/Application/src/main/res/values/dimens.xml b/MultiWindowPlayground/Application/src/main/res/values/dimens.xml
new file mode 100644
index 00000000..3a61036f
--- /dev/null
+++ b/MultiWindowPlayground/Application/src/main/res/values/dimens.xml
@@ -0,0 +1,22 @@
+
+
+
+
+ 16dp
+ 16dp
+ 16dp
+
diff --git a/MultiWindowPlayground/Application/src/main/res/values/strings.xml b/MultiWindowPlayground/Application/src/main/res/values/strings.xml
new file mode 100644
index 00000000..430742db
--- /dev/null
+++ b/MultiWindowPlayground/Application/src/main/res/values/strings.xml
@@ -0,0 +1,66 @@
+
+
+
+ MultiWindow Playground
+ Multiwindow Playground
+ This sample demonstrates the use of the multi-window API
+ available in Android N.\nFirst, switch this app into
+ split-screen mode
+ (for example by long-pressing the recents button). Each button below starts a new activity
+ with special flags.\nSee the files MainActivity.java and AndroidManifest.xml for
+ implementation details.
+
+ The buttons below demonstrate features only
+ available in free-form multi-window mode.
+ Start basic, default Activity
+ Start unresizable Activity
+ Start Activity adjacent
+ Start Activity with minimum size
+ Start Activity with launch bounds
+ Start activity that handles configuration changes.
+
+ This Activity was launched in a new task without any
+ additional flags or options.
+
+ This activity is set as unresizable in the
+ AndroidManifest. This is done by setting the resizeableActivity property to
+ false for this activity.
+
+ This activity was launched with the flag
+ Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT.\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.
+
+ This activity handles configuration changes
+ itself.\n\nIn the AndroidManifest, this activity has been configured to receive callbacks
+ for screenSize|smallestScreenSize|screenLayout|orientation
+ changes.\nTry resizing this activity to different sizes to see which configuration
+ properties change.
+
+ 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.
+
+ 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 layout attribute in the AndroidManifest definition.
+ \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.
+
+
diff --git a/MultiWindowPlayground/Application/src/main/res/values/styles.xml b/MultiWindowPlayground/Application/src/main/res/values/styles.xml
new file mode 100644
index 00000000..0324dbd7
--- /dev/null
+++ b/MultiWindowPlayground/Application/src/main/res/values/styles.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MultiWindowPlayground/README.md b/MultiWindowPlayground/README.md
new file mode 100644
index 00000000..d0889ab9
--- /dev/null
+++ b/MultiWindowPlayground/README.md
@@ -0,0 +1,96 @@
+
+Android MultiWindowPlayground Sample
+===================================
+
+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 24
+- Android Build Tools v28.0.3
+- Android Support Repository
+
+Screenshots
+-------------
+
+
+
+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 2019 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/MultiWindowPlayground/build.gradle b/MultiWindowPlayground/build.gradle
new file mode 100644
index 00000000..1901ba93
--- /dev/null
+++ b/MultiWindowPlayground/build.gradle
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MultiWindowPlayground/gradle/wrapper/gradle-wrapper.jar b/MultiWindowPlayground/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 00000000..94336fca
Binary files /dev/null and b/MultiWindowPlayground/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/MultiWindowPlayground/gradle/wrapper/gradle-wrapper.properties b/MultiWindowPlayground/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000..c4486d47
--- /dev/null
+++ b/MultiWindowPlayground/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/MultiWindowPlayground/gradlew b/MultiWindowPlayground/gradlew
new file mode 100755
index 00000000..cccdd3d5
--- /dev/null
+++ b/MultiWindowPlayground/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# 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\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# 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
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+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" -a "$nonstop" = "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"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # 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
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/MultiWindowPlayground/gradlew.bat b/MultiWindowPlayground/gradlew.bat
new file mode 100644
index 00000000..e95643d6
--- /dev/null
+++ b/MultiWindowPlayground/gradlew.bat
@@ -0,0 +1,84 @@
+@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
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@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=
+
+@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 Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_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=%*
+
+: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/MultiWindowPlayground/kotlinApp/Application/build.gradle b/MultiWindowPlayground/kotlinApp/Application/build.gradle
new file mode 100644
index 00000000..41383f08
--- /dev/null
+++ b/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-jdk7:$kotlin_version"
+}
diff --git a/MultiWindowPlayground/kotlinApp/Application/src/main/AndroidManifest.xml b/MultiWindowPlayground/kotlinApp/Application/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..10a81740
--- /dev/null
+++ b/MultiWindowPlayground/kotlinApp/Application/src/main/AndroidManifest.xml
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/MainActivity.kt b/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/MainActivity.kt
new file mode 100644
index 00000000..0c371a28
--- /dev/null
+++ b/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(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/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/AdjacentActivity.kt b/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/AdjacentActivity.kt
new file mode 100644
index 00000000..fb8206ed
--- /dev/null
+++ b/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/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/BasicActivity.kt b/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/BasicActivity.kt
new file mode 100644
index 00000000..cc7821a3
--- /dev/null
+++ b/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/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/CustomConfigurationChangeActivity.kt b/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/CustomConfigurationChangeActivity.kt
new file mode 100644
index 00000000..5e48f2e9
--- /dev/null
+++ b/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/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/LaunchBoundsActivity.kt b/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/LaunchBoundsActivity.kt
new file mode 100644
index 00000000..50838c6f
--- /dev/null
+++ b/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/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/LoggingActivity.kt b/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/LoggingActivity.kt
new file mode 100644
index 00000000..313bb109
--- /dev/null
+++ b/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(R.id.description).setText(textId)
+ }
+
+ protected fun setBackgroundColor(@ColorRes colorId: Int) {
+ findViewById(R.id.scrollview).setBackgroundResource(colorId)
+ }
+
+}
diff --git a/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/MinimumSizeActivity.kt b/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/MinimumSizeActivity.kt
new file mode 100644
index 00000000..d91cd473
--- /dev/null
+++ b/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/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/UnresizableActivity.kt b/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/activities/UnresizableActivity.kt
new file mode 100644
index 00000000..285fb821
--- /dev/null
+++ b/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/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/logger/Log.kt b/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/logger/Log.kt
new file mode 100755
index 00000000..2bce5c87
--- /dev/null
+++ b/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/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/logger/LogFragment.kt b/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/logger/LogFragment.kt
new file mode 100755
index 00000000..2014d0ef
--- /dev/null
+++ b/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/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/logger/LogNode.kt b/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/logger/LogNode.kt
new file mode 100755
index 00000000..a9c23f91
--- /dev/null
+++ b/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/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/logger/LogView.kt b/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/logger/LogView.kt
new file mode 100755
index 00000000..22488dce
--- /dev/null
+++ b/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/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/logger/LogWrapper.kt b/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/logger/LogWrapper.kt
new file mode 100755
index 00000000..046b743d
--- /dev/null
+++ b/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/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/logger/MessageOnlyLogFilter.kt b/MultiWindowPlayground/kotlinApp/Application/src/main/java/com/android/multiwindowplayground/logger/MessageOnlyLogFilter.kt
new file mode 100755
index 00000000..a3f1e627
--- /dev/null
+++ b/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/MultiWindowPlayground/kotlinApp/Application/src/main/res/layout/activity_logging.xml b/MultiWindowPlayground/kotlinApp/Application/src/main/res/layout/activity_logging.xml
new file mode 100644
index 00000000..2389e121
--- /dev/null
+++ b/MultiWindowPlayground/kotlinApp/Application/src/main/res/layout/activity_logging.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MultiWindowPlayground/kotlinApp/Application/src/main/res/layout/activity_main.xml b/MultiWindowPlayground/kotlinApp/Application/src/main/res/layout/activity_main.xml
new file mode 100644
index 00000000..7c405a38
--- /dev/null
+++ b/MultiWindowPlayground/kotlinApp/Application/src/main/res/layout/activity_main.xml
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MultiWindowPlayground/kotlinApp/Application/src/main/res/layout/logging.xml b/MultiWindowPlayground/kotlinApp/Application/src/main/res/layout/logging.xml
new file mode 100644
index 00000000..abd9018c
--- /dev/null
+++ b/MultiWindowPlayground/kotlinApp/Application/src/main/res/layout/logging.xml
@@ -0,0 +1,22 @@
+
+
+
+
diff --git a/MultiWindowPlayground/kotlinApp/Application/src/main/res/mipmap-hdpi/ic_launcher.png b/MultiWindowPlayground/kotlinApp/Application/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100755
index 00000000..a150a5c4
Binary files /dev/null and b/MultiWindowPlayground/kotlinApp/Application/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/MultiWindowPlayground/kotlinApp/Application/src/main/res/mipmap-mdpi/ic_launcher.png b/MultiWindowPlayground/kotlinApp/Application/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100755
index 00000000..1a482dde
Binary files /dev/null and b/MultiWindowPlayground/kotlinApp/Application/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/MultiWindowPlayground/kotlinApp/Application/src/main/res/mipmap-xhdpi/ic_launcher.png b/MultiWindowPlayground/kotlinApp/Application/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100755
index 00000000..148db37f
Binary files /dev/null and b/MultiWindowPlayground/kotlinApp/Application/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/MultiWindowPlayground/kotlinApp/Application/src/main/res/mipmap-xxhdpi/ic_launcher.png b/MultiWindowPlayground/kotlinApp/Application/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100755
index 00000000..7c5c6a46
Binary files /dev/null and b/MultiWindowPlayground/kotlinApp/Application/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/MultiWindowPlayground/kotlinApp/Application/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/MultiWindowPlayground/kotlinApp/Application/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100755
index 00000000..944fc543
Binary files /dev/null and b/MultiWindowPlayground/kotlinApp/Application/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/MultiWindowPlayground/kotlinApp/Application/src/main/res/values-w820dp/dimens.xml b/MultiWindowPlayground/kotlinApp/Application/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 00000000..e2ec1175
--- /dev/null
+++ b/MultiWindowPlayground/kotlinApp/Application/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,20 @@
+
+
+
+
+ 64dp
+
diff --git a/MultiWindowPlayground/kotlinApp/Application/src/main/res/values/colors.xml b/MultiWindowPlayground/kotlinApp/Application/src/main/res/values/colors.xml
new file mode 100644
index 00000000..18da4e19
--- /dev/null
+++ b/MultiWindowPlayground/kotlinApp/Application/src/main/res/values/colors.xml
@@ -0,0 +1,32 @@
+
+
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
+ #512DA8
+ #C2185B
+ #00695C
+ #9E9D24
+ #424242
+ #F5F5F5
+ #00838F
+
+ #FFFFFF
+
diff --git a/MultiWindowPlayground/kotlinApp/Application/src/main/res/values/dimens.xml b/MultiWindowPlayground/kotlinApp/Application/src/main/res/values/dimens.xml
new file mode 100644
index 00000000..a349e62a
--- /dev/null
+++ b/MultiWindowPlayground/kotlinApp/Application/src/main/res/values/dimens.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+ 16dp
+ 16dp
+ 16dp
+ 30sp
+
diff --git a/MultiWindowPlayground/kotlinApp/Application/src/main/res/values/strings.xml b/MultiWindowPlayground/kotlinApp/Application/src/main/res/values/strings.xml
new file mode 100644
index 00000000..7bd6fbde
--- /dev/null
+++ b/MultiWindowPlayground/kotlinApp/Application/src/main/res/values/strings.xml
@@ -0,0 +1,69 @@
+
+
+
+
+ MultiWindow Playground
+ Multiwindow Playground
+ This sample demonstrates the use of the multi-window API
+ available in Android N.\nFirst, switch this app into
+ split-screen mode
+ (for example by long-pressing the recents button). Each button below starts a new activity
+ with special flags.\nSee the files MainActivity.kt and AndroidManifest.xml for
+ implementation details.
+
+ The buttons below demonstrate features only
+ available in free-form multi-window mode.
+ Start basic, default Activity
+ Start unresizable Activity
+ Start Activity adjacent
+ Start Activity with minimum size
+ Start Activity with launch bounds
+ Start activity that handles configuration changes.
+
+ This Activity was launched in a new task without any
+ additional flags or options.
+
+ This activity is set as unresizable in the
+ AndroidManifest. This is done by setting the resizeableActivity property to
+ false for this activity.
+
+ This activity was launched with the flag
+ Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT.\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.
+
+ This activity handles configuration changes
+ itself.\n\nIn the AndroidManifest, this activity has been configured to receive callbacks
+ for screenSize|smallestScreenSize|screenLayout|orientation
+ changes.\nTry resizing this activity to different sizes to see which configuration
+ properties change.
+
+ 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.
+
+ 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 layout attribute in the AndroidManifest definition.
+ \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.
+
+ Enable multi-window mode to see this sample in
+ action!
+
diff --git a/MultiWindowPlayground/kotlinApp/Application/src/main/res/values/styles.xml b/MultiWindowPlayground/kotlinApp/Application/src/main/res/values/styles.xml
new file mode 100644
index 00000000..e62ba347
--- /dev/null
+++ b/MultiWindowPlayground/kotlinApp/Application/src/main/res/values/styles.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MultiWindowPlayground/kotlinApp/README.md b/MultiWindowPlayground/kotlinApp/README.md
new file mode 100644
index 00000000..89b6e2fe
--- /dev/null
+++ b/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
+-------------
+
+
+
+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/MultiWindowPlayground/kotlinApp/build.gradle b/MultiWindowPlayground/kotlinApp/build.gradle
new file mode 100644
index 00000000..65610946
--- /dev/null
+++ b/MultiWindowPlayground/kotlinApp/build.gradle
@@ -0,0 +1,22 @@
+buildscript {
+ ext.kotlin_version = '1.3.11'
+ repositories {
+ google()
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.3.0'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/MultiWindowPlayground/kotlinApp/gradle/wrapper/gradle-wrapper.jar b/MultiWindowPlayground/kotlinApp/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 00000000..8c0fb64a
Binary files /dev/null and b/MultiWindowPlayground/kotlinApp/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/MultiWindowPlayground/kotlinApp/gradle/wrapper/gradle-wrapper.properties b/MultiWindowPlayground/kotlinApp/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000..8e7447f0
--- /dev/null
+++ b/MultiWindowPlayground/kotlinApp/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https://services.gradle.org/distributions/gradle-5.1.1-all.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/MultiWindowPlayground/kotlinApp/gradlew b/MultiWindowPlayground/kotlinApp/gradlew
new file mode 100755
index 00000000..91a7e269
--- /dev/null
+++ b/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/MultiWindowPlayground/kotlinApp/gradlew.bat b/MultiWindowPlayground/kotlinApp/gradlew.bat
new file mode 100644
index 00000000..8a0b282a
--- /dev/null
+++ b/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/MultiWindowPlayground/kotlinApp/screenshots/icon-web.png b/MultiWindowPlayground/kotlinApp/screenshots/icon-web.png
new file mode 100755
index 00000000..8d99c127
Binary files /dev/null and b/MultiWindowPlayground/kotlinApp/screenshots/icon-web.png differ
diff --git a/MultiWindowPlayground/kotlinApp/screenshots/main.png b/MultiWindowPlayground/kotlinApp/screenshots/main.png
new file mode 100644
index 00000000..d9222003
Binary files /dev/null and b/MultiWindowPlayground/kotlinApp/screenshots/main.png differ
diff --git a/MultiWindowPlayground/kotlinApp/settings.gradle b/MultiWindowPlayground/kotlinApp/settings.gradle
new file mode 100644
index 00000000..9464a359
--- /dev/null
+++ b/MultiWindowPlayground/kotlinApp/settings.gradle
@@ -0,0 +1 @@
+include 'Application'
diff --git a/MultiWindowPlayground/screenshots/icon-web.png b/MultiWindowPlayground/screenshots/icon-web.png
new file mode 100755
index 00000000..8d99c127
Binary files /dev/null and b/MultiWindowPlayground/screenshots/icon-web.png differ
diff --git a/MultiWindowPlayground/screenshots/main.png b/MultiWindowPlayground/screenshots/main.png
new file mode 100644
index 00000000..d9222003
Binary files /dev/null and b/MultiWindowPlayground/screenshots/main.png differ
diff --git a/MultiWindowPlayground/settings.gradle b/MultiWindowPlayground/settings.gradle
new file mode 100644
index 00000000..0a5c310b
--- /dev/null
+++ b/MultiWindowPlayground/settings.gradle
@@ -0,0 +1,2 @@
+
+include 'Application'