3333import java .io .IOException ;
3434import java .util .ArrayList ;
3535import java .util .Collections ;
36+ import java .util .HashSet ;
3637import java .util .List ;
3738import java .util .Random ;
39+ import java .util .Set ;
3840
3941public class AndroidStudioFragment extends Fragment {
4042
4143 private static boolean mobileAdsInitialized = false ;
42- private final List <Lesson > allLessons = new ArrayList <>();
44+ private final List <Object > allItems = new ArrayList <>();
4345 private LessonsAdapter adapter ;
4446
4547 @ Nullable
@@ -58,9 +60,9 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
5860 list .setLayoutManager (new LinearLayoutManager (requireContext ()));
5961 adapter = new LessonsAdapter ();
6062 list .setAdapter (adapter );
61- allLessons .clear ();
62- allLessons .addAll (loadLessons ());
63- populateAdapter (allLessons );
63+ allItems .clear ();
64+ allItems .addAll (loadItems ());
65+ populateAdapter (allItems );
6466 }
6567
6668 private void ensureMobileAdsInitialized () {
@@ -70,23 +72,29 @@ private void ensureMobileAdsInitialized() {
7072 }
7173 }
7274
73- private List <Lesson > loadLessons () {
74- List <Lesson > lessons = new ArrayList <>();
75+ private List <Object > loadItems () {
76+ List <Object > items = new ArrayList <>();
7577 XmlResourceParser parser = getResources ().getXml (R .xml .preferences_android_studio );
7678 try {
7779 int event = parser .getEventType ();
78- Lesson current = null ;
80+ Lesson currentLesson = null ;
7981 while (event != XmlPullParser .END_DOCUMENT ) {
8082 if (event == XmlPullParser .START_TAG ) {
8183 String name = parser .getName ();
82- if (name .endsWith ("Preference" ) && !name .endsWith ("PreferenceCategory" )) {
83- current = new Lesson ();
84+ if (name .endsWith ("PreferenceCategory" )) {
85+ Category category = new Category ();
86+ int titleRes = parser .getAttributeResourceValue ("http://schemas.android.com/apk/res-auto" , "title" , 0 );
87+ if (titleRes != 0 ) category .title = getString (titleRes );
88+ category .iconRes = parser .getAttributeResourceValue ("http://schemas.android.com/apk/res-auto" , "icon" , 0 );
89+ items .add (category );
90+ } else if (name .endsWith ("Preference" ) && !name .endsWith ("PreferenceCategory" )) {
91+ currentLesson = new Lesson ();
8492 int titleRes = parser .getAttributeResourceValue ("http://schemas.android.com/apk/res-auto" , "title" , 0 );
85- if (titleRes != 0 ) current .title = getString (titleRes );
93+ if (titleRes != 0 ) currentLesson .title = getString (titleRes );
8694 int summaryRes = parser .getAttributeResourceValue ("http://schemas.android.com/apk/res-auto" , "summary" , 0 );
87- if (summaryRes != 0 ) current .summary = getString (summaryRes );
88- current .iconRes = parser .getAttributeResourceValue ("http://schemas.android.com/apk/res-auto" , "icon" , 0 );
89- } else if ("intent" .equals (name ) && current != null ) {
95+ if (summaryRes != 0 ) currentLesson .summary = getString (summaryRes );
96+ currentLesson .iconRes = parser .getAttributeResourceValue ("http://schemas.android.com/apk/res-auto" , "icon" , 0 );
97+ } else if ("intent" .equals (name ) && currentLesson != null ) {
9098 Intent intent = new Intent ();
9199 String action = parser .getAttributeValue ("http://schemas.android.com/apk/res/android" , "action" );
92100 if (action != null ) intent .setAction (action );
@@ -97,13 +105,13 @@ private List<Lesson> loadLessons() {
97105 }
98106 String data = parser .getAttributeValue ("http://schemas.android.com/apk/res/android" , "data" );
99107 if (data != null ) intent .setData (Uri .parse (data ));
100- current .intent = intent ;
108+ currentLesson .intent = intent ;
101109 }
102110 } else if (event == XmlPullParser .END_TAG ) {
103111 String name = parser .getName ();
104- if (current != null && name .endsWith ("Preference" ) && !name .endsWith ("PreferenceCategory" )) {
105- lessons .add (current );
106- current = null ;
112+ if (currentLesson != null && name .endsWith ("Preference" ) && !name .endsWith ("PreferenceCategory" )) {
113+ items .add (currentLesson );
114+ currentLesson = null ;
107115 }
108116 }
109117 event = parser .next ();
@@ -112,26 +120,37 @@ private List<Lesson> loadLessons() {
112120 } finally {
113121 parser .close ();
114122 }
115- return lessons ;
123+ return items ;
116124 }
117125
118- private void populateAdapter (List <Lesson > lessons ) {
126+ private void populateAdapter (List <Object > source ) {
119127 List <Object > items = new ArrayList <>();
120- int adCount = lessons .size () / 3 ;
121- List <Integer > positions = new ArrayList <>();
122- for (int i = 0 ; i < lessons .size (); i ++) {
123- positions .add (i );
128+ int lessonCount = 0 ;
129+ for (Object item : source ) {
130+ if (item instanceof Lesson ) {
131+ lessonCount ++;
132+ }
133+ }
134+ int adCount = lessonCount / 3 ;
135+ List <Integer > indices = new ArrayList <>();
136+ for (int i = 0 ; i < lessonCount ; i ++) {
137+ indices .add (i );
124138 }
125- Collections .shuffle (positions , new Random ());
126- positions = positions .subList (0 , adCount );
127- Collections .sort (positions );
128- int adIndex = 0 ;
129- for (int i = 0 ; i < lessons .size (); i ++) {
130- if (adIndex < positions .size () && i == positions .get (adIndex )) {
131- items .add (new AdItem ());
132- adIndex ++;
139+ Collections .shuffle (indices , new Random ());
140+ indices = indices .subList (0 , adCount );
141+ Collections .sort (indices );
142+ Set <Integer > adPositions = new HashSet <>(indices );
143+ int lessonIndex = 0 ;
144+ for (Object item : source ) {
145+ if (item instanceof Lesson ) {
146+ if (adPositions .contains (lessonIndex )) {
147+ items .add (new AdItem ());
148+ }
149+ items .add (item );
150+ lessonIndex ++;
151+ } else {
152+ items .add (item );
133153 }
134- items .add (lessons .get (i ));
135154 }
136155 adapter .setItems (items );
137156 }
@@ -172,10 +191,26 @@ public boolean onMenuItemActionCollapse(MenuItem item) {
172191
173192 private void filterLessons (String query ) {
174193 String lower = query == null ? "" : query .toLowerCase ();
175- List <Lesson > filtered = new ArrayList <>();
176- for (Lesson l : allLessons ) {
177- if (l .title != null && l .title .toLowerCase ().contains (lower )) {
178- filtered .add (l );
194+ if (lower .isEmpty ()) {
195+ populateAdapter (allItems );
196+ return ;
197+ }
198+ List <Object > filtered = new ArrayList <>();
199+ Category lastCategory = null ;
200+ boolean categoryAdded = false ;
201+ for (Object item : allItems ) {
202+ if (item instanceof Category ) {
203+ lastCategory = (Category ) item ;
204+ categoryAdded = false ;
205+ } else if (item instanceof Lesson ) {
206+ Lesson l = (Lesson ) item ;
207+ if (l .title != null && l .title .toLowerCase ().contains (lower )) {
208+ if (lastCategory != null && !categoryAdded ) {
209+ filtered .add (lastCategory );
210+ categoryAdded = true ;
211+ }
212+ filtered .add (l );
213+ }
179214 }
180215 }
181216 populateAdapter (filtered );
@@ -190,9 +225,15 @@ private static class Lesson {
190225 Intent intent ;
191226 }
192227
228+ private static class Category {
229+ String title ;
230+ int iconRes ;
231+ }
232+
193233 private static class LessonsAdapter extends RecyclerView .Adapter <RecyclerView .ViewHolder > {
194234 private static final int TYPE_LESSON = 0 ;
195235 private static final int TYPE_AD = 1 ;
236+ private static final int TYPE_CATEGORY = 2 ;
196237 private final List <Object > items = new ArrayList <>();
197238
198239 void setItems (List <Object > newItems ) {
@@ -204,7 +245,9 @@ void setItems(List<Object> newItems) {
204245 @ Override
205246 public int getItemViewType (int position ) {
206247 Object item = items .get (position );
207- return item instanceof Lesson ? TYPE_LESSON : TYPE_AD ;
248+ if (item instanceof Lesson ) return TYPE_LESSON ;
249+ if (item instanceof Category ) return TYPE_CATEGORY ;
250+ return TYPE_AD ;
208251 }
209252
210253 @ NonNull
@@ -217,6 +260,10 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int
217260 ViewGroup .LayoutParams .WRAP_CONTENT ));
218261 adView .setNativeAdLayout (R .layout .android_studio_list_native_ad );
219262 return new AdHolder (adView );
263+ } else if (viewType == TYPE_CATEGORY ) {
264+ View view = LayoutInflater .from (parent .getContext ())
265+ .inflate (R .layout .android_studio_category_item , parent , false );
266+ return new CategoryHolder (view );
220267 } else {
221268 View view = LayoutInflater .from (parent .getContext ())
222269 .inflate (R .layout .android_studio_lesson_item , parent , false );
@@ -226,14 +273,18 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int
226273
227274 @ Override
228275 public void onBindViewHolder (@ NonNull RecyclerView .ViewHolder holder , int position ) {
229- if (getItemViewType (position ) == TYPE_AD ) {
276+ int type = getItemViewType (position );
277+ if (type == TYPE_AD ) {
230278 AdHolder adHolder = (AdHolder ) holder ;
231279 adHolder .adView .loadAd (new AdRequest .Builder ().build (), new AdListener () {
232280 @ Override
233281 public void onAdFailedToLoad (@ NonNull LoadAdError error ) {
234282 adHolder .adView .setVisibility (View .GONE );
235283 }
236284 });
285+ } else if (type == TYPE_CATEGORY ) {
286+ Category category = (Category ) items .get (position );
287+ ((CategoryHolder ) holder ).bind (category );
237288 } else {
238289 Lesson lesson = (Lesson ) items .get (position );
239290 ((LessonHolder ) holder ).bind (lesson );
@@ -286,5 +337,26 @@ void bind(Lesson lesson) {
286337 });
287338 }
288339 }
340+
341+ static class CategoryHolder extends RecyclerView .ViewHolder {
342+ final ImageView icon ;
343+ final TextView title ;
344+
345+ CategoryHolder (@ NonNull View itemView ) {
346+ super (itemView );
347+ icon = itemView .findViewById (R .id .category_icon );
348+ title = itemView .findViewById (R .id .category_title );
349+ }
350+
351+ void bind (Category category ) {
352+ if (category .iconRes != 0 ) {
353+ icon .setImageResource (category .iconRes );
354+ icon .setVisibility (View .VISIBLE );
355+ } else {
356+ icon .setVisibility (View .GONE );
357+ }
358+ title .setText (category .title );
359+ }
360+ }
289361 }
290362}
0 commit comments