diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f3d5e97e..5482e9f1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,7 @@ androidx-activity-compose = "1.10.0" androidx-appcompat = "1.7.0" androidx-compose-bom = "2025.02.00" androidx-compose-ui-test = "1.7.0-alpha08" -androidx-constraintlayout = "2.2.0" +androidx-constraintlayout = "2.2.1" androidx-constraintlayout-compose = "1.1.0" androidx-coordinator-layout = "1.2.0" androidx-corektx = "1.16.0-beta01" @@ -18,10 +18,14 @@ androidx-lifecycle-compose = "2.8.7" androidx-lifecycle-runtime-compose = "2.8.7" androidx-navigation = "2.8.7" androidx-paging = "3.3.6" +androidx-startup-runtime = "1.2.0" androidx-test = "1.6.1" androidx-test-espresso = "3.6.1" -androidx-window = "1.4.0-beta02" +androidx-window = "1.4.0-rc01" +androidx-window-core = "1.4.0-beta02" +androidx-window-java = "1.3.0" androidxHiltNavigationCompose = "1.2.0" +appcompat = "1.7.0" coil = "2.7.0" # @keep compileSdk = "35" @@ -90,7 +94,7 @@ androidx-compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling" } androidx-compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" } androidx-compose-ui-util = { module = "androidx.compose.ui:ui-util" } androidx-compose-ui-viewbinding = { module = "androidx.compose.ui:ui-viewbinding" } -androidx-constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "androidx-constraintlayout" } +androidx-constraintlayout = {module = "androidx.constraintlayout:constraintlayout", version.ref = "androidx-constraintlayout" } androidx-constraintlayout-compose = { module = "androidx.constraintlayout:constraintlayout-compose", version.ref = "androidx-constraintlayout-compose" } androidx-coordinator-layout = { module = "androidx.coordinatorlayout:coordinatorlayout", version.ref = "androidx-coordinator-layout" } androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "androidx-corektx" } @@ -116,6 +120,7 @@ androidx-protolayout = { module = "androidx.wear.protolayout:protolayout", versi androidx-protolayout-expression = { module = "androidx.wear.protolayout:protolayout-expression", version.ref = "protolayout" } androidx-protolayout-material = { module = "androidx.wear.protolayout:protolayout-material", version.ref = "protolayout" } androidx-recyclerview = { module = "androidx.recyclerview:recyclerview", version.ref = "recyclerview" } +androidx-startup-runtime = {module = "androidx.startup:startup-runtime", version.ref = "androidx-startup-runtime" } androidx-test-core = { module = "androidx.test:core", version.ref = "androidx-test" } androidx-test-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "androidx-test-espresso" } androidx-test-runner = "androidx.test:runner:1.6.2" @@ -126,8 +131,11 @@ androidx-tiles-tooling = { module = "androidx.wear.tiles:tiles-tooling", version androidx-tiles-tooling-preview = { module = "androidx.wear.tiles:tiles-tooling-preview", version.ref = "tiles" } androidx-wear = { module = "androidx.wear:wear", version.ref = "wear" } androidx-wear-tooling-preview = { module = "androidx.wear:wear-tooling-preview", version.ref = "wearToolingPreview" } -androidx-window-core = { module = "androidx.window:window-core", version.ref = "androidx-window" } +androidx-window = { module = "androidx.window:window", version.ref = "androidx-window" } +androidx-window-core = { module = "androidx.window:window-core", version.ref = "androidx-window-core" } +androidx-window-java = {module = "androidx.window:window-java", version.ref = "androidx-window-java" } androidx-work-runtime-ktx = "androidx.work:work-runtime-ktx:2.10.0" +appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" } coil-kt-compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" } compose-foundation = { module = "androidx.wear.compose:compose-foundation", version.ref = "wearComposeFoundation" } compose-material = { module = "androidx.wear.compose:compose-material", version.ref = "wearComposeMaterial" } diff --git a/misc/build.gradle.kts b/misc/build.gradle.kts index de867bb8..b2619b9e 100644 --- a/misc/build.gradle.kts +++ b/misc/build.gradle.kts @@ -63,7 +63,12 @@ dependencies { implementation(libs.kotlinx.serialization.json) ksp(libs.hilt.compiler) + implementation(libs.androidx.constraintlayout) implementation(libs.androidx.lifecycle.runtime) + implementation(libs.androidx.window) + implementation(libs.androidx.startup.runtime) + implementation(libs.androidx.window.java) + implementation(libs.appcompat) testImplementation(libs.junit) androidTestImplementation(libs.junit) androidTestImplementation(libs.androidx.test.core) diff --git a/misc/src/main/AndroidManifest.xml b/misc/src/main/AndroidManifest.xml index 0a7a5c6f..2ad7db1e 100644 --- a/misc/src/main/AndroidManifest.xml +++ b/misc/src/main/AndroidManifest.xml @@ -45,6 +45,17 @@ + + + + - \ No newline at end of file + diff --git a/misc/src/main/java/com/example/snippets/ActivityEmbeddingJavaSnippets.java b/misc/src/main/java/com/example/snippets/ActivityEmbeddingJavaSnippets.java new file mode 100644 index 00000000..61dd91ce --- /dev/null +++ b/misc/src/main/java/com/example/snippets/ActivityEmbeddingJavaSnippets.java @@ -0,0 +1,376 @@ +package com.example.snippets; + +import android.app.Activity; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.res.Configuration; +import android.os.Build.VERSION_CODES; +import android.os.Bundle; +import android.view.View; +import android.widget.Button; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.OptIn; +import androidx.annotation.RequiresApi;import androidx.appcompat.app.AppCompatActivity; +import androidx.core.content.ContextCompat; +import androidx.startup.Initializer; +import androidx.window.WindowSdkExtensions; +import androidx.window.core.ExperimentalWindowApi; +import androidx.window.embedding.ActivityEmbeddingController; +import androidx.window.embedding.ActivityFilter; +import androidx.window.embedding.ActivityRule; +import androidx.window.embedding.DividerAttributes; +import androidx.window.embedding.EmbeddingAspectRatio; +import androidx.window.embedding.RuleController; +import androidx.window.embedding.SplitAttributes; +import androidx.window.embedding.SplitAttributes.SplitType; +import androidx.window.embedding.SplitController; +import androidx.window.embedding.SplitPairFilter; +import androidx.window.embedding.SplitPairRule; +import androidx.window.embedding.SplitPinRule; +import androidx.window.embedding.SplitPlaceholderRule; +import androidx.window.embedding.SplitRule; +import androidx.window.java.embedding.SplitControllerCallbackAdapter; +import androidx.window.layout.FoldingFeature; +import androidx.window.layout.WindowMetrics; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +public class ActivityEmbeddingJavaSnippets { + + static class SnippetsActivity extends Activity { + + private Context context; + + @RequiresApi(api=VERSION_CODES.N) + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // [START android_activity_embedding_split_attributes_calculator_java] + if (WindowSdkExtensions.getInstance().getExtensionVersion() >= 2) { + SplitController.getInstance(this).setSplitAttributesCalculator(params -> { + Configuration parentConfiguration = params.getParentConfiguration(); + SplitAttributes.Builder builder = new SplitAttributes.Builder(); + if (parentConfiguration.screenWidthDp >= 840) { + // Side-by-side dual-pane layout for wide displays. + return builder + .setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE) + .build(); + } else if (parentConfiguration.screenHeightDp >= 600) { + // Horizontal split for tall displays. + return builder + .setLayoutDirection(SplitAttributes.LayoutDirection.BOTTOM_TO_TOP) + .build(); + } else { + // Fallback to expand the secondary container. + return builder + .setSplitType(SplitType.SPLIT_TYPE_EXPAND) + .build(); + } + }); + } + // [END android_activity_embedding_split_attributes_calculator_java] + + // [START android_activity_embedding_split_attributes_calculator_tabletop_java] + if (WindowSdkExtensions.getInstance().getExtensionVersion() >= 2) { + SplitController.getInstance(this).setSplitAttributesCalculator(params -> { + String tag = params.getSplitRuleTag(); + WindowMetrics parentWindowMetrics = params.getParentWindowMetrics(); + Configuration parentConfiguration = params.getParentConfiguration(); + List foldingFeatures = + params.getParentWindowLayoutInfo().getDisplayFeatures().stream().filter( + item -> item instanceof FoldingFeature) + .map(item -> (FoldingFeature) item) + .collect(Collectors.toList()); + FoldingFeature feature = foldingFeatures.size() == 1 ? foldingFeatures.get(0) : null; + SplitAttributes.Builder builder = new SplitAttributes.Builder(); + builder.setSplitType(SplitType.SPLIT_TYPE_HINGE); + if (feature != null && feature.isSeparating()) { + // Horizontal slit for tabletop posture. + return builder + .setSplitType(SplitType.SPLIT_TYPE_HINGE) + .setLayoutDirection( + feature.getOrientation() == FoldingFeature.Orientation.HORIZONTAL + ? SplitAttributes.LayoutDirection.BOTTOM_TO_TOP + : SplitAttributes.LayoutDirection.LOCALE) + .build(); + } + else if (parentConfiguration.screenWidthDp >= 840) { + // Side-by-side dual-pane layout for wide displays. + return builder + .setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE) + .build(); + } else { + // No split for tall displays. + return builder + .setSplitType(SplitType.SPLIT_TYPE_EXPAND) + .build(); + } + }); + } + // [END android_activity_embedding_split_attributes_calculator_tabletop_java] + + // [START android_activity_embedding_splitPairFilter_java] + SplitPairFilter splitPairFilter = new SplitPairFilter( + new ComponentName(this, ListActivity.class), + new ComponentName(this, DetailActivity.class), + null + ); + // [END android_activity_embedding_splitPairFilter_java] + + // [START android_activity_embedding_filterSet_java] + Set filterSet = new HashSet<>(); + filterSet.add(splitPairFilter); + // [END android_activity_embedding_filterSet_java] + + // [START android_activity_embedding_splitAttributes_java] + SplitAttributes splitAttributes = new SplitAttributes.Builder() + .setSplitType(SplitAttributes.SplitType.ratio(0.33f)) + .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT) + .build(); + // [END android_activity_embedding_splitAttributes_java] + + // [START android_activity_embedding_splitPairRule_java] + SplitPairRule splitPairRule = new SplitPairRule.Builder(filterSet) + .setDefaultSplitAttributes(splitAttributes) + .setMinWidthDp(840) + .setMinSmallestWidthDp(600) + .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f)) + .setFinishPrimaryWithSecondary(SplitRule.FinishBehavior.NEVER) + .setFinishSecondaryWithPrimary(SplitRule.FinishBehavior.ALWAYS) + .setClearTop(false) + .build(); + // [END android_activity_embedding_splitPairRule_java] + + // [START android_activity_embedding_ruleController_java] + RuleController ruleController = RuleController.getInstance(this); + ruleController.addRule(splitPairRule); + // [END android_activity_embedding_ruleController_java] + + // [START android_activity_embedding_placeholderActivityFilter_java] + ActivityFilter placeholderActivityFilter = new ActivityFilter( + new ComponentName(this, ListActivity.class), + null + ); + // [END android_activity_embedding_placeholderActivityFilter_java] + + // [START android_activity_embedding_placeholderActivityFilterSet_java] + Set placeholderActivityFilterSet = new HashSet<>(); + placeholderActivityFilterSet.add(placeholderActivityFilter); + // [END android_activity_embedding_placeholderActivityFilterSet_java] + + // [START android_activity_embedding_splitPlaceholderRule_java] + SplitPlaceholderRule splitPlaceholderRule = new SplitPlaceholderRule.Builder( + placeholderActivityFilterSet, + new Intent(this, PlaceholderActivity.class) + ).setDefaultSplitAttributes(splitAttributes) + .setMinWidthDp(840) + .setMinSmallestWidthDp(600) + .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f)) + .setFinishPrimaryWithPlaceholder(SplitRule.FinishBehavior.ALWAYS) + .setSticky(false) + .build(); + // [END android_activity_embedding_splitPlaceholderRule_java] + + // [START android_activity_embedding_addRuleSplitPlaceholderRule_java] + ruleController.addRule(splitPlaceholderRule); + // [END android_activity_embedding_addRuleSplitPlaceholderRule_java] + + // [START android_activity_embedding_expandedActivityFilter_java] + ActivityFilter expandedActivityFilter = new ActivityFilter( + new ComponentName(this, ExpandedActivity.class), + null + ); + // [END android_activity_embedding_expandedActivityFilter_java] + + // [START android_activity_embedding_expandedActivityFilterSet_java] + Set expandedActivityFilterSet = new HashSet<>(); + expandedActivityFilterSet.add(expandedActivityFilter); + // [END android_activity_embedding_expandedActivityFilterSet_java] + + // [START android_activity_embedding_activityRule_java] + ActivityRule activityRule = new ActivityRule.Builder( + expandedActivityFilterSet + ).setAlwaysExpand(true) + .build(); + // [END android_activity_embedding_activityRule_java] + + // [START android_activity_embedding_addRuleActivityRule_java] + ruleController.addRule(activityRule); + // [END android_activity_embedding_addRuleActivityRule_java] + + // [START android_activity_embedding_splitAttributesBuilder_java] + SplitAttributes.Builder _splitAttributesBuilder = new SplitAttributes.Builder() + .setSplitType(SplitAttributes.SplitType.ratio(0.33f)) + .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT); + + if (WindowSdkExtensions.getInstance().getExtensionVersion() >= 6) { + _splitAttributesBuilder.setDividerAttributes( + new DividerAttributes.DraggableDividerAttributes.Builder() + .setColor(ContextCompat.getColor(this, R.color.divider_color)) + .setWidthDp(4) + .setDragRange(DividerAttributes.DragRange.DRAG_RANGE_SYSTEM_DEFAULT) + .build() + ); + } + SplitAttributes _splitAttributes = _splitAttributesBuilder.build(); + // [END android_activity_embedding_splitAttributesBuilder_java] + + } + + + // [START android_activity_embedding_isActivityEmbedded_java] + boolean isActivityEmbedded(Activity activity) { + return ActivityEmbeddingController.getInstance(context).isActivityEmbedded(activity); + } + // [END android_activity_embedding_isActivityEmbedded_java] + + } + + + /** @noinspection InnerClassMayBeStatic */ + // [START android_activity_embedding_DetailActivity_class_java] + public class DetailActivity extends AppCompatActivity { + void onOpenSubdetail() { + startActivity(new Intent(this, SubdetailActivity.class)); + } + } + // [END android_activity_embedding_DetailActivity_class_java] + + + /** @noinspection InnerClassMayBeStatic */ + // [START android_activity_embedding_SplitInitializer_class_java] + public class SplitInitializer implements Initializer { + + @NonNull + @Override + public RuleController create(@NonNull Context context) { + RuleController ruleController = RuleController.getInstance(context); + ruleController.setRules( + RuleController.parseRules(context, R.xml.main_split_config) + ); + return ruleController; + } + + @NonNull + @Override + public List>> dependencies() { + return Collections.emptyList(); + } + } + // [END android_activity_embedding_SplitInitializer_class_java] + + + /** + * Function used by snippet. + */ + private Class classForItem(int item) { return Class.class; } + + /** @noinspection InnerClassMayBeStatic */ + // [START android_activity_embedding_MenuActivity_class_java] + public class MenuActivity extends AppCompatActivity{ + void onMenuItemSelected(int selectedMenuItem) { + startActivity(new Intent(this, classForItem(selectedMenuItem))); + } + } + // [END android_activity_embedding_MenuActivity_class_java] + + + /** @noinspection InnerClassMayBeStatic */ + // [START android_activity_embedding_B_class_java] + public class B extends AppCompatActivity{ + void onOpenC() { + startActivity(new Intent(this, C.class)); + } + } + // [END android_activity_embedding_B_class_java] + + + static class SnippetActivity2 extends Activity { + + private Set filterSet = new HashSet<>(); + + // [START android_activity_embedding_onCreate_RuleController_java] + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + RuleController.getInstance(this) + .addRule(new SplitPairRule.Builder(filterSet).build()); + startActivity(new Intent(this, DetailActivity.class)); + } + // [END android_activity_embedding_onCreate_RuleController_java] + + } + + + static class SnippetActivity3 extends AppCompatActivity { + + @OptIn(markerClass = ExperimentalWindowApi.class) + // [START android_activity_embedding_onCreate_SplitControllerCallbackAdapter_java] + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + new SplitControllerCallbackAdapter(SplitController.getInstance(this)) + .addSplitListener( + this, + Runnable::run, + splitInfoList -> { + View layout = getLayoutInflater().inflate(R.layout.activity_main, null); + layout.findViewById(R.id.infoButton).setVisibility( + splitInfoList.isEmpty() ? View.VISIBLE : View.GONE); + }); + } + // [END android_activity_embedding_onCreate_SplitControllerCallbackAdapter_java] + + } + + static class SnippetActivity4 extends Activity { + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + // [START android_activity_embedding_pinButton_java] + Button pinButton = findViewById(R.id.pinButton); + pinButton.setOnClickListener( (view) -> { + SplitAttributes splitAttributes = new SplitAttributes.Builder() + .setSplitType(SplitAttributes.SplitType.ratio(0.66f)) + .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT) + .build(); + + SplitPinRule pinSplitRule = new SplitPinRule.Builder() + .setSticky(true) + .setDefaultSplitAttributes(splitAttributes) + .build(); + + SplitController.getInstance( + getApplicationContext()).pinTopActivityStack(getTaskId(), + pinSplitRule); + }); + // [END android_activity_embedding_pinButton_java] + + // [START android_activity_embedding_getSplitSupportStatus_java] + if (SplitController.getInstance(this).getSplitSupportStatus() == + SplitController.SplitSupportStatus.SPLIT_AVAILABLE) { + // Device supports split activity features. + } + // [END android_activity_embedding_getSplitSupportStatus_java] + + } + } + + + // Classes used by snippets. + static class ListActivity {} + static class SubdetailActivity {} + static class PlaceholderActivity {} + static class ExpandedActivity {} + static class C {} + +} diff --git a/misc/src/main/java/com/example/snippets/ActivityEmbeddingKotlinSnippets.kt b/misc/src/main/java/com/example/snippets/ActivityEmbeddingKotlinSnippets.kt new file mode 100644 index 00000000..a2c90e0f --- /dev/null +++ b/misc/src/main/java/com/example/snippets/ActivityEmbeddingKotlinSnippets.kt @@ -0,0 +1,352 @@ +/* + * Copyright 2025 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 + * + * https://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.snippets + +import android.app.Activity +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.os.Build.VERSION_CODES +import android.os.Bundle +import android.view.View +import android.widget.Button +import androidx.annotation.RequiresApi +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import androidx.startup.Initializer +import androidx.window.WindowSdkExtensions +import androidx.window.core.ExperimentalWindowApi +import androidx.window.embedding.ActivityEmbeddingController +import androidx.window.embedding.ActivityFilter +import androidx.window.embedding.ActivityRule +import androidx.window.embedding.DividerAttributes +import androidx.window.embedding.EmbeddingAspectRatio +import androidx.window.embedding.RuleController +import androidx.window.embedding.SplitAttributes +import androidx.window.embedding.SplitAttributes.SplitType.Companion.SPLIT_TYPE_EXPAND +import androidx.window.embedding.SplitAttributes.SplitType.Companion.SPLIT_TYPE_HINGE +import androidx.window.embedding.SplitController +import androidx.window.embedding.SplitPairFilter +import androidx.window.embedding.SplitPairRule +import androidx.window.embedding.SplitPinRule +import androidx.window.embedding.SplitPlaceholderRule +import androidx.window.embedding.SplitRule +import androidx.window.layout.FoldingFeature +import kotlinx.coroutines.launch + +class ActivityEmbeddingKotlinSnippets { + + class SnippetActivity : Activity() { + + private val context = this + + @RequiresApi(api = VERSION_CODES.N) + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + // [START android_activity_embedding_split_attributes_calculator_kotlin] + if (WindowSdkExtensions.getInstance().extensionVersion >= 2) { + SplitController.getInstance(this).setSplitAttributesCalculator { params -> + val parentConfiguration = params.parentConfiguration + val builder = SplitAttributes.Builder() + return@setSplitAttributesCalculator if (parentConfiguration.screenWidthDp >= 840) { + // Side-by-side dual-pane layout for wide displays. + builder + .setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE) + .build() + } else if (parentConfiguration.screenHeightDp >= 600) { + // Horizontal split for tall displays. + builder + .setLayoutDirection(SplitAttributes.LayoutDirection.BOTTOM_TO_TOP) + .build() + } else { + // Fallback to expand the secondary container. + builder + .setSplitType(SPLIT_TYPE_EXPAND) + .build() + } + } + } + // [END android_activity_embedding_split_attributes_calculator_kotlin] + + // [START android_activity_embedding_split_attributes_calculator_tabletop_kotlin] + if (WindowSdkExtensions.getInstance().extensionVersion >= 2) { + SplitController.getInstance(this).setSplitAttributesCalculator { params -> + val tag = params.splitRuleTag + val parentWindowMetrics = params.parentWindowMetrics + val parentConfiguration = params.parentConfiguration + val foldingFeatures = + params.parentWindowLayoutInfo.displayFeatures.filterIsInstance() + val feature = if (foldingFeatures.size == 1) foldingFeatures[0] else null + val builder = SplitAttributes.Builder() + builder.setSplitType(SPLIT_TYPE_HINGE) + return@setSplitAttributesCalculator if (feature?.isSeparating == true) { + // Horizontal split for tabletop posture. + builder + .setSplitType(SPLIT_TYPE_HINGE) + .setLayoutDirection( + if (feature.orientation == FoldingFeature.Orientation.HORIZONTAL) { + SplitAttributes.LayoutDirection.BOTTOM_TO_TOP + } else { + SplitAttributes.LayoutDirection.LOCALE + } + ) + .build() + } else if (parentConfiguration.screenWidthDp >= 840) { + // Side-by-side dual-pane layout for wide displays. + builder + .setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE) + .build() + } else { + // No split for tall displays. + builder + .setSplitType(SPLIT_TYPE_EXPAND) + .build() + } + } + } + // [END android_activity_embedding_split_attributes_calculator_tabletop_kotlin] + + // [START android_activity_embedding_splitPairFilter_kotlin] + val splitPairFilter = SplitPairFilter( + ComponentName(this, ListActivity::class.java), + ComponentName(this, DetailActivity::class.java), + null + ) + // [END android_activity_embedding_splitPairFilter_kotlin] + + // [START android_activity_embedding_filterSet_kotlin] + val filterSet = setOf(splitPairFilter) + // [END android_activity_embedding_filterSet_kotlin] + + // [START android_activity_embedding_splitAttributes_kotlin] + val splitAttributes: SplitAttributes = SplitAttributes.Builder() + .setSplitType(SplitAttributes.SplitType.ratio(0.33f)) + .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT) + .build() + // [END android_activity_embedding_splitAttributes_kotlin] + + // [START android_activity_embedding_splitPairRule_kotlin] + val splitPairRule = SplitPairRule.Builder(filterSet) + .setDefaultSplitAttributes(splitAttributes) + .setMinWidthDp(840) + .setMinSmallestWidthDp(600) + .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f)) + .setFinishPrimaryWithSecondary(SplitRule.FinishBehavior.NEVER) + .setFinishSecondaryWithPrimary(SplitRule.FinishBehavior.ALWAYS) + .setClearTop(false) + .build() + // [END android_activity_embedding_splitPairRule_kotlin] + + // [START android_activity_embedding_ruleController_kotlin] + val ruleController = RuleController.getInstance(this) + ruleController.addRule(splitPairRule) + // [END android_activity_embedding_ruleController_kotlin] + + // [START android_activity_embedding_placeholderActivityFilter_kotlin] + val placeholderActivityFilter = ActivityFilter( + ComponentName(this, ListActivity::class.java), + null + ) + // [END android_activity_embedding_placeholderActivityFilter_kotlin] + + // [START android_activity_embedding_placeholderActivityFilterSet_kotlin] + val placeholderActivityFilterSet = setOf(placeholderActivityFilter) + // [END android_activity_embedding_placeholderActivityFilterSet_kotlin] + + // [START android_activity_embedding_splitPlaceholderRule_kotlin] + val splitPlaceholderRule = SplitPlaceholderRule.Builder( + placeholderActivityFilterSet, + Intent(context, PlaceholderActivity::class.java) + ).setDefaultSplitAttributes(splitAttributes) + .setMinWidthDp(840) + .setMinSmallestWidthDp(600) + .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f)) + .setFinishPrimaryWithPlaceholder(SplitRule.FinishBehavior.ALWAYS) + .setSticky(false) + .build() + // [END android_activity_embedding_splitPlaceholderRule_kotlin] + + // [START android_activity_embedding_addRuleSplitPlaceholderRule_kotlin] + ruleController.addRule(splitPlaceholderRule) + // [END android_activity_embedding_addRuleSplitPlaceholderRule_kotlin] + + // [START android_activity_embedding_expandedActivityFilter_kotlin] + val expandedActivityFilter = ActivityFilter( + ComponentName(this, ExpandedActivity::class.java), + null + ) + // [END android_activity_embedding_expandedActivityFilter_kotlin] + + // [START android_activity_embedding_expandedActivityFilterSet_kotlin] + val expandedActivityFilterSet = setOf(expandedActivityFilter) + // [END android_activity_embedding_expandedActivityFilterSet_kotlin] + + // [START android_activity_embedding_activityRule_kotlin] + val activityRule = ActivityRule.Builder(expandedActivityFilterSet) + .setAlwaysExpand(true) + .build() + // [END android_activity_embedding_activityRule_kotlin] + + // [START android_activity_embedding_addRuleActivityRule_kotlin] + ruleController.addRule(activityRule) + // [END android_activity_embedding_addRuleActivityRule_kotlin] + + // [START android_activity_embedding_splitAttributesBuilder_kotlin] + val _splitAttributesBuilder: SplitAttributes.Builder = SplitAttributes.Builder() + .setSplitType(SplitAttributes.SplitType.ratio(0.33f)) + .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT) + + if (WindowSdkExtensions.getInstance().extensionVersion >= 6) { + _splitAttributesBuilder.setDividerAttributes( + DividerAttributes.DraggableDividerAttributes.Builder() + .setColor(getColor(R.color.divider_color)) + .setWidthDp(4) + .setDragRange(DividerAttributes.DragRange.DRAG_RANGE_SYSTEM_DEFAULT) + .build() + ) + } + val _splitAttributes: SplitAttributes = _splitAttributesBuilder.build() + // [END android_activity_embedding_splitAttributesBuilder_kotlin] + + // [START android_activity_embedding_isActivityEmbedded_kotlin] + fun isActivityEmbedded(activity: Activity): Boolean { + return ActivityEmbeddingController.getInstance(this).isActivityEmbedded(activity) + } + // [END android_activity_embedding_isActivityEmbedded_kotlin] + } + } + + // [START android_activity_embedding_DetailActivity_class_kotlin] + class DetailActivity : AppCompatActivity() { + fun onOpenSubdetail() { + startActivity(Intent(this, SubdetailActivity::class.java)) + } + } + // [END android_activity_embedding_DetailActivity_class_kotlin] + + // [START android_activity_embedding_SplitInitializer_class_kotlin] + class SplitInitializer : Initializer { + + override fun create(context: Context): RuleController { + return RuleController.getInstance(context).apply { + setRules(RuleController.parseRules(context, R.xml.main_split_config)) + } + } + + override fun dependencies(): List>> { + return emptyList() + } + } + // [END android_activity_embedding_SplitInitializer_class_kotlin] + + /** + * Function used by snippet. + */ + fun classForItem(item: Int): Class<*> { return Class::class.java } + + // [START android_activity_embedding_MenuActivity_class_kotlin] + inner class MenuActivity : AppCompatActivity() { + fun onMenuItemSelected(selectedMenuItem: Int) { + startActivity(Intent(this, classForItem(selectedMenuItem))) + } + } + // [END android_activity_embedding_MenuActivity_class_kotlin] + + // [START android_activity_embedding_B_class_kotlin] + class B : AppCompatActivity() { + fun onOpenC() { + startActivity(Intent(this, C::class.java)) + } + } + // [END android_activity_embedding_B_class_kotlin] + + class SnippetActivity2 : Activity() { + + private val filterSet = HashSet() + + // [START android_activity_embedding_onCreate_RuleController_kotlin] + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + RuleController.getInstance(this) + .addRule(SplitPairRule.Builder(filterSet).build()) + startActivity(Intent(this, DetailActivity::class.java)) + } + // [END android_activity_embedding_onCreate_RuleController_kotlin] + } + + class SplitDeviceActivity : AppCompatActivity() { + + @OptIn(ExperimentalWindowApi::class) + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val splitController = SplitController.getInstance(this) + // [START android_activity_embedding_onCreate_SplitControllerCallbackAdapter_kotlin] + val layout = layoutInflater.inflate(R.layout.activity_main, null) + val view = layout.findViewById(R.id.infoButton) + lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + splitController.splitInfoList(this@SplitDeviceActivity) // The activity instance. + .collect { list -> + view.visibility = if (list.isEmpty()) View.VISIBLE else View.GONE + } + } + } + // [END android_activity_embedding_onCreate_SplitControllerCallbackAdapter_kotlin] + } + } + + class SnippetActivity3 : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + // [START android_activity_embedding_pinButton_kotlin] + val pinButton: Button = findViewById(R.id.pinButton) + pinButton.setOnClickListener { + val splitAttributes: SplitAttributes = SplitAttributes.Builder() + .setSplitType(SplitAttributes.SplitType.ratio(0.66f)) + .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT) + .build() + + val pinSplitRule = SplitPinRule.Builder() + .setSticky(true) + .setDefaultSplitAttributes(splitAttributes) + .build() + + SplitController.getInstance(applicationContext).pinTopActivityStack(taskId, pinSplitRule) + } + // [END android_activity_embedding_pinButton_kotlin] + + // [START android_activity_embedding_getSplitSupportStatus_kotlin] + if (SplitController.getInstance(this).splitSupportStatus == + SplitController.SplitSupportStatus.SPLIT_AVAILABLE + ) { + // Device supports split activity features. + } + // [END android_activity_embedding_getSplitSupportStatus_kotlin] + } + } + + // Classes used by snippets. + class ListActivity + class SubdetailActivity + class PlaceholderActivity + class ExpandedActivity + class C +} diff --git a/misc/src/main/res/layout/activity_main.xml b/misc/src/main/res/layout/activity_main.xml new file mode 100644 index 00000000..59cbc1b4 --- /dev/null +++ b/misc/src/main/res/layout/activity_main.xml @@ -0,0 +1,29 @@ + + + + +