Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ class SettingsFragment : PreferenceFragmentCompat() {
findPreference<MultiSelectListPreference>(Constants.ASSISTANT_MANAGER_MANUAL_PREF_KEY)?.isVisible =
!isDynamic
}
assistantSelectorViewModel.isGridViewEnabled.observe(this) { isGrid ->
findPreference<ListPreference>(Constants.ASSISTANT_GRID_COLUMNS_PREF_KEY)?.isVisible = isGrid
findPreference<ListPreference>(Constants.ASSISTANT_GRID_COLUMNS_LAND_PREF_KEY)?.isVisible = isGrid
}
}

override fun onResume() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import android.view.ViewGroup
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.wstxda.switchai.databinding.ListItemAssistantCategoryBinding
import com.wstxda.switchai.databinding.ListItemAssistantGridViewBinding
import com.wstxda.switchai.databinding.ListItemAssistantViewBinding
import com.wstxda.switchai.databinding.ListItemReorderTipBinding
import com.wstxda.switchai.ui.viewholder.AssistantSelectorCategoryViewHolder
import com.wstxda.switchai.ui.viewholder.AssistantSelectorGridItemViewHolder
import com.wstxda.switchai.ui.viewholder.AssistantSelectorItemViewHolder
import com.wstxda.switchai.ui.viewholder.ReorderTipViewHolder
import com.wstxda.switchai.utils.Constants
Expand All @@ -17,13 +19,15 @@ class AssistantSelectorAdapter(
private val onAssistantClicked: (String) -> Unit,
private val onPinClicked: (String) -> Unit,
private val onDismissTipClicked: () -> Unit,
val isGridMode: Boolean = false,
) : ListAdapter<AssistantSelectorRecyclerView, RecyclerView.ViewHolder>(
AssistantSelectorDiffCallback()
) {

override fun getItemViewType(position: Int) = when (getItem(position)) {
is AssistantSelectorRecyclerView.CategoryHeader -> Constants.VIEW_TYPE_CATEGORY_HEADER
is AssistantSelectorRecyclerView.AssistantSelector -> Constants.VIEW_TYPE_ASSISTANT_ITEM
is AssistantSelectorRecyclerView.AssistantSelector ->
if (isGridMode) Constants.VIEW_TYPE_ASSISTANT_GRID_ITEM else Constants.VIEW_TYPE_ASSISTANT_ITEM
is AssistantSelectorRecyclerView.ReorderTip -> Constants.VIEW_TYPE_REORDER_TIP
}

Expand All @@ -42,6 +46,13 @@ class AssistantSelectorAdapter(
AssistantSelectorItemViewHolder(binding)
}

Constants.VIEW_TYPE_ASSISTANT_GRID_ITEM -> {
val binding = ListItemAssistantGridViewBinding.inflate(
LayoutInflater.from(parent.context), parent, false
)
AssistantSelectorGridItemViewHolder(binding)
}

Constants.VIEW_TYPE_REORDER_TIP -> {
val binding = ListItemReorderTipBinding.inflate(
LayoutInflater.from(parent.context), parent, false
Expand All @@ -55,15 +66,17 @@ class AssistantSelectorAdapter(
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, pos: Int) {
when (val item = getItem(pos)) {
is AssistantSelectorRecyclerView.CategoryHeader -> (holder as AssistantSelectorCategoryViewHolder).bind(
item
item, isGridMode
)

is AssistantSelectorRecyclerView.AssistantSelector -> (holder as AssistantSelectorItemViewHolder).bind(
item, onAssistantClicked, onPinClicked
)
is AssistantSelectorRecyclerView.AssistantSelector -> when (holder) {
is AssistantSelectorItemViewHolder -> holder.bind(item, onAssistantClicked, onPinClicked)
is AssistantSelectorGridItemViewHolder -> holder.bind(item, onAssistantClicked, onPinClicked)
else -> Unit
}

is AssistantSelectorRecyclerView.ReorderTip -> (holder as ReorderTipViewHolder).bind(
onDismissTipClicked
onDismissTipClicked, isGridMode
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.core.widget.doOnTextChanged
import androidx.fragment.app.viewModels
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
Expand Down Expand Up @@ -42,12 +43,19 @@ class AssistantSelectorBottomSheet : BaseBottomSheet<FragmentAssistantDialogBind

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupTitle()
setupRecyclerView()
setupObservers()
setupSearch()
setupReorder()
}

private fun setupTitle() {
val isTitleVisible =
preferenceHelper.getBoolean(Constants.ASSISTANT_SELECTOR_TITLE_PREF_KEY, true)
titleTextView.isVisible = isTitleVisible
}

private fun setupSearch() {
val isSearchBarEnabled =
preferenceHelper.getBoolean(Constants.ASSISTANT_SEARCH_BAR_PREF_KEY, true)
Expand All @@ -62,6 +70,7 @@ class AssistantSelectorBottomSheet : BaseBottomSheet<FragmentAssistantDialogBind
}

private fun setupRecyclerView() {
val isGridMode = preferenceHelper.getBoolean(Constants.ASSISTANT_GRID_VIEW_PREF_KEY, false)
assistantSelectorAdapter = AssistantSelectorAdapter(onAssistantClicked = { assistantKey ->
openAssistant(assistantKey)
viewModel.updateRecentlyUsedAssistants(assistantKey)
Expand All @@ -70,9 +79,51 @@ class AssistantSelectorBottomSheet : BaseBottomSheet<FragmentAssistantDialogBind
viewModel.togglePinAssistant(assistantKey)
}, onDismissTipClicked = {
viewModel.dismissReorderTip()
})
}, isGridMode = isGridMode)
binding.assistantsRecyclerView.apply {
layoutManager = LinearLayoutManager(context)
if (isGridMode) {
val isLandscape = resources.configuration.orientation ==
android.content.res.Configuration.ORIENTATION_LANDSCAPE
val userPref = if (isLandscape) {
preferenceHelper.getString(
Constants.ASSISTANT_GRID_COLUMNS_LAND_PREF_KEY, "0"
)?.toIntOrNull() ?: 0
} else {
preferenceHelper.getString(
Constants.ASSISTANT_GRID_COLUMNS_PREF_KEY, "0"
)?.toIntOrNull() ?: 0
}
val autoSpan = run {
val isLowDensity = resources.displayMetrics.densityDpi <=
android.util.DisplayMetrics.DENSITY_MEDIUM
when {
isLandscape && isLowDensity -> 3
isLandscape -> 4
isLowDensity -> 2
else -> 3
}
}
val spanCount = if (userPref > 0) userPref else autoSpan
val gridLayoutManager = GridLayoutManager(context, spanCount)
gridLayoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
return when (assistantSelectorAdapter.getItemViewType(position)) {
Constants.VIEW_TYPE_ASSISTANT_GRID_ITEM -> 1
else -> spanCount
}
}
}
val tv = android.util.TypedValue()
context.theme.resolveAttribute(android.R.attr.dialogPreferredPadding, tv, true)
val dialogPadding = android.util.TypedValue.complexToDimensionPixelSize(tv.data, resources.displayMetrics)
val itemMargin = (3 * resources.displayMetrics.density).toInt()
val horizontalPadding = dialogPadding - itemMargin
setPadding(horizontalPadding, 0, horizontalPadding, paddingBottom)
scrollBarStyle = View.SCROLLBARS_OUTSIDE_INSET
layoutManager = gridLayoutManager
} else {
layoutManager = LinearLayoutManager(context)
}
adapter = assistantSelectorAdapter
}
}
Expand Down Expand Up @@ -120,7 +171,9 @@ class AssistantSelectorBottomSheet : BaseBottomSheet<FragmentAssistantDialogBind
private class PinnedItemReorderCallback(
private val adapter: AssistantSelectorAdapter,
private val onReorderFinished: (List<AssistantItem>) -> Unit
) : ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP or ItemTouchHelper.DOWN, 0) {
) : ItemTouchHelper.SimpleCallback(
ItemTouchHelper.UP or ItemTouchHelper.DOWN or ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT, 0
) {

override fun getDragDirs(
recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,13 @@ class AssistantSelectorCategoryViewHolder(
private val binding: ListItemAssistantCategoryBinding
) : RecyclerView.ViewHolder(binding.root) {

fun bind(categoryHeader: AssistantSelectorRecyclerView.CategoryHeader) {
fun bind(categoryHeader: AssistantSelectorRecyclerView.CategoryHeader, isGridMode: Boolean = false) {
binding.categoryTitleTextView.text = categoryHeader.title
binding.categoryCountChip.text = categoryHeader.count.toString()
val density = binding.root.resources.displayMetrics.density
val startPadding = ((if (isGridMode) 8 else 40) * density).toInt()
val endPadding = ((if (isGridMode) 8 else 28) * density).toInt()
val topPadding = (12 * density).toInt()
binding.root.setPadding(startPadding, topPadding, endPadding, 0)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.wstxda.switchai.ui.viewholder

import android.view.View
import androidx.recyclerview.widget.RecyclerView
import com.wstxda.switchai.R
import com.wstxda.switchai.databinding.ListItemAssistantGridViewBinding
import com.wstxda.switchai.ui.adapter.AssistantSelectorRecyclerView
import com.wstxda.switchai.ui.utils.VibrationService.buttonVibration

class AssistantSelectorGridItemViewHolder(
private val binding: ListItemAssistantGridViewBinding,
) : RecyclerView.ViewHolder(binding.root) {

fun bind(
wrapper: AssistantSelectorRecyclerView.AssistantSelector,
onAssistantClicked: (String) -> Unit,
onPinClicked: (String) -> Unit,
) {
val item = wrapper.assistantItem
binding.assistantCheckedTextView.text = item.name

val isItemEnabled = item.isInstalled
binding.pinButton.visibility = if (isItemEnabled) View.VISIBLE else View.GONE

binding.assistantIcon.setImageResource(
if (item.iconRes != 0) item.iconRes else R.drawable.ic_assistant
)

binding.pinButton.setIconResource(
if (item.isPinned) R.drawable.ic_pin_filled else R.drawable.ic_pin_outline
)

binding.pinButton.setOnClickListener {
onPinClicked(item.key)
it.context.buttonVibration()
}

itemView.setOnClickListener {
onAssistantClicked(item.key)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,15 @@ class ReorderTipViewHolder(
private val binding: ListItemReorderTipBinding
) : RecyclerView.ViewHolder(binding.root) {

fun bind(onDismissClicked: () -> Unit) {
fun bind(onDismissClicked: () -> Unit, isGridMode: Boolean = false) {
binding.dismissButton.setOnClickListener {
onDismissClicked()
}
if (isGridMode) {
val lp = binding.root.layoutParams as? android.view.ViewGroup.MarginLayoutParams
lp?.marginStart = 0
lp?.marginEnd = 0
binding.root.layoutParams = lp
}
}
}
6 changes: 6 additions & 0 deletions app/src/main/java/com/wstxda/switchai/utils/Constants.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ object Constants {
const val DIGITAL_ASSISTANT_SELECT_PREF_KEY = "digital_assistant_select"
const val ASSISTANT_SELECTOR_DIALOG_PREF_KEY = "assistant_selector_dialog"
const val ASSISTANT_SEARCH_BAR_PREF_KEY = "assistant_search_bar"
const val ASSISTANT_GRID_VIEW_PREF_KEY = "assistant_grid_view"
const val ASSISTANT_GRID_COLUMNS_PREF_KEY = "assistant_grid_columns"
const val ASSISTANT_GRID_COLUMNS_LAND_PREF_KEY = "assistant_grid_columns_land"
const val ASSISTANT_SELECTOR_TITLE_PREF_KEY = "assistant_selector_title"
const val ASSISTANT_MANAGER_MANUAL_PREF_KEY = "assistant_manager_manual"
const val ASSISTANT_MANAGER_DYNAMIC_PREF_KEY = "assistant_manager_dynamic"
const val OPEN_ASSISTANT_TILE_PREF_KEY = "open_assistant_tile"
Expand Down Expand Up @@ -51,6 +55,8 @@ object Constants {
const val VIEW_TYPE_CATEGORY_HEADER = 0
const val VIEW_TYPE_ASSISTANT_ITEM = 1
const val VIEW_TYPE_REORDER_TIP = 2
const val VIEW_TYPE_ASSISTANT_GRID_ITEM = 3
const val GRID_SPAN_COUNT = 3
// Maximum number of recently used assistants
const val CAT_MAX_RECENTLY_USED = 3
// Category for AssistantSelectorBottomSheet
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ class AssistantSelectorViewModel(application: Application) : AndroidViewModel(ap
private val _isDynamicModeEnabled = MutableLiveData<Boolean>()
val isDynamicModeEnabled: LiveData<Boolean> = _isDynamicModeEnabled

private val _isGridViewEnabled = MutableLiveData<Boolean>()
val isGridViewEnabled: LiveData<Boolean> = _isGridViewEnabled

private val pinnedAssistantKeys = mutableListOf<String>()
private val recentlyUsedAssistants = mutableListOf<Pair<String, Long>>()

Expand Down Expand Up @@ -77,9 +80,15 @@ class AssistantSelectorViewModel(application: Application) : AndroidViewModel(ap
Constants.ASSISTANT_MANAGER_DYNAMIC_PREF_KEY, false
)

private val isGridView: Boolean
get() = defaultSharedPreferences.getBoolean(
Constants.ASSISTANT_GRID_VIEW_PREF_KEY, false
)

init {
defaultSharedPreferences.registerOnSharedPreferenceChangeListener(this)
_isDynamicModeEnabled.value = isDynamicMode
_isGridViewEnabled.value = isGridView

val intentFilter = IntentFilter().apply {
addAction(Intent.ACTION_PACKAGE_ADDED)
Expand Down Expand Up @@ -347,6 +356,7 @@ class AssistantSelectorViewModel(application: Application) : AndroidViewModel(ap
_isDynamicModeEnabled.value = isDynamicMode
loadAssistants()
}
Constants.ASSISTANT_GRID_VIEW_PREF_KEY -> _isGridViewEnabled.value = isGridView
}
}

Expand Down
9 changes: 9 additions & 0 deletions app/src/main/res/drawable/ic_grid.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M3,3h7v7H3zM14,3h7v7h-7zM3,14h7v7H3zM14,14h7v7h-7z" />
</vector>
76 changes: 76 additions & 0 deletions app/src/main/res/layout/list_item_assistant_grid_view.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
style="?attr/materialCardViewFilledStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="2dp"
android:clickable="true"
android:focusable="false"
app:cardBackgroundColor="?attr/colorSecondaryContainer"
app:cardCornerRadius="16dp">

<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical"
android:paddingTop="10dp"
android:paddingBottom="8dp"
android:paddingHorizontal="2dp">

<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/assistant_icon"
android:layout_width="32dp"
android:layout_height="40dp"
android:tint="?attr/colorSecondary"
tools:src="@drawable/ic_assistant_chatgpt" />

<com.google.android.material.textview.MaterialTextView
android:id="@+id/assistant_checked_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:ellipsize="end"
android:maxLines="2"
android:textAlignment="center"
android:textAppearance="?attr/textAppearanceLabelSmall"
android:textColor="?attr/colorOnSecondaryContainer"
tools:text="ChatGPT" />

</LinearLayout>

<FrameLayout
android:id="@+id/pin_button_container"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="top|end"
android:layout_margin="2dp">

<com.google.android.material.button.MaterialButton
android:id="@+id/pin_button"
style="@style/Widget.Material3Expressive.Button.IconButton.Tonal"
android:layout_width="26dp"
android:layout_height="26dp"
android:layout_gravity="center"
android:gravity="center"
android:insetLeft="0dp"
android:insetTop="0dp"
android:insetRight="0dp"
android:insetBottom="0dp"
app:backgroundTint="?attr/colorPrimaryInverse"
app:iconGravity="textStart"
app:iconPadding="0dp"
app:iconSize="14dp"
app:iconTint="?attr/colorOnPrimaryContainer"
tools:icon="@drawable/ic_pin_filled" />

</FrameLayout>

</FrameLayout>

</com.google.android.material.card.MaterialCardView>
Loading