Adding SecondaryDisplayLauncher in Launcher using common listener

Bug: 141596722
Change-Id: I480bfadf592f7d0309f17c33a3fe14bb77fb5586
This commit is contained in:
Sunny Goyal 2020-01-07 13:07:55 -08:00
parent ae5e991f5e
commit 9c2b96090b
33 changed files with 1369 additions and 255 deletions

View File

@ -78,9 +78,8 @@ include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
LOCAL_MODULE_TAGS := optional
LOCAL_STATIC_ANDROID_LIBRARIES := \
Launcher3CommonDepsLib \
SecondaryDisplayLauncherLib
LOCAL_STATIC_ANDROID_LIBRARIES := Launcher3CommonDepsLib
LOCAL_SRC_FILES := \
$(call all-java-files-under, src) \
$(call all-java-files-under, src_shortcuts_overrides) \
@ -154,9 +153,7 @@ else
endif
LOCAL_MODULE := Launcher3QuickStepLib
LOCAL_PRIVILEGED_MODULE := true
LOCAL_STATIC_ANDROID_LIBRARIES := \
Launcher3CommonDepsLib \
SecondaryDisplayLauncherLib
LOCAL_STATIC_ANDROID_LIBRARIES := Launcher3CommonDepsLib
LOCAL_SRC_FILES := \
$(call all-java-files-under, src) \

View File

@ -184,5 +184,20 @@
android:writePermission="android.permission.WRITE_SECURE_SETTINGS"
android:exported="true"
android:enabled="false" />
<!--
Launcher activity for secondary display
-->
<activity
android:name="com.android.launcher3.secondarydisplay.SecondaryDisplayLauncher"
android:theme="@style/AppTheme"
android:launchMode="singleTop"
android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.SECONDARY_HOME" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2020, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?android:attr/colorControlHighlight">
<item android:id="@android:id/mask">
<shape android:shape="oval" >
<solid android:color="#ffffffff" />
</shape>
</item>
<item>
<shape android:shape="oval" >
<solid android:color="?android:attr/colorAccent" />
</shape>
</item>
</ripple>

22
res/drawable/ic_apps.xml Normal file
View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2018 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M4,8h4L8,4L4,4v4zM10,20h4v-4h-4v4zM4,20h4v-4L4,16v4zM4,14h4v-4L4,10v4zM10,14h4v-4h-4v4zM16,4v4h4L20,4h-4zM10,8h4L14,4h-4v4zM16,14h4v-4h-4v4zM16,20h4v-4h-4v4z"/>
</vector>

View File

@ -16,7 +16,7 @@
<!-- The top and bottom paddings are defined in this container, but since we want
the list view to span the full width (for touch interception purposes), we
will bake the left/right padding into that view's background itself. -->
<com.android.launcher3.allapps.AllAppsContainerView
<com.android.launcher3.allapps.LauncherAllAppsContainerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/apps_view"
android:layout_width="match_parent"
@ -79,4 +79,4 @@
layout="@layout/search_container_all_apps"/>
<include layout="@layout/all_apps_fast_scroller" />
</com.android.launcher3.allapps.AllAppsContainerView>
</com.android.launcher3.allapps.LauncherAllAppsContainerView>

View File

@ -0,0 +1,127 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2020 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<com.android.launcher3.secondarydisplay.SecondaryDragLayer
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/drag_layer" >
<GridView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="100dp"
android:theme="@style/HomeScreenElementTheme"
android:layout_gravity="center_horizontal|top"
android:layout_margin="@dimen/dynamic_grid_edge_margin"
android:id="@+id/workspace_grid" />
<ImageButton
android:id="@+id/all_apps_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="40dp"
android:padding="16dp"
android:src="@drawable/ic_apps"
android:background="@drawable/bg_all_apps_button"
android:contentDescription="@string/all_apps_button_label"
android:onClick="onAppsButtonClicked" />
<com.android.launcher3.allapps.AllAppsContainerView
android:id="@+id/apps_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="true"
android:clipToPadding="false"
android:focusable="false"
android:saveEnabled="false"
android:layout_gravity="bottom|end"
android:background="@drawable/round_rect_primary"
android:elevation="2dp"
android:visibility="invisible" >
<include
layout="@layout/all_apps_rv_layout"
android:visibility="gone" />
<com.android.launcher3.allapps.FloatingHeaderView
android:id="@+id/all_apps_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/search_container_all_apps"
android:clipToPadding="false"
android:paddingTop="@dimen/all_apps_header_top_padding"
android:orientation="vertical" >
<com.android.launcher3.allapps.PersonalWorkSlidingTabStrip
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="@dimen/all_apps_header_tab_height"
android:layout_marginLeft="@dimen/all_apps_tabs_side_padding"
android:layout_marginRight="@dimen/all_apps_tabs_side_padding"
android:orientation="horizontal"
style="@style/TextHeadline">
<Button
android:id="@+id/tab_personal"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="?android:attr/selectableItemBackground"
android:text="@string/all_apps_personal_tab"
android:textAllCaps="true"
android:textColor="@color/all_apps_tab_text"
android:textSize="14sp" />
<Button
android:id="@+id/tab_work"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="?android:attr/selectableItemBackground"
android:text="@string/all_apps_work_tab"
android:textAllCaps="true"
android:textColor="@color/all_apps_tab_text"
android:textSize="14sp" />
</com.android.launcher3.allapps.PersonalWorkSlidingTabStrip>
</com.android.launcher3.allapps.FloatingHeaderView>
<com.android.launcher3.allapps.search.AppsSearchContainerLayout
android:id="@id/search_container_all_apps"
android:layout_width="match_parent"
android:layout_height="@dimen/all_apps_search_bar_field_height"
android:layout_centerHorizontal="true"
android:layout_gravity="top|center_horizontal"
android:background="@drawable/bg_all_apps_searchbox"
android:elevation="1dp"
android:focusableInTouchMode="true"
android:gravity="center"
android:hint="@string/all_apps_search_bar_hint"
android:imeOptions="actionSearch|flagNoExtractUi"
android:inputType="text|textNoSuggestions|textCapWords"
android:maxLines="1"
android:padding="8dp"
android:saveEnabled="false"
android:scrollHorizontally="true"
android:singleLine="true"
android:textColor="?android:attr/textColorSecondary"
android:textColorHint="@drawable/all_apps_search_hint"
android:textSize="16sp"
android:translationY="24dp" />
<include layout="@layout/all_apps_fast_scroller" />
</com.android.launcher3.allapps.AllAppsContainerView>
</com.android.launcher3.secondarydisplay.SecondaryDragLayer>

View File

@ -29,6 +29,7 @@ import android.os.UserHandle;
import android.util.Log;
import android.view.ActionMode;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Toast;
import androidx.annotation.Nullable;
@ -36,6 +37,7 @@ import androidx.annotation.Nullable;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.uioverrides.DisplayRotationListener;
import com.android.launcher3.uioverrides.WallpaperColorInfo;
import com.android.launcher3.util.PackageManagerHelper;
@ -262,5 +264,9 @@ public abstract class BaseDraggingActivity extends BaseActivity
}
}
public OnClickListener getItemOnClickListener() {
return ItemClickHandler.INSTANCE;
}
protected abstract void reapplyUi();
}

View File

@ -245,7 +245,7 @@ public class DeviceProfile {
allAppsIconTextSizePx = originalProfile.iconTextSizePx;
allAppsCellHeightPx = originalProfile.allAppsCellHeightPx;
allAppsIconDrawablePaddingPx = originalProfile.iconDrawablePaddingOriginalPx;
allAppsCellWidthPx = allAppsIconSizePx + allAppsIconDrawablePaddingPx;
allAppsCellWidthPx = allAppsIconSizePx + 2 * allAppsIconDrawablePaddingPx;
}
updateWorkspacePadding();
@ -360,7 +360,7 @@ public class DeviceProfile {
allAppsIconTextSizePx = iconTextSizePx;
allAppsIconDrawablePaddingPx = iconDrawablePaddingPx;
allAppsCellHeightPx = getCellSize().y;
allAppsCellWidthPx = allAppsIconSizePx + allAppsIconDrawablePaddingPx;
allAppsCellWidthPx = allAppsIconSizePx + 2 * allAppsIconDrawablePaddingPx;
if (isVerticalBarLayout()) {
// Always hide the Workspace text with vertical bar layout.

View File

@ -18,8 +18,8 @@ package com.android.launcher3;
import static com.android.launcher3.Utilities.getDevicePrefs;
import static com.android.launcher3.config.FeatureFlags.APPLY_CONFIG_AT_RUNTIME;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.settings.SettingsActivity.GRID_OPTIONS_PREFERENCE_KEY;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.PackageManagerHelper.getPackageFilter;
import android.annotation.TargetApi;
@ -41,6 +41,7 @@ import android.util.Log;
import android.util.SparseArray;
import android.util.TypedValue;
import android.util.Xml;
import android.view.Display;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
@ -48,6 +49,7 @@ import androidx.annotation.VisibleForTesting;
import com.android.launcher3.graphics.IconShape;
import com.android.launcher3.util.ConfigMonitor;
import com.android.launcher3.util.DefaultDisplay;
import com.android.launcher3.util.DefaultDisplay.Info;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.Themes;
@ -171,6 +173,13 @@ public class InvariantDeviceProfile {
}
}
/**
* This constructor should NOT have any monitors by design.
*/
public InvariantDeviceProfile(Context context, Display display) {
initGrid(context, null, new Info(display));
}
/**
* Retrieve system defined or RRO overriden icon shape.
*/
@ -183,8 +192,10 @@ public class InvariantDeviceProfile {
}
private String initGrid(Context context, String gridName) {
DefaultDisplay.Info displayInfo = DefaultDisplay.INSTANCE.get(context).getInfo();
return initGrid(context, gridName, DefaultDisplay.INSTANCE.get(context).getInfo());
}
private String initGrid(Context context, String gridName, DefaultDisplay.Info displayInfo) {
Point smallestSize = new Point(displayInfo.smallestSize);
Point largestSize = new Point(displayInfo.largestSize);

View File

@ -371,7 +371,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
mLauncherView = LayoutInflater.from(this).inflate(R.layout.launcher, null);
setupViews();
mPopupDataProvider = new PopupDataProvider(this);
mPopupDataProvider = new PopupDataProvider(this::updateNotificationDots);
mAppTransitionManager = LauncherAppTransitionManager.newInstance(this);
mAppTransitionManager.registerRemoteAnimations();
@ -1344,7 +1344,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
}
};
public void updateNotificationDots(Predicate<PackageUserKey> updatedDots) {
private void updateNotificationDots(Predicate<PackageUserKey> updatedDots) {
mWorkspace.updateNotificationDots(updatedDots);
mAppsView.getAppsStore().updateNotificationDots(updatedDots);
}
@ -1807,7 +1807,6 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
// Note: There should be at most one log per method call. This is enforced implicitly
// by using if-else statements.
UserEventDispatcher ued = getUserEventDispatcher();
AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(this);
if (topView != null && topView.onBackPressed()) {
// Handled by the floating view.
@ -1875,6 +1874,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
}
}
@Override
public boolean startActivitySafely(View v, Intent intent, ItemInfo item,
@Nullable String sourceContainer) {
if (!hasBeenResumed()) {

View File

@ -40,6 +40,7 @@ import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.android.launcher3.AppInfo;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.DragSource;
@ -47,9 +48,6 @@ import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.Insettable;
import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.keyboard.FocusedItemDecorator;
@ -61,7 +59,6 @@ import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.RecyclerViewFastScroller;
import com.android.launcher3.views.SpringRelativeLayout;
import com.android.launcher3.views.WorkEduView;
/**
* The all apps view container.
@ -74,8 +71,8 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
private static final float FLING_ANIMATION_THRESHOLD = 0.55f;
private static final int ALPHA_CHANNEL_COUNT = 2;
private final Launcher mLauncher;
private final AdapterHolder[] mAH;
protected final BaseDraggingActivity mLauncher;
protected final AdapterHolder[] mAH;
private final ItemInfoMatcher mPersonalMatcher = ItemInfoMatcher.ofUser(Process.myUserHandle());
private final ItemInfoMatcher mWorkMatcher = ItemInfoMatcher.not(mPersonalMatcher);
private final AllAppsStore mAllAppsStore = new AllAppsStore();
@ -83,18 +80,16 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
private final Paint mNavBarScrimPaint;
private int mNavBarScrimHeight = 0;
private SearchUiManager mSearchUiManager;
protected SearchUiManager mSearchUiManager;
private View mSearchContainer;
private AllAppsPagedView mViewPager;
private FloatingHeaderView mHeader;
private SpannableStringBuilder mSearchQueryBuilder = null;
private boolean mUsingTabs;
protected boolean mUsingTabs;
private boolean mSearchModeWhileUsingTabs = false;
private LauncherStateManager.StateListener mWorkTabListener;
private RecyclerViewFastScroller mTouchHandler;
private final Point mFastScrollerOffset = new Point();
@ -111,7 +106,7 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
public AllAppsContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mLauncher = Launcher.getLauncher(context);
mLauncher = BaseDraggingActivity.fromContext(context);
mLauncher.addOnDeviceProfileChangeListener(this);
mSearchQueryBuilder = new SpannableStringBuilder();
@ -133,6 +128,15 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
mMultiValueAlpha = new MultiValueAlpha(this, ALPHA_CHANNEL_COUNT);
}
/**
* Sets the long click listener for icons
*/
public void setOnIconLongClickListener(OnLongClickListener listener) {
for (AdapterHolder holder : mAH) {
holder.adapter.setOnIconLongClickListener(listener);
}
}
public AllAppsStore getAppsStore() {
return mAllAppsStore;
}
@ -193,11 +197,6 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// The AllAppsContainerView houses the QSB and is hence visible from the Workspace
// Overview states. We shouldn't intercept for the scrubber in these cases.
if (!mLauncher.isInState(LauncherState.ALL_APPS)) return false;
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
AllAppsRecyclerView rv = getActiveRecyclerView();
if (rv != null &&
@ -309,7 +308,6 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
+ grid.cellLayoutPaddingLeftRightPx;
for (int i = 0; i < mAH.length; i++) {
mAH[i].adapter.setAppsPerRow(grid.inv.numAllAppsColumns);
mAH[i].padding.bottom = insets.bottom;
mAH[i].padding.left = mAH[i].padding.right = leftRightPadding;
mAH[i].applyPadding();
@ -327,8 +325,6 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
setLayoutParams(mlp);
InsettableFrameLayout.dispatchInsets(this, insets);
mLauncher.getAllAppsController()
.setScrollRangeDelta(mSearchUiManager.getScrollRangeDelta(insets));
}
@Override
@ -375,7 +371,6 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
mAH[AdapterHolder.MAIN].setup(mViewPager.getChildAt(0), mPersonalMatcher);
mAH[AdapterHolder.WORK].setup(mViewPager.getChildAt(1), mWorkMatcher);
onTabChanged(mViewPager.getNextPage());
mWorkTabListener = WorkEduView.showEduFlowIfNeeded(mLauncher, mWorkTabListener);
} else {
mAH[AdapterHolder.MAIN].setup(findViewById(R.id.apps_list_view), null);
mAH[AdapterHolder.WORK].recyclerView = null;
@ -421,9 +416,6 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
.setOnClickListener((View view) -> mViewPager.snapToPage(AdapterHolder.MAIN));
findViewById(R.id.tab_work)
.setOnClickListener((View view) -> mViewPager.snapToPage(AdapterHolder.WORK));
if (pos == AdapterHolder.WORK) {
WorkEduView.showWorkEduIfNeeded(mLauncher);
}
}
}
@ -588,7 +580,7 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
appsList.updateItemFilter(matcher);
recyclerView = (AllAppsRecyclerView) rv;
recyclerView.setEdgeEffectFactory(createEdgeEffectFactory());
recyclerView.setApps(appsList, mUsingTabs);
recyclerView.setApps(appsList);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);
recyclerView.setHasFixedSize(true);

View File

@ -15,17 +15,22 @@
*/
package com.android.launcher3.allapps;
import static com.android.launcher3.touch.ItemLongClickListener.INSTANCE_ALL_APPS;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.core.view.accessibility.AccessibilityEventCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import androidx.core.view.accessibility.AccessibilityRecordCompat;
@ -33,14 +38,12 @@ import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.android.launcher3.AppInfo;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.allapps.AlphabeticalAppsList.AdapterItem;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.touch.ItemLongClickListener;
import com.android.launcher3.util.PackageManagerHelper;
import java.util.List;
@ -174,23 +177,26 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
}
}
private final Launcher mLauncher;
private final BaseDraggingActivity mLauncher;
private final LayoutInflater mLayoutInflater;
private final AlphabeticalAppsList mApps;
private final GridLayoutManager mGridLayoutMgr;
private final GridSpanSizer mGridSizer;
private final OnClickListener mOnIconClickListener;
private OnLongClickListener mOnIconLongClickListener = INSTANCE_ALL_APPS;
private int mAppsPerRow;
private BindViewCallback mBindViewCallback;
private OnFocusChangeListener mIconFocusListener;
// The text to show when there are no search results and no market search handler.
private String mEmptySearchMessage;
protected String mEmptySearchMessage;
// The intent to send off to the market app, updated each time the search query changes.
private Intent mMarketSearchIntent;
public AllAppsGridAdapter(Launcher launcher, AlphabeticalAppsList apps) {
public AllAppsGridAdapter(BaseDraggingActivity launcher, AlphabeticalAppsList apps) {
Resources res = launcher.getResources();
mLauncher = launcher;
mApps = apps;
@ -200,6 +206,8 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
mGridLayoutMgr.setSpanSizeLookup(mGridSizer);
mLayoutInflater = LayoutInflater.from(launcher);
mOnIconClickListener = launcher.getItemOnClickListener();
setAppsPerRow(mLauncher.getDeviceProfile().inv.numAllAppsColumns);
}
@ -208,6 +216,13 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
mGridLayoutMgr.setSpanCount(mAppsPerRow);
}
/**
* Sets the long click listener for icons
*/
public void setOnIconLongClickListener(@Nullable OnLongClickListener listener) {
mOnIconLongClickListener = listener;
}
public static boolean isDividerViewType(int viewType) {
return isViewType(viewType, VIEW_TYPE_MASK_DIVIDER);
}
@ -254,8 +269,8 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
case VIEW_TYPE_ICON:
BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
R.layout.all_apps_icon, parent, false);
icon.setOnClickListener(ItemClickHandler.INSTANCE);
icon.setOnLongClickListener(ItemLongClickListener.INSTANCE_ALL_APPS);
icon.setOnClickListener(mOnIconClickListener);
icon.setOnLongClickListener(mOnIconLongClickListener);
icon.setLongPressTimeoutFactor(1f);
icon.setOnFocusChangeListener(mIconFocusListener);

View File

@ -28,14 +28,13 @@ import android.view.View;
import androidx.recyclerview.widget.RecyclerView;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.BaseRecyclerView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.allapps.AllAppsGridAdapter.AppsGridLayoutManager;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
@ -84,7 +83,7 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine
/**
* Sets the list of apps in this view, used to determine the fastscroll position.
*/
public void setApps(AlphabeticalAppsList apps, boolean usingTabs) {
public void setApps(AlphabeticalAppsList apps) {
mApps = apps;
mFastScrollHelper = new AllAppsFastScrollHelper(this, apps);
}
@ -94,7 +93,7 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine
}
private void updatePoolSize() {
DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
DeviceProfile grid = BaseDraggingActivity.fromContext(getContext()).getDeviceProfile();
RecyclerView.RecycledViewPool pool = getRecycledViewPool();
int approxRows = (int) Math.ceil(grid.availableHeightPx / grid.allAppsIconSizePx);
pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_EMPTY_SEARCH, 1);

View File

@ -21,7 +21,7 @@ import android.content.Context;
import android.content.pm.PackageManager;
import com.android.launcher3.AppInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.ItemInfoMatcher;
@ -126,7 +126,7 @@ public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener {
}
}
private final Launcher mLauncher;
private final BaseDraggingActivity mLauncher;
// The set of apps from the system
private final List<AppInfo> mApps = new ArrayList<>();
@ -151,7 +151,7 @@ public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener {
public AlphabeticalAppsList(Context context, AllAppsStore appsStore, boolean isWork) {
mAllAppsStore = appsStore;
mLauncher = Launcher.getLauncher(context);
mLauncher = BaseDraggingActivity.fromContext(context);
mAppNameComparator = new AppInfoComparator(context);
mIsWork = isWork;
mNumAppsPerRow = mLauncher.getDeviceProfile().inv.numColumns;

View File

@ -31,9 +31,9 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
@ -393,7 +393,7 @@ public class FloatingHeaderView extends LinearLayout implements
@Override
public void setInsets(Rect insets) {
DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
DeviceProfile grid = BaseDraggingActivity.fromContext(getContext()).getDeviceProfile();
for (FloatingHeaderRow row : mAllRows) {
row.setInsets(insets, grid);
}

View File

@ -0,0 +1,77 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.allapps;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.views.WorkEduView;
/**
* AllAppsContainerView with launcher specific callbacks
*/
public class LauncherAllAppsContainerView extends AllAppsContainerView {
private final Launcher mLauncher;
private LauncherStateManager.StateListener mWorkTabListener;
public LauncherAllAppsContainerView(Context context) {
this(context, null);
}
public LauncherAllAppsContainerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public LauncherAllAppsContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mLauncher = Launcher.getLauncher(context);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// The AllAppsContainerView houses the QSB and is hence visible from the Workspace
// Overview states. We shouldn't intercept for the scrubber in these cases.
if (!mLauncher.isInState(LauncherState.ALL_APPS)) return false;
return super.onInterceptTouchEvent(ev);
}
@Override
public void setInsets(Rect insets) {
super.setInsets(insets);
mLauncher.getAllAppsController()
.setScrollRangeDelta(mSearchUiManager.getScrollRangeDelta(insets));
}
@Override
public void onTabChanged(int pos) {
super.onTabChanged(pos);
if (mUsingTabs) {
if (pos == AdapterHolder.WORK) {
WorkEduView.showWorkEduIfNeeded(mLauncher);
} else {
mWorkTabListener = WorkEduView.showEduFlowIfNeeded(mLauncher, mWorkTabListener);
}
}
}
}

View File

@ -24,15 +24,14 @@ import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import com.android.launcher3.Launcher;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.pageindicators.PageIndicator;
import com.android.launcher3.util.Themes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
/**
* Supports two indicator colors, dedicated for personal and work tabs.
*/
@ -73,7 +72,7 @@ public class PersonalWorkSlidingTabStrip extends LinearLayout implements PageInd
mDividerPaint.setStrokeWidth(
getResources().getDimensionPixelSize(R.dimen.all_apps_divider_height));
mSharedPreferences = Launcher.getLauncher(getContext()).getSharedPrefs();
mSharedPreferences = Utilities.getPrefs(context);
mIsRtl = Utilities.isRtl(getResources());
}

View File

@ -25,8 +25,8 @@ import android.view.inputmethod.EditorInfo;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.ExtendedEditText;
import com.android.launcher3.Launcher;
import com.android.launcher3.Utilities;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.util.ComponentKey;
@ -41,7 +41,7 @@ public class AllAppsSearchBarController
implements TextWatcher, OnEditorActionListener, ExtendedEditText.OnBackKeyListener,
OnFocusChangeListener {
protected Launcher mLauncher;
protected BaseDraggingActivity mLauncher;
protected Callbacks mCb;
protected ExtendedEditText mInput;
protected String mQuery;
@ -56,7 +56,7 @@ public class AllAppsSearchBarController
*/
public final void initialize(
SearchAlgorithm searchAlgorithm, ExtendedEditText input,
Launcher launcher, Callbacks cb) {
BaseDraggingActivity launcher, Callbacks cb) {
mCb = cb;
mLauncher = launcher;

View File

@ -26,8 +26,6 @@ import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTO
import android.content.Context;
import android.graphics.Rect;
import android.text.Selection;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.method.TextKeyListener;
import android.util.AttributeSet;
@ -36,18 +34,16 @@ import android.view.View;
import android.view.ViewGroup.MarginLayoutParams;
import android.view.animation.Interpolator;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.ExtendedEditText;
import com.android.launcher3.Insettable;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.AllAppsStore;
import com.android.launcher3.allapps.AlphabeticalAppsList;
import com.android.launcher3.allapps.SearchUiManager;
import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.graphics.TintedDrawableSpan;
import com.android.launcher3.util.ComponentKey;
import java.util.ArrayList;
@ -59,8 +55,7 @@ public class AppsSearchContainerLayout extends ExtendedEditText
implements SearchUiManager, AllAppsSearchBarController.Callbacks,
AllAppsStore.OnUpdateListener, Insettable {
private final Launcher mLauncher;
private final BaseDraggingActivity mLauncher;
private final AllAppsSearchBarController mSearchBarController;
private final SpannableStringBuilder mSearchQueryBuilder;
@ -82,7 +77,7 @@ public class AppsSearchContainerLayout extends ExtendedEditText
public AppsSearchContainerLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mLauncher = Launcher.getLauncher(context);
mLauncher = BaseDraggingActivity.fromContext(context);
mSearchBarController = new AllAppsSearchBarController();
mSearchQueryBuilder = new SpannableStringBuilder();
@ -97,13 +92,13 @@ public class AppsSearchContainerLayout extends ExtendedEditText
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mLauncher.getAppsView().getAppsStore().addUpdateListener(this);
mAppsView.getAppsStore().addUpdateListener(this);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mLauncher.getAppsView().getAppsStore().removeUpdateListener(this);
mAppsView.getAppsStore().removeUpdateListener(this);
}
@Override

View File

@ -40,8 +40,8 @@ import android.view.ViewOutlineProvider;
import android.widget.FrameLayout;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@ -57,14 +57,16 @@ import java.util.Collections;
/**
* A container for shortcuts to deep links and notifications associated with an app.
*
* @param <T> The activity on with the popup shows
*/
public abstract class ArrowPopup extends AbstractFloatingView {
public abstract class ArrowPopup<T extends BaseDraggingActivity> extends AbstractFloatingView {
private final Rect mTempRect = new Rect();
protected final LayoutInflater mInflater;
private final float mOutlineRadius;
protected final Launcher mLauncher;
protected final T mLauncher;
protected final boolean mIsRtl;
private final int mArrowOffset;
@ -83,7 +85,7 @@ public abstract class ArrowPopup extends AbstractFloatingView {
super(context, attrs, defStyleAttr);
mInflater = LayoutInflater.from(context);
mOutlineRadius = Themes.getDialogCornerRadius(context);
mLauncher = Launcher.getLauncher(context);
mLauncher = BaseDraggingActivity.fromContext(context);
mIsRtl = Utilities.isRtl(getResources());
setClipToOutline(true);
@ -120,16 +122,22 @@ public abstract class ArrowPopup extends AbstractFloatingView {
}
}
public <T extends View> T inflateAndAdd(int resId, ViewGroup container) {
/**
* Utility method for inflating and adding a view
*/
public <R extends View> R inflateAndAdd(int resId, ViewGroup container) {
View view = mInflater.inflate(resId, container, false);
container.addView(view);
return (T) view;
return (R) view;
}
public <T extends View> T inflateAndAdd(int resId, ViewGroup container, int index) {
/**
* Utility method for inflating and adding a view
*/
public <R extends View> R inflateAndAdd(int resId, ViewGroup container, int index) {
View view = mInflater.inflate(resId, container, false);
container.addView(view, index);
return (T) view;
return (R) view;
}
/**

View File

@ -44,6 +44,7 @@ import android.view.ViewGroup;
import android.widget.ImageView;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget;
@ -65,7 +66,6 @@ import com.android.launcher3.notification.NotificationKeyData;
import com.android.launcher3.popup.PopupDataProvider.PopupDataChangeListener;
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.touch.ItemLongClickListener;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.ShortcutUtil;
@ -74,22 +74,22 @@ import com.android.launcher3.views.BaseDragLayer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* A container for shortcuts to deep links and notifications associated with an app.
*
* @param <T> The activity on with the popup shows
*/
public class PopupContainerWithArrow extends ArrowPopup implements DragSource,
DragController.DragListener, View.OnLongClickListener,
View.OnTouchListener, PopupDataChangeListener {
public class PopupContainerWithArrow<T extends BaseDraggingActivity> extends ArrowPopup<T>
implements DragSource, DragController.DragListener {
private final List<DeepShortcutView> mShortcuts = new ArrayList<>();
private final PointF mInterceptTouchDown = new PointF();
protected final Point mIconLastTouchPos = new Point();
private final int mStartDragThreshold;
private final LauncherAccessibilityDelegate mAccessibilityDelegate;
private BubbleTextView mOriginalIcon;
private NotificationItemView mNotificationItemView;
@ -97,11 +97,13 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource,
private ViewGroup mSystemShortcutContainer;
protected PopupItemDragHandler mPopupItemDragHandler;
protected LauncherAccessibilityDelegate mAccessibilityDelegate;
public PopupContainerWithArrow(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mStartDragThreshold = getResources().getDimensionPixelSize(
R.dimen.deep_shortcuts_start_drag_threshold);
mAccessibilityDelegate = new ShortcutMenuAccessibilityDelegate(mLauncher);
}
public PopupContainerWithArrow(Context context, AttributeSet attrs) {
@ -116,18 +118,6 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource,
return mAccessibilityDelegate;
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mLauncher.getPopupDataProvider().setChangeListener(this);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mLauncher.getPopupDataProvider().setChangeListener(null);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
@ -168,11 +158,15 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource,
public OnClickListener getItemClickListener() {
return (view) -> {
ItemClickHandler.INSTANCE.onClick(view);
mLauncher.getItemOnClickListener().onClick(view);
close(true);
};
}
public PopupItemDragHandler getItemDragHandler() {
return mPopupItemDragHandler;
}
@Override
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
@ -201,18 +195,35 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource,
icon.clearFocus();
return null;
}
ItemInfo itemInfo = (ItemInfo) icon.getTag();
if (!ShortcutUtil.supportsShortcuts(itemInfo)) {
ItemInfo item = (ItemInfo) icon.getTag();
if (!ShortcutUtil.supportsShortcuts(item)) {
return null;
}
final PopupContainerWithArrow container =
(PopupContainerWithArrow) launcher.getLayoutInflater().inflate(
R.layout.popup_container, launcher.getDragLayer(), false);
container.populateAndShow(icon, itemInfo);
container.configureForLauncher(launcher);
PopupDataProvider popupDataProvider = launcher.getPopupDataProvider();
container.populateAndShow(icon,
popupDataProvider.getShortcutCountForItem(item),
popupDataProvider.getNotificationKeysForItem(item),
launcher.getSupportedShortcuts()
.map(s -> s.getShortcut(launcher, item))
.filter(Objects::nonNull)
.collect(Collectors.toList()));
launcher.refreshAndBindWidgetsForPackageUser(PackageUserKey.fromItemInfo(item));
return container;
}
private void configureForLauncher(Launcher launcher) {
addOnAttachStateChangeListener(new LiveUpdateHandler(launcher));
mPopupItemDragHandler = new LauncherPopupItemDragHandler(launcher, this);
mAccessibilityDelegate = new ShortcutMenuAccessibilityDelegate(launcher);
launcher.getDragController().addDragListener(this);
}
@Override
protected void onInflationComplete(boolean isReversed) {
if (isReversed && mNotificationItemView != null) {
@ -234,23 +245,8 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource,
}
}
protected void populateAndShow(BubbleTextView icon, ItemInfo item) {
PopupDataProvider popupDataProvider = mLauncher.getPopupDataProvider();
populateAndShow(icon,
popupDataProvider.getShortcutCountForItem(item),
popupDataProvider.getNotificationKeysForItem(item),
mLauncher.getSupportedShortcuts()
.map(s -> s.getShortcut(mLauncher, item))
.filter(s -> s != null)
.collect(Collectors.toList()));
}
public ViewGroup getSystemShortcutContainerForTesting() {
return mSystemShortcutContainer;
}
@TargetApi(Build.VERSION_CODES.P)
protected void populateAndShow(final BubbleTextView originalIcon, int shortcutCount,
public void populateAndShow(final BubbleTextView originalIcon, int shortcutCount,
final List<NotificationKeyData> notificationKeys, List<SystemShortcut> systemShortcuts) {
mNumNotifications = notificationKeys.size();
mOriginalIcon = originalIcon;
@ -316,7 +312,6 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource,
setAccessibilityPaneTitle(getTitleForAccessibility());
}
mLauncher.getDragController().addDragListener(this);
mOriginalIcon.setForceHideDot(true);
// All views are added. Animate layout from now on.
@ -390,44 +385,6 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource,
}
}
@Override
public void onWidgetsBound() {
ItemInfo itemInfo = (ItemInfo) mOriginalIcon.getTag();
SystemShortcut widgetInfo = SystemShortcut.WIDGETS.getShortcut(mLauncher, itemInfo);
View widgetsView = null;
int count = mSystemShortcutContainer.getChildCount();
for (int i = 0; i < count; i++) {
View systemShortcutView = mSystemShortcutContainer.getChildAt(i);
if (systemShortcutView.getTag() instanceof SystemShortcut.Widgets) {
widgetsView = systemShortcutView;
break;
}
}
if (widgetInfo != null && widgetsView == null) {
// We didn't have any widgets cached but now there are some, so enable the shortcut.
if (mSystemShortcutContainer != this) {
initializeSystemShortcut(
R.layout.system_shortcut_icon_only, mSystemShortcutContainer, widgetInfo);
} else {
// If using the expanded system shortcut (as opposed to just the icon), we need to
// reopen the container to ensure measurements etc. all work out. While this could
// be quite janky, in practice the user would typically see a small flicker as the
// animation restarts partway through, and this is a very rare edge case anyway.
close(false);
PopupContainerWithArrow.showForIcon(mOriginalIcon);
}
} else if (widgetInfo == null && widgetsView != null) {
// No widgets exist, but we previously added the shortcut so remove it.
if (mSystemShortcutContainer != this) {
mSystemShortcutContainer.removeView(widgetsView);
} else {
close(false);
PopupContainerWithArrow.showForIcon(mOriginalIcon);
}
}
}
private void initializeSystemShortcut(int resId, ViewGroup container, SystemShortcut info) {
View view = inflateAndAdd(
resId, container, getInsertIndexForSystemShortcut(container, info));
@ -498,18 +455,6 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource,
};
}
/**
* Updates the notification header if the original icon's dot updated.
*/
@Override
public void onNotificationDotsUpdated(Predicate<PackageUserKey> updatedDots) {
ItemInfo itemInfo = (ItemInfo) mOriginalIcon.getTag();
PackageUserKey packageUser = PackageUserKey.fromItemInfo(itemInfo);
if (updatedDots.test(packageUser)) {
updateNotificationHeader();
}
}
private void updateNotificationHeader() {
ItemInfoWithIcon itemInfo = (ItemInfoWithIcon) mOriginalIcon.getTag();
DotInfo dotInfo = mLauncher.getDotInfoForItem(itemInfo);
@ -519,25 +464,6 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource,
}
}
@Override
public void trimNotifications(Map<PackageUserKey, DotInfo> updatedDots) {
if (mNotificationItemView == null) {
return;
}
ItemInfo originalInfo = (ItemInfo) mOriginalIcon.getTag();
DotInfo dotInfo = updatedDots.get(PackageUserKey.fromItemInfo(originalInfo));
if (dotInfo == null || dotInfo.getNotificationKeys().size() == 0) {
// No more notifications, remove the notification views and expand all shortcuts.
mNotificationItemView.removeAllViews();
mNotificationItemView = null;
updateHiddenShortcuts();
updateDividers();
} else {
mNotificationItemView.trimNotifications(
NotificationKeyData.extractKeysOnly(dotInfo.getNotificationKeys()));
}
}
@Override
public void onDropCompleted(View target, DragObject d, boolean success) { }
@ -592,47 +518,164 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource,
super.closeComplete();
}
@Override
public boolean onTouch(View v, MotionEvent ev) {
// Touched a shortcut, update where it was touched so we can drag from there on long click.
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
mIconLastTouchPos.set((int) ev.getX(), (int) ev.getY());
break;
}
return false;
}
@Override
public boolean onLongClick(View v) {
if (!ItemLongClickListener.canStartDrag(mLauncher)) return false;
// Return early if not the correct view
if (!(v.getParent() instanceof DeepShortcutView)) return false;
// Long clicked on a shortcut.
DeepShortcutView sv = (DeepShortcutView) v.getParent();
sv.setWillDrawIcon(false);
// Move the icon to align with the center-top of the touch point
Point iconShift = new Point();
iconShift.x = mIconLastTouchPos.x - sv.getIconCenter().x;
iconShift.y = mIconLastTouchPos.y - mLauncher.getDeviceProfile().iconSizePx;
DragView dv = mLauncher.getWorkspace().beginDragShared(sv.getIconView(),
this, sv.getFinalInfo(),
new ShortcutDragPreviewProvider(sv.getIconView(), iconShift), new DragOptions());
dv.animateShift(-iconShift.x, -iconShift.y);
// TODO: support dragging from within folder without having to close it
AbstractFloatingView.closeOpenContainer(mLauncher, AbstractFloatingView.TYPE_FOLDER);
return false;
}
/**
* Returns a PopupContainerWithArrow which is already open or null
*/
public static PopupContainerWithArrow getOpen(Launcher launcher) {
public static PopupContainerWithArrow getOpen(BaseDraggingActivity launcher) {
return getOpenView(launcher, TYPE_ACTION_POPUP);
}
/**
* Utility class to handle updates while the popup is visible (like widgets and
* notification changes)
*/
private class LiveUpdateHandler implements
PopupDataChangeListener, View.OnAttachStateChangeListener {
private final Launcher mLauncher;
LiveUpdateHandler(Launcher launcher) {
mLauncher = launcher;
}
@Override
public void onViewAttachedToWindow(View view) {
mLauncher.getPopupDataProvider().setChangeListener(this);
}
@Override
public void onViewDetachedFromWindow(View view) {
mLauncher.getPopupDataProvider().setChangeListener(null);
}
@Override
public void onWidgetsBound() {
ItemInfo itemInfo = (ItemInfo) mOriginalIcon.getTag();
SystemShortcut widgetInfo = SystemShortcut.WIDGETS.getShortcut(mLauncher, itemInfo);
View widgetsView = null;
int count = mSystemShortcutContainer.getChildCount();
for (int i = 0; i < count; i++) {
View systemShortcutView = mSystemShortcutContainer.getChildAt(i);
if (systemShortcutView.getTag() instanceof SystemShortcut.Widgets) {
widgetsView = systemShortcutView;
break;
}
}
if (widgetInfo != null && widgetsView == null) {
// We didn't have any widgets cached but now there are some, so enable the shortcut.
if (mSystemShortcutContainer != PopupContainerWithArrow.this) {
initializeSystemShortcut(R.layout.system_shortcut_icon_only,
mSystemShortcutContainer, widgetInfo);
} else {
// If using the expanded system shortcut (as opposed to just the icon), we need
// to reopen the container to ensure measurements etc. all work out. While this
// could be quite janky, in practice the user would typically see a small
// flicker as the animation restarts partway through, and this is a very rare
// edge case anyway.
close(false);
PopupContainerWithArrow.showForIcon(mOriginalIcon);
}
} else if (widgetInfo == null && widgetsView != null) {
// No widgets exist, but we previously added the shortcut so remove it.
if (mSystemShortcutContainer != PopupContainerWithArrow.this) {
mSystemShortcutContainer.removeView(widgetsView);
} else {
close(false);
PopupContainerWithArrow.showForIcon(mOriginalIcon);
}
}
}
/**
* Updates the notification header if the original icon's dot updated.
*/
@Override
public void onNotificationDotsUpdated(Predicate<PackageUserKey> updatedDots) {
ItemInfo itemInfo = (ItemInfo) mOriginalIcon.getTag();
PackageUserKey packageUser = PackageUserKey.fromItemInfo(itemInfo);
if (updatedDots.test(packageUser)) {
updateNotificationHeader();
}
}
@Override
public void trimNotifications(Map<PackageUserKey, DotInfo> updatedDots) {
if (mNotificationItemView == null) {
return;
}
ItemInfo originalInfo = (ItemInfo) mOriginalIcon.getTag();
DotInfo dotInfo = updatedDots.get(PackageUserKey.fromItemInfo(originalInfo));
if (dotInfo == null || dotInfo.getNotificationKeys().size() == 0) {
// No more notifications, remove the notification views and expand all shortcuts.
mNotificationItemView.removeAllViews();
mNotificationItemView = null;
updateHiddenShortcuts();
updateDividers();
} else {
mNotificationItemView.trimNotifications(
NotificationKeyData.extractKeysOnly(dotInfo.getNotificationKeys()));
}
}
}
/**
* Handler to control drag-and-drop for popup items
*/
public interface PopupItemDragHandler extends OnLongClickListener, OnTouchListener { }
/**
* Drag and drop handler for popup items in Launcher activity
*/
public static class LauncherPopupItemDragHandler implements PopupItemDragHandler {
protected final Point mIconLastTouchPos = new Point();
private final Launcher mLauncher;
private final PopupContainerWithArrow mContainer;
LauncherPopupItemDragHandler(Launcher launcher, PopupContainerWithArrow container) {
mLauncher = launcher;
mContainer = container;
}
@Override
public boolean onTouch(View v, MotionEvent ev) {
// Touched a shortcut, update where it was touched so we can drag from there on
// long click.
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
mIconLastTouchPos.set((int) ev.getX(), (int) ev.getY());
break;
}
return false;
}
@Override
public boolean onLongClick(View v) {
if (!ItemLongClickListener.canStartDrag(mLauncher)) return false;
// Return early if not the correct view
if (!(v.getParent() instanceof DeepShortcutView)) return false;
// Long clicked on a shortcut.
DeepShortcutView sv = (DeepShortcutView) v.getParent();
sv.setWillDrawIcon(false);
// Move the icon to align with the center-top of the touch point
Point iconShift = new Point();
iconShift.x = mIconLastTouchPos.x - sv.getIconCenter().x;
iconShift.y = mIconLastTouchPos.y - mLauncher.getDeviceProfile().iconSizePx;
DragView dv = mLauncher.getWorkspace().beginDragShared(sv.getIconView(),
mContainer, sv.getFinalInfo(),
new ShortcutDragPreviewProvider(sv.getIconView(), iconShift),
new DragOptions());
dv.animateShift(-iconShift.x, -iconShift.y);
// TODO: support dragging from within folder without having to close it
AbstractFloatingView.closeOpenContainer(mLauncher, AbstractFloatingView.TYPE_FOLDER);
return false;
}
}
}

View File

@ -24,7 +24,6 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.dot.DotInfo;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.notification.NotificationKeyData;
@ -41,6 +40,7 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@ -52,7 +52,7 @@ public class PopupDataProvider implements NotificationListener.NotificationsChan
private static final boolean LOGD = false;
private static final String TAG = "PopupDataProvider";
private final Launcher mLauncher;
private final Consumer<Predicate<PackageUserKey>> mNotificationDotsChangeListener;
/** Maps launcher activity components to a count of how many shortcuts they have. */
private HashMap<ComponentKey, Integer> mDeepShortcutMap = new HashMap<>();
@ -63,12 +63,12 @@ public class PopupDataProvider implements NotificationListener.NotificationsChan
private PopupDataChangeListener mChangeListener = PopupDataChangeListener.INSTANCE;
public PopupDataProvider(Launcher launcher) {
mLauncher = launcher;
public PopupDataProvider(Consumer<Predicate<PackageUserKey>> notificationDotsChangeListener) {
mNotificationDotsChangeListener = notificationDotsChangeListener;
}
private void updateNotificationDots(Predicate<PackageUserKey> updatedDots) {
mLauncher.updateNotificationDots(updatedDots);
mNotificationDotsChangeListener.accept(updatedDots);
mChangeListener.onNotificationDotsUpdated(updatedDots);
}

View File

@ -24,8 +24,8 @@ import android.os.UserHandle;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.notification.NotificationInfo;
@ -33,7 +33,6 @@ import com.android.launcher3.notification.NotificationKeyData;
import com.android.launcher3.notification.NotificationListener;
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.shortcuts.ShortcutRequest;
import com.android.launcher3.util.PackageUserKey;
import java.util.ArrayList;
import java.util.Collections;
@ -123,7 +122,11 @@ public class PopupPopulator {
return filteredShortcuts;
}
public static Runnable createUpdateRunnable(final Launcher launcher, final ItemInfo originalInfo,
/**
* Returns a runnable to update the provided shortcuts and notifications
*/
public static Runnable createUpdateRunnable(final BaseDraggingActivity launcher,
final ItemInfo originalInfo,
final Handler uiHandler, final PopupContainerWithArrow container,
final List<DeepShortcutView> shortcutViews,
final List<NotificationKeyData> notificationKeys) {
@ -162,11 +165,6 @@ public class PopupPopulator {
final DeepShortcutView view = shortcutViews.get(i);
uiHandler.post(() -> view.applyShortcutInfo(si, shortcut, container));
}
// This ensures that mLauncher.getWidgetsForPackageUser()
// doesn't return null (it puts all the widgets in memory).
uiHandler.post(() -> launcher.refreshAndBindWidgetsForPackageUser(
PackageUserKey.fromItemInfo(originalInfo)));
};
}
}

View File

@ -0,0 +1,234 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.secondarydisplay;
import static android.content.Context.MODE_PRIVATE;
import android.content.ComponentName;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.os.Process;
import android.os.UserHandle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.AppInfo;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.R;
import com.android.launcher3.allapps.AllAppsStore;
import com.android.launcher3.allapps.AppInfoComparator;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.Executors;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* Adapter to manage pinned apps and show then in a grid.
*/
public class PinnedAppsAdapter extends BaseAdapter implements OnSharedPreferenceChangeListener {
private static final String PINNED_APPS_KEY = "pinned_apps";
private final SecondaryDisplayLauncher mLauncher;
private final OnClickListener mOnClickListener;
private final OnLongClickListener mOnLongClickListener;
private final SharedPreferences mPrefs;
private final AllAppsStore mAllAppsList;
private final AppInfoComparator mAppNameComparator;
private final Set<ComponentKey> mPinnedApps = new HashSet<>();
private final ArrayList<AppInfo> mItems = new ArrayList<>();
public PinnedAppsAdapter(SecondaryDisplayLauncher launcher, AllAppsStore allAppsStore,
OnLongClickListener onLongClickListener) {
mLauncher = launcher;
mOnClickListener = launcher.getItemOnClickListener();
mOnLongClickListener = onLongClickListener;
mAllAppsList = allAppsStore;
mPrefs = launcher.getSharedPreferences(PINNED_APPS_KEY, MODE_PRIVATE);
mAppNameComparator = new AppInfoComparator(launcher);
mAllAppsList.addUpdateListener(this::createFilteredAppsList);
}
/**
* {@inheritDoc}
*/
@Override
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
if (PINNED_APPS_KEY.equals(key)) {
Executors.MODEL_EXECUTOR.submit(() -> {
Set<ComponentKey> apps = prefs.getStringSet(key, Collections.emptySet())
.stream()
.map(this::parseComponentKey)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
Executors.MAIN_EXECUTOR.submit(() -> {
mPinnedApps.clear();
mPinnedApps.addAll(apps);
createFilteredAppsList();
});
});
}
}
/**
* {@inheritDoc}
*/
@Override
public int getCount() {
return mItems.size();
}
/**
* {@inheritDoc}
*/
@Override
public AppInfo getItem(int position) {
return mItems.get(position);
}
/**
* {@inheritDoc}
*/
@Override
public long getItemId(int position) {
return position;
}
/**
* {@inheritDoc}
*/
@Override
public View getView(int position, View view, ViewGroup parent) {
BubbleTextView icon;
if (view instanceof BubbleTextView) {
icon = (BubbleTextView) view;
} else {
icon = (BubbleTextView) LayoutInflater.from(parent.getContext())
.inflate(R.layout.app_icon, parent, false);
icon.setOnClickListener(mOnClickListener);
icon.setOnLongClickListener(mOnLongClickListener);
icon.setLongPressTimeoutFactor(1f);
int padding = mLauncher.getDeviceProfile().edgeMarginPx;
icon.setPadding(padding, padding, padding, padding);
}
icon.applyFromApplicationInfo(mItems.get(position));
return icon;
}
private void createFilteredAppsList() {
mItems.clear();
mPinnedApps.stream().map(mAllAppsList::getApp)
.filter(Objects::nonNull).forEach(mItems::add);
mItems.sort(mAppNameComparator);
notifyDataSetChanged();
}
/**
* Initialized the pinned apps list and starts listening for changes
*/
public void init() {
mPrefs.registerOnSharedPreferenceChangeListener(this);
onSharedPreferenceChanged(mPrefs, PINNED_APPS_KEY);
}
/**
* Stops listening for any pinned apps changes
*/
public void destroy() {
mPrefs.unregisterOnSharedPreferenceChangeListener(this);
}
private void update(ItemInfo info, Function<ComponentKey, Boolean> op) {
ComponentKey key = new ComponentKey(info.getTargetComponent(), info.user);
if (op.apply(key)) {
createFilteredAppsList();
Set<ComponentKey> copy = new HashSet<>(mPinnedApps);
Executors.MODEL_EXECUTOR.submit(() ->
mPrefs.edit().putStringSet(PINNED_APPS_KEY,
copy.stream().map(this::encode).collect(Collectors.toSet()))
.apply());
}
}
private ComponentKey parseComponentKey(String string) {
try {
String[] parts = string.split("#");
UserHandle user;
if (parts.length > 2) {
user = UserCache.INSTANCE.get(mLauncher)
.getUserForSerialNumber(Long.parseLong(parts[2]));
} else {
user = Process.myUserHandle();
}
ComponentName cn = ComponentName.unflattenFromString(parts[0]);
return new ComponentKey(cn, user);
} catch (Exception e) {
return null;
}
}
private String encode(ComponentKey key) {
return key.componentName.flattenToShortString() + "#"
+ UserCache.INSTANCE.get(mLauncher).getSerialNumberForUser(key.user);
}
/**
* Returns a system shortcut to pin/unpin a shortcut
*/
public SystemShortcut getSystemShortcut(ItemInfo info) {
return new PinUnPinShortcut(mLauncher, info,
mPinnedApps.contains(new ComponentKey(info.getTargetComponent(), info.user)));
}
private class PinUnPinShortcut extends SystemShortcut<SecondaryDisplayLauncher> {
private final boolean mIsPinned;
PinUnPinShortcut(SecondaryDisplayLauncher target, ItemInfo info, boolean isPinned) {
super(isPinned ? R.drawable.ic_remove_no_shadow : R.drawable.ic_pin,
isPinned ? R.string.remove_drop_target_label : R.string.action_add_to_workspace,
target, info);
mIsPinned = isPinned;
}
@Override
public void onClick(View view) {
if (mIsPinned) {
update(mItemInfo, mPinnedApps::remove);
} else {
update(mItemInfo, mPinnedApps::add);
}
AbstractFloatingView.closeAllOpenViews(mLauncher);
}
}
}

View File

@ -0,0 +1,331 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.secondarydisplay;
import static com.android.launcher3.model.AppLaunchTracker.CONTAINER_ALL_APPS;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.app.ActivityOptions;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewAnimationUtils;
import android.view.inputmethod.InputMethodManager;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.AppInfo;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.PromiseAppInfo;
import com.android.launcher3.R;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.popup.PopupDataProvider;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.ViewOnDrawExecutor;
import com.android.launcher3.views.BaseDragLayer;
import com.android.launcher3.widget.WidgetListRowEntry;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
/**
* Launcher activity for secondary displays
*/
public class SecondaryDisplayLauncher extends BaseDraggingActivity
implements BgDataModel.Callbacks {
private LauncherModel mModel;
private BaseDragLayer mDragLayer;
private AllAppsContainerView mAppsView;
private View mAppsButton;
private PopupDataProvider mPopupDataProvider;
private boolean mAppDrawerShown = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mModel = LauncherAppState.getInstance(this).getModel();
if (getWindow().getDecorView().isAttachedToWindow()) {
initUi();
}
}
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
initUi();
}
private void initUi() {
if (mDragLayer != null) {
return;
}
InvariantDeviceProfile mainIdp = LauncherAppState.getIDP(this);
InvariantDeviceProfile currentDisplayIdp =
new InvariantDeviceProfile(this, getWindow().getDecorView().getDisplay());
// Pick the device profile with the smaller icon size so that the cached icons are
// shown properly
if (mainIdp.iconBitmapSize <= currentDisplayIdp.iconBitmapSize) {
mDeviceProfile = mainIdp.getDeviceProfile(this).copy(this);
} else {
mDeviceProfile = currentDisplayIdp.getDeviceProfile(this);
}
setContentView(R.layout.secondary_launcher);
mDragLayer = findViewById(R.id.drag_layer);
mAppsView = findViewById(R.id.apps_view);
mAppsButton = findViewById(R.id.all_apps_button);
mPopupDataProvider = new PopupDataProvider(
mAppsView.getAppsStore()::updateNotificationDots);
mModel.addCallbacksAndLoad(this);
}
@Override
public void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (Intent.ACTION_MAIN.equals(intent.getAction())) {
// Hide keyboard.
final View v = getWindow().peekDecorView();
if (v != null && v.getWindowToken() != null) {
getSystemService(InputMethodManager.class).hideSoftInputFromWindow(
v.getWindowToken(), 0);
}
}
// A new intent will bring the launcher to top. Hide the app drawer to reset the state.
showAppDrawer(false);
}
@Override
public void onBackPressed() {
if (finishAutoCancelActionMode()) {
return;
}
// Note: There should be at most one log per method call. This is enforced implicitly
// by using if-else statements.
AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(this);
if (topView != null && topView.onBackPressed()) {
// Handled by the floating view.
} else {
showAppDrawer(false);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
mModel.removeCallbacks(this);
}
public boolean isAppDrawerShown() {
return mAppDrawerShown;
}
public AllAppsContainerView getAppsView() {
return mAppsView;
}
@Override
public <T extends View> T getOverviewPanel() {
return null;
}
@Override
public View getRootView() {
return mDragLayer;
}
@Override
public ActivityOptions getActivityLaunchOptions(View v) {
return null;
}
@Override
protected void reapplyUi() { }
@Override
public BaseDragLayer getDragLayer() {
return mDragLayer;
}
@Override
public int getPageToBindSynchronously() {
return 0;
}
@Override
public void clearPendingBinds() { }
@Override
public void startBinding() { }
@Override
public void bindItems(List<ItemInfo> shortcuts, boolean forceAnimateIcons) { }
@Override
public void bindScreens(IntArray orderedScreenIds) { }
@Override
public void finishFirstPageBind(ViewOnDrawExecutor executor) {
if (executor != null) {
executor.onLoadAnimationCompleted();
}
}
@Override
public void finishBindingItems(int pageBoundFirst) { }
@Override
public void preAddApps() { }
@Override
public void bindAppsAdded(IntArray newScreens, ArrayList<ItemInfo> addNotAnimated,
ArrayList<ItemInfo> addAnimated) { }
@Override
public void bindPromiseAppProgressUpdated(PromiseAppInfo app) {
mAppsView.getAppsStore().updatePromiseAppProgress(app);
}
@Override
public void bindWorkspaceItemsChanged(ArrayList<WorkspaceItemInfo> updated) { }
@Override
public void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets) { }
@Override
public void bindRestoreItemsChange(HashSet<ItemInfo> updates) { }
@Override
public void bindWorkspaceComponentsRemoved(ItemInfoMatcher matcher) { }
@Override
public void bindAllWidgets(ArrayList<WidgetListRowEntry> widgets) { }
@Override
public void onPageBoundSynchronously(int page) { }
@Override
public void executeOnNextDraw(ViewOnDrawExecutor executor) {
executor.attachTo(getDragLayer(), false, null);
}
/**
* Called when apps-button is clicked
*/
public void onAppsButtonClicked(View v) {
showAppDrawer(true);
}
/**
* Show/hide app drawer card with animation.
*/
public void showAppDrawer(boolean show) {
if (show == mAppDrawerShown) {
return;
}
float openR = (float) Math.hypot(mAppsView.getWidth(), mAppsView.getHeight());
float closeR = Themes.getDialogCornerRadius(this);
float startR = mAppsButton.getWidth() / 2f;
float[] buttonPos = new float[] { startR, startR};
mDragLayer.getDescendantCoordRelativeToSelf(mAppsButton, buttonPos);
mDragLayer.mapCoordInSelfToDescendant(mAppsView, buttonPos);
final Animator animator = ViewAnimationUtils.createCircularReveal(mAppsView,
(int) buttonPos[0], (int) buttonPos[1],
show ? closeR : openR, show ? openR : closeR);
if (show) {
mAppDrawerShown = true;
mAppsView.setVisibility(View.VISIBLE);
mAppsButton.setVisibility(View.INVISIBLE);
} else {
mAppDrawerShown = false;
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mAppsView.setVisibility(View.INVISIBLE);
mAppsButton.setVisibility(View.VISIBLE);
mAppsView.getSearchUiManager().resetSearch();
}
});
}
animator.start();
}
@Override
public void bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMap) {
mPopupDataProvider.setDeepShortcutMap(deepShortcutMap);
}
@Override
public void bindAllApplications(AppInfo[] apps) {
mAppsView.getAppsStore().setApps(apps);
}
public PopupDataProvider getPopupDataProvider() {
return mPopupDataProvider;
}
@Override
public OnClickListener getItemOnClickListener() {
return this::onIconClicked;
}
private void onIconClicked(View v) {
// Make sure that rogue clicks don't get through while allapps is launching, or after the
// view has detached (it's possible for this to happen if the view is removed mid touch).
if (v.getWindowToken() == null) return;
Object tag = v.getTag();
if (tag instanceof ItemInfo) {
ItemInfo item = (ItemInfo) tag;
Intent intent;
if (item instanceof PromiseAppInfo) {
PromiseAppInfo promiseAppInfo = (PromiseAppInfo) item;
intent = promiseAppInfo.getMarketIntent(this);
} else {
intent = item.getIntent();
}
if (intent == null) {
throw new IllegalArgumentException("Input must have a valid intent");
}
startActivitySafely(v, intent, item, CONTAINER_ALL_APPS);
}
}
}

View File

@ -0,0 +1,188 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.secondarydisplay;
import static android.view.View.MeasureSpec.EXACTLY;
import static android.view.View.MeasureSpec.makeMeasureSpec;
import static com.android.launcher3.popup.SystemShortcut.APP_INFO;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.GridView;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.R;
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.util.ShortcutUtil;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.views.BaseDragLayer;
import java.util.Arrays;
import java.util.Collections;
/**
* DragLayer for Secondary launcher
*/
public class SecondaryDragLayer extends BaseDragLayer<SecondaryDisplayLauncher> {
private View mAllAppsButton;
private AllAppsContainerView mAppsView;
private GridView mWorkspace;
private PinnedAppsAdapter mPinnedAppsAdapter;
public SecondaryDragLayer(Context context, AttributeSet attrs) {
super(context, attrs, 1 /* alphaChannelCount */);
mControllers = new TouchController[] {new CloseAllAppsTouchController()};
}
/**
* {@inheritDoc}
*/
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mAllAppsButton = findViewById(R.id.all_apps_button);
mAppsView = findViewById(R.id.apps_view);
mAppsView.setOnIconLongClickListener(this::onIconLongClicked);
// Setup workspace
mWorkspace = findViewById(R.id.workspace_grid);
mPinnedAppsAdapter = new PinnedAppsAdapter(mActivity, mAppsView.getAppsStore(),
this::onIconLongClicked);
mWorkspace.setAdapter(mPinnedAppsAdapter);
mWorkspace.setNumColumns(mActivity.getDeviceProfile().inv.numColumns);
}
/**
* {@inheritDoc}
*/
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mPinnedAppsAdapter.init();
}
/**
* {@inheritDoc}
*/
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mPinnedAppsAdapter.destroy();
}
/**
* {@inheritDoc}
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(width, height);
DeviceProfile grid = mActivity.getDeviceProfile();
InvariantDeviceProfile idp = grid.inv;
int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child == mAppsView) {
int padding = 2 * (grid.desiredWorkspaceLeftRightMarginPx
+ grid.cellLayoutPaddingLeftRightPx);
int maxWidth = grid.allAppsCellWidthPx * idp.numAllAppsColumns + padding;
int appsWidth = Math.min(width, maxWidth);
int appsHeight = Math.round(appsWidth * (float) height / (float) width);
mAppsView.measure(
makeMeasureSpec(appsWidth, EXACTLY), makeMeasureSpec(appsHeight, EXACTLY));
} else if (child == mAllAppsButton) {
int appsButtonSpec = makeMeasureSpec(grid.iconSizePx, EXACTLY);
mAllAppsButton.measure(appsButtonSpec, appsButtonSpec);
} else if (child == mWorkspace) {
measureChildWithMargins(mWorkspace, widthMeasureSpec, 0, heightMeasureSpec,
grid.iconSizePx + grid.edgeMarginPx);
} else {
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
}
}
}
private class CloseAllAppsTouchController implements TouchController {
@Override
public boolean onControllerTouchEvent(MotionEvent ev) {
return false;
}
@Override
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
if (!mActivity.isAppDrawerShown()) {
return false;
}
if (AbstractFloatingView.getTopOpenView(mActivity) != null) {
return false;
}
if (ev.getAction() == MotionEvent.ACTION_DOWN
&& !isEventOverView(mActivity.getAppsView(), ev)) {
mActivity.showAppDrawer(false);
return true;
}
return false;
}
}
private boolean onIconLongClicked(View v) {
if (!(v instanceof BubbleTextView)) {
return false;
}
if (PopupContainerWithArrow.getOpen(mActivity) != null) {
// There is already an items container open, so don't open this one.
v.clearFocus();
return false;
}
ItemInfo item = (ItemInfo) v.getTag();
if (!ShortcutUtil.supportsShortcuts(item)) {
return false;
}
final PopupContainerWithArrow container =
(PopupContainerWithArrow) mActivity.getLayoutInflater().inflate(
R.layout.popup_container, mActivity.getDragLayer(), false);
container.populateAndShow((BubbleTextView) v,
mActivity.getPopupDataProvider().getShortcutCountForItem(item),
Collections.emptyList(),
Arrays.asList(mPinnedAppsAdapter.getSystemShortcut(item),
APP_INFO.getShortcut(mActivity, item)));
v.getParent().requestDisallowInterceptTouchEvent(true);
return true;
}
}

View File

@ -27,8 +27,8 @@ import android.widget.FrameLayout;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.popup.PopupContainerWithArrow;
/**
@ -111,8 +111,8 @@ public class DeepShortcutView extends FrameLayout {
// TODO: Add the click handler to this view directly and not the child view.
mBubbleText.setOnClickListener(container.getItemClickListener());
mBubbleText.setOnLongClickListener(container);
mBubbleText.setOnTouchListener(container);
mBubbleText.setOnLongClickListener(container.getItemDragHandler());
mBubbleText.setOnTouchListener(container.getItemDragHandler());
}
/**

View File

@ -128,8 +128,10 @@ public class DefaultDisplay implements DisplayListener {
public final DisplayMetrics metrics;
private Info(Context context) {
Display display = context.getSystemService(WindowManager.class).getDefaultDisplay();
this(context.getSystemService(WindowManager.class).getDefaultDisplay());
}
public Info(Display display) {
id = display.getDisplayId();
rotation = display.getRotation();

View File

@ -29,6 +29,7 @@ import com.android.launcher3.Launcher;
import java.util.ArrayList;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
* An executor which runs all the tasks after the first onDraw is called on the target view.
@ -38,7 +39,7 @@ public class ViewOnDrawExecutor implements Executor, OnDrawListener, Runnable,
private final ArrayList<Runnable> mTasks = new ArrayList<>();
private Launcher mLauncher;
private Consumer<ViewOnDrawExecutor> mOnClearCallback;
private View mAttachedView;
private boolean mCompleted;
@ -46,11 +47,16 @@ public class ViewOnDrawExecutor implements Executor, OnDrawListener, Runnable,
private boolean mFirstDrawCompleted;
public void attachTo(Launcher launcher) {
attachTo(launcher, launcher.getWorkspace(), true /* waitForLoadAnimation */);
attachTo(launcher.getWorkspace(), true /* waitForLoadAnimation */,
launcher::clearPendingExecutor);
}
public void attachTo(Launcher launcher, View attachedView, boolean waitForLoadAnimation) {
mLauncher = launcher;
/**
* Attached the executor to the existence of the view
*/
public void attachTo(View attachedView, boolean waitForLoadAnimation,
Consumer<ViewOnDrawExecutor> onClearCallback) {
mOnClearCallback = onClearCallback;
mAttachedView = attachedView;
mAttachedView.addOnAttachStateChangeListener(this);
if (!waitForLoadAnimation) {
@ -110,8 +116,8 @@ public class ViewOnDrawExecutor implements Executor, OnDrawListener, Runnable,
mAttachedView.getViewTreeObserver().removeOnDrawListener(this);
mAttachedView.removeOnAttachStateChangeListener(this);
}
if (mLauncher != null) {
mLauncher.clearPendingExecutor(this);
if (mOnClearCallback != null) {
mOnClearCallback.accept(this);
}
MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
}

View File

@ -126,7 +126,8 @@ public class OptionsPopupView extends ArrowPopup
popup.mTargetRect = targetRect;
for (OptionItem item : items) {
DeepShortcutView view = popup.inflateAndAdd(R.layout.system_shortcut, popup);
DeepShortcutView view =
(DeepShortcutView) popup.inflateAndAdd(R.layout.system_shortcut, popup);
view.getIconView().setBackgroundResource(item.mIconRes);
view.getBubbleText().setText(item.mLabelRes);
view.setDividerVisibility(View.INVISIBLE);

View File

@ -104,7 +104,7 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener {
}
private void setContainerWidth() {
mCellSize = (int) (mDeviceProfile.allAppsCellWidthPx * WIDTH_SCALE);
mCellSize = (int) (mDeviceProfile.cellWidthPx * WIDTH_SCALE);
mPresetPreviewSize = (int) (mCellSize * PREVIEW_SCALE);
}

View File

@ -24,6 +24,8 @@ import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.Direction;
import androidx.test.uiautomator.UiObject2;
import com.android.launcher3.tapl.LauncherInstrumentation.GestureScope;
import java.util.Collection;
/**
@ -93,7 +95,7 @@ public final class Widgets extends LauncherInstrumentation.VisibleContainer {
int i = 0;
for (; ; ) {
final Collection<UiObject2> cells = mLauncher.getObjectsInContainer(
widgetsContainer, "widgets_cell_list_container");
widgetsContainer, "widgets_scroll_container");
mLauncher.assertTrue("Widgets doesn't have 2 rows", cells.size() >= 2);
for (UiObject2 cell : cells) {
final UiObject2 label = cell.findObject(labelSelector);
@ -105,6 +107,19 @@ public final class Widgets extends LauncherInstrumentation.VisibleContainer {
"com.android.launcher3.widget.WidgetCell",
widget.getClassName());
int maxWidth = 0;
for (UiObject2 sibling : widget.getParent().getChildren()) {
maxWidth = Math.max(sibling.getVisibleBounds().width(), maxWidth);
}
int visibleDelta = maxWidth - widget.getVisibleBounds().width();
if (visibleDelta > 0) {
Rect parentBounds = cell.getVisibleBounds();
mLauncher.linearGesture(parentBounds.centerX() + visibleDelta,
parentBounds.centerY(), parentBounds.centerX(),
parentBounds.centerY(), 10, true, GestureScope.INSIDE);
}
if (widget.getVisibleBounds().bottom
<= displaySize.y - mLauncher.getBottomGestureSize()) {
return new Widget(mLauncher, widget);