Glimpse: Add search fragment

Change-Id: Ic21ce286314aa53e286ed1eab634b1d326afdfcf
diff --git a/app/src/main/java/org/lineageos/glimpse/flow/AlbumFlow.kt b/app/src/main/java/org/lineageos/glimpse/flow/AlbumFlow.kt
index 3f2212c..9eac117 100644
--- a/app/src/main/java/org/lineageos/glimpse/flow/AlbumFlow.kt
+++ b/app/src/main/java/org/lineageos/glimpse/flow/AlbumFlow.kt
@@ -28,16 +28,26 @@
     override fun flowCursor(): Flow<Cursor?> {
         val uri = MediaQuery.MediaStoreFileUri
         val projection = MediaQuery.AlbumsProjection
-        val imageOrVideo =
-            (MediaStore.Files.FileColumns.MEDIA_TYPE eq MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE) or
-                    (MediaStore.Files.FileColumns.MEDIA_TYPE eq MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO)
+        val image =
+            MediaStore.Files.FileColumns.MEDIA_TYPE eq MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE
+        val video =
+            MediaStore.Files.FileColumns.MEDIA_TYPE eq MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO
+        val imageOrVideo = when (bucketId) {
+            MediaStoreBuckets.MEDIA_STORE_BUCKET_PHOTOS.id -> image
+
+            MediaStoreBuckets.MEDIA_STORE_BUCKET_VIDEOS.id -> video
+
+            else -> image or video
+        }
         val albumFilter = when (bucketId) {
             MediaStoreBuckets.MEDIA_STORE_BUCKET_FAVORITES.id -> MediaStore.Files.FileColumns.IS_FAVORITE eq 1
 
             MediaStoreBuckets.MEDIA_STORE_BUCKET_TRASH.id ->
                 MediaStore.Files.FileColumns.IS_TRASHED eq 1
 
-            MediaStoreBuckets.MEDIA_STORE_BUCKET_REELS.id -> null
+            MediaStoreBuckets.MEDIA_STORE_BUCKET_REELS.id,
+            MediaStoreBuckets.MEDIA_STORE_BUCKET_PHOTOS.id,
+            MediaStoreBuckets.MEDIA_STORE_BUCKET_VIDEOS.id -> null
 
             else -> MediaStore.Files.FileColumns.BUCKET_ID eq Query.ARG
         }
@@ -148,6 +158,14 @@
                             R.string.album_reels
                         )
 
+                        MediaStoreBuckets.MEDIA_STORE_BUCKET_PHOTOS.id -> context.getString(
+                            R.string.album_photos
+                        )
+
+                        MediaStoreBuckets.MEDIA_STORE_BUCKET_VIDEOS.id -> context.getString(
+                            R.string.album_videos
+                        )
+
                         else -> bucketDisplayName ?: Build.MODEL
                     },
                     media
diff --git a/app/src/main/java/org/lineageos/glimpse/flow/MediaFlow.kt b/app/src/main/java/org/lineageos/glimpse/flow/MediaFlow.kt
index db75169..abf0952 100644
--- a/app/src/main/java/org/lineageos/glimpse/flow/MediaFlow.kt
+++ b/app/src/main/java/org/lineageos/glimpse/flow/MediaFlow.kt
@@ -27,16 +27,26 @@
     override fun flowCursor(): Flow<Cursor?> {
         val uri = MediaQuery.MediaStoreFileUri
         val projection = MediaQuery.MediaProjection
-        val imageOrVideo =
-            (MediaStore.Files.FileColumns.MEDIA_TYPE eq MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE) or
-                    (MediaStore.Files.FileColumns.MEDIA_TYPE eq MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO)
+        val image =
+            MediaStore.Files.FileColumns.MEDIA_TYPE eq MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE
+        val video =
+            MediaStore.Files.FileColumns.MEDIA_TYPE eq MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO
+        val imageOrVideo = when (bucketId) {
+            MediaStoreBuckets.MEDIA_STORE_BUCKET_PHOTOS.id -> image
+
+            MediaStoreBuckets.MEDIA_STORE_BUCKET_VIDEOS.id -> video
+
+            else -> image or video
+        }
         val albumFilter = when (bucketId) {
             MediaStoreBuckets.MEDIA_STORE_BUCKET_FAVORITES.id -> MediaStore.Files.FileColumns.IS_FAVORITE eq 1
 
             MediaStoreBuckets.MEDIA_STORE_BUCKET_TRASH.id ->
                 MediaStore.Files.FileColumns.IS_TRASHED eq 1
 
-            MediaStoreBuckets.MEDIA_STORE_BUCKET_REELS.id -> null
+            MediaStoreBuckets.MEDIA_STORE_BUCKET_REELS.id,
+            MediaStoreBuckets.MEDIA_STORE_BUCKET_PHOTOS.id,
+            MediaStoreBuckets.MEDIA_STORE_BUCKET_VIDEOS.id -> null
 
             else -> MediaStore.Files.FileColumns.BUCKET_ID eq Query.ARG
         }
diff --git a/app/src/main/java/org/lineageos/glimpse/fragments/SearchFragment.kt b/app/src/main/java/org/lineageos/glimpse/fragments/SearchFragment.kt
new file mode 100644
index 0000000..99373dd
--- /dev/null
+++ b/app/src/main/java/org/lineageos/glimpse/fragments/SearchFragment.kt
@@ -0,0 +1,85 @@
+/*
+ * SPDX-FileCopyrightText: 2023 The LineageOS Project
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.lineageos.glimpse.fragments
+
+import android.os.Bundle
+import android.view.View
+import android.widget.LinearLayout
+import androidx.core.os.bundleOf
+import androidx.fragment.app.Fragment
+import androidx.navigation.fragment.findNavController
+import com.google.android.material.appbar.AppBarLayout
+import com.google.android.material.shape.MaterialShapeDrawable
+import org.lineageos.glimpse.R
+import org.lineageos.glimpse.ext.getViewProperty
+import org.lineageos.glimpse.ui.ListItem
+import org.lineageos.glimpse.utils.MediaStoreBuckets
+
+/**
+ * A fragment showing a search bar with categories.
+ * Use the [SearchFragment.newInstance] factory method to
+ * create an instance of this fragment.
+ */
+class SearchFragment : Fragment(R.layout.fragment_search) {
+    // Views
+    private val appBarLayout by getViewProperty<AppBarLayout>(R.id.appBarLayout)
+    private val favoritesAlbumListItem by getViewProperty<ListItem>(R.id.favoritesAlbumListItem)
+    private val photosAlbumListItem by getViewProperty<ListItem>(R.id.photosAlbumListItem)
+    private val trashAlbumListItem by getViewProperty<ListItem>(R.id.trashAlbumListItem)
+    private val videosAlbumListItem by getViewProperty<ListItem>(R.id.videosAlbumListItem)
+
+    // Fragments
+    private val parentNavController by lazy {
+        requireParentFragment().requireParentFragment().findNavController()
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+
+        val context = requireContext()
+
+        appBarLayout.statusBarForeground = MaterialShapeDrawable.createWithElevationOverlay(context)
+
+        photosAlbumListItem.setOnClickListener {
+            openAlbum(MediaStoreBuckets.MEDIA_STORE_BUCKET_PHOTOS)
+        }
+
+        videosAlbumListItem.setOnClickListener {
+            openAlbum(MediaStoreBuckets.MEDIA_STORE_BUCKET_VIDEOS)
+        }
+
+        favoritesAlbumListItem.setOnClickListener {
+            openAlbum(MediaStoreBuckets.MEDIA_STORE_BUCKET_FAVORITES)
+        }
+
+        trashAlbumListItem.setOnClickListener {
+            openAlbum(MediaStoreBuckets.MEDIA_STORE_BUCKET_TRASH)
+        }
+    }
+
+    private fun openAlbum(mediaStoreBucket: MediaStoreBuckets) {
+        parentNavController.navigate(
+            R.id.action_mainFragment_to_albumViewerFragment,
+            AlbumViewerFragment.createBundle(
+                bucketId = mediaStoreBucket.id
+            )
+        )
+    }
+
+    companion object {
+        private fun createBundle() = bundleOf()
+
+        /**
+         * Use this factory method to create a new instance of
+         * this fragment using the provided parameters.
+         *
+         * @return A new instance of fragment SearchFragment.
+         */
+        fun newInstance() = SearchFragment().apply {
+            arguments = createBundle()
+        }
+    }
+}
diff --git a/app/src/main/java/org/lineageos/glimpse/utils/MediaStoreBuckets.kt b/app/src/main/java/org/lineageos/glimpse/utils/MediaStoreBuckets.kt
index ad81e52..9805e6d 100644
--- a/app/src/main/java/org/lineageos/glimpse/utils/MediaStoreBuckets.kt
+++ b/app/src/main/java/org/lineageos/glimpse/utils/MediaStoreBuckets.kt
@@ -24,7 +24,17 @@
     /**
      * Reserved bucket ID for placeholders, throw an exception if this value is used.
      */
-    MEDIA_STORE_BUCKET_PLACEHOLDER;
+    MEDIA_STORE_BUCKET_PLACEHOLDER,
+
+    /**
+     * Reels album, contains only photos.
+     */
+    MEDIA_STORE_BUCKET_PHOTOS,
+
+    /**
+     * Reels album, contains only videos.
+     */
+    MEDIA_STORE_BUCKET_VIDEOS;
 
     val id = -0x0000DEAD - ((ordinal + 1) shl 16)
 }
diff --git a/app/src/main/res/layout/fragment_search.xml b/app/src/main/res/layout/fragment_search.xml
new file mode 100644
index 0000000..70476d5
--- /dev/null
+++ b/app/src/main/res/layout/fragment_search.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     SPDX-FileCopyrightText: 2023 The LineageOS Project
+     SPDX-License-Identifier: Apache-2.0
+-->
+<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <com.google.android.material.appbar.AppBarLayout
+        android:id="@+id/appBarLayout"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:fitsSystemWindows="true"
+        app:liftOnScrollTargetViewId="@+id/searchScrollLayout">
+
+        <com.google.android.material.search.SearchBar
+            android:id="@+id/searchBar"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
+
+    </com.google.android.material.appbar.AppBarLayout>
+
+    <androidx.core.widget.NestedScrollView
+        android:id="@+id/searchScrollLayout"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical"
+        app:layout_behavior="@string/appbar_scrolling_view_behavior">
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
+
+            <TextView
+                android:id="@+id/categoriesTitleTextView"
+                style="@style/TextAppearance.Material3.TitleLarge"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginHorizontal="17dp"
+                android:layout_marginVertical="22dp"
+                android:text="@string/categories_title" />
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical">
+
+                <org.lineageos.glimpse.ui.ListItem
+                    android:id="@+id/photosAlbumListItem"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    app:headlineText="@string/album_photos"
+                    app:leadingIconImage="@drawable/ic_photo_size_select_actual" />
+
+                <org.lineageos.glimpse.ui.ListItem
+                    android:id="@+id/videosAlbumListItem"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    app:headlineText="@string/album_videos"
+                    app:leadingIconImage="@drawable/ic_play_circle_outline" />
+
+                <org.lineageos.glimpse.ui.ListItem
+                    android:id="@+id/favoritesAlbumListItem"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    app:headlineText="@string/album_favorites"
+                    app:leadingIconImage="@drawable/ic_star_border" />
+
+                <org.lineageos.glimpse.ui.ListItem
+                    android:id="@+id/trashAlbumListItem"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    app:headlineText="@string/album_trash"
+                    app:leadingIconImage="@drawable/ic_delete" />
+
+            </LinearLayout>
+
+        </LinearLayout>
+
+    </androidx.core.widget.NestedScrollView>
+
+</androidx.coordinatorlayout.widget.CoordinatorLayout>
diff --git a/app/src/main/res/navigation/main_fragment_navigation.xml b/app/src/main/res/navigation/main_fragment_navigation.xml
index a824736..888c9ab 100644
--- a/app/src/main/res/navigation/main_fragment_navigation.xml
+++ b/app/src/main/res/navigation/main_fragment_navigation.xml
@@ -21,4 +21,10 @@
         android:label="@string/albums_title"
         tools:layout="@layout/fragment_albums" />
 
+    <fragment
+        android:id="@+id/searchFragment"
+        android:name="org.lineageos.glimpse.fragments.SearchFragment"
+        android:label="@string/search_title"
+        tools:layout="@layout/fragment_search" />
+
 </navigation>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 66079e4..24cdad9 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -13,11 +13,14 @@
     <string name="reels_title">Reels</string>
     <string name="albums_title">Albums</string>
     <string name="search_title">Search</string>
+    <string name="categories_title">Categories</string>
 
     <!-- Album buckets -->
     <string name="album_favorites">Favorites</string>
     <string name="album_trash">Trash</string>
     <string name="album_reels">Reels</string>
+    <string name="album_photos">Photos</string>
+    <string name="album_videos">Videos</string>
 
     <!-- Album thumbnail -->
     <plurals name="album_thumbnail_items">