Make number of grid columns adjustable
This commit is contained in:
parent
dded695b81
commit
185e54bab2
@ -10,24 +10,32 @@
|
|||||||
app:title="@string/preference_theme" />
|
app:title="@string/preference_theme" />
|
||||||
|
|
||||||
<ListPreference
|
<ListPreference
|
||||||
app:key="card_background"
|
|
||||||
android:defaultValue="0"
|
android:defaultValue="0"
|
||||||
app:summary="%s"
|
|
||||||
app:title="@string/preference_screen_colors"
|
|
||||||
app:entries="@array/preference_card_background_entries"
|
app:entries="@array/preference_card_background_entries"
|
||||||
app:entryValues="@array/preference_card_background_values"
|
app:entryValues="@array/preference_card_background_values"
|
||||||
/>
|
app:key="card_background"
|
||||||
|
app:summary="%s"
|
||||||
|
app:title="@string/preference_screen_colors" />
|
||||||
<Preference
|
<Preference
|
||||||
app:key="cards"
|
app:key="cards"
|
||||||
app:summary="@string/preference_cards_summary"
|
app:summary="@string/preference_cards_summary"
|
||||||
app:title="@string/preference_cards" />
|
app:title="@string/preference_cards" />
|
||||||
|
|
||||||
|
<PreferenceCategory app:title="@string/preference_category_grid">
|
||||||
|
<SeekBarPreference
|
||||||
|
app:key="grid_column_count"
|
||||||
|
app:title="@string/preference_grid_column_count"
|
||||||
|
app:min="3"
|
||||||
|
android:max="8"
|
||||||
|
app:defaultValue="@integer/config_columnCount"
|
||||||
|
app:showSeekBarValue="true" />
|
||||||
|
</PreferenceCategory>
|
||||||
<PreferenceCategory app:title="@string/preference_category_icons">
|
<PreferenceCategory app:title="@string/preference_category_icons">
|
||||||
<SwitchPreference
|
<SwitchPreference
|
||||||
app:key="themed_icons"
|
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
app:title="@string/preference_themed_icons"
|
app:key="themed_icons"
|
||||||
app:summary="@string/preference_themed_icons_summary"
|
app:summary="@string/preference_themed_icons_summary"
|
||||||
/>
|
app:title="@string/preference_themed_icons" />
|
||||||
<ListPreference
|
<ListPreference
|
||||||
app:key="icon_pack"
|
app:key="icon_pack"
|
||||||
app:persistent="false"
|
app:persistent="false"
|
||||||
|
|||||||
@ -416,4 +416,7 @@
|
|||||||
<string name="preference_themed_icons">Eingefärbte Symbole</string>
|
<string name="preference_themed_icons">Eingefärbte Symbole</string>
|
||||||
<string name="preference_themed_icons_summary">Symbole an das Farbschema der App anpassen</string>
|
<string name="preference_themed_icons_summary">Symbole an das Farbschema der App anpassen</string>
|
||||||
<string name="weather_no_data">Keine Wetterdaten verfügbar.</string>
|
<string name="weather_no_data">Keine Wetterdaten verfügbar.</string>
|
||||||
|
|
||||||
|
<string name="preference_category_grid">Raster</string>
|
||||||
|
<string name="preference_grid_column_count">Spaltenanzahl</string>
|
||||||
</resources>
|
</resources>
|
||||||
@ -454,4 +454,7 @@
|
|||||||
<string name="feature_not_available">This feature is not available in this version of %1$s</string>
|
<string name="feature_not_available">This feature is not available in this version of %1$s</string>
|
||||||
<string name="preference_category_clock_widget">Clock</string>
|
<string name="preference_category_clock_widget">Clock</string>
|
||||||
<string name="preference_clock_widget_style">Clock style</string>
|
<string name="preference_clock_widget_style">Clock style</string>
|
||||||
|
|
||||||
|
<string name="preference_category_grid">Grid</string>
|
||||||
|
<string name="preference_grid_column_count">Number of columns</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@ -66,6 +66,7 @@ dependencies {
|
|||||||
|
|
||||||
implementation(project(":ktx"))
|
implementation(project(":ktx"))
|
||||||
implementation(project(":i18n"))
|
implementation(project(":i18n"))
|
||||||
|
implementation(project(":base"))
|
||||||
implementation(project(":crashreporter"))
|
implementation(project(":crashreporter"))
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -106,6 +106,8 @@ class LauncherPreferences(val context: Application, version: Int = 3) {
|
|||||||
|
|
||||||
var easterEggEnabled by BooleanPreference("easter_egg", default = false)
|
var easterEggEnabled by BooleanPreference("easter_egg", default = false)
|
||||||
|
|
||||||
|
var gridColumnCount by IntPreference("grid_column_count", default = context.resources.getInteger(R.integer.config_columnCount))
|
||||||
|
|
||||||
|
|
||||||
fun doOnPreferenceChange(vararg keys: String, action: (String) -> Unit) {
|
fun doOnPreferenceChange(vararg keys: String, action: (String) -> Unit) {
|
||||||
preferences.registerOnSharedPreferenceChangeListener { _, key ->
|
preferences.registerOnSharedPreferenceChangeListener { _, key ->
|
||||||
|
|||||||
@ -332,8 +332,6 @@ class LauncherActivity : AppCompatActivity() {
|
|||||||
).apply {
|
).apply {
|
||||||
setMargins((8 * dp).toInt())
|
setMargins((8 * dp).toInt())
|
||||||
}
|
}
|
||||||
hiddenItemsGrid.columnCount =
|
|
||||||
resources.getInteger(R.integer.config_columnCount)
|
|
||||||
val hiddenItems = favoritesViewModel.hiddenItems
|
val hiddenItems = favoritesViewModel.hiddenItems
|
||||||
hiddenItems.observe(this) {
|
hiddenItems.observe(this) {
|
||||||
hiddenItemsGrid.submitItems(it)
|
hiddenItemsGrid.submitItems(it)
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import android.widget.FrameLayout
|
|||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.lifecycle.*
|
import androidx.lifecycle.*
|
||||||
import de.mm20.launcher2.favorites.FavoritesViewModel
|
import de.mm20.launcher2.favorites.FavoritesViewModel
|
||||||
|
import de.mm20.launcher2.preferences.LauncherPreferences
|
||||||
import de.mm20.launcher2.search.data.Searchable
|
import de.mm20.launcher2.search.data.Searchable
|
||||||
import de.mm20.launcher2.ui.R
|
import de.mm20.launcher2.ui.R
|
||||||
import de.mm20.launcher2.ui.databinding.ViewFavoritesBinding
|
import de.mm20.launcher2.ui.databinding.ViewFavoritesBinding
|
||||||
@ -27,7 +28,7 @@ class FavoritesView : FrameLayout {
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
val viewModel: FavoritesViewModel by (context as AppCompatActivity).viewModel()
|
val viewModel: FavoritesViewModel by (context as AppCompatActivity).viewModel()
|
||||||
favorites = viewModel.getFavorites(context.resources.getInteger(R.integer.config_columnCount))
|
favorites = viewModel.getFavorites(LauncherPreferences.instance.gridColumnCount)
|
||||||
favorites.observe(context as AppCompatActivity, Observer {
|
favorites.observe(context as AppCompatActivity, Observer {
|
||||||
visibility = if (it?.isEmpty() == true) View.GONE else View.VISIBLE
|
visibility = if (it?.isEmpty() == true) View.GONE else View.VISIBLE
|
||||||
binding.favoritesGrid.submitItems(it)
|
binding.favoritesGrid.submitItems(it)
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import de.mm20.launcher2.ktx.ceilToInt
|
|||||||
import de.mm20.launcher2.ktx.lifecycleScope
|
import de.mm20.launcher2.ktx.lifecycleScope
|
||||||
import de.mm20.launcher2.legacy.helper.ActivityStarter
|
import de.mm20.launcher2.legacy.helper.ActivityStarter
|
||||||
import de.mm20.launcher2.legacy.helper.ActivityStarterCallback
|
import de.mm20.launcher2.legacy.helper.ActivityStarterCallback
|
||||||
|
import de.mm20.launcher2.preferences.LauncherPreferences
|
||||||
import de.mm20.launcher2.search.data.Searchable
|
import de.mm20.launcher2.search.data.Searchable
|
||||||
import de.mm20.launcher2.ui.R
|
import de.mm20.launcher2.ui.R
|
||||||
import de.mm20.launcher2.ui.legacy.searchable.SearchableView
|
import de.mm20.launcher2.ui.legacy.searchable.SearchableView
|
||||||
@ -40,21 +41,22 @@ class SearchGridView : ViewGroup, ActivityStarterCallback {
|
|||||||
|
|
||||||
@ObsoleteCoroutinesApi
|
@ObsoleteCoroutinesApi
|
||||||
private val updateActor = lifecycleScope
|
private val updateActor = lifecycleScope
|
||||||
.actor<List<Searchable>>(Dispatchers.Main, capacity = Channel.CONFLATED) {
|
.actor<List<Searchable>>(Dispatchers.Main, capacity = Channel.CONFLATED) {
|
||||||
for (newItems in channel) {
|
for (newItems in channel) {
|
||||||
val oldItems = currentItems
|
val oldItems = currentItems
|
||||||
val diffResult = withContext(Dispatchers.Default) {
|
val diffResult = withContext(Dispatchers.Default) {
|
||||||
SearchDiffUtil.calculateDiff(oldItems, newItems)
|
SearchDiffUtil.calculateDiff(oldItems, newItems)
|
||||||
}
|
|
||||||
currentItems = newItems
|
|
||||||
applyDiff(diffResult)
|
|
||||||
}
|
}
|
||||||
|
currentItems = newItems
|
||||||
|
applyDiff(diffResult)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ObsoleteCoroutinesApi
|
@ObsoleteCoroutinesApi
|
||||||
fun submitItems(items: List<Searchable>?) {
|
fun submitItems(items: List<Searchable>?) {
|
||||||
if (items == null) return
|
if (items == null) return
|
||||||
if (items.getOrNull(expandedItem)?.key != currentItems.getOrNull(expandedItem)?.key) expandedItem = -1
|
if (items.getOrNull(expandedItem)?.key != currentItems.getOrNull(expandedItem)?.key) expandedItem =
|
||||||
|
-1
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
updateActor.send(items)
|
updateActor.send(items)
|
||||||
}
|
}
|
||||||
@ -79,10 +81,14 @@ class SearchGridView : ViewGroup, ActivityStarterCallback {
|
|||||||
|
|
||||||
constructor(context: Context) : this(context, null)
|
constructor(context: Context) : this(context, null)
|
||||||
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
|
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
|
||||||
constructor(context: Context, attrs: AttributeSet?, defStyleRes: Int) : super(context, attrs, defStyleRes) {
|
constructor(context: Context, attrs: AttributeSet?, defStyleRes: Int) : super(
|
||||||
|
context,
|
||||||
|
attrs,
|
||||||
|
defStyleRes
|
||||||
|
) {
|
||||||
attrs?.let {
|
attrs?.let {
|
||||||
val ta = context.theme.obtainStyledAttributes(it, R.styleable.SearchGridView, 0, defStyleRes)
|
val ta =
|
||||||
columnCount = ta.getInt(R.styleable.SearchGridView_columnCount, 1)
|
context.theme.obtainStyledAttributes(it, R.styleable.SearchGridView, 0, defStyleRes)
|
||||||
rowHeight = ta.getDimensionPixelSize(R.styleable.SearchGridView_rowHeight, -1)
|
rowHeight = ta.getDimensionPixelSize(R.styleable.SearchGridView_rowHeight, -1)
|
||||||
ta.recycle()
|
ta.recycle()
|
||||||
}
|
}
|
||||||
@ -93,11 +99,16 @@ class SearchGridView : ViewGroup, ActivityStarterCallback {
|
|||||||
ActivityStarter.registerCallback(this)
|
ActivityStarter.registerCallback(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
columnCount = LauncherPreferences.instance.gridColumnCount.takeIf { it > 1 }
|
||||||
|
?: context.resources.getInteger(R.integer.config_columnCount)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
||||||
val widthSpec = MeasureSpec.makeMeasureSpec(
|
val widthSpec = MeasureSpec.makeMeasureSpec(
|
||||||
(MeasureSpec.getSize(widthMeasureSpec) - paddingLeft - paddingRight) / columnCount,
|
(MeasureSpec.getSize(widthMeasureSpec) - paddingLeft - paddingRight) / columnCount,
|
||||||
MeasureSpec.EXACTLY
|
MeasureSpec.EXACTLY
|
||||||
)
|
)
|
||||||
val heightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
|
val heightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
|
||||||
|
|
||||||
@ -118,7 +129,10 @@ class SearchGridView : ViewGroup, ActivityStarterCallback {
|
|||||||
|
|
||||||
val width = when (MeasureSpec.getMode(widthMeasureSpec)) {
|
val width = when (MeasureSpec.getMode(widthMeasureSpec)) {
|
||||||
MeasureSpec.EXACTLY -> MeasureSpec.getSize(widthMeasureSpec)
|
MeasureSpec.EXACTLY -> MeasureSpec.getSize(widthMeasureSpec)
|
||||||
MeasureSpec.AT_MOST -> min(colWidth * columnCount + paddingLeft + paddingRight, MeasureSpec.getSize(widthMeasureSpec))
|
MeasureSpec.AT_MOST -> min(
|
||||||
|
colWidth * columnCount + paddingLeft + paddingRight,
|
||||||
|
MeasureSpec.getSize(widthMeasureSpec)
|
||||||
|
)
|
||||||
MeasureSpec.UNSPECIFIED -> colWidth * columnCount + paddingLeft + paddingRight
|
MeasureSpec.UNSPECIFIED -> colWidth * columnCount + paddingLeft + paddingRight
|
||||||
else -> colWidth * columnCount
|
else -> colWidth * columnCount
|
||||||
}
|
}
|
||||||
@ -127,13 +141,16 @@ class SearchGridView : ViewGroup, ActivityStarterCallback {
|
|||||||
val visibleChildCount = children.count { it.visibility != View.GONE }
|
val visibleChildCount = children.count { it.visibility != View.GONE }
|
||||||
val rowCount = (visibleChildCount / columnCount.toFloat()).ceilToInt()
|
val rowCount = (visibleChildCount / columnCount.toFloat()).ceilToInt()
|
||||||
var height = rowHeight * rowCount + (getChildAt(expandedItem)?.measuredHeight
|
var height = rowHeight * rowCount + (getChildAt(expandedItem)?.measuredHeight
|
||||||
?: 0) + paddingTop + paddingBottom
|
?: 0) + paddingTop + paddingBottom
|
||||||
|
|
||||||
if (expandedItem == childCount - 1 && (childCount % columnCount == 1) || expandedItem != -1 && columnCount == 1) {
|
if (expandedItem == childCount - 1 && (childCount % columnCount == 1) || expandedItem != -1 && columnCount == 1) {
|
||||||
height -= rowHeight
|
height -= rowHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
setMeasuredDimension(View.resolveSize(width, widthMeasureSpec), View.resolveSize(height, heightMeasureSpec))
|
setMeasuredDimension(
|
||||||
|
View.resolveSize(width, widthMeasureSpec),
|
||||||
|
View.resolveSize(height, heightMeasureSpec)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
|
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
|
||||||
@ -190,7 +207,8 @@ class SearchGridView : ViewGroup, ActivityStarterCallback {
|
|||||||
postponedDiffs.push(diff)
|
postponedDiffs.push(diff)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val representation = if (columnCount == 1) SearchableView.REPRESENTATION_LIST else SearchableView.REPRESENTATION_GRID
|
val representation =
|
||||||
|
if (columnCount == 1) SearchableView.REPRESENTATION_LIST else SearchableView.REPRESENTATION_GRID
|
||||||
while (diff.isNotEmpty()) {
|
while (diff.isNotEmpty()) {
|
||||||
val action = diff.poll() ?: continue
|
val action = diff.poll() ?: continue
|
||||||
if (action.action == DiffAction.ACTION_INSERT) {
|
if (action.action == DiffAction.ACTION_INSERT) {
|
||||||
@ -241,7 +259,10 @@ class QueueUpdateCallback : ListUpdateCallback {
|
|||||||
|
|
||||||
override fun onRemoved(position: Int, count: Int) {
|
override fun onRemoved(position: Int, count: Int) {
|
||||||
for (i in 1..count) {
|
for (i in 1..count) {
|
||||||
operations += DiffAction(action = DiffAction.ACTION_DELETE, position = position + (count - i))
|
operations += DiffAction(
|
||||||
|
action = DiffAction.ACTION_DELETE,
|
||||||
|
position = position + (count - i)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -17,6 +17,5 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:padding="8dp"
|
android:padding="8dp"
|
||||||
android:clipChildren="false"
|
android:clipChildren="false"
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"/>
|
||||||
app:columnCount="@integer/config_columnCount"/>
|
|
||||||
</de.mm20.launcher2.ui.legacy.view.LauncherCardView>
|
</de.mm20.launcher2.ui.legacy.view.LauncherCardView>
|
||||||
@ -16,6 +16,5 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:clipChildren="false"
|
android:clipChildren="false"
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
android:padding="8dp"
|
android:padding="8dp" />
|
||||||
app:columnCount="@integer/config_columnCount" />
|
|
||||||
</de.mm20.launcher2.ui.legacy.view.LauncherCardView>
|
</de.mm20.launcher2.ui.legacy.view.LauncherCardView>
|
||||||
@ -1,7 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<declare-styleable name="SearchGridView">
|
<declare-styleable name="SearchGridView">
|
||||||
<attr name="columnCount" format="integer" />
|
|
||||||
<attr name="rowHeight" format="dimension" />
|
<attr name="rowHeight" format="dimension" />
|
||||||
</declare-styleable>
|
</declare-styleable>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user