Adding SecondaryDisplayLauncher in Launcher using common listener
Bug: 141596722 Change-Id: I480bfadf592f7d0309f17c33a3fe14bb77fb5586
This commit is contained in:
parent
ae5e991f5e
commit
9c2b96090b
|
@ -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) \
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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)));
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue