diff --git a/app-catalog/androidres/.gitignore b/app-catalog/androidres/.gitignore
new file mode 100644
index 0000000..06a2b8e
--- /dev/null
+++ b/app-catalog/androidres/.gitignore
@@ -0,0 +1,7 @@
+#Exclude everything
+*
+
+!README.md
+!.gitignore
+!build.gradle
+!src/main/res/placeholder
diff --git a/app-catalog/samples/androidres/README.md b/app-catalog/androidres/README.md
similarity index 89%
rename from app-catalog/samples/androidres/README.md
rename to app-catalog/androidres/README.md
index 75c7109..a3e88e5 100644
--- a/app-catalog/samples/androidres/README.md
+++ b/app-catalog/androidres/README.md
@@ -5,4 +5,4 @@
> 本地带着复制来的 res 参与编译可能会失败,删除即可。
-[回到主页](../../../README.md)
+[回到主页](../../README.md)
diff --git a/app-catalog/samples/androidres/build.gradle b/app-catalog/androidres/build.gradle
similarity index 100%
rename from app-catalog/samples/androidres/build.gradle
rename to app-catalog/androidres/build.gradle
diff --git a/app-catalog/samples/androidres/src/main/res/placeholder b/app-catalog/androidres/src/main/res/placeholder
similarity index 100%
rename from app-catalog/samples/androidres/src/main/res/placeholder
rename to app-catalog/androidres/src/main/res/placeholder
diff --git a/app-catalog/app/proguard-rules.pro b/app-catalog/app/proguard-rules.pro
index 2adf2a4..55ccbde 100644
--- a/app-catalog/app/proguard-rules.pro
+++ b/app-catalog/app/proguard-rules.pro
@@ -49,3 +49,7 @@
-dontwarn org.joda.time.format.DateTimeFormat
-dontwarn org.joda.time.format.DateTimeFormatter
-dontwarn springfox.documentation.spring.web.json.Json
+
+-dontwarn com.squareup.okhttp.Cache
+-dontwarn com.squareup.okhttp.OkHttpClient
+-dontwarn com.squareup.okhttp.OkUrlFactory
diff --git a/app-catalog/samples/androidres/src/main/.gitignore b/app-catalog/samples/androidres/src/main/.gitignore
deleted file mode 100644
index 607f973..0000000
--- a/app-catalog/samples/androidres/src/main/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-**/**
-!res/placeholder
-!.gitignore
diff --git a/app-catalog/samples/wBasis/build.gradle b/app-catalog/samples/wBasis/build.gradle
index 2a1e011..b22e42b 100644
--- a/app-catalog/samples/wBasis/build.gradle
+++ b/app-catalog/samples/wBasis/build.gradle
@@ -55,7 +55,7 @@ dependencies {
implementation libs.androidx.lifecycle.runtime.ktx
implementation libs.androidx.ui.graphics
implementation libs.compose.ui.tooling.preview
- androidTestImplementation libs.compose.ui.test.junit4
- debugImplementation libs.compose.ui.tooling
- debugImplementation libs.compose.ui.test.manifest
+ implementation libs.glide
+
+ implementation 'com.squareup.picasso:picasso:2.4.0'
}
\ No newline at end of file
diff --git a/app-catalog/samples/wBasis/src/main/AndroidManifest.xml b/app-catalog/samples/wBasis/src/main/AndroidManifest.xml
index 883c0c2..20f1063 100644
--- a/app-catalog/samples/wBasis/src/main/AndroidManifest.xml
+++ b/app-catalog/samples/wBasis/src/main/AndroidManifest.xml
@@ -37,7 +37,7 @@
android:label="title_activity_main2"
android:theme="@style/Theme.Android_Kickoff" />
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+ android:theme="@style/Theme.AppCompat.DayNight" />
+
-
-
@@ -356,6 +354,7 @@
android:name=".aidl.PeopleRemoteService"
android:enabled="true"
android:exported="true"
+ android:permission="TODO"
android:process=":remote" />
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app-catalog/samples/wBasis/src/main/java/com/wintmain/wBasis/transition/GridToPagerFragment.java b/app-catalog/samples/wBasis/src/main/java/com/wintmain/wBasis/transition/GridToPagerFragment.java
new file mode 100644
index 0000000..506b539
--- /dev/null
+++ b/app-catalog/samples/wBasis/src/main/java/com/wintmain/wBasis/transition/GridToPagerFragment.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2023-2025 wintmain
+ *
+ * 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.wintmain.wBasis.transition;
+
+import android.os.Bundle;
+import android.transition.TransitionInflater;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnLayoutChangeListener;
+import android.view.ViewGroup;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.app.SharedElementCallback;
+import androidx.fragment.app.Fragment;
+import androidx.recyclerview.widget.RecyclerView;
+import com.wintmain.wBasis.R;
+import com.wintmain.wBasis.transition.adapter.GridAdapter;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A fragment for displaying a grid of images.
+ */
+public class GridToPagerFragment extends Fragment {
+
+ private RecyclerView recyclerView;
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ recyclerView = (RecyclerView) inflater.inflate(R.layout.fragment_grid_to_pager, container,
+ false);
+ recyclerView.setAdapter(new GridAdapter(this));
+
+ prepareTransitions();
+ postponeEnterTransition();
+
+ return recyclerView;
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ scrollToPosition();
+ }
+
+ /**
+ * Scrolls the recycler view to show the last viewed item in the grid. This is important when
+ * navigating back from the grid.
+ */
+ private void scrollToPosition() {
+ recyclerView.addOnLayoutChangeListener(new OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v,
+ int left,
+ int top,
+ int right,
+ int bottom,
+ int oldLeft,
+ int oldTop,
+ int oldRight,
+ int oldBottom) {
+ recyclerView.removeOnLayoutChangeListener(this);
+ final RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
+ View viewAtPosition = layoutManager.findViewByPosition(
+ GridToPagerMainActivity.currentPosition);
+ // Scroll to position if the view for the current position is null (not currently
+ // part of
+ // layout manager children), or it's not completely visible.
+ if (viewAtPosition == null || layoutManager
+ .isViewPartiallyVisible(viewAtPosition, false, true)) {
+ recyclerView.post(() -> layoutManager.scrollToPosition(
+ GridToPagerMainActivity.currentPosition));
+ }
+ }
+ });
+ }
+
+ /**
+ * Prepares the shared element transition to the pager fragment, as well as the other
+ * transitions
+ * that affect the flow.
+ */
+ private void prepareTransitions() {
+ setExitTransition(TransitionInflater.from(getContext())
+ .inflateTransition(R.transition.grid_exit_transition));
+
+ // A similar mapping is set at the ImagePagerFragment with a setEnterSharedElementCallback.
+ setExitSharedElementCallback(
+ new SharedElementCallback() {
+ @Override
+ public void onMapSharedElements(List names,
+ Map sharedElements) {
+ // Locate the ViewHolder for the clicked position.
+ RecyclerView.ViewHolder selectedViewHolder = recyclerView
+ .findViewHolderForAdapterPosition(
+ GridToPagerMainActivity.currentPosition);
+ if (selectedViewHolder == null) {
+ return;
+ }
+
+ // Map the first shared element name to the child ImageView.
+ sharedElements
+ .put(names.get(0),
+ selectedViewHolder.itemView.findViewById(R.id.card_image));
+ }
+ });
+ }
+}
\ No newline at end of file
diff --git a/app-catalog/samples/wBasis/src/main/java/com/wintmain/wBasis/transition/GridToPagerMainActivity.java b/app-catalog/samples/wBasis/src/main/java/com/wintmain/wBasis/transition/GridToPagerMainActivity.java
new file mode 100644
index 0000000..57928f8
--- /dev/null
+++ b/app-catalog/samples/wBasis/src/main/java/com/wintmain/wBasis/transition/GridToPagerMainActivity.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2023-2025 wintmain
+ *
+ * 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.wintmain.wBasis.transition;
+
+import android.os.Bundle;
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.fragment.app.FragmentManager;
+import com.google.android.catalog.framework.annotations.Sample;
+import com.wintmain.wBasis.R;
+
+/**
+ * Grid to pager app's main activity.
+ */
+@Sample(name = "Grid to pager",
+ description = "viewpager & glide 照片展示",
+ tags = {"android-samples", "animation-samples"}
+)
+public class GridToPagerMainActivity extends AppCompatActivity {
+
+ private static final String KEY_CURRENT_POSITION =
+ "com.wintmain.wBasis.gridtopager.key.currentPosition";
+ /**
+ * Holds the current image position to be shared between the grid and the pager fragments. This
+ * position updated when a grid item is clicked, or when paging the pager.
+ *
+ * In this demo app, the position always points to an image index at the {@link
+ * com.wintmain.wBasis.transition.util.ImageData} class.
+ */
+ public static int currentPosition;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_grid_to_pager_main);
+ if (savedInstanceState != null) {
+ currentPosition = savedInstanceState.getInt(KEY_CURRENT_POSITION, 0);
+ // Return here to prevent adding additional GridFragments when changing orientation.
+ return;
+ }
+ FragmentManager fragmentManager = getSupportFragmentManager();
+ fragmentManager
+ .beginTransaction()
+ .add(R.id.fragment_container, new GridToPagerFragment(),
+ GridToPagerFragment.class.getSimpleName())
+ .commit();
+ }
+
+ @Override
+ protected void onSaveInstanceState(@NonNull Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putInt(KEY_CURRENT_POSITION, currentPosition);
+ }
+
+}
diff --git a/app-catalog/samples/wBasis/src/main/java/com/wintmain/wBasis/transition/SceneTransitionBasicDetailActivity.java b/app-catalog/samples/wBasis/src/main/java/com/wintmain/wBasis/transition/SceneTransitionBasicDetailActivity.java
new file mode 100644
index 0000000..9613376
--- /dev/null
+++ b/app-catalog/samples/wBasis/src/main/java/com/wintmain/wBasis/transition/SceneTransitionBasicDetailActivity.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2023-2025 wintmain
+ *
+ * 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.wintmain.wBasis.transition;
+
+import android.os.Build;
+import android.os.Bundle;
+import android.transition.Transition;
+import android.widget.ImageView;
+import android.widget.TextView;
+import androidx.annotation.RequiresApi;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.view.ViewCompat;
+import com.squareup.picasso.Picasso;
+import com.wintmain.wBasis.R;
+import com.wintmain.wBasis.transition.util.SceneTransitionBasicItem;
+
+/**
+ * Our secondary Activity which is launched from {@link SceneTransitionBasicMainActivity}.
+ * Has a simple detail UI which has a large banner image, title and body text.
+ */
+public class SceneTransitionBasicDetailActivity extends AppCompatActivity {
+
+ // Extra name for the ID parameter
+ public static final String EXTRA_PARAM_ID = "detail:_id";
+
+ // View name of the header image. Used for activity scene transitions
+ public static final String VIEW_NAME_HEADER_IMAGE = "detail:header:image";
+
+ // View name of the header title. Used for activity scene transitions
+ public static final String VIEW_NAME_HEADER_TITLE = "detail:header:title";
+
+ private ImageView mHeaderImageView;
+ private TextView mHeaderTitle;
+
+ private SceneTransitionBasicItem mSceneTransitionBasicItem;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_scene_transition_basic_details);
+
+ // Retrieve the correct Item instance, using the ID provided in the Intent
+ mSceneTransitionBasicItem = SceneTransitionBasicItem.getItem(
+ getIntent().getIntExtra(EXTRA_PARAM_ID, 0));
+
+ mHeaderImageView = findViewById(R.id.imageview_header);
+ mHeaderTitle = findViewById(R.id.textview_title);
+
+ // BEGIN_INCLUDE(detail_set_view_name)
+ /*
+ * Set the name of the view's which will be transition to, using the static values above.
+ * This could be done in the layout XML, but exposing it via static variables allows easy
+ * querying from other Activities
+ */
+ ViewCompat.setTransitionName(mHeaderImageView, VIEW_NAME_HEADER_IMAGE);
+ ViewCompat.setTransitionName(mHeaderTitle, VIEW_NAME_HEADER_TITLE);
+ // END_INCLUDE(detail_set_view_name)
+
+ loadItem();
+ }
+
+ private void loadItem() {
+ // Set the title TextView to the item's name and author
+ mHeaderTitle.setText(getString(R.string.image_header, mSceneTransitionBasicItem.getName(),
+ mSceneTransitionBasicItem.getAuthor()));
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && addTransitionListener()) {
+ // If we're running on Lollipop and we have added a listener to the shared element
+ // transition, load the thumbnail. The listener will load the full-size image when
+ // the transition is complete.
+ loadThumbnail();
+ } else {
+ // If all other cases we should just load the full-size image now
+ loadFullSizeImage();
+ }
+ }
+
+ /**
+ * Load the item's thumbnail image into our {@link ImageView}.
+ */
+ private void loadThumbnail() {
+ Picasso.with(mHeaderImageView.getContext())
+ .load(mSceneTransitionBasicItem.getThumbnailUrl())
+ .noFade()
+ .into(mHeaderImageView);
+ }
+
+ /**
+ * Load the item's full-size image into our {@link ImageView}.
+ */
+ private void loadFullSizeImage() {
+ Picasso.with(mHeaderImageView.getContext())
+ .load(mSceneTransitionBasicItem.getPhotoUrl())
+ .noFade()
+ .noPlaceholder()
+ .into(mHeaderImageView);
+ }
+
+ /**
+ * Try and add a {@link Transition.TransitionListener} to the entering shared element
+ * {@link Transition}. We do this so that we can load the full-size image after the transition
+ * has completed.
+ *
+ * @return true if we were successful in adding a listener to the enter transition
+ */
+ @RequiresApi(21)
+ private boolean addTransitionListener() {
+ final Transition transition = getWindow().getSharedElementEnterTransition();
+
+ if (transition != null) {
+ // There is an entering shared element transition so add a listener to it
+ transition.addListener(new Transition.TransitionListener() {
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ // As the transition has ended, we can now load the full-size image
+ loadFullSizeImage();
+
+ // Make sure we remove ourselves as a listener
+ transition.removeListener(this);
+ }
+
+ @Override
+ public void onTransitionStart(Transition transition) {
+ // No-op
+ }
+
+ @Override
+ public void onTransitionCancel(Transition transition) {
+ // Make sure we remove ourselves as a listener
+ transition.removeListener(this);
+ }
+
+ @Override
+ public void onTransitionPause(Transition transition) {
+ // No-op
+ }
+
+ @Override
+ public void onTransitionResume(Transition transition) {
+ // No-op
+ }
+ });
+ return true;
+ }
+
+ // If we reach here then we have not added a listener
+ return false;
+ }
+
+}
\ No newline at end of file
diff --git a/app-catalog/samples/wBasis/src/main/java/com/wintmain/wBasis/transition/SceneTransitionBasicMainActivity.java b/app-catalog/samples/wBasis/src/main/java/com/wintmain/wBasis/transition/SceneTransitionBasicMainActivity.java
new file mode 100644
index 0000000..e08815b
--- /dev/null
+++ b/app-catalog/samples/wBasis/src/main/java/com/wintmain/wBasis/transition/SceneTransitionBasicMainActivity.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2023-2025 wintmain
+ *
+ * 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.wintmain.wBasis.transition;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.*;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.app.ActivityCompat;
+import androidx.core.app.ActivityOptionsCompat;
+import androidx.core.util.Pair;
+import com.google.android.catalog.framework.annotations.Sample;
+import com.squareup.picasso.Picasso;
+import com.wintmain.wBasis.R;
+import com.wintmain.wBasis.transition.util.SceneTransitionBasicItem;
+
+/**
+ * Displays a grid of items which an image and title.
+ * When the user clicks on an item, {@link SceneTransitionBasicDetailActivity} is launched,
+ * using the Activity Scene Transitions framework to animatedly do so.
+ */
+@Sample(name = "ActivitySceneTransitionBaic",
+ description = "Activity Scene的变化",
+ tags = {"android-samples", "animation-samples"}
+)
+public class SceneTransitionBasicMainActivity extends AppCompatActivity {
+
+ private final AdapterView.OnItemClickListener mOnItemClickListener
+ = new AdapterView.OnItemClickListener() {
+
+ /**
+ * Called when an item in the {@link android.widget.GridView} is clicked. Here will launch
+ * the {@link SceneTransitionBasicDetailActivity}, using the Scene Transition animation
+ * functionality.
+ */
+ @Override
+ public void onItemClick(AdapterView> adapterView, View view, int position, long id) {
+ SceneTransitionBasicItem sceneTransitionBasicItem =
+ (SceneTransitionBasicItem) adapterView.getItemAtPosition(position);
+
+ // Construct an Intent as normal
+ Intent intent = new Intent(SceneTransitionBasicMainActivity.this,
+ SceneTransitionBasicDetailActivity.class);
+ intent.putExtra(SceneTransitionBasicDetailActivity.EXTRA_PARAM_ID,
+ sceneTransitionBasicItem.getId());
+
+ // BEGIN_INCLUDE(start_activity)
+ /*
+ * Now create an {@link android.app.ActivityOptions} instance using the
+ * {@link ActivityOptionsCompat#makeSceneTransitionAnimation(Activity, Pair[])} factory
+ * method.
+ */
+ @SuppressWarnings("unchecked")
+ ActivityOptionsCompat activityOptions =
+ ActivityOptionsCompat.makeSceneTransitionAnimation(
+ SceneTransitionBasicMainActivity.this,
+
+ // Now we provide a list of Pair items which contain the view we can
+ // transitioning from, and the name of the view it is transitioning to,
+ // in the launched activity
+ new Pair<>(view.findViewById(R.id.imageview_item),
+ SceneTransitionBasicDetailActivity.VIEW_NAME_HEADER_IMAGE),
+ new Pair<>(view.findViewById(R.id.textview_name),
+ SceneTransitionBasicDetailActivity.VIEW_NAME_HEADER_TITLE));
+
+ // Now we can start the Activity, providing the activity options as a bundle
+ ActivityCompat.startActivity(SceneTransitionBasicMainActivity.this, intent,
+ activityOptions.toBundle());
+ // END_INCLUDE(start_activity)
+ }
+ };
+
+ /**
+ * {@link android.widget.BaseAdapter} which displays items.
+ */
+ private class GridAdapter extends BaseAdapter {
+
+ @Override
+ public int getCount() {
+ return SceneTransitionBasicItem.SceneTransitionBasicITEMS.length;
+ }
+
+ @Override
+ public SceneTransitionBasicItem getItem(int position) {
+ return SceneTransitionBasicItem.SceneTransitionBasicITEMS[position];
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return getItem(position).getId();
+ }
+
+ @Override
+ public View getView(int position, View view, ViewGroup viewGroup) {
+ if (view == null) {
+ view = getLayoutInflater().inflate(R.layout.activity_scene_transition_basic_grid_item, viewGroup, false);
+ }
+
+ final SceneTransitionBasicItem item = getItem(position);
+
+ // Load the thumbnail image
+ ImageView image = (ImageView) view.findViewById(R.id.imageview_item);
+ Picasso.with(image.getContext()).load(item.getThumbnailUrl()).into(image);
+
+ // Set the TextView's contents
+ TextView name = (TextView) view.findViewById(R.id.textview_name);
+ name.setText(item.getName());
+
+ return view;
+ }
+ }
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_scene_transition_basic_grid);
+
+ GridView gridView = findViewById(R.id.grid);
+ gridView.setOnItemClickListener(mOnItemClickListener);
+
+ // 没有adapter,页面是空白的
+ GridAdapter mAdapter = new GridAdapter();
+ gridView.setAdapter(mAdapter);
+ }
+}
diff --git a/app-catalog/samples/wBasis/src/main/java/com/wintmain/wBasis/transition/adapter/GridAdapter.java b/app-catalog/samples/wBasis/src/main/java/com/wintmain/wBasis/transition/adapter/GridAdapter.java
new file mode 100644
index 0000000..bd22bdb
--- /dev/null
+++ b/app-catalog/samples/wBasis/src/main/java/com/wintmain/wBasis/transition/adapter/GridAdapter.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2023-2025 wintmain
+ *
+ * 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.wintmain.wBasis.transition.adapter;
+
+import android.graphics.drawable.Drawable;
+import android.transition.TransitionSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import androidx.recyclerview.widget.RecyclerView;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.RequestManager;
+import com.bumptech.glide.load.DataSource;
+import com.bumptech.glide.load.engine.GlideException;
+import com.bumptech.glide.request.RequestListener;
+import com.bumptech.glide.request.target.Target;
+import com.wintmain.wBasis.R;
+import com.wintmain.wBasis.transition.GridToPagerMainActivity;
+import com.wintmain.wBasis.transition.fragment.ImagePagerFragment;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import static com.wintmain.wBasis.transition.util.ImageData.IMAGE_DRAWABLES;
+
+/**
+ * A fragment for displaying a grid of images.
+ */
+public class GridAdapter extends RecyclerView.Adapter {
+ private final RequestManager requestManager;
+ private final ViewHolderListener viewHolderListener;
+
+ /**
+ * Constructs a new grid adapter for the given {@link Fragment}.
+ */
+ public GridAdapter(Fragment fragment) {
+ this.requestManager = Glide.with(fragment);
+ this.viewHolderListener = new ViewHolderListenerImpl(fragment);
+ }
+
+ @NonNull
+ @Override
+ public ImageViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ View view = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.view_image_card, parent, false);
+ return new ImageViewHolder(view, requestManager, viewHolderListener);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull ImageViewHolder holder, int position) {
+ holder.onBind();
+ }
+
+ @Override
+ public int getItemCount() {
+ return IMAGE_DRAWABLES.length;
+ }
+
+ /**
+ * A listener that is attached to all ViewHolders to handle image loading events and clicks.
+ */
+ private interface ViewHolderListener {
+
+ void onLoadCompleted(ImageView view, int adapterPosition);
+
+ void onItemClicked(View view, int adapterPosition);
+ }
+
+ /**
+ * Default {@link ViewHolderListener} implementation.
+ */
+ private static class ViewHolderListenerImpl implements ViewHolderListener {
+
+ private Fragment fragment;
+ private AtomicBoolean enterTransitionStarted;
+
+ ViewHolderListenerImpl(Fragment fragment) {
+ this.fragment = fragment;
+ this.enterTransitionStarted = new AtomicBoolean();
+ }
+
+ @Override
+ public void onLoadCompleted(ImageView view, int position) {
+ // Call startPostponedEnterTransition only when the 'selected' image loading is
+ // completed.
+ if (GridToPagerMainActivity.currentPosition != position) {
+ return;
+ }
+ if (enterTransitionStarted.getAndSet(true)) {
+ return;
+ }
+ fragment.startPostponedEnterTransition();
+ }
+
+ /**
+ * Handles a view click by setting the current position to the given {@code position} and
+ * starting a {@link ImagePagerFragment} which displays the image at the position.
+ *
+ * @param view the clicked {@link ImageView} (the shared element view will be
+ * re-mapped at the
+ * GridFragment's SharedElementCallback)
+ * @param position the selected view position
+ */
+ @Override
+ public void onItemClicked(View view, int position) {
+ // Update the position.
+ GridToPagerMainActivity.currentPosition = position;
+
+ // Exclude the clicked card from the exit transition (e.g. the card will disappear
+ // immediately
+ // instead of fading out with the rest to prevent an overlapping animation of fade
+ // and move).
+ ((TransitionSet) fragment.getExitTransition()).excludeTarget(view, true);
+
+ ImageView transitioningView = view.findViewById(R.id.card_image);
+ fragment.getFragmentManager()
+ .beginTransaction()
+ .setReorderingAllowed(true) // Optimize for shared element transition
+ .addSharedElement(transitioningView, transitioningView.getTransitionName())
+ .replace(R.id.fragment_container, new ImagePagerFragment(),
+ ImagePagerFragment.class.getSimpleName())
+ .addToBackStack(null)
+ .commit();
+ }
+ }
+
+ /**
+ * ViewHolder for the grid's images.
+ */
+ static class ImageViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
+
+ private final ImageView image;
+ private final RequestManager requestManager;
+ private final ViewHolderListener viewHolderListener;
+
+ public ImageViewHolder(@NonNull View itemView, RequestManager requestManager,
+ ViewHolderListener viewHolderListener) {
+ super(itemView);
+
+ this.image = itemView.findViewById(R.id.card_image);
+ this.requestManager = requestManager;
+ this.viewHolderListener = viewHolderListener;
+ itemView.findViewById(R.id.card_view).setOnClickListener(this);
+ }
+
+ /**
+ * Binds this view holder to the given adapter position.
+ *
+ * The binding will load the image into the image view, as well as set its transition
+ * name for
+ * later.
+ */
+ void onBind() {
+ int adapterPosition = getAdapterPosition();
+ setImage(adapterPosition);
+ // Set the string value of the image resource as the unique transition name for the
+ // view.
+ image.setTransitionName(String.valueOf(IMAGE_DRAWABLES[adapterPosition]));
+ }
+
+ void setImage(final int adapterPosition) {
+ // Load the image with Glide to prevent OOM error when the image drawables are very
+ // large.
+ requestManager
+ .load(IMAGE_DRAWABLES[adapterPosition])
+ .listener(new RequestListener() {
+ @Override
+ public boolean onLoadFailed(@Nullable GlideException e, Object model,
+ Target target, boolean isFirstResource) {
+ viewHolderListener.onLoadCompleted(image, adapterPosition);
+ return false;
+ }
+
+ @Override
+ public boolean onResourceReady(Drawable resource, Object model,
+ Target
+ target, DataSource dataSource, boolean isFirstResource) {
+ viewHolderListener.onLoadCompleted(image, adapterPosition);
+ return false;
+ }
+ }).into(image);
+ }
+
+
+ @Override
+ public void onClick(View v) {
+ // Let the listener start the ImagePagerFragment.
+ viewHolderListener.onItemClicked(v, getAdapterPosition());
+ }
+ }
+}
diff --git a/app-catalog/samples/wBasis/src/main/java/com/wintmain/wBasis/transition/adapter/ImagePagerAdapter.java b/app-catalog/samples/wBasis/src/main/java/com/wintmain/wBasis/transition/adapter/ImagePagerAdapter.java
new file mode 100644
index 0000000..6a97bce
--- /dev/null
+++ b/app-catalog/samples/wBasis/src/main/java/com/wintmain/wBasis/transition/adapter/ImagePagerAdapter.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2023-2025 wintmain
+ *
+ * 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.wintmain.wBasis.transition.adapter;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentStatePagerAdapter;
+import com.wintmain.wBasis.transition.fragment.ImageFragment;
+
+import static com.wintmain.wBasis.transition.util.ImageData.IMAGE_DRAWABLES;
+
+public class ImagePagerAdapter extends FragmentStatePagerAdapter {
+
+ // public ImagePagerAdapter(@NonNull FragmentManager fm) {
+// super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
+// }
+ public ImagePagerAdapter(Fragment fragment) {
+ // Note: Initialize with the child fragment manager.
+ super(fragment.getChildFragmentManager(), BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
+ }
+
+ @NonNull
+ @Override
+ public Fragment getItem(int position) {
+ return ImageFragment.newInstance(IMAGE_DRAWABLES[position]);
+ }
+
+ @Override
+ public int getCount() {
+ return IMAGE_DRAWABLES.length;
+ }
+}
diff --git a/app-catalog/samples/wBasis/src/main/java/com/wintmain/wBasis/transition/fragment/ImageFragment.java b/app-catalog/samples/wBasis/src/main/java/com/wintmain/wBasis/transition/fragment/ImageFragment.java
new file mode 100644
index 0000000..65d937a
--- /dev/null
+++ b/app-catalog/samples/wBasis/src/main/java/com/wintmain/wBasis/transition/fragment/ImageFragment.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2023-2025 wintmain
+ *
+ * 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.wintmain.wBasis.transition.fragment;
+
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.DataSource;
+import com.bumptech.glide.load.engine.GlideException;
+import com.bumptech.glide.request.RequestListener;
+import com.bumptech.glide.request.target.Target;
+import com.wintmain.wBasis.R;
+
+/**
+ * A fragment for displaying an image.
+ */
+public class ImageFragment extends Fragment {
+ private static final String KEY_IMAGE_RES = "com.wintmain.wBasis.gridtopager.key.imageRes";
+
+ public static ImageFragment newInstance(@DrawableRes int drawableRes) {
+ ImageFragment fragment = new ImageFragment();
+ Bundle argument = new Bundle();
+ argument.putInt(KEY_IMAGE_RES, drawableRes);
+ fragment.setArguments(argument);
+ return fragment;
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ final View view = inflater.inflate(R.layout.fragment_grid_image_view, container, false);
+ Bundle arguments = getArguments();
+ @DrawableRes int imageRes = arguments.getInt(KEY_IMAGE_RES);
+ // Just like we do when binding views at the grid, we set the transition name to be the
+ // string
+ // value of the image res.
+ view.findViewById(R.id.image).setTransitionName(String.valueOf(imageRes));
+
+ // Load the image with Glide to prevent OOM error when the image drawables are very large.
+ Glide.with(this)
+ .load(imageRes)
+ .listener(new RequestListener() {
+ @Override
+ public boolean onLoadFailed(@Nullable GlideException e, Object model,
+ Target target, boolean isFirstResource) {
+ // The postponeEnterTransition is called on the parent
+ // ImagePagerFragment, so the
+ // startPostponedEnterTransition() should also be called on it to get the
+ // transition
+ // going in case of a failure.
+ getParentFragment().startPostponedEnterTransition();
+ return false;
+ }
+
+ @Override
+ public boolean onResourceReady(Drawable resource, Object model,
+ Target target, DataSource dataSource,
+ boolean isFirstResource) {
+ // The postponeEnterTransition is called on the parent
+ // ImagePagerFragment, so the
+ // startPostponedEnterTransition() should also be called on it to get the
+ // transition
+ // going when the image is ready.
+ getParentFragment().startPostponedEnterTransition();
+ return false;
+ }
+ }).into((android.widget.ImageView) view.findViewById(R.id.image));
+
+ return view;
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ }
+}
diff --git a/app-catalog/samples/wBasis/src/main/java/com/wintmain/wBasis/transition/fragment/ImagePagerFragment.java b/app-catalog/samples/wBasis/src/main/java/com/wintmain/wBasis/transition/fragment/ImagePagerFragment.java
new file mode 100644
index 0000000..7753ea1
--- /dev/null
+++ b/app-catalog/samples/wBasis/src/main/java/com/wintmain/wBasis/transition/fragment/ImagePagerFragment.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2023-2025 wintmain
+ *
+ * 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.wintmain.wBasis.transition.fragment;
+
+import android.os.Bundle;
+import android.transition.Transition;
+import android.transition.TransitionInflater;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.app.SharedElementCallback;
+import androidx.fragment.app.Fragment;
+import androidx.viewpager.widget.ViewPager;
+import com.wintmain.wBasis.R;
+import com.wintmain.wBasis.transition.GridToPagerMainActivity;
+import com.wintmain.wBasis.transition.adapter.ImagePagerAdapter;
+
+import java.util.List;
+import java.util.Map;
+
+public class ImagePagerFragment extends Fragment {
+ private ViewPager viewPager;
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ viewPager = (ViewPager) inflater.inflate(R.layout.fragment_grid_pager_view, container, false);
+ viewPager.setAdapter(new ImagePagerAdapter(this));
+ // Set the current position and add a listener that will update the selection coordinator
+ // when
+ // paging the images.
+ viewPager.setCurrentItem(GridToPagerMainActivity.currentPosition);
+
+ viewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
+ @Override
+ public void onPageSelected(int position) {
+ GridToPagerMainActivity.currentPosition = position;
+ }
+ });
+
+ prepareSharedElementTransition();
+
+ return viewPager;
+ }
+
+ /**
+ * Prepares the shared element transition from and back to the grid fragment.
+ */
+ private void prepareSharedElementTransition() {
+ Transition transition =
+ TransitionInflater.from(getContext())
+ .inflateTransition(R.transition.image_shared_element_transition);
+ setSharedElementEnterTransition(transition);
+
+ // A similar mapping is set at the GridFragment with a setExitSharedElementCallback.
+ setEnterSharedElementCallback(
+ new SharedElementCallback() {
+ @Override
+ public void onMapSharedElements(List names,
+ Map sharedElements) {
+ // Locate the image view at the primary fragment (the ImageFragment that
+ // is currently
+ // visible). To locate the fragment, call instantiateItem with the
+ // selection position.
+ // At this stage, the method will simply return the fragment at the
+ // position and will
+ // not create a new one.
+ Fragment currentFragment = (Fragment) viewPager.getAdapter()
+ .instantiateItem(viewPager,
+ GridToPagerMainActivity.currentPosition);
+ View view = currentFragment.getView();
+ if (view == null) {
+ return;
+ }
+
+ // Map the first shared element name to the child ImageView.
+ sharedElements.put(names.get(0), view.findViewById(R.id.image));
+ }
+ });
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ }
+}
diff --git a/app-catalog/samples/wBasis/src/main/java/com/wintmain/wBasis/transition/util/ImageData.java b/app-catalog/samples/wBasis/src/main/java/com/wintmain/wBasis/transition/util/ImageData.java
new file mode 100644
index 0000000..baa10d8
--- /dev/null
+++ b/app-catalog/samples/wBasis/src/main/java/com/wintmain/wBasis/transition/util/ImageData.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2023-2025 wintmain
+ *
+ * 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.wintmain.wBasis.transition.util;
+
+import androidx.annotation.DrawableRes;
+import com.wintmain.wBasis.R;
+
+/**
+ * Holds the image resource references used by the grid and the pager fragments.
+ */
+public abstract class ImageData {
+
+ // Image assets (free for commercial use, no attribution required, from pixabay.com)
+ @DrawableRes
+ public static final int[] IMAGE_DRAWABLES = {
+ R.drawable.icon_badminton,
+ R.drawable.icon_baseball,
+ R.drawable.icon_basketball,
+ R.drawable.icon_bowling,
+ R.drawable.icon_cycling,
+ R.drawable.icon_golf,
+ R.drawable.icon_running,
+ R.drawable.icon_soccer,
+ R.drawable.icon_swimming,
+ R.drawable.icon_tabletennis,
+ R.drawable.icon_tennis,
+ R.drawable.img_badminton,
+ R.drawable.img_baseball,
+ R.drawable.img_basketball,
+ R.drawable.img_bowling,
+ R.drawable.img_cycling,
+ R.drawable.img_golf,
+ R.drawable.img_running,
+ R.drawable.img_soccer,
+ R.drawable.img_swimming,
+ R.drawable.img_tabletennis,
+ R.drawable.img_tennis,
+ };
+}
diff --git a/app-catalog/samples/wBasis/src/main/java/com/wintmain/wBasis/transition/util/SceneTransitionBasicItem.java b/app-catalog/samples/wBasis/src/main/java/com/wintmain/wBasis/transition/util/SceneTransitionBasicItem.java
new file mode 100644
index 0000000..ff8a83f
--- /dev/null
+++ b/app-catalog/samples/wBasis/src/main/java/com/wintmain/wBasis/transition/util/SceneTransitionBasicItem.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2023-2025 wintmain
+ *
+ * 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.wintmain.wBasis.transition.util;
+
+/**
+ * Represents an Item in our application. Each item has a name, id, full size image url and
+ * thumbnail url.
+ */
+public class SceneTransitionBasicItem {
+
+ private static final String LARGE_BASE_URL = "https://picsum.photos/600/400?image=";
+ private static final String THUMB_BASE_URL = "https://picsum.photos/100/100?image=";
+
+ public static SceneTransitionBasicItem[] SceneTransitionBasicITEMS = new SceneTransitionBasicItem[]{
+ new SceneTransitionBasicItem("Flying in the Light", "Romain Guy", "1"),
+ new SceneTransitionBasicItem("Caterpillar", "Romain Guy", "10"),
+ new SceneTransitionBasicItem("Look Me in the Eye", "Romain Guy", "100"),
+ new SceneTransitionBasicItem("Flamingo", "Romain Guy", "1000"),
+ new SceneTransitionBasicItem("Rainbow", "Romain Guy", "1001"),
+ new SceneTransitionBasicItem("Over there", "Romain Guy", "1002"),
+ new SceneTransitionBasicItem("Jelly Fish 2", "Romain Guy", "1003"),
+ new SceneTransitionBasicItem("Lone Pine Sunset", "Romain Guy", "1004"),
+ };
+ private final String mName;
+ private final String mAuthor;
+ private final String mFileName;
+ SceneTransitionBasicItem(String name, String author, String fileName) {
+ mName = name;
+ mAuthor = author;
+ mFileName = fileName;
+ }
+
+ public static SceneTransitionBasicItem getItem(int id) {
+ for (SceneTransitionBasicItem sceneTransitionBasicItem : SceneTransitionBasicITEMS) {
+ if (sceneTransitionBasicItem.getId() == id) {
+ return sceneTransitionBasicItem;
+ }
+ }
+ return null;
+ }
+
+ public int getId() {
+ return mName.hashCode() + mFileName.hashCode();
+ }
+
+ public String getAuthor() {
+ return mAuthor;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public String getPhotoUrl() {
+ return LARGE_BASE_URL + mFileName;
+ }
+
+ public String getThumbnailUrl() {
+ return THUMB_BASE_URL + mFileName;
+ }
+
+}
diff --git a/app-catalog/samples/wBasis/src/main/java/com/wintmain/wBasis/transition/util/SquareFrameLayout.java b/app-catalog/samples/wBasis/src/main/java/com/wintmain/wBasis/transition/util/SquareFrameLayout.java
new file mode 100644
index 0000000..efb1980
--- /dev/null
+++ b/app-catalog/samples/wBasis/src/main/java/com/wintmain/wBasis/transition/util/SquareFrameLayout.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2023-2025 wintmain
+ *
+ * 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.wintmain.wBasis.transition.util;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+/**
+ * {@link android.widget.FrameLayout} which forces itself to be laid out as square.
+ */
+public class SquareFrameLayout extends FrameLayout {
+
+ public SquareFrameLayout(Context context) {
+ this(context, null);
+ }
+
+ public SquareFrameLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public SquareFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+ final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
+
+ if (widthSize == 0 && heightSize == 0) {
+ // If there are no constraints on size, let FrameLayout measure
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ // Now use the smallest of the measured dimensions for both dimensions
+ final int minSize = Math.min(getMeasuredWidth(), getMeasuredHeight());
+ setMeasuredDimension(minSize, minSize);
+ return;
+ }
+
+ final int size;
+ if (widthSize == 0 || heightSize == 0) {
+ // If one of the dimensions has no restriction on size, set both dimensions to be the
+ // on that does
+ size = Math.max(widthSize, heightSize);
+ } else {
+ // Both dimensions have restrictions on size, set both dimensions to be the
+ // smallest of the two
+ size = Math.min(widthSize, heightSize);
+ }
+
+ final int newMeasureSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
+ super.onMeasure(newMeasureSpec, newMeasureSpec);
+ }
+}
diff --git a/app-catalog/samples/wBasis/src/main/res-transition/layout/activity_grid_to_pager_main.xml b/app-catalog/samples/wBasis/src/main/res-transition/layout/activity_grid_to_pager_main.xml
new file mode 100644
index 0000000..bb622d1
--- /dev/null
+++ b/app-catalog/samples/wBasis/src/main/res-transition/layout/activity_grid_to_pager_main.xml
@@ -0,0 +1,20 @@
+
+
+
diff --git a/app-catalog/samples/wBasis/src/main/res-transition/layout/activity_scene_transition_basic_details.xml b/app-catalog/samples/wBasis/src/main/res-transition/layout/activity_scene_transition_basic_details.xml
new file mode 100644
index 0000000..bc89006
--- /dev/null
+++ b/app-catalog/samples/wBasis/src/main/res-transition/layout/activity_scene_transition_basic_details.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app-catalog/samples/wBasis/src/main/res-transition/layout/activity_scene_transition_basic_grid.xml b/app-catalog/samples/wBasis/src/main/res-transition/layout/activity_scene_transition_basic_grid.xml
new file mode 100644
index 0000000..623c145
--- /dev/null
+++ b/app-catalog/samples/wBasis/src/main/res-transition/layout/activity_scene_transition_basic_grid.xml
@@ -0,0 +1,26 @@
+
+
+
diff --git a/app-catalog/samples/wBasis/src/main/res-transition/layout/activity_scene_transition_basic_grid_item.xml b/app-catalog/samples/wBasis/src/main/res-transition/layout/activity_scene_transition_basic_grid_item.xml
new file mode 100644
index 0000000..98d55d8
--- /dev/null
+++ b/app-catalog/samples/wBasis/src/main/res-transition/layout/activity_scene_transition_basic_grid_item.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app-catalog/samples/wBasis/src/main/res-transition/layout/fragment_grid_image_view.xml b/app-catalog/samples/wBasis/src/main/res-transition/layout/fragment_grid_image_view.xml
new file mode 100644
index 0000000..1a11c07
--- /dev/null
+++ b/app-catalog/samples/wBasis/src/main/res-transition/layout/fragment_grid_image_view.xml
@@ -0,0 +1,22 @@
+
+
+
\ No newline at end of file
diff --git a/app-catalog/samples/wBasis/src/main/res-transition/layout/fragment_grid_pager_view.xml b/app-catalog/samples/wBasis/src/main/res-transition/layout/fragment_grid_pager_view.xml
new file mode 100644
index 0000000..2dc44b2
--- /dev/null
+++ b/app-catalog/samples/wBasis/src/main/res-transition/layout/fragment_grid_pager_view.xml
@@ -0,0 +1,20 @@
+
+
+
\ No newline at end of file
diff --git a/app-catalog/samples/wBasis/src/main/res-transition/layout/fragment_grid_to_pager.xml b/app-catalog/samples/wBasis/src/main/res-transition/layout/fragment_grid_to_pager.xml
new file mode 100644
index 0000000..8b896fc
--- /dev/null
+++ b/app-catalog/samples/wBasis/src/main/res-transition/layout/fragment_grid_to_pager.xml
@@ -0,0 +1,27 @@
+
+
+
\ No newline at end of file
diff --git a/app-catalog/samples/wBasis/src/main/res-transition/layout/view_image_card.xml b/app-catalog/samples/wBasis/src/main/res-transition/layout/view_image_card.xml
new file mode 100644
index 0000000..71edfa0
--- /dev/null
+++ b/app-catalog/samples/wBasis/src/main/res-transition/layout/view_image_card.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
diff --git a/app-catalog/samples/wBasis/src/main/res-transition/transition/grid_detail_transition.xml b/app-catalog/samples/wBasis/src/main/res-transition/transition/grid_detail_transition.xml
new file mode 100644
index 0000000..5e0d0ed
--- /dev/null
+++ b/app-catalog/samples/wBasis/src/main/res-transition/transition/grid_detail_transition.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app-catalog/samples/wBasis/src/main/res-transition/transition/grid_exit_transition.xml b/app-catalog/samples/wBasis/src/main/res-transition/transition/grid_exit_transition.xml
new file mode 100644
index 0000000..5ffe1f4
--- /dev/null
+++ b/app-catalog/samples/wBasis/src/main/res-transition/transition/grid_exit_transition.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app-catalog/samples/wBasis/src/main/res-transition/transition/image_shared_element_transition.xml b/app-catalog/samples/wBasis/src/main/res-transition/transition/image_shared_element_transition.xml
new file mode 100644
index 0000000..e30d095
--- /dev/null
+++ b/app-catalog/samples/wBasis/src/main/res-transition/transition/image_shared_element_transition.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app-catalog/samples/wBasis/src/main/res-transition/values/colors.xml b/app-catalog/samples/wBasis/src/main/res-transition/values/colors.xml
new file mode 100644
index 0000000..9e84818
--- /dev/null
+++ b/app-catalog/samples/wBasis/src/main/res-transition/values/colors.xml
@@ -0,0 +1,21 @@
+
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
\ No newline at end of file
diff --git a/app-catalog/samples/wBasis/src/main/res-transition/values/dimens.xml b/app-catalog/samples/wBasis/src/main/res-transition/values/dimens.xml
index 168aa8d..bcfaaec 100644
--- a/app-catalog/samples/wBasis/src/main/res-transition/values/dimens.xml
+++ b/app-catalog/samples/wBasis/src/main/res-transition/values/dimens.xml
@@ -32,4 +32,7 @@
@dimen/margin_medium
@dimen/margin_medium
+
+ 4dp
+ 2
diff --git a/app-catalog/samples/wBasis/src/main/res-transition/values/strings.xml b/app-catalog/samples/wBasis/src/main/res-transition/values/strings.xml
index 1765a8b..a77b517 100644
--- a/app-catalog/samples/wBasis/src/main/res-transition/values/strings.xml
+++ b/app-catalog/samples/wBasis/src/main/res-transition/values/strings.xml
@@ -46,4 +46,43 @@
Show Log
Hide Log
+
+
+
+
+
+ Bacon ipsum dolor sit amet venison shankle pork chop meatball tri-tip beef ribs turducken.
+ Strip steak ball tip boudin shank, turducken leberkas pork chop beef ribs ham hock sausage
+ frankfurter prosciutto doner ham. Bacon landjaeger ball tip, andouille chuck beef ribs jowl
+ kevin tri-tip turkey biltong frankfurter sausage. Boudin pork belly meatloaf chicken cow
+ tri-tip kielbasa shoulder. Pork loin pig boudin hamburger pastrami short ribs. Sirloin
+ tongue pork loin chicken spare ribs bresaola pastrami.\n\n
+ Tenderloin turducken pork chop, pork belly beef ribs brisket ham. Turducken landjaeger short
+ loin capicola pancetta pork chop strip steak rump meatloaf brisket kevin doner short ribs
+ salami. Beef prosciutto flank leberkas landjaeger swine. Fatback prosciutto sausage, jerky
+ tail tongue hamburger jowl biltong shank pork belly swine filet mignon chicken ground round.
+ Pork chop porchetta ground round tri-tip tail t-bone.\n\n
+ Landjaeger rump bacon salami sausage pork loin pig brisket strip steak corned beef. Biltong
+ sirloin meatloaf ribeye, bresaola cow chicken t-bone frankfurter andouille strip steak jerky
+ capicola. Ribeye porchetta strip steak boudin. Kielbasa cow brisket pastrami ball tip
+ tenderloin bresaola ham. Biltong andouille chuck ham hock jerky beef chicken, flank shankle
+ ball tip venison porchetta kevin fatback kielbasa. Boudin tongue ground round, turkey pork
+ belly salami corned beef pork tri-tip meatloaf sausage andouille strip steak pig. Spare ribs
+ beef meatloaf rump sausage doner frankfurter.\n\n
+ Fatback porchetta pork loin meatball, turducken pork chop drumstick boudin. Kevin tri-tip
+ ground round corned beef, ribeye swine filet mignon salami pork spare ribs pork chop
+ brisket. Filet mignon shankle t-bone bacon. Turducken capicola turkey porchetta kielbasa
+ shank pork loin jerky venison tenderloin boudin ham hock ground round.
+
+ %1$s by %2$s
+ Image
diff --git a/app-catalog/samples/wBasis/src/main/res-transition/values/styles.xml b/app-catalog/samples/wBasis/src/main/res-transition/values/styles.xml
new file mode 100644
index 0000000..e375bce
--- /dev/null
+++ b/app-catalog/samples/wBasis/src/main/res-transition/values/styles.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
index 347bd6f..30ded5d 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -21,6 +21,7 @@ import java.util.regex.Matcher
*/
include ':app-catalog:app'
+include ':app-catalog:androidres'
// Dynamically include samples under /app-catalog/samples/ folder
def samples = []
// Find all build.gradle files under samples folder