diff --git a/flexera/res/layout/all_apps_icon.xml b/flexera/res/layout/all_apps_icon.xml new file mode 100644 index 0000000000..35e1338262 --- /dev/null +++ b/flexera/res/layout/all_apps_icon.xml @@ -0,0 +1,21 @@ + + + diff --git a/flexera/res/layout/flexera_all_apps_section_header.xml b/flexera/res/layout/flexera_all_apps_section_header.xml new file mode 100644 index 0000000000..4fe50bbf59 --- /dev/null +++ b/flexera/res/layout/flexera_all_apps_section_header.xml @@ -0,0 +1,15 @@ + + + diff --git a/flexera/res/values/flexera_config.xml b/flexera/res/values/flexera_config.xml index 0e872da73f..29f6bb8dfa 100644 --- a/flexera/res/values/flexera_config.xml +++ b/flexera/res/values/flexera_config.xml @@ -14,4 +14,7 @@ false + + + false diff --git a/flexera/res/values/flexera_dimens.xml b/flexera/res/values/flexera_dimens.xml new file mode 100644 index 0000000000..ef0d5370aa --- /dev/null +++ b/flexera/res/values/flexera_dimens.xml @@ -0,0 +1,16 @@ + + + + 64dp + + 46dp + 20dp + 17sp + 24dp + + 56dp + + 44dp + 6dp + 14sp + diff --git a/flexera/src/app/flexera/drawer/FlexeraDrawer.kt b/flexera/src/app/flexera/drawer/FlexeraDrawer.kt new file mode 100644 index 0000000000..4258cfaf13 --- /dev/null +++ b/flexera/src/app/flexera/drawer/FlexeraDrawer.kt @@ -0,0 +1,77 @@ +package app.flexera.drawer + +import android.content.Context +import android.util.TypedValue +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import com.android.launcher3.BubbleTextView +import com.android.launcher3.R +import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem + +/** + * Flexera Launcher's app drawer: a single-column alphabetical list with letter headers + * (PRD AD-2 and AD-3), built on Launcher3's all-apps adapter rather than replacing it. + * + * Launcher3 keeps doing the work it already does well: loading apps, sorting them, live updates, + * click, long-press menu and drag. This object supplies only what differs from the stock grid: + * - the column count ([COLUMNS]), + * - the look of a row ([styleAppRow]; the row layout itself is flexera/res/layout/all_apps_icon.xml, + * which overrides Launcher3's layout of the same name), and + * - a letter header row ([VIEW_TYPE_SECTION_HEADER] and the functions that create and bind it). + */ +object FlexeraDrawer { + + /** The drawer is one column wide. */ + const val COLUMNS = 1 + + /** + * Adapter view type of a letter header row. Launcher3 uses bits 1 to 10 and Lawnchair's search + * results use bits 10 to 22, so this stays clear of both. + */ + const val VIEW_TYPE_SECTION_HEADER = 1 shl 24 + + /** Creates the adapter item for the header that starts the section named [sectionName]. */ + @JvmStatic + fun sectionHeader(sectionName: String): AdapterItem = SectionHeaderItem(sectionName) + + @JvmStatic + fun createSectionHeader(inflater: LayoutInflater, parent: ViewGroup): View = + inflater.inflate(R.layout.flexera_all_apps_section_header, parent, false) + + @JvmStatic + fun bindSectionHeader(view: View, item: AdapterItem) { + (view as TextView).text = (item as? SectionHeaderItem)?.sectionName.orEmpty() + } + + /** + * Finishes turning a freshly inflated all-apps icon into a list row. Launcher3 sizes the view + * as a grid cell (icon above a small label); a row needs its own height, a wider gap between + * icon and name, and a label large enough to read in a list. + */ + @JvmStatic + fun styleAppRow(icon: BubbleTextView) { + val res = icon.resources + icon.layoutParams.height = res.getDimensionPixelSize(R.dimen.flexera_drawer_row_height) + icon.compoundDrawablePadding = res.getDimensionPixelSize(R.dimen.flexera_drawer_row_icon_gap) + icon.setTextSize(TypedValue.COMPLEX_UNIT_PX, res.getDimension(R.dimen.flexera_drawer_row_text_size)) + } + + /** + * How far a row's icon sits from the edge of the list. Launcher3 uses its grid version of + * this number to line the search bar up with the first icon, so the list supplies its own. + */ + @JvmStatic + fun rowStartPadding(context: Context): Int = + context.resources.getDimensionPixelSize(R.dimen.flexera_drawer_row_padding_start) + + /** + * A letter header. It carries its letter so that the adapter's diffing notices when the + * header at a position changes, for example after an app is installed or removed. + */ + class SectionHeaderItem(val sectionName: String) : AdapterItem(VIEW_TYPE_SECTION_HEADER) { + override fun isContentSame(other: AdapterItem): Boolean = + other is SectionHeaderItem && other.sectionName == sectionName + } +} diff --git a/lawnchair/src/app/lawnchair/DeviceProfileOverrides.kt b/lawnchair/src/app/lawnchair/DeviceProfileOverrides.kt index 01b9ad6d36..c20c3671af 100644 --- a/lawnchair/src/app/lawnchair/DeviceProfileOverrides.kt +++ b/lawnchair/src/app/lawnchair/DeviceProfileOverrides.kt @@ -158,8 +158,9 @@ class DeviceProfileOverrides @Inject constructor( fun applyUi(idp: InvariantDeviceProfile) { // apply grid size - idp.numAllAppsColumns = numAllAppsColumns - idp.numDatabaseAllAppsColumns = numAllAppsColumns + // Flexera Launcher: the drawer is a one-column list, whatever the column preference says. + idp.numAllAppsColumns = app.flexera.drawer.FlexeraDrawer.COLUMNS + idp.numDatabaseAllAppsColumns = app.flexera.drawer.FlexeraDrawer.COLUMNS idp.numFolderRows[INDEX_DEFAULT] = numFolderRows idp.numFolderColumns[INDEX_DEFAULT] = numFolderColumns diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 4e22812b41..2bf7f0ce2f 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -1979,6 +1979,14 @@ public class DeviceProfile { /** The margin between the edge of all apps and the edge of the first icon. */ public int getAllAppsIconStartMargin(Context context) { + // Flexera Launcher: in the one-column drawer list the first icon sits at the row's start + // padding. The grid maths below would instead centre it in a full-width cell, which + // squeezed the search bar down to a small pill. + if (numShownAllAppsColumns == app.flexera.drawer.FlexeraDrawer.COLUMNS) { + return (Utilities.isRtl(context.getResources()) ? allAppsPadding.right + : allAppsPadding.left) + + app.flexera.drawer.FlexeraDrawer.rowStartPadding(context); + } int allAppsSpacing; if (isVerticalBarLayout()) { // On phones, the landscape layout uses a different setup. diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java index 7dadc50a76..7301fedb73 100644 --- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java @@ -491,6 +491,17 @@ public class AlphabeticalAppsList implement Log.d(TAG, "Adding apps with sections. HasPrivateApps: " + hasPrivateApps); for (int i = 0; i < appList.size(); i++) { AppInfo info = appList.get(i); + // Flexera Launcher: start each letter group with a header row (PRD AD-3). The header + // takes this position, so the fast-scroll section created below lands on the header. + // Private Space keeps Launcher3's own framing and gets no headers. + if (!hasPrivateApps && !info.sectionName.equals(lastSectionName)) { + mAdapterItems.add(app.flexera.drawer.FlexeraDrawer.sectionHeader(info.sectionName)); + Log.d(TAG, "addAppsWithSections: adding sectionName: " + info.sectionName + + " with header at position: " + position); + lastSectionName = info.sectionName; + mFastScrollerSections.add(new FastScrollSectionInfo(info.sectionName, position)); + position++; + } // Apply decorator to private apps. if (hasPrivateApps) { mAdapterItems.add(AdapterItem.asAppWithDecorationInfo(info, diff --git a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java index 6f995b5ae3..e06e668151 100644 --- a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java +++ b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java @@ -243,7 +243,13 @@ public abstract class BaseAllAppsAdapter ex // Ensure the all apps icon height matches the workspace icons in portrait mode. icon.getLayoutParams().height = mActivityContext.getDeviceProfile().getAllAppsProfile().getCellHeightPx(); + // Flexera Launcher: the drawer is a list, so size and style this as a row. + app.flexera.drawer.FlexeraDrawer.styleAppRow(icon); return new ViewHolder(icon); + case app.flexera.drawer.FlexeraDrawer.VIEW_TYPE_SECTION_HEADER: + // Flexera Launcher: letter header that starts each group in the drawer list. + return new ViewHolder(app.flexera.drawer.FlexeraDrawer.createSectionHeader( + mLayoutInflater, parent)); case VIEW_TYPE_EMPTY_SEARCH: return new ViewHolder(mLayoutInflater.inflate(R.layout.all_apps_empty_search, parent, false)); @@ -325,6 +331,11 @@ public abstract class BaseAllAppsAdapter ex } break; } + case app.flexera.drawer.FlexeraDrawer.VIEW_TYPE_SECTION_HEADER: + // Flexera Launcher: letter header in the drawer list. + app.flexera.drawer.FlexeraDrawer.bindSectionHeader( + holder.itemView, mApps.getAdapterItems().get(position)); + break; case VIEW_TYPE_EMPTY_SEARCH: { AppInfo info = mApps.getAdapterItems().get(position).itemInfo; if (info != null) {