feat : Initial implementation Caddy (#5166)
- Introduced "Caddy" functionality to organize apps within the app drawer. - Allows grouping of applications for better accessibility and user experience. - Serves as a foundational feature for enhancing app drawer usability.
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ This file is part of Lawnchair Launcher.
|
||||
~
|
||||
~ Lawnchair Launcher is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ Lawnchair Launcher is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with Lawnchair Launcher. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<com.android.launcher3.BubbleTextView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:launcher="http://schemas.android.com/apk/res-auto"
|
||||
style="@style/BaseIcon"
|
||||
android:includeFontPadding="false"
|
||||
launcher:iconDisplay="drawer_folder" />
|
||||
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ This file is part of Lawnchair Launcher.
|
||||
~
|
||||
~ Lawnchair Launcher is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ Lawnchair Launcher is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with Lawnchair Launcher. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<com.android.launcher3.folder.FolderIcon
|
||||
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="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="@dimen/dynamic_grid_cell_padding_x"
|
||||
android:paddingRight="@dimen/dynamic_grid_cell_padding_x"
|
||||
android:focusable="true" >
|
||||
<com.android.launcher3.BubbleTextView
|
||||
style="@style/BaseIcon"
|
||||
android:id="@+id/folder_icon_name"
|
||||
android:focusable="false"
|
||||
android:layout_gravity="top"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:iconDisplay="all_apps"/>
|
||||
</com.android.launcher3.folder.FolderIcon>
|
||||
@@ -59,6 +59,7 @@
|
||||
General "words" used throughout Lawnchair
|
||||
|
||||
-->
|
||||
<string name="others_category_label">Others</string>
|
||||
|
||||
<!-- Selection options -->
|
||||
<string name="dynamic">Dynamic</string>
|
||||
@@ -90,6 +91,9 @@
|
||||
|
||||
<string name="wallpapers">Wallpapers</string>
|
||||
|
||||
<string name="caddy_beta">Caddy (Beta)</string>
|
||||
<string name="caddy">Caddy</string>
|
||||
|
||||
<!-- A11y description -->
|
||||
<string name="accessibility_service_description">To lock your phone when performing a gesture, and to open Recents via gesture, Lawnchair requires accessibility access.\n\nLawnchair doesn\'t watch any user action, though the privilege to do so is required for all accessibility services. Lawnchair discards any event sent by the system.\n\nIn order to lock your phone, or to open Recents, Lawnchair uses the performGlobalAction Accessibility service.</string>
|
||||
|
||||
|
||||
@@ -4,19 +4,23 @@ import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import app.lawnchair.launcher
|
||||
import app.lawnchair.preferences.PreferenceManager
|
||||
import app.lawnchair.preferences2.PreferenceManager2
|
||||
import app.lawnchair.util.categorizeApps
|
||||
import com.android.launcher3.allapps.AllAppsStore
|
||||
import com.android.launcher3.allapps.AlphabeticalAppsList
|
||||
import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem
|
||||
import com.android.launcher3.allapps.PrivateProfileManager
|
||||
import com.android.launcher3.allapps.WorkProfileManager
|
||||
import com.android.launcher3.model.data.AppInfo
|
||||
import com.android.launcher3.model.data.FolderInfo
|
||||
import com.android.launcher3.model.data.ItemInfo
|
||||
import com.android.launcher3.views.ActivityContext
|
||||
import com.patrykmichalik.opto.core.onEach
|
||||
import java.util.function.Predicate
|
||||
|
||||
class LawnchairAlphabeticalAppsList<T>(
|
||||
context: T,
|
||||
private val context: T,
|
||||
appsStore: AllAppsStore<T>,
|
||||
workProfileManager: WorkProfileManager?,
|
||||
privateProfileManager: PrivateProfileManager?,
|
||||
@@ -24,11 +28,11 @@ class LawnchairAlphabeticalAppsList<T>(
|
||||
where T : Context, T : ActivityContext {
|
||||
|
||||
private var hiddenApps: Set<String> = setOf()
|
||||
private val prefs = PreferenceManager2.getInstance(context)
|
||||
|
||||
private val prefs2 = PreferenceManager2.getInstance(context)
|
||||
private val prefs = PreferenceManager.getInstance(context)
|
||||
init {
|
||||
try {
|
||||
prefs.hiddenApps.onEach(launchIn = context.launcher.lifecycleScope) {
|
||||
prefs2.hiddenApps.onEach(launchIn = context.launcher.lifecycleScope) {
|
||||
hiddenApps = it
|
||||
onAppsUpdated()
|
||||
}
|
||||
@@ -45,4 +49,35 @@ class LawnchairAlphabeticalAppsList<T>(
|
||||
}
|
||||
onAppsUpdated()
|
||||
}
|
||||
|
||||
override fun addAppsWithSections(appList: List<AppInfo?>?, startPosition: Int): Int {
|
||||
if (appList.isNullOrEmpty()) return startPosition
|
||||
val drawerListDefault = prefs.drawerList.get()
|
||||
var position = startPosition
|
||||
|
||||
if (!drawerListDefault) {
|
||||
val categorizedApps = categorizeApps(context, appList)
|
||||
|
||||
if (categorizedApps.isNotEmpty()) {
|
||||
for ((category, apps) in categorizedApps) {
|
||||
if (apps.size <= 1) {
|
||||
val app = apps[0]
|
||||
mAdapterItems.add(AdapterItem.asApp(app))
|
||||
} else {
|
||||
val folderInfo = FolderInfo()
|
||||
folderInfo.title = category
|
||||
for (app in apps) {
|
||||
folderInfo.add(app)
|
||||
}
|
||||
mAdapterItems.add(AdapterItem.asFolder(folderInfo))
|
||||
}
|
||||
position++
|
||||
}
|
||||
}
|
||||
} else {
|
||||
position = super.addAppsWithSections(appList, startPosition)
|
||||
}
|
||||
|
||||
return position
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,6 +115,8 @@ class PreferenceManager private constructor(private val context: Context) :
|
||||
val wallpaperBlur = IntPref("pref_wallpaperBlur", 25, recreate)
|
||||
val wallpaperBlurFactorThreshold = FloatPref("pref_wallpaperBlurFactor", 3.0F, recreate)
|
||||
|
||||
val drawerList = BoolPref("pref_drawerList", true, recreate)
|
||||
|
||||
val recentsActionScreenshot = BoolPref("pref_recentsActionScreenshot", !isOnePlusStock)
|
||||
val recentsActionShare = BoolPref("pref_recentsActionShare", isOnePlusStock)
|
||||
val recentsActionLens = BoolPref("pref_recentsActionLens", true)
|
||||
|
||||
@@ -53,6 +53,7 @@ fun AppDrawerPreferences(
|
||||
backArrowVisible = !LocalIsExpandedScreen.current,
|
||||
modifier = modifier,
|
||||
) {
|
||||
AppDrawerSettings()
|
||||
PreferenceGroup(heading = stringResource(id = R.string.general_label)) {
|
||||
ColorPreference(preference = prefs2.appDrawerBackgroundColor)
|
||||
SliderPreference(
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
package app.lawnchair.ui.preferences.destinations
|
||||
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import app.lawnchair.preferences.preferenceManager
|
||||
import com.android.launcher3.R
|
||||
|
||||
@Composable
|
||||
fun AppDrawerSettings(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val prefs = preferenceManager()
|
||||
val context = LocalContext.current
|
||||
val resources = context.resources
|
||||
var selectedOption by remember { mutableStateOf(prefs.drawerList.get()) }
|
||||
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.padding(16.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
ButtonSection(
|
||||
modifier = Modifier,
|
||||
label = resources.getString(R.string.feed_default),
|
||||
isSelected = selectedOption,
|
||||
onClick = {
|
||||
selectedOption = true
|
||||
prefs.drawerList.set(true)
|
||||
},
|
||||
gridLayout = {
|
||||
Column(modifier = Modifier, horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Row(
|
||||
modifier = Modifier,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
repeat(4) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(16.dp)
|
||||
.background(
|
||||
MaterialTheme.colorScheme.surfaceVariant,
|
||||
CircleShape,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
|
||||
ButtonSection(
|
||||
modifier = Modifier,
|
||||
label = resources.getString(R.string.caddy_beta),
|
||||
isSelected = !selectedOption,
|
||||
onClick = {
|
||||
selectedOption = false
|
||||
prefs.drawerList.set(false)
|
||||
},
|
||||
gridLayout = {
|
||||
Column(modifier = Modifier, horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Row(
|
||||
modifier = Modifier,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
repeat(2) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(16.dp)
|
||||
.background(
|
||||
MaterialTheme.colorScheme.surfaceVariant,
|
||||
CircleShape,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Row(
|
||||
modifier = Modifier,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
repeat(2) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(16.dp)
|
||||
.background(
|
||||
MaterialTheme.colorScheme.surfaceVariant,
|
||||
CircleShape,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ButtonSection(
|
||||
label: String,
|
||||
isSelected: Boolean,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
gridLayout: @Composable () -> Unit,
|
||||
) {
|
||||
val backgroundColor by animateColorAsState(
|
||||
targetValue = if (isSelected) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.surfaceContainer,
|
||||
animationSpec = tween(durationMillis = 300),
|
||||
)
|
||||
|
||||
val textColor by animateColorAsState(
|
||||
targetValue = if (isSelected) MaterialTheme.colorScheme.onPrimary else MaterialTheme.colorScheme.onSurface,
|
||||
animationSpec = tween(durationMillis = 300),
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = modifier
|
||||
.size(160.dp, 120.dp)
|
||||
.background(backgroundColor, RoundedCornerShape(16.dp))
|
||||
.clickable { onClick() }
|
||||
.padding(12.dp),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.SpaceBetween,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(24.dp)
|
||||
.fillMaxWidth(0.8f)
|
||||
.background(MaterialTheme.colorScheme.surfaceVariant, RoundedCornerShape(8.dp)),
|
||||
)
|
||||
|
||||
gridLayout()
|
||||
|
||||
Text(
|
||||
modifier = Modifier,
|
||||
text = label,
|
||||
color = textColor,
|
||||
fontSize = 14.sp,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun PreviewToggleSelectionUI() {
|
||||
Surface {
|
||||
AppDrawerSettings()
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
package app.lawnchair.util
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.LauncherActivityInfo
|
||||
import android.content.pm.LauncherApps
|
||||
import android.graphics.Bitmap
|
||||
@@ -29,6 +30,7 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import com.android.launcher3.AppFilter
|
||||
import com.android.launcher3.LauncherAppState
|
||||
import com.android.launcher3.R
|
||||
import com.android.launcher3.Utilities
|
||||
import com.android.launcher3.model.data.AppInfo
|
||||
import com.android.launcher3.pm.UserCache
|
||||
@@ -76,3 +78,22 @@ class App(context: Context, private val info: LauncherActivityInfo) {
|
||||
}
|
||||
|
||||
val appComparator: Comparator<App> = comparing { it.label.lowercase(Locale.getDefault()) }
|
||||
val packageInfoCache = mutableMapOf<String, ApplicationInfo>()
|
||||
|
||||
fun categorizeApps(context: Context, appList: List<AppInfo?>?): Map<String, List<AppInfo>> {
|
||||
val categories = mutableMapOf<String, MutableList<AppInfo>>()
|
||||
val validAppList = appList?.filterNotNull() ?: emptyList()
|
||||
for (appInfo in validAppList) {
|
||||
val packageName = appInfo.targetPackage!!
|
||||
val applicationInfo = packageInfoCache.getOrPut(packageName) {
|
||||
context.packageManager.getApplicationInfo(packageName, 0)
|
||||
}
|
||||
val categoryTitle = ApplicationInfo.getCategoryTitle(context, applicationInfo.category) ?: context.resources.getString(R.string.others_category_label)
|
||||
val categoryList = categories.getOrPut(categoryTitle.toString()) { mutableListOf() }
|
||||
if (!categoryList.contains(appInfo)) {
|
||||
categoryList.add(appInfo)
|
||||
}
|
||||
}
|
||||
|
||||
return categories.toSortedMap().mapValues { it.value }
|
||||
}
|
||||
|
||||
@@ -94,6 +94,7 @@
|
||||
<enum name="search_result_small" value="7" />
|
||||
<enum name="prediction_row" value="8" />
|
||||
<enum name="search_result_app_row" value="9" />
|
||||
<enum name="drawer_folder" value="10"/>
|
||||
</attr>
|
||||
<attr name="centerVertically" format="boolean" />
|
||||
</declare-styleable>
|
||||
|
||||
@@ -116,6 +116,8 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||
public static final int DISPLAY_PREDICTION_ROW = 8;
|
||||
public static final int DISPLAY_SEARCH_RESULT_APP_ROW = 9;
|
||||
|
||||
private static final int DISPLAY_DRAWER_FOLDER = 10;
|
||||
|
||||
private static final float MIN_LETTER_SPACING = -0.05f;
|
||||
private static final int MAX_SEARCH_LOOP_COUNT = 20;
|
||||
private static final Character NEW_LINE = '\n';
|
||||
@@ -250,7 +252,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||
defaultIconSize = mDeviceProfile.iconSizePx;
|
||||
setCenterVertically(mDeviceProfile.iconCenterVertically);
|
||||
} else if (mDisplay == DISPLAY_ALL_APPS || mDisplay == DISPLAY_PREDICTION_ROW
|
||||
|| mDisplay == DISPLAY_SEARCH_RESULT_APP_ROW) {
|
||||
|| mDisplay == DISPLAY_SEARCH_RESULT_APP_ROW || mDisplay == DISPLAY_DRAWER_FOLDER) {
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_PX, mDeviceProfile.allAppsIconTextSizePx);
|
||||
setCompoundDrawablePadding(mDeviceProfile.allAppsIconDrawablePaddingPx);
|
||||
defaultIconSize = mDeviceProfile.allAppsIconSizePx;
|
||||
|
||||
@@ -92,7 +92,7 @@ public class AlphabeticalAppsList<T extends Context & ActivityContext> implement
|
||||
// The number of results in current adapter
|
||||
private int mAccessibilityResultsCount = 0;
|
||||
// The current set of adapter items
|
||||
private final ArrayList<AdapterItem> mAdapterItems = new ArrayList<>();
|
||||
protected final ArrayList<AdapterItem> mAdapterItems = new ArrayList<>();
|
||||
// The set of sections that we allow fast-scrolling to (includes non-merged
|
||||
// sections)
|
||||
private final List<FastScrollSectionInfo> mFastScrollerSections = new ArrayList<>();
|
||||
@@ -399,7 +399,7 @@ public class AlphabeticalAppsList<T extends Context & ActivityContext> implement
|
||||
return position;
|
||||
}
|
||||
|
||||
private int addAppsWithSections(List<AppInfo> appList, int startPosition) {
|
||||
protected int addAppsWithSections(List<AppInfo> appList, int startPosition) {
|
||||
String lastSectionName = null;
|
||||
boolean hasPrivateApps = false;
|
||||
int position = startPosition;
|
||||
|
||||
@@ -32,6 +32,7 @@ import android.view.View.OnClickListener;
|
||||
import android.view.View.OnFocusChangeListener;
|
||||
import android.view.View.OnLongClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
@@ -44,7 +45,9 @@ import com.android.launcher3.LauncherPrefs;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.allapps.search.SearchAdapterProvider;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.folder.FolderIcon;
|
||||
import com.android.launcher3.model.data.AppInfo;
|
||||
import com.android.launcher3.model.data.FolderInfo;
|
||||
import com.android.launcher3.util.Themes;
|
||||
import com.android.launcher3.views.ActivityContext;
|
||||
|
||||
@@ -74,11 +77,13 @@ public abstract class BaseAllAppsAdapter<T extends Context & ActivityContext> ex
|
||||
public static final int VIEW_TYPE_PRIVATE_SPACE_SYS_APPS_DIVIDER = 1 << 7;
|
||||
public static final int VIEW_TYPE_WORK_EDU_CARD = 1 << 8;
|
||||
|
||||
public static final int NEXT_ID = 8;
|
||||
public static final int VIEW_TYPE_FOLDER = 1 << 9;
|
||||
|
||||
public static final int NEXT_ID = 10;
|
||||
|
||||
// Common view type masks
|
||||
public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER;
|
||||
public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON;
|
||||
public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON | VIEW_TYPE_FOLDER;
|
||||
|
||||
public static final int VIEW_TYPE_MASK_PRIVATE_SPACE_HEADER = VIEW_TYPE_PRIVATE_SPACE_HEADER;
|
||||
public static final int VIEW_TYPE_MASK_PRIVATE_SPACE_SYS_APPS_DIVIDER = VIEW_TYPE_PRIVATE_SPACE_SYS_APPS_DIVIDER;
|
||||
@@ -115,6 +120,8 @@ public abstract class BaseAllAppsAdapter<T extends Context & ActivityContext> ex
|
||||
public int rowAppIndex;
|
||||
// The associated ItemInfoWithIcon for the item
|
||||
public AppInfo itemInfo = new AppInfo();
|
||||
|
||||
public FolderInfo folderInfo = new FolderInfo();
|
||||
|
||||
// Private App Decorator
|
||||
public SectionDecorationInfo decorationInfo = null;
|
||||
@@ -131,6 +138,12 @@ public abstract class BaseAllAppsAdapter<T extends Context & ActivityContext> ex
|
||||
item.itemInfo = appInfo;
|
||||
return item;
|
||||
}
|
||||
|
||||
public static AdapterItem asFolder(FolderInfo folderInfo) {
|
||||
AdapterItem item = new AdapterItem(VIEW_TYPE_FOLDER);
|
||||
item.folderInfo = folderInfo;
|
||||
return item;
|
||||
}
|
||||
|
||||
public static AdapterItem asAppWithDecorationInfo(AppInfo appInfo,
|
||||
SectionDecorationInfo decorationInfo) {
|
||||
@@ -140,7 +153,7 @@ public abstract class BaseAllAppsAdapter<T extends Context & ActivityContext> ex
|
||||
}
|
||||
|
||||
protected boolean isCountedForAccessibility() {
|
||||
return viewType == VIEW_TYPE_ICON;
|
||||
return viewType == VIEW_TYPE_ICON || viewType == VIEW_TYPE_FOLDER;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -257,6 +270,12 @@ public abstract class BaseAllAppsAdapter<T extends Context & ActivityContext> ex
|
||||
case VIEW_TYPE_PRIVATE_SPACE_HEADER:
|
||||
return new ViewHolder(mLayoutInflater.inflate(
|
||||
R.layout.private_space_header, parent, false));
|
||||
case VIEW_TYPE_FOLDER:
|
||||
FrameLayout fl = new FrameLayout(mActivityContext);
|
||||
ViewGroup.MarginLayoutParams lp = new ViewGroup.MarginLayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
return new ViewHolder(fl);
|
||||
default:
|
||||
if (mAdapterProvider.isViewSupported(viewType)) {
|
||||
return mAdapterProvider.onCreateViewHolder(mLayoutInflater, parent, viewType);
|
||||
@@ -333,6 +352,13 @@ public abstract class BaseAllAppsAdapter<T extends Context & ActivityContext> ex
|
||||
case VIEW_TYPE_WORK_EDU_CARD:
|
||||
((WorkEduCard) holder.itemView).setPosition(position);
|
||||
break;
|
||||
case VIEW_TYPE_FOLDER:
|
||||
FolderInfo folderInfo = mApps.getAdapterItems().get(position).folderInfo;
|
||||
ViewGroup container = (ViewGroup) holder.itemView;
|
||||
container.removeAllViews();
|
||||
container.addView(FolderIcon.inflateFolderAndIcon(R.layout.all_apps_folder_icon, mActivityContext,
|
||||
container, folderInfo));
|
||||
break;
|
||||
default:
|
||||
if (mAdapterProvider.isViewSupported(holder.getItemViewType())) {
|
||||
mAdapterProvider.onBindView(holder, position);
|
||||
|
||||
@@ -403,6 +403,8 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
if (dragObject.dragSource != this) {
|
||||
return;
|
||||
}
|
||||
if (isInAppDrawer()) close(true);
|
||||
|
||||
mContent.removeItem(mCurrentDragView);
|
||||
mItemsInvalidated = true;
|
||||
|
||||
@@ -415,6 +417,10 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
mItemAddedBackToSelfViaIcon = false;
|
||||
}
|
||||
|
||||
public boolean isInAppDrawer() {
|
||||
return mInfo.container == ItemInfo.NO_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDragEnd() {
|
||||
if (mIsExternalDrag && mIsDragInProgress) {
|
||||
@@ -583,7 +589,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
}
|
||||
// In case any children didn't come across during loading, clean up the folder accordingly
|
||||
mFolderIcon.post(() -> {
|
||||
if (getItemCount() <= 1) {
|
||||
if (getItemCount() <= 1 && !isInAppDrawer()) {
|
||||
replaceFolderWithFinalItem();
|
||||
}
|
||||
});
|
||||
@@ -971,7 +977,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
mRearrangeOnClose = false;
|
||||
}
|
||||
if (getItemCount() <= 1) {
|
||||
if (!mIsDragInProgress && !mSuppressFolderDeletion) {
|
||||
if (!mIsDragInProgress && !mSuppressFolderDeletion && !isInAppDrawer()) {
|
||||
replaceFolderWithFinalItem();
|
||||
} else if (mIsDragInProgress) {
|
||||
mDeleteFolderOnDropCompleted = true;
|
||||
|
||||
@@ -207,7 +207,12 @@ public class FolderIcon extends FrameLayout implements FolderListener, FloatingI
|
||||
icon.mFolderName.setText(folderInfo.title);
|
||||
icon.mFolderName.setCompoundDrawablePadding(0);
|
||||
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) icon.mFolderName.getLayoutParams();
|
||||
lp.topMargin = grid.iconSizePx + grid.iconDrawablePaddingPx;
|
||||
if (folderInfo.container == ItemInfo.NO_ID) {
|
||||
lp.topMargin = grid.allAppsIconSizePx + grid.allAppsIconDrawablePaddingPx;
|
||||
icon.mBackground = new PreviewBackground(activity.getDragLayer().getContext());
|
||||
} else {
|
||||
lp.topMargin = grid.iconSizePx + grid.iconDrawablePaddingPx;
|
||||
}
|
||||
|
||||
icon.setTag(folderInfo);
|
||||
icon.setOnClickListener(activity.getItemOnClickListener());
|
||||
@@ -344,7 +349,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, FloatingI
|
||||
final Launcher launcher = (Launcher) mActivity;
|
||||
DragLayer dragLayer = launcher.getDragLayer();
|
||||
Rect to = finalRect;
|
||||
if (to == null) {
|
||||
if (to == null && !isInAppDrawer()) {
|
||||
to = new Rect();
|
||||
Workspace<?> workspace = launcher.getWorkspace();
|
||||
// Set cellLayout and this to it's final state to compute final animation locations
|
||||
@@ -434,6 +439,10 @@ public class FolderIcon extends FrameLayout implements FolderListener, FloatingI
|
||||
addItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isInAppDrawer() {
|
||||
return mInfo.container == ItemInfo.NO_ID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the suggested folder name.
|
||||
@@ -772,12 +781,14 @@ public class FolderIcon extends FrameLayout implements FolderListener, FloatingI
|
||||
}
|
||||
|
||||
public void clearLeaveBehindIfExists() {
|
||||
if (isInAppDrawer()) return;
|
||||
if (getParent() instanceof FolderIconParent) {
|
||||
((FolderIconParent) getParent()).clearFolderLeaveBehind(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void drawLeaveBehindIfExists() {
|
||||
if (isInAppDrawer()) return;
|
||||
if (getParent() instanceof FolderIconParent) {
|
||||
((FolderIconParent) getParent()).drawFolderLeaveBehindForIcon(this);
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ import com.android.launcher3.AbstractFloatingView;
|
||||
import com.android.launcher3.BubbleTextView;
|
||||
import com.android.launcher3.CellLayout;
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.Item;
|
||||
import com.android.launcher3.PagedView;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.ShortcutAndWidgetContainer;
|
||||
@@ -46,6 +47,7 @@ import com.android.launcher3.celllayout.CellLayoutLayoutParams;
|
||||
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
|
||||
import com.android.launcher3.model.data.AppPairInfo;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.model.data.ItemInfoWithIcon;
|
||||
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||
import com.android.launcher3.pageindicators.PageIndicatorDots;
|
||||
import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator;
|
||||
@@ -225,8 +227,13 @@ public class FolderPagedView extends PagedView<PageIndicatorDots> implements Cli
|
||||
icon = AppPairIcon.inflateIcon(R.layout.folder_app_pair, ActivityContext.lookupContext(
|
||||
getContext()), null , api, BubbleTextView.DISPLAY_FOLDER);
|
||||
} else {
|
||||
icon = mViewCache.getView(R.layout.folder_application, getContext(), null);
|
||||
((BubbleTextView) icon).applyFromWorkspaceItem((WorkspaceItemInfo) item);
|
||||
if (mFolder.isInAppDrawer()) {
|
||||
icon = mViewCache.getView(R.layout.all_apps_folder_application, getContext(), null);
|
||||
((BubbleTextView) icon).applyFromItemInfoWithIcon((ItemInfoWithIcon) item);
|
||||
} else {
|
||||
icon = mViewCache.getView(R.layout.folder_application, getContext(), null);
|
||||
((BubbleTextView) icon).applyFromWorkspaceItem((WorkspaceItemInfo) item);
|
||||
}
|
||||
}
|
||||
|
||||
icon.setOnClickListener(mFolder.mActivityContext.getItemOnClickListener());
|
||||
|
||||
@@ -35,6 +35,7 @@ import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.FloatProperty;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -47,6 +48,7 @@ import com.android.launcher3.apppairs.AppPairIconDrawingParams;
|
||||
import com.android.launcher3.apppairs.AppPairIconGraphic;
|
||||
import com.android.launcher3.graphics.PreloadIconDrawable;
|
||||
import com.android.launcher3.model.data.AppPairInfo;
|
||||
import com.android.launcher3.model.data.FolderInfo;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.model.data.ItemInfoWithIcon;
|
||||
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||
@@ -463,6 +465,10 @@ public class PreviewItemManager {
|
||||
AppPairIconDrawingParams appPairParams = new AppPairIconDrawingParams(mContext, DISPLAY_FOLDER);
|
||||
p.drawable = AppPairIconGraphic.composeDrawable(api, appPairParams);
|
||||
p.drawable.setBounds(0, 0, mIconSize, mIconSize);
|
||||
} else if (item instanceof ItemInfoWithIcon withIcon){
|
||||
p.drawable = withIcon.newIcon(mContext,
|
||||
Themes.isThemedIconEnabled(mContext) ? FLAG_THEMED : 0);
|
||||
p.drawable.setBounds(0, 0, mIconSize, mIconSize);
|
||||
}
|
||||
|
||||
p.item = item;
|
||||
|
||||
Reference in New Issue
Block a user