Skip to content

Commit c64bb76

Browse files
leticiarossidsn5ft
authored andcommitted
[Catalog][Adaptive] Added adaptive canonical layout demos to catalog.
PiperOrigin-RevId: 399672106
1 parent 181851b commit c64bb76

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+3365
-2
lines changed

catalog/build.gradle

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,12 @@ dependencies {
99
annotationProcessor 'com.google.dagger:dagger-android-processor:2.28.1'
1010

1111
api 'androidx.multidex:multidex:2.0.1'
12-
api 'androidx.constraintlayout:constraintlayout:1.1.3'
12+
api 'androidx.constraintlayout:constraintlayout:2.1.0'
1313
api 'androidx.gridlayout:gridlayout:1.0.0'
14+
api "androidx.multidex:multidex:2.0.1"
15+
api "androidx.recyclerview:recyclerview:1.2.1"
16+
api 'androidx.window:window:1.0.0-beta02'
17+
api "androidx.window:window-java:1.0.0-beta02"
1418

1519
api 'com.google.guava:guava:27.0.1-android'
1620

@@ -35,6 +39,7 @@ dependencies {
3539
}
3640

3741
def srcDirs = [
42+
'adaptive',
3843
'application',
3944
'application/attrs',
4045
'application/legacymultidex',
@@ -89,6 +94,7 @@ android {
8994

9095
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
9196
vectorDrawables.useSupportLibrary = true
97+
multiDexEnabled true
9298
minSdkVersion 14
9399
targetSdkVersion 31
94100
}
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
/*
2+
* Copyright 2021 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.material.catalog.adaptive;
18+
19+
import io.material.catalog.R;
20+
21+
import android.content.res.Configuration;
22+
import android.os.Bundle;
23+
import android.os.Handler;
24+
import android.os.Looper;
25+
import androidx.core.util.Consumer;
26+
import android.view.LayoutInflater;
27+
import android.view.View;
28+
import android.view.ViewGroup;
29+
import androidx.annotation.NonNull;
30+
import androidx.annotation.Nullable;
31+
import androidx.drawerlayout.widget.DrawerLayout;
32+
import androidx.window.java.layout.WindowInfoRepositoryCallbackAdapter;
33+
import androidx.window.layout.DisplayFeature;
34+
import androidx.window.layout.FoldingFeature;
35+
import androidx.window.layout.WindowInfoRepository;
36+
import androidx.window.layout.WindowLayoutInfo;
37+
import com.google.android.material.bottomnavigation.BottomNavigationView;
38+
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton;
39+
import com.google.android.material.navigation.NavigationView;
40+
import com.google.android.material.navigationrail.NavigationRailView;
41+
import io.material.catalog.feature.DemoActivity;
42+
import java.util.List;
43+
import java.util.concurrent.Executor;
44+
45+
/** An Activity which hosts the Adaptive feed demo flow. */
46+
public class AdaptiveFeedDemoActivity extends DemoActivity {
47+
48+
private View container;
49+
private DrawerLayout drawerLayout;
50+
private NavigationView modalNavDrawer;
51+
private BottomNavigationView bottomNav;
52+
private NavigationRailView navRail;
53+
private NavigationView navDrawer;
54+
private ExtendedFloatingActionButton navFab;
55+
private AdaptiveFeedDemoFragment feedFragment;
56+
57+
@Nullable private WindowInfoRepositoryCallbackAdapter windowInfoRepo;
58+
private final Consumer<WindowLayoutInfo> stateContainer = new StateContainer();
59+
private final Handler handler = new Handler(Looper.getMainLooper());
60+
private final Executor executor = command -> handler.post(() -> handler.post(command));
61+
private Configuration configuration;
62+
63+
@Nullable
64+
@Override
65+
public View onCreateDemoView(
66+
@NonNull LayoutInflater layoutInflater,
67+
@Nullable ViewGroup viewGroup,
68+
@Nullable Bundle bundle) {
69+
View view = layoutInflater.inflate(R.layout.cat_adaptive_feed_activity, viewGroup, false);
70+
container = view.findViewById(R.id.feed_activity_container);
71+
drawerLayout = view.findViewById(R.id.drawer_layout);
72+
modalNavDrawer = view.findViewById(R.id.modal_nav_drawer);
73+
windowInfoRepo =
74+
new WindowInfoRepositoryCallbackAdapter(WindowInfoRepository.getOrCreate(this));
75+
configuration = getResources().getConfiguration();
76+
bottomNav = view.findViewById(R.id.bottom_nav);
77+
navRail = view.findViewById(R.id.nav_rail);
78+
navDrawer = view.findViewById(R.id.nav_drawer);
79+
navFab = view.findViewById(R.id.nav_fab);
80+
return view;
81+
}
82+
83+
@Override
84+
protected void onCreate(@Nullable Bundle bundle) {
85+
super.onCreate(bundle);
86+
feedFragment = new AdaptiveFeedDemoFragment();
87+
88+
// Update navigation views according to screen width size.
89+
int screenWidth = configuration.screenWidthDp;
90+
AdaptiveUtils.updateNavigationViewLayout(
91+
screenWidth,
92+
drawerLayout,
93+
modalNavDrawer,
94+
/* fab= */ null,
95+
bottomNav,
96+
navRail,
97+
navDrawer,
98+
navFab);
99+
100+
getSupportFragmentManager()
101+
.beginTransaction()
102+
.replace(R.id.fragment_container, feedFragment)
103+
.commit();
104+
}
105+
106+
@Override
107+
public void onStart() {
108+
super.onStart();
109+
if (windowInfoRepo != null) {
110+
windowInfoRepo.addWindowLayoutInfoListener(executor, stateContainer);
111+
}
112+
}
113+
114+
@Override
115+
public void onStop() {
116+
super.onStop();
117+
if (windowInfoRepo != null) {
118+
windowInfoRepo.removeWindowLayoutInfoListener(stateContainer);
119+
}
120+
}
121+
122+
private class StateContainer implements Consumer<WindowLayoutInfo> {
123+
124+
public StateContainer() {}
125+
126+
@Override
127+
public void accept(WindowLayoutInfo windowLayoutInfo) {
128+
if (feedFragment == null) {
129+
return;
130+
}
131+
132+
if (configuration.screenWidthDp < AdaptiveUtils.MEDIUM_SCREEN_WIDTH_SIZE) {
133+
feedFragment.updateFoldPosition(0);
134+
feedFragment.setClosedLayout();
135+
} else {
136+
List<DisplayFeature> displayFeatures = windowLayoutInfo.getDisplayFeatures();
137+
boolean isClosed = true;
138+
139+
for (DisplayFeature displayFeature : displayFeatures) {
140+
if (displayFeature instanceof FoldingFeature) {
141+
FoldingFeature foldingFeature = (FoldingFeature) displayFeature;
142+
if (foldingFeature.getState().equals(FoldingFeature.State.HALF_OPENED)
143+
|| foldingFeature.getState().equals(FoldingFeature.State.FLAT)) {
144+
feedFragment.setOpenLayout();
145+
if (foldingFeature.getOrientation().equals(FoldingFeature.Orientation.VERTICAL)) {
146+
// Device is open and fold is vertical.
147+
feedFragment.updateFoldPosition(
148+
AdaptiveUtils.getFoldPosition(container, foldingFeature));
149+
} else {
150+
// Device is open and fold is horizontal.
151+
feedFragment.updateFoldPosition(container.getWidth() / 2);
152+
}
153+
isClosed = false;
154+
}
155+
}
156+
}
157+
if (isClosed) {
158+
if (configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
159+
// Device is closed or not foldable and in portrait.
160+
feedFragment.updateFoldPosition(0);
161+
feedFragment.setClosedLayout();
162+
} else {
163+
// Device is closed or not foldable and in landscape.
164+
feedFragment.setOpenLayout();
165+
feedFragment.updateFoldPosition(container.getWidth() / 2);
166+
}
167+
}
168+
}
169+
}
170+
}
171+
172+
@Override
173+
protected boolean shouldShowDefaultDemoActionBar() {
174+
return false;
175+
}
176+
}
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
/*
2+
* Copyright 2021 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.material.catalog.adaptive;
18+
19+
import io.material.catalog.R;
20+
21+
import android.os.Bundle;
22+
import androidx.fragment.app.Fragment;
23+
import androidx.core.view.ViewCompat;
24+
import androidx.recyclerview.widget.LinearLayoutManager;
25+
import androidx.recyclerview.widget.RecyclerView;
26+
import android.view.LayoutInflater;
27+
import android.view.View;
28+
import android.view.ViewGroup;
29+
import androidx.annotation.LayoutRes;
30+
import androidx.annotation.NonNull;
31+
import androidx.annotation.Nullable;
32+
import androidx.constraintlayout.widget.ConstraintLayout;
33+
import androidx.constraintlayout.widget.ConstraintSet;
34+
import androidx.constraintlayout.widget.ReactiveGuide;
35+
36+
/** A Fragment that hosts a feed demo. */
37+
public class AdaptiveFeedDemoFragment extends Fragment {
38+
39+
private ReactiveGuide fold;
40+
private ConstraintLayout constraintLayout;
41+
private ConstraintSet closedLayout;
42+
private ConstraintSet openLayout;
43+
44+
@Nullable
45+
@Override
46+
public View onCreateView(
47+
@NonNull LayoutInflater layoutInflater,
48+
@Nullable ViewGroup viewGroup,
49+
@Nullable Bundle bundle) {
50+
View view = layoutInflater.inflate(R.layout.cat_adaptive_feed_fragment, viewGroup, false);
51+
fold = view.findViewById(R.id.fold);
52+
// Set up content lists.
53+
RecyclerView smallContentList = view.findViewById(R.id.small_content_list);
54+
setUpContentRecyclerView(smallContentList, /* isSmallContent= */ true, 15);
55+
RecyclerView largeContentList = view.findViewById(R.id.large_content_list);
56+
setUpContentRecyclerView(largeContentList, /* isSmallContent= */ false, 5);
57+
// Set up constraint sets.
58+
constraintLayout = view.findViewById(R.id.feed_constraint_layout);
59+
closedLayout = new ConstraintSet();
60+
closedLayout.clone(constraintLayout);
61+
openLayout = getOpenLayout(constraintLayout);
62+
return view;
63+
}
64+
65+
/* Sets up a recycler view with either small or large items list. */
66+
private void setUpContentRecyclerView(
67+
@NonNull RecyclerView recyclerView, boolean isSmallContent, int itemCount) {
68+
RecyclerView.LayoutManager layoutManager =
69+
new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false);
70+
FeedAdapter feedAdapter =
71+
new FeedAdapter(
72+
isSmallContent
73+
? R.layout.cat_adaptive_feed_small_item
74+
: R.layout.cat_adaptive_feed_large_item,
75+
itemCount);
76+
recyclerView.setLayoutManager(layoutManager);
77+
recyclerView.setAdapter(feedAdapter);
78+
ViewCompat.setNestedScrollingEnabled(recyclerView, /* enabled= */ false);
79+
}
80+
81+
/* Returns the constraint set to be used for the open layout configuration. */
82+
private ConstraintSet getOpenLayout(@NonNull ConstraintLayout constraintLayout) {
83+
ConstraintSet constraintSet = new ConstraintSet();
84+
constraintSet.clone(constraintLayout);
85+
// Change top button to be on the right of the fold.
86+
constraintSet.connect(R.id.top_button, ConstraintSet.START, R.id.fold, ConstraintSet.END);
87+
// Change small content list to be on the right of the fold and below top button.
88+
constraintSet.connect(
89+
R.id.small_content_list, ConstraintSet.START, R.id.fold, ConstraintSet.END);
90+
constraintSet.connect(
91+
R.id.small_content_list, ConstraintSet.TOP, R.id.top_button, ConstraintSet.BOTTOM);
92+
constraintSet.setVisibility(R.id.highlight_content_card, View.GONE);
93+
constraintSet.setVisibility(R.id.large_content_list, View.VISIBLE);
94+
95+
return constraintSet;
96+
}
97+
98+
/* Updates the fold guideline's distance from the end. */
99+
void updateFoldPosition(int position) {
100+
fold.setGuidelineEnd(position);
101+
}
102+
103+
/* Applies the open layout configuration. */
104+
void setOpenLayout() {
105+
openLayout.applyTo(constraintLayout);
106+
}
107+
108+
/* Applies the closed layout configuration. */
109+
void setClosedLayout() {
110+
closedLayout.applyTo(constraintLayout);
111+
}
112+
113+
/** A RecyclerView adapter for the content lists of the feed. */
114+
private static final class FeedAdapter extends RecyclerView.Adapter<FeedAdapter.FeedViewHolder> {
115+
116+
@LayoutRes private final int itemLayout;
117+
private final int itemCount;
118+
119+
FeedAdapter(@LayoutRes int itemLayout, int itemCount) {
120+
this.itemLayout = itemLayout;
121+
this.itemCount = itemCount;
122+
}
123+
124+
/** Provides a reference to the views for each data item. */
125+
static class FeedViewHolder extends RecyclerView.ViewHolder {
126+
public FeedViewHolder(@NonNull View view) {
127+
super(view);
128+
}
129+
}
130+
131+
@NonNull
132+
@Override
133+
public FeedViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
134+
View view = LayoutInflater.from(parent.getContext()).inflate(itemLayout, parent, false);
135+
return new FeedViewHolder(view);
136+
}
137+
138+
@Override
139+
public void onBindViewHolder(@NonNull FeedViewHolder holder, int position) {
140+
// Populate content. Empty for demo purposes.
141+
}
142+
143+
@Override
144+
public int getItemCount() {
145+
return itemCount;
146+
}
147+
}
148+
}

0 commit comments

Comments
 (0)