blob: dd5b22eb84ff97bc851963a1da910c90f17c4178 [file] [log] [blame]
/*
* 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.
*/
package com.android.launcher3.views;
import static com.android.launcher3.logging.KeyboardStateManager.KeyboardState.HIDE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_KEYBOARD_CLOSED;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import android.content.Context;
import android.content.ContextWrapper;
import android.graphics.Rect;
import android.os.IBinder;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.AccessibilityDelegate;
import android.view.WindowInsets;
import android.view.WindowInsetsController;
import android.view.inputmethod.InputMethodManager;
import androidx.annotation.Nullable;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.ActivityAllAppsContainerView;
import com.android.launcher3.allapps.search.SearchAdapterProvider;
import com.android.launcher3.dot.DotInfo;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.StringCache;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.popup.PopupDataProvider;
import com.android.launcher3.util.OnboardingPrefs;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.ViewCache;
/**
* An interface to be used along with a context for various activities in Launcher. This allows a
* generic class to depend on Context subclass instead of an Activity.
*/
public interface ActivityContext {
default boolean finishAutoCancelActionMode() {
return false;
}
default DotInfo getDotInfoForItem(ItemInfo info) {
return null;
}
/**
* For items with tree hierarchy, notifies the activity to invalidate the parent when a root
* is invalidated
* @param info info associated with a root node.
*/
default void invalidateParent(ItemInfo info) { }
default AccessibilityDelegate getAccessibilityDelegate() {
return null;
}
default Rect getFolderBoundingBox() {
return getDeviceProfile().getAbsoluteOpenFolderBounds();
}
/**
* After calling {@link #getFolderBoundingBox()}, we calculate a (left, top) position for a
* Folder of size width x height to be within those bounds. However, the chosen position may
* not be visually ideal (e.g. uncanny valley of centeredness), so here's a chance to update it.
* @param inOutPosition A 2-size array where the first element is the left position of the open
* folder and the second element is the top position. Should be updated in place if desired.
* @param bounds The bounds that the open folder should fit inside.
* @param width The width of the open folder.
* @param height The height of the open folder.
*/
default void updateOpenFolderPosition(int[] inOutPosition, Rect bounds, int width, int height) {
}
/**
* Returns a LayoutInflater that is cloned in this Context, so that Views inflated by it will
* have the same Context. (i.e. {@link #lookupContext(Context)} will find this ActivityContext.)
*/
default LayoutInflater getLayoutInflater() {
if (this instanceof Context) {
Context context = (Context) this;
return LayoutInflater.from(context).cloneInContext(context);
}
return null;
}
/**
* The root view to support drag-and-drop and popup support.
*/
BaseDragLayer getDragLayer();
/**
* The all apps container, if it exists in this context.
*/
default ActivityAllAppsContainerView<?> getAppsView() {
return null;
}
DeviceProfile getDeviceProfile();
default ViewCache getViewCache() {
return new ViewCache();
}
/**
* Controller for supporting item drag-and-drop
*/
default <T extends DragController> T getDragController() {
return null;
}
/**
* Returns the FolderIcon with the given item id, if it exists.
*/
default @Nullable FolderIcon findFolderIcon(final int folderIconId) {
return null;
}
default StatsLogManager getStatsLogManager() {
return StatsLogManager.newInstance((Context) this);
}
/**
* Returns {@code true} if popups should use color extraction.
*/
default boolean shouldUseColorExtractionForPopup() {
return true;
}
/**
* Called just before logging the given item.
*/
default void applyOverwritesToLogItem(LauncherAtom.ItemInfo.Builder itemInfoBuilder) { }
/** Onboarding preferences for any onboarding data within this context. */
@Nullable
default OnboardingPrefs<?> getOnboardingPrefs() {
return null;
}
/** Returns {@code true} if items are currently being bound within this context. */
default boolean isBindingItems() {
return false;
}
/**
* Returns the ActivityContext associated with the given Context, or throws an exception if
* the Context is not associated with any ActivityContext.
*/
static <T extends Context & ActivityContext> T lookupContext(Context context) {
T activityContext = lookupContextNoThrow(context);
if (activityContext == null) {
throw new IllegalArgumentException("Cannot find ActivityContext in parent tree");
}
return activityContext;
}
/**
* Returns the ActivityContext associated with the given Context, or null if
* the Context is not associated with any ActivityContext.
*/
static <T extends Context & ActivityContext> T lookupContextNoThrow(Context context) {
if (context instanceof ActivityContext) {
return (T) context;
} else if (context instanceof ContextWrapper) {
return lookupContextNoThrow(((ContextWrapper) context).getBaseContext());
} else {
return null;
}
}
default View.OnClickListener getItemOnClickListener() {
return v -> {
// No op.
};
}
@Nullable
default PopupDataProvider getPopupDataProvider() {
return null;
}
@Nullable
default StringCache getStringCache() {
return null;
}
/**
* Creates and returns {@link SearchAdapterProvider} for build variant specific search result
* views.
*/
@Nullable
default SearchAdapterProvider<?> createSearchAdapterProvider(
ActivityAllAppsContainerView<?> appsView) {
return null;
}
/**
* Hides the keyboard if it is visible
*/
default void hideKeyboard() {
View root = getDragLayer();
if (root == null) {
return;
}
if (Utilities.ATLEAST_R) {
Preconditions.assertUIThread();
// Hide keyboard with WindowInsetsController if could. In case
// hideSoftInputFromWindow may get ignored by input connection being finished
// when the screen is off.
//
// In addition, inside IMF, the keyboards are closed asynchronously that launcher no
// longer need to post to the message queue.
final WindowInsetsController wic = root.getWindowInsetsController();
WindowInsets insets = root.getRootWindowInsets();
boolean isImeShown = insets != null && insets.isVisible(WindowInsets.Type.ime());
if (wic != null && isImeShown) {
StatsLogManager slm = getStatsLogManager();
slm.keyboardStateManager().setKeyboardState(HIDE);
// this method cannot be called cross threads
wic.hide(WindowInsets.Type.ime());
slm.logger().log(LAUNCHER_ALLAPPS_KEYBOARD_CLOSED);
return;
}
}
InputMethodManager imm = root.getContext().getSystemService(InputMethodManager.class);
IBinder token = root.getWindowToken();
if (imm != null && token != null) {
UI_HELPER_EXECUTOR.execute(() -> {
if (imm.hideSoftInputFromWindow(token, 0)) {
// log keyboard close event only when keyboard is actually closed
MAIN_EXECUTOR.execute(() ->
getStatsLogManager().logger().log(LAUNCHER_ALLAPPS_KEYBOARD_CLOSED));
}
});
}
}
}