diff options
author | Jan-Felix Schmakeit <jfschmakeit@google.com> | 2016-03-01 19:12:05 +1100 |
---|---|---|
committer | Jan-Felix Schmakeit <jfschmakeit@google.com> | 2016-03-07 14:39:35 +1100 |
commit | 9c92857766bf42ca1dadbcd85b6249c26ec683e1 (patch) | |
tree | be0d2da1d4d86e549a54842363582f0cd76e821c /ui/window | |
parent | 4ead35ed29820288f8c48c300aa957785c86dc02 (diff) | |
download | android-9c92857766bf42ca1dadbcd85b6249c26ec683e1.tar.gz |
Add MultiWindowPlayground sample.
This sample demonstrates the use and behavior of the
multi-window features in Android N.
Change-Id: Idcabb1cac26890c914adf3a0eefa5e38b7939e7f
Diffstat (limited to 'ui/window')
45 files changed, 2415 insertions, 0 deletions
diff --git a/ui/window/MultiWindowPlayground/.google/packaging.yaml b/ui/window/MultiWindowPlayground/.google/packaging.yaml new file mode 100644 index 00000000..a6ca69ec --- /dev/null +++ b/ui/window/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: DRAFT +technologies: [Android] +categories: [Android N Preview] +languages: [Java] +solutions: [Mobile] +github: android-MultiWindowPlayground +level: INTERMEDIATE +icon: screenshots/icon-web.png +apiRefs: + - android:android.content.Intent + - android:android.app.ActivityOptions +license: apache2 diff --git a/ui/window/MultiWindowPlayground/Application/.gitignore b/ui/window/MultiWindowPlayground/Application/.gitignore new file mode 100644 index 00000000..537302e9 --- /dev/null +++ b/ui/window/MultiWindowPlayground/Application/.gitignore @@ -0,0 +1,15 @@ +# Copyright 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. +src/template/ +src/common/ diff --git a/ui/window/MultiWindowPlayground/Application/build.gradle b/ui/window/MultiWindowPlayground/Application/build.gradle new file mode 100644 index 00000000..c423c256 --- /dev/null +++ b/ui/window/MultiWindowPlayground/Application/build.gradle @@ -0,0 +1,66 @@ +/* + * 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 { + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:1.2.3' + } +} + +allprojects { + repositories { + jcenter() + } +} + +apply plugin: 'com.android.application' + +android { + compileSdkVersion 'android-N' + buildToolsVersion '23.0.1' + + defaultConfig { + applicationId "com.android.multiwindowplayground" + minSdkVersion 'N' + targetSdkVersion 'N' + 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:23.2.0' + androidTestCompile 'com.android.support:support-annotations:23.2.0' + androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.1' +} diff --git a/ui/window/MultiWindowPlayground/Application/proguard-project.txt b/ui/window/MultiWindowPlayground/Application/proguard-project.txt new file mode 100644 index 00000000..f2fe1559 --- /dev/null +++ b/ui/window/MultiWindowPlayground/Application/proguard-project.txt @@ -0,0 +1,20 @@ +# To enable ProGuard in your project, edit project.properties +# to define the proguard.config property as described in that file. +# +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in ${sdk.dir}/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the ProGuard +# include property in project.properties. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/ui/window/MultiWindowPlayground/Application/src/androidTest/java/LaunchTests.java b/ui/window/MultiWindowPlayground/Application/src/androidTest/java/LaunchTests.java new file mode 100644 index 00000000..e0567efa --- /dev/null +++ b/ui/window/MultiWindowPlayground/Application/src/androidTest/java/LaunchTests.java @@ -0,0 +1,107 @@ +/* + * 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. + */ + +import com.android.multiwindowplayground.MainActivity; +import com.android.multiwindowplayground.R; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import android.support.test.rule.ActivityTestRule; +import android.support.test.runner.AndroidJUnit4; +import android.test.suitebuilder.annotation.LargeTest; + +import static android.support.test.espresso.Espresso.onView; +import static android.support.test.espresso.action.ViewActions.click; +import static android.support.test.espresso.action.ViewActions.scrollTo; +import static android.support.test.espresso.assertion.ViewAssertions.matches; +import static android.support.test.espresso.matcher.ViewMatchers.withId; +import static android.support.test.espresso.matcher.ViewMatchers.withText; + +@LargeTest +@RunWith(AndroidJUnit4.class) +public class LaunchTests { + + @Rule + public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>( + MainActivity.class); + + + @Test + public void testLaunchBasicActivity() { + // Click the 'start basic activity' button. + onView(withId(R.id.button_start_basic)).perform(scrollTo(), click()); + + // Verify that the description for the basic activity is now displayed. + onView(withId(R.id.description)) + .check(matches(withText(R.string.activity_description_basic))); + } + + @Test + public void testLaunchUnresizableActivity() { + // Click the 'start unresizable activity' button. + onView(withId(R.id.start_unresizable)).perform(scrollTo(), click()); + + // Verify that the description for the unresizable activity is now displayed. + onView(withId(R.id.description)) + .check(matches(withText(R.string.activity_description_unresizable))); + } + + @Test + public void testLaunchAdjacentActivity() { + + // Click the 'start adjacent activity' button. + onView(withId(R.id.start_adjacent)).perform(scrollTo(), click()); + + // Verify that the correct description is now displayed. + onView(withId(R.id.description)) + .check(matches(withText(R.string.activity_adjacent_description))); + } + + @Test + public void testLaunchCustomConfigurationActivity() { + // Click the 'start activity that handles configuration changes' button. + onView(withId(R.id.start_customconfiguration)).perform(scrollTo(), click()); + + // Verify that the correct description is now displayed. + onView(withId(R.id.description)) + .check(matches(withText(R.string.activity_custom_description))); + } + + + @Test + public void testLaunchMinimumSizeActivity() { + // Click the 'start activity with minimum size' button. + onView(withId(R.id.start_minimumsize)).perform(scrollTo(), click()); + + // Verify that the correct description is now displayed. + onView(withId(R.id.description)) + .check(matches(withText(R.string.activity_minimum_description))); + } + + @Test + public void testLaunchBoundsActivity() { + // Click the 'start activity with launch bounds' button. + onView(withId(R.id.start_launchbounds)).perform(scrollTo(), click()); + + // Verify that the correct description is now displayed. + onView(withId(R.id.description)) + .check(matches(withText(R.string.activity_bounds_description))); + } + + +} diff --git a/ui/window/MultiWindowPlayground/Application/src/main/AndroidManifest.xml b/ui/window/MultiWindowPlayground/Application/src/main/AndroidManifest.xml new file mode 100644 index 00000000..b27bc1e4 --- /dev/null +++ b/ui/window/MultiWindowPlayground/Application/src/main/AndroidManifest.xml @@ -0,0 +1,85 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + 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. + --> + +<manifest package="com.android.multiwindowplayground" + xmlns:android="http://schemas.android.com/apk/res/android"> + + <application + android:allowBackup="true" + android:icon="@mipmap/ic_launcher" + android:label="@string/app_name" + android:supportsRtl="true" + android:theme="@style/MultiWindowSampleTheme"> + <!-- The launcher Activity that is started when the application is first started. + Note that we are setting the task affinity to "" to ensure each activity is launched + into a separate task stack. --> + <activity + android:name="com.android.multiwindowplayground.MainActivity" + android:taskAffinity=""> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + + <!-- This Activity cannot be resized and is always displayed full screen. --> + <activity + android:name="com.android.multiwindowplayground.activities.UnresizableActivity" + android:resizeableActivity="false" + android:taskAffinity="" /> + + <!-- This Activity has a default size (750x500dp) with a minimum size + (500dp at its shortest side). It is launched in the top/end (top/right) corner by default. + These attributes are defined in the 'layout' tag within an Activity definition. --> + <activity + android:name="com.android.multiwindowplayground.activities.MinimumSizeActivity" + android:launchMode="singleInstance" + android:taskAffinity=""> + <layout + android:defaultHeight="500dp" + android:defaultWidth="750dp" + android:gravity="top|end" + android:minimalSize="500dp" /> + </activity> + + <!-- In split-screen mode, this Activity is launched adjacent to another Activity. This is + controlled via a flag set in the intent that launches this Activity. --> + <activity + android:name="com.android.multiwindowplayground.activities.AdjacentActivity" + android:taskAffinity="" /> + + <!-- This Activity is launched within an area defined in its launch intent. --> + <activity + android:name="com.android.multiwindowplayground.activities.LaunchBoundsActivity" + android:taskAffinity="" /> + + <!-- This activity handles all configuration changes itself. + Callbacks for configuration changes are received in 'onConfigurationChanged'. --> + <activity + android:name="com.android.multiwindowplayground.activities.CustomConfigurationChangeActivity" + android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation" + android:launchMode="singleInstance" + android:taskAffinity="" /> + + <!-- This Activity is launched in a new task without any special flags or settings. --> + <activity + android:name="com.android.multiwindowplayground.activities.BasicActivity" + android:launchMode="singleInstance" + android:taskAffinity="" /> + + </application> + +</manifest> diff --git a/ui/window/MultiWindowPlayground/Application/src/main/java/com/android/multiwindowplayground/MainActivity.java b/ui/window/MultiWindowPlayground/Application/src/main/java/com/android/multiwindowplayground/MainActivity.java new file mode 100644 index 00000000..395385d6 --- /dev/null +++ b/ui/window/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 (!inMultiWindow()) { + 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/ui/window/MultiWindowPlayground/Application/src/main/java/com/android/multiwindowplayground/activities/AdjacentActivity.java b/ui/window/MultiWindowPlayground/Application/src/main/java/com/android/multiwindowplayground/activities/AdjacentActivity.java new file mode 100644 index 00000000..cfb915b6 --- /dev/null +++ b/ui/window/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/ui/window/MultiWindowPlayground/Application/src/main/java/com/android/multiwindowplayground/activities/BasicActivity.java b/ui/window/MultiWindowPlayground/Application/src/main/java/com/android/multiwindowplayground/activities/BasicActivity.java new file mode 100644 index 00000000..c40dee22 --- /dev/null +++ b/ui/window/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/ui/window/MultiWindowPlayground/Application/src/main/java/com/android/multiwindowplayground/activities/CustomConfigurationChangeActivity.java b/ui/window/MultiWindowPlayground/Application/src/main/java/com/android/multiwindowplayground/activities/CustomConfigurationChangeActivity.java new file mode 100644 index 00000000..32e5233e --- /dev/null +++ b/ui/window/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/ui/window/MultiWindowPlayground/Application/src/main/java/com/android/multiwindowplayground/activities/LaunchBoundsActivity.java b/ui/window/MultiWindowPlayground/Application/src/main/java/com/android/multiwindowplayground/activities/LaunchBoundsActivity.java new file mode 100644 index 00000000..80a98f2c --- /dev/null +++ b/ui/window/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/ui/window/MultiWindowPlayground/Application/src/main/java/com/android/multiwindowplayground/activities/LoggingActivity.java b/ui/window/MultiWindowPlayground/Application/src/main/java/com/android/multiwindowplayground/activities/LoggingActivity.java new file mode 100644 index 00000000..604485da --- /dev/null +++ b/ui/window/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 onMultiWindowChanged(boolean inMultiWindow) { + super.onMultiWindowChanged(inMultiWindow); + + Log.d(mLogTag, "onMultiWindowChanged: " + inMultiWindow); + } + + // 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 <code>description</code> 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/ui/window/MultiWindowPlayground/Application/src/main/java/com/android/multiwindowplayground/activities/MinimumSizeActivity.java b/ui/window/MultiWindowPlayground/Application/src/main/java/com/android/multiwindowplayground/activities/MinimumSizeActivity.java new file mode 100644 index 00000000..a42c379e --- /dev/null +++ b/ui/window/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/ui/window/MultiWindowPlayground/Application/src/main/java/com/android/multiwindowplayground/activities/UnresizableActivity.java b/ui/window/MultiWindowPlayground/Application/src/main/java/com/android/multiwindowplayground/activities/UnresizableActivity.java new file mode 100644 index 00000000..eb2523e9 --- /dev/null +++ b/ui/window/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/ui/window/MultiWindowPlayground/Application/src/main/java/com/example/android/common/logger/Log.java b/ui/window/MultiWindowPlayground/Application/src/main/java/com/example/android/common/logger/Log.java new file mode 100755 index 00000000..7c112230 --- /dev/null +++ b/ui/window/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. + * + * <p>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.</p> + */ +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/ui/window/MultiWindowPlayground/Application/src/main/java/com/example/android/common/logger/LogFragment.java b/ui/window/MultiWindowPlayground/Application/src/main/java/com/example/android/common/logger/LogFragment.java new file mode 100755 index 00000000..0e9ea9e4 --- /dev/null +++ b/ui/window/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/ui/window/MultiWindowPlayground/Application/src/main/java/com/example/android/common/logger/LogNode.java b/ui/window/MultiWindowPlayground/Application/src/main/java/com/example/android/common/logger/LogNode.java new file mode 100755 index 00000000..7620ca6c --- /dev/null +++ b/ui/window/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/ui/window/MultiWindowPlayground/Application/src/main/java/com/example/android/common/logger/LogView.java b/ui/window/MultiWindowPlayground/Application/src/main/java/com/example/android/common/logger/LogView.java new file mode 100755 index 00000000..b1fb2054 --- /dev/null +++ b/ui/window/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/ui/window/MultiWindowPlayground/Application/src/main/java/com/example/android/common/logger/LogWrapper.java b/ui/window/MultiWindowPlayground/Application/src/main/java/com/example/android/common/logger/LogWrapper.java new file mode 100755 index 00000000..8594bc47 --- /dev/null +++ b/ui/window/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/ui/window/MultiWindowPlayground/Application/src/main/java/com/example/android/common/logger/MessageOnlyLogFilter.java b/ui/window/MultiWindowPlayground/Application/src/main/java/com/example/android/common/logger/MessageOnlyLogFilter.java new file mode 100755 index 00000000..b86e1aef --- /dev/null +++ b/ui/window/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/ui/window/MultiWindowPlayground/Application/src/main/res/layout/activity_logging.xml b/ui/window/MultiWindowPlayground/Application/src/main/res/layout/activity_logging.xml new file mode 100644 index 00000000..6c6d79b2 --- /dev/null +++ b/ui/window/MultiWindowPlayground/Application/src/main/res/layout/activity_logging.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + 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. + --> + +<!-- This layout contains a TextView and a LogFragment that logs some text to the screen. --> +<LinearLayout android:id="@+id/layout" + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@color/white" + android:orientation="vertical"> + + <ScrollView + android:id="@+id/scrollview" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_gravity="top" + android:layout_weight="0.75"> + + <TextView + android:id="@+id/description" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:paddingBottom="@dimen/activity_vertical_margin" + android:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin" + android:textColor="@color/white" /> + </ScrollView> + + + <include + layout="@layout/logging" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_gravity="bottom" + android:layout_weight="0.25" /> +</LinearLayout>
\ No newline at end of file diff --git a/ui/window/MultiWindowPlayground/Application/src/main/res/layout/activity_main.xml b/ui/window/MultiWindowPlayground/Application/src/main/res/layout/activity_main.xml new file mode 100644 index 00000000..640d9cc9 --- /dev/null +++ b/ui/window/MultiWindowPlayground/Application/src/main/res/layout/activity_main.xml @@ -0,0 +1,124 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + 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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_height="match_parent" + android:layout_width="match_parent" + android:orientation="vertical" + android:background="@color/lightgray"> + + <ScrollView xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="0.75" + android:background="@color/white" + android:layout_gravity="top" + android:id="@+id/scrollview" + tools:context="com.android.multiwindowplayground.MainActivity"> + + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingBottom="@dimen/activity_vertical_margin" + android:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin" + android:orientation="vertical"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/introduction_title" + android:textSize="30sp" /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/warning_multiwindow_disabled" + android:visibility="gone" + tools:visibility="visible" + style="@style/TextWarning" + android:paddingTop="@dimen/content_vertical_dividing_padding" + android:paddingBottom="@dimen/content_vertical_dividing_padding" + android:text="Enable multi-window mode to see this sample in action!" /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/description" + android:text="@string/sample_introduction" /> + + <Button + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/button_start_basic" + android:onClick="onStartBasicActivity" + android:text="@string/start_default" /> + + <Button + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/start_unresizable" + android:onClick="onStartUnresizableClick" + android:text="@string/start_unresizable" /> + + <Button + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/start_adjacent" + android:onClick="onStartAdjacentActivity" + android:text="@string/start_adjacent" /> + + <Button + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/start_customconfiguration" + android:onClick="onStartCustomConfigurationActivity" + android:text="@string/start_custom_activity" /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingTop="@dimen/content_vertical_dividing_padding" + android:text="@string/sample_freeform_introduction" /> + + <Button + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/start_minimumsize" + android:onClick="onStartMinimumSizeActivity" + android:text="@string/start_minimum" /> + + <Button + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/start_launchbounds" + android:onClick="onStartLaunchBoundsActivity" + android:text="@string/start_bounds" /> + + </LinearLayout> + </ScrollView> + + <include + layout="@layout/logging" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_gravity="bottom" + android:layout_weight="0.25" /> + +</LinearLayout>
\ No newline at end of file diff --git a/ui/window/MultiWindowPlayground/Application/src/main/res/layout/logging.xml b/ui/window/MultiWindowPlayground/Application/src/main/res/layout/logging.xml new file mode 100644 index 00000000..b2da5f1b --- /dev/null +++ b/ui/window/MultiWindowPlayground/Application/src/main/res/layout/logging.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + 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. + --> + +<fragment xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:id="@+id/log_fragment" + android:name="com.example.android.common.logger.LogFragment" /> + diff --git a/ui/window/MultiWindowPlayground/Application/src/main/res/mipmap-hdpi/ic_launcher.png b/ui/window/MultiWindowPlayground/Application/src/main/res/mipmap-hdpi/ic_launcher.png Binary files differnew file mode 100755 index 00000000..a150a5c4 --- /dev/null +++ b/ui/window/MultiWindowPlayground/Application/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/ui/window/MultiWindowPlayground/Application/src/main/res/mipmap-mdpi/ic_launcher.png b/ui/window/MultiWindowPlayground/Application/src/main/res/mipmap-mdpi/ic_launcher.png Binary files differnew file mode 100755 index 00000000..1a482dde --- /dev/null +++ b/ui/window/MultiWindowPlayground/Application/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/ui/window/MultiWindowPlayground/Application/src/main/res/mipmap-xhdpi/ic_launcher.png b/ui/window/MultiWindowPlayground/Application/src/main/res/mipmap-xhdpi/ic_launcher.png Binary files differnew file mode 100755 index 00000000..148db37f --- /dev/null +++ b/ui/window/MultiWindowPlayground/Application/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/ui/window/MultiWindowPlayground/Application/src/main/res/mipmap-xxhdpi/ic_launcher.png b/ui/window/MultiWindowPlayground/Application/src/main/res/mipmap-xxhdpi/ic_launcher.png Binary files differnew file mode 100755 index 00000000..7c5c6a46 --- /dev/null +++ b/ui/window/MultiWindowPlayground/Application/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/ui/window/MultiWindowPlayground/Application/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/ui/window/MultiWindowPlayground/Application/src/main/res/mipmap-xxxhdpi/ic_launcher.png Binary files differnew file mode 100755 index 00000000..944fc543 --- /dev/null +++ b/ui/window/MultiWindowPlayground/Application/src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/ui/window/MultiWindowPlayground/Application/src/main/res/values-w820dp/dimens.xml b/ui/window/MultiWindowPlayground/Application/src/main/res/values-w820dp/dimens.xml new file mode 100644 index 00000000..e8b7e246 --- /dev/null +++ b/ui/window/MultiWindowPlayground/Application/src/main/res/values-w820dp/dimens.xml @@ -0,0 +1,19 @@ +<!-- + 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. + --> + +<resources> + <dimen name="activity_horizontal_margin">64dp</dimen> +</resources> diff --git a/ui/window/MultiWindowPlayground/Application/src/main/res/values/colors.xml b/ui/window/MultiWindowPlayground/Application/src/main/res/values/colors.xml new file mode 100644 index 00000000..e18e61e2 --- /dev/null +++ b/ui/window/MultiWindowPlayground/Application/src/main/res/values/colors.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + 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. + --> + +<resources> + <color name="colorPrimary">#3F51B5</color> + <color name="colorPrimaryDark">#303F9F</color> + <color name="colorAccent">#FF4081</color> + + <color name="purple">#512DA8</color> + <color name="pink">#C2185B</color> + <color name="teal">#00695C</color> + <color name="lime">#9E9D24</color> + <color name="gray">#424242</color> + <color name="lightgray">#F5F5F5</color> + <color name="cyan">#00838F</color> + + <color name="white">#FFFFFF</color> +</resources> diff --git a/ui/window/MultiWindowPlayground/Application/src/main/res/values/dimens.xml b/ui/window/MultiWindowPlayground/Application/src/main/res/values/dimens.xml new file mode 100644 index 00000000..3a61036f --- /dev/null +++ b/ui/window/MultiWindowPlayground/Application/src/main/res/values/dimens.xml @@ -0,0 +1,22 @@ +<!-- + 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. + --> + +<resources> + <!-- Default screen margins, per the Android Design guidelines. --> + <dimen name="activity_horizontal_margin">16dp</dimen> + <dimen name="activity_vertical_margin">16dp</dimen> + <dimen name="content_vertical_dividing_padding">16dp</dimen> +</resources> diff --git a/ui/window/MultiWindowPlayground/Application/src/main/res/values/strings.xml b/ui/window/MultiWindowPlayground/Application/src/main/res/values/strings.xml new file mode 100644 index 00000000..430742db --- /dev/null +++ b/ui/window/MultiWindowPlayground/Application/src/main/res/values/strings.xml @@ -0,0 +1,66 @@ +<!-- + 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. + --> + +<resources> + <string name="app_name">MultiWindow Playground</string> + <string name="introduction_title">Multiwindow Playground</string> + <string name="sample_introduction">This sample demonstrates the use of the multi-window API + available in Android N.\nFirst, switch this app into + <b>split-screen mode</b> + (for example by long-pressing the recents button). Each button below starts a new activity + with special flags.\n<b>See the files MainActivity.java and AndroidManifest.xml for + implementation details.</b> + </string> + <string name="sample_freeform_introduction">The buttons below demonstrate features only + available in <b>free-form multi-window mode</b>.</string> + <string name="start_default">Start basic, default Activity</string> + <string name="start_unresizable">Start unresizable Activity</string> + <string name="start_adjacent">Start Activity adjacent</string> + <string name="start_minimum">Start Activity with minimum size</string> + <string name="start_bounds">Start Activity with launch bounds</string> + <string name="start_custom_activity">Start activity that handles configuration changes.</string> + + <string name="activity_description_basic">This Activity was launched in a new task without any + additional flags or options. + </string> + <string name="activity_description_unresizable">This activity is set as unresizable in the + AndroidManifest. This is done by setting the <i>resizeableActivity</i> property to + <i>false</i> for this activity. + </string> + <string name="activity_adjacent_description">This activity was launched with the flag + <b>Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT</b>.\n\nIf possible, it has been launched into the + adjacent area from the activity that started it.\nThis is only a hint to the system. For + example - if the application is not in split-screen mode, it will be launched full-screen. + If it is launched in the same task as the initial Activity, it will retain its activity + properties and its location. + </string> + <string name="activity_custom_description">This activity handles configuration changes + itself.\n\nIn the AndroidManifest, this activity has been configured to receive callbacks + for <b>screenSize|smallestScreenSize|screenLayout|orientation</b> + changes.\nTry resizing this activity to different sizes to see which configuration + properties change. + </string> + <string name="activity_bounds_description">This activity has been launched with a launch bounds + set in its intent. The bounds define the area into which the activity should be launched. + \n\nNote that this flag only applies in free-form mode. + </string> + <string name="activity_minimum_description">This activity has a minimum size.\nIt was launched + into the top/end corner with a a default size of 750dp by 500dp, with a minimum size of 750dp + as defined in its <b>layout attribute in the AndroidManifest definition</b>. + \n\nNote that this Activity was launched in a different task, otherwise the properties from + the Activity that launched this one would have been applied. + </string> +</resources> diff --git a/ui/window/MultiWindowPlayground/Application/src/main/res/values/styles.xml b/ui/window/MultiWindowPlayground/Application/src/main/res/values/styles.xml new file mode 100644 index 00000000..0324dbd7 --- /dev/null +++ b/ui/window/MultiWindowPlayground/Application/src/main/res/values/styles.xml @@ -0,0 +1,36 @@ +<!-- + 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. + --> + +<resources> + + <!-- Base application theme. --> + <style name="MultiWindowSampleTheme" parent="Theme.AppCompat.Light.DarkActionBar"> + <!-- Customize your theme here. --> + <item name="colorPrimary">@color/colorPrimary</item> + <item name="colorPrimaryDark">@color/colorPrimaryDark</item> + <item name="colorAccent">@color/colorAccent</item> + + <!-- Drawable to use in the background while the window is resizing on Android N. --> + <item name="android:windowBackgroundFallback">@color/colorAccent</item> + <item name="android:windowBackground">@color/colorAccent</item> + </style> + + <style name="TextWarning" parent="TextAppearance.AppCompat.Medium"> + + </style> + + +</resources> diff --git a/ui/window/MultiWindowPlayground/CONTRIBUTING.md b/ui/window/MultiWindowPlayground/CONTRIBUTING.md new file mode 100644 index 00000000..faa8b5c6 --- /dev/null +++ b/ui/window/MultiWindowPlayground/CONTRIBUTING.md @@ -0,0 +1,35 @@ +# How to become a contributor and submit your own code + +## Contributor License Agreements + +We'd love to accept your sample apps and patches! Before we can take them, we +have to jump a couple of legal hurdles. + +Please fill out either the individual or corporate Contributor License Agreement (CLA). + + * If you are an individual writing original source code and you're sure you + own the intellectual property, then you'll need to sign an [individual CLA] + (https://cla.developers.google.com). + * If you work for a company that wants to allow you to contribute your work, + then you'll need to sign a [corporate CLA] + (https://cla.developers.google.com). + +Follow either of the two links above to access the appropriate CLA and +instructions for how to sign and return it. Once we receive it, we'll be able to +accept your pull requests. + +## Contributing A Patch + +1. Submit an issue describing your proposed change to the repo in question. +1. The repo owner will respond to your issue promptly. +1. If your proposed change is accepted, and you haven't already done so, sign a + Contributor License Agreement (see details above). +1. Fork the desired repo, develop and test your code changes. +1. Ensure that your code adheres to the existing style in the sample to which + you are contributing. Refer to the + [Android Code Style Guide] + (https://source.android.com/source/code-style.html) for the + recommended coding standards for this organization. +1. Ensure that your code has an appropriate set of unit tests which all pass. +1. Submit a pull request. + diff --git a/ui/window/MultiWindowPlayground/README.md b/ui/window/MultiWindowPlayground/README.md new file mode 100644 index 00000000..ea73dc10 --- /dev/null +++ b/ui/window/MultiWindowPlayground/README.md @@ -0,0 +1,95 @@ + +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 Preview N +- Android Build Tools v23.0.0 +- Android Support Repository + +Screenshots +------------- + +<img src="screenshots/main.png" height="400" alt="Screenshot"/> + +Getting Started +--------------- + +This sample uses the Gradle build system. To build this project, use the +"gradlew build" command or use "Import Project" in Android Studio. + +Support +------- + +- Google+ Community: https://plus.google.com/communities/105153134372062985968 +- Stack Overflow: http://stackoverflow.com/questions/tagged/android + +If you've found an error in this sample, please file an issue: +https://github.com/googlesamples/android-MultiWindowPlayground + +Patches are encouraged, and may be submitted by forking this project and +submitting a pull request through GitHub. Please see CONTRIBUTING.md for more details. + +License +------- + +Copyright 2016 The Android Open Source Project, Inc. + +Licensed to the Apache Software Foundation (ASF) under one or more contributor +license agreements. See the NOTICE file distributed with this work for +additional information regarding copyright ownership. The ASF licenses this +file to you under the Apache License, Version 2.0 (the "License"); you may not +use this file except in compliance with the License. You may obtain a copy of +the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +License for the specific language governing permissions and limitations under +the License. diff --git a/ui/window/MultiWindowPlayground/build.gradle b/ui/window/MultiWindowPlayground/build.gradle new file mode 100644 index 00000000..b95a8604 --- /dev/null +++ b/ui/window/MultiWindowPlayground/build.gradle @@ -0,0 +1,12 @@ + + +// BEGIN_EXCLUDE +import com.example.android.samples.build.SampleGenPlugin +apply plugin: SampleGenPlugin + +samplegen { + pathToBuild "../../../../../build" + pathToSamplesCommon "../../../common" +} +apply from: "../../../../../build/build.gradle" +// END_EXCLUDE diff --git a/ui/window/MultiWindowPlayground/buildSrc/build.gradle b/ui/window/MultiWindowPlayground/buildSrc/build.gradle new file mode 100644 index 00000000..7c150e4b --- /dev/null +++ b/ui/window/MultiWindowPlayground/buildSrc/build.gradle @@ -0,0 +1,16 @@ + +repositories { + jcenter() +} +dependencies { + compile 'org.freemarker:freemarker:2.3.20' +} + +sourceSets { + main { + groovy { + srcDir new File(rootDir, "../../../../../../build/buildSrc/src/main/groovy") + } + } +} + diff --git a/ui/window/MultiWindowPlayground/gradle/wrapper/gradle-wrapper.jar b/ui/window/MultiWindowPlayground/gradle/wrapper/gradle-wrapper.jar Binary files differnew file mode 100644 index 00000000..8c0fb64a --- /dev/null +++ b/ui/window/MultiWindowPlayground/gradle/wrapper/gradle-wrapper.jar diff --git a/ui/window/MultiWindowPlayground/gradle/wrapper/gradle-wrapper.properties b/ui/window/MultiWindowPlayground/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..afb32963 --- /dev/null +++ b/ui/window/MultiWindowPlayground/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Apr 10 15:27:10 PDT 2013 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=http\://services.gradle.org/distributions/gradle-2.2.1-bin.zip diff --git a/ui/window/MultiWindowPlayground/gradlew b/ui/window/MultiWindowPlayground/gradlew new file mode 100755 index 00000000..91a7e269 --- /dev/null +++ b/ui/window/MultiWindowPlayground/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/ui/window/MultiWindowPlayground/gradlew.bat b/ui/window/MultiWindowPlayground/gradlew.bat new file mode 100644 index 00000000..aec99730 --- /dev/null +++ b/ui/window/MultiWindowPlayground/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/ui/window/MultiWindowPlayground/screenshots/icon-web.png b/ui/window/MultiWindowPlayground/screenshots/icon-web.png Binary files differnew file mode 100755 index 00000000..8d99c127 --- /dev/null +++ b/ui/window/MultiWindowPlayground/screenshots/icon-web.png diff --git a/ui/window/MultiWindowPlayground/screenshots/main.png b/ui/window/MultiWindowPlayground/screenshots/main.png Binary files differnew file mode 100644 index 00000000..d9222003 --- /dev/null +++ b/ui/window/MultiWindowPlayground/screenshots/main.png diff --git a/ui/window/MultiWindowPlayground/settings.gradle b/ui/window/MultiWindowPlayground/settings.gradle new file mode 100644 index 00000000..0a5c310b --- /dev/null +++ b/ui/window/MultiWindowPlayground/settings.gradle @@ -0,0 +1,2 @@ + +include 'Application' diff --git a/ui/window/MultiWindowPlayground/template-params.xml b/ui/window/MultiWindowPlayground/template-params.xml new file mode 100644 index 00000000..6b6e1070 --- /dev/null +++ b/ui/window/MultiWindowPlayground/template-params.xml @@ -0,0 +1,106 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright 2013 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. +--> + +<sample> + <name>MultiWindowPlayground</name> + <group>Android N Preview</group> + <package>com.example.android.multiwindowplayground</package> + + <minSdk>"N"</minSdk> + <targetSdkVersion>"N"</targetSdkVersion> + <compileSdkVersion>"android-N"</compileSdkVersion> + + <strings> + <intro> + <![CDATA[ +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. + ]]> + </intro> + </strings> + + <template src="base-build" /> + + <metadata> + <status>DRAFT</status> + <categories>Android N Preview</categories> + <technologies>Android</technologies> + <languages>Java</languages> + <solutions>Mobile</solutions> + <level>INTERMEDIATE</level> + <!-- Dimensions: 512x512, PNG fomrat --> + <icon>screenshots/icon-web.png</icon> + <!-- Path to screenshots. Use <img> tags for each. --> + <screenshots> + <img>screenshots/main.png</img> + </screenshots> + <api_refs> + <android>android.content.Intent</android> + <android>android.app.ActivityOptions</android> + </api_refs> + <description> +<![CDATA[ +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. +]]> + </description> + + <intro> +<![CDATA[ +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 +]]> + </intro> + </metadata> +</sample> |