diff options
Diffstat (limited to 'src')
33 files changed, 1466 insertions, 106 deletions
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java index f9a8d1bbe..b377c0ce6 100644 --- a/src/com/android/launcher3/AppWidgetResizeFrame.java +++ b/src/com/android/launcher3/AppWidgetResizeFrame.java @@ -9,7 +9,6 @@ import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.appwidget.AppWidgetHostView; -import android.appwidget.AppWidgetProviderInfo; import android.content.Context; import android.graphics.Point; import android.graphics.Rect; @@ -76,8 +75,6 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O private boolean mTopBorderActive; private boolean mBottomBorderActive; - private int mResizeMode; - private int mRunningHInc; private int mRunningVInc; private int mMinHSpan; @@ -160,7 +157,6 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O mWidgetView = widgetView; LauncherAppWidgetProviderInfo info = (LauncherAppWidgetProviderInfo) widgetView.getAppWidgetInfo(); - mResizeMode = info.resizeMode; mDragLayer = dragLayer; mMinHSpan = info.minSpanX; @@ -169,14 +165,6 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O mWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(getContext(), widgetView.getAppWidgetInfo().provider, null); - if (mResizeMode == AppWidgetProviderInfo.RESIZE_HORIZONTAL) { - mDragHandles[INDEX_TOP].setVisibility(GONE); - mDragHandles[INDEX_BOTTOM].setVisibility(GONE); - } else if (mResizeMode == AppWidgetProviderInfo.RESIZE_VERTICAL) { - mDragHandles[INDEX_LEFT].setVisibility(GONE); - mDragHandles[INDEX_RIGHT].setVisibility(GONE); - } - // When we create the resize frame, we first mark all cells as unoccupied. The appropriate // cells (same if not resized, or different) will be marked as occupied when the resize // frame is dismissed. @@ -186,14 +174,10 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O } public boolean beginResizeIfPointInRegion(int x, int y) { - boolean horizontalActive = (mResizeMode & AppWidgetProviderInfo.RESIZE_HORIZONTAL) != 0; - boolean verticalActive = (mResizeMode & AppWidgetProviderInfo.RESIZE_VERTICAL) != 0; - - mLeftBorderActive = (x < mTouchTargetWidth) && horizontalActive; - mRightBorderActive = (x > getWidth() - mTouchTargetWidth) && horizontalActive; - mTopBorderActive = (y < mTouchTargetWidth + mTopTouchRegionAdjustment) && verticalActive; - mBottomBorderActive = (y > getHeight() - mTouchTargetWidth + mBottomTouchRegionAdjustment) - && verticalActive; + mLeftBorderActive = x < mTouchTargetWidth; + mRightBorderActive = x > getWidth() - mTouchTargetWidth; + mTopBorderActive = y < mTouchTargetWidth + mTopTouchRegionAdjustment; + mBottomBorderActive = y > getHeight() - mTouchTargetWidth + mBottomTouchRegionAdjustment; boolean anyBordersActive = mLeftBorderActive || mRightBorderActive || mTopBorderActive || mBottomBorderActive; diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 7adb6a442..5e73880a8 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -16,12 +16,15 @@ package com.android.launcher3; +import static com.android.launcher3.InvariantDeviceProfile.KEY_SHOW_DESKTOP_LABELS; +import static com.android.launcher3.InvariantDeviceProfile.KEY_SHOW_DRAWER_LABELS; import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.content.Context; +import android.content.SharedPreferences; import android.content.res.ColorStateList; import android.content.res.TypedArray; import android.graphics.Canvas; @@ -72,7 +75,6 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, private static final int[] STATE_PRESSED = new int[] {android.R.attr.state_pressed}; - private static final Property<BubbleTextView, Float> DOT_SCALE_PROPERTY = new Property<BubbleTextView, Float>(Float.TYPE, "dotScale") { @Override @@ -134,10 +136,11 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, private boolean mIgnorePressedStateChange; @ViewDebug.ExportedProperty(category = "launcher") private boolean mDisableRelayout = false; - @ViewDebug.ExportedProperty(category = "launcher") private final boolean mIgnorePaddingTouch; + private boolean mShouldShowLabel; + private IconLoadRequest mIconLoadRequest; public BubbleTextView(Context context) { @@ -157,6 +160,8 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, R.styleable.BubbleTextView, defStyle, 0); mLayoutHorizontal = a.getBoolean(R.styleable.BubbleTextView_layoutHorizontal, false); + SharedPreferences prefs = Utilities.getPrefs(context.getApplicationContext()); + mDisplay = a.getInteger(R.styleable.BubbleTextView_iconDisplay, DISPLAY_WORKSPACE); final int defaultIconSize; if (mDisplay == DISPLAY_WORKSPACE) { @@ -164,12 +169,14 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx); setCompoundDrawablePadding(grid.iconDrawablePaddingPx); defaultIconSize = grid.iconSizePx; + mShouldShowLabel = prefs.getBoolean(KEY_SHOW_DESKTOP_LABELS, true); mIgnorePaddingTouch = true; } else if (mDisplay == DISPLAY_ALL_APPS) { DeviceProfile grid = mActivity.getDeviceProfile(); setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.allAppsIconTextSizePx); setCompoundDrawablePadding(grid.allAppsIconDrawablePaddingPx); defaultIconSize = grid.allAppsIconSizePx; + mShouldShowLabel = prefs.getBoolean(KEY_SHOW_DRAWER_LABELS, true); mIgnorePaddingTouch = true; } else if (mDisplay == DISPLAY_FOLDER) { DeviceProfile grid = mActivity.getDeviceProfile(); @@ -177,10 +184,12 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, setCompoundDrawablePadding(grid.folderChildDrawablePaddingPx); defaultIconSize = grid.folderChildIconSizePx; mIgnorePaddingTouch = true; + mShouldShowLabel = prefs.getBoolean(KEY_SHOW_DESKTOP_LABELS, true); } else { // widget_selection or shortcut_popup defaultIconSize = mActivity.getDeviceProfile().iconSizePx; mIgnorePaddingTouch = false; + mShouldShowLabel = prefs.getBoolean(KEY_SHOW_DESKTOP_LABELS, true); } mCenterVertically = a.getBoolean(R.styleable.BubbleTextView_centerVertically, false); @@ -292,7 +301,9 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, mDotParams.color = IconPalette.getMutedColor(info.iconColor, 0.54f); setIcon(iconDrawable); - setText(info.title); + if (mShouldShowLabel) { + setText(info.title); + } if (info.contentDescription != null) { setContentDescription(info.isDisabled() ? getContext().getString(R.string.disabled_app_label, info.contentDescription) diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index bc6fa6e90..0ffe2d77c 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -16,7 +16,10 @@ package com.android.launcher3; +import static com.android.launcher3.InvariantDeviceProfile.KEY_SHOW_LABELS_LANDSCAPE; + import android.content.Context; +import android.content.SharedPreferences; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Point; @@ -137,6 +140,8 @@ public class DeviceProfile { public DotRenderer mDotRendererWorkSpace; public DotRenderer mDotRendererAllApps; + private SharedPreferences mPrefs; + public DeviceProfile(Context context, InvariantDeviceProfile inv, InvariantDeviceProfile originalIDP, Point minSize, Point maxSize, int width, int height, boolean isLandscape, boolean isMultiWindowMode) { @@ -171,6 +176,8 @@ public class DeviceProfile { transposeLayoutWithOrientation = res.getBoolean(R.bool.hotseat_transpose_layout_with_orientation); + mPrefs = Utilities.getPrefs(context.getApplicationContext()); + context = getContext(context, isVerticalBarLayout() ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT); @@ -362,8 +369,8 @@ public class DeviceProfile { allAppsCellHeightPx = getCellSize().y; allAppsCellWidthPx = allAppsIconSizePx + allAppsIconDrawablePaddingPx; - if (isVerticalBarLayout()) { - // Always hide the Workspace text with vertical bar layout. + if (isVerticalBarLayout() && !mPrefs.getBoolean(KEY_SHOW_LABELS_LANDSCAPE, false)) { + // Hide Workspace text with vertical bar layout if needed. adjustToHideWorkspaceLabels(); } @@ -559,9 +566,15 @@ public class DeviceProfile { } public static int calculateCellWidth(int width, int countX) { + if (countX == 0) { + countX = 4; + } return width / countX; } public static int calculateCellHeight(int height, int countY) { + if (countY == 0) { + countY = 5; + } return height / countY; } diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java index d66ba7317..b80cff810 100644 --- a/src/com/android/launcher3/InvariantDeviceProfile.java +++ b/src/com/android/launcher3/InvariantDeviceProfile.java @@ -19,7 +19,6 @@ 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.PackageManagerHelper.getPackageFilter; import android.annotation.TargetApi; @@ -28,6 +27,8 @@ import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; +import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; @@ -60,7 +61,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; -public class InvariantDeviceProfile { +public class InvariantDeviceProfile implements OnSharedPreferenceChangeListener { public static final String TAG = "IDP"; // We do not need any synchronization for this variable as its only written on UI thread. @@ -74,7 +75,11 @@ public class InvariantDeviceProfile { public static final int CHANGE_FLAG_GRID = 1 << 0; public static final int CHANGE_FLAG_ICON_PARAMS = 1 << 1; + public static final String KEY_SHOW_DESKTOP_LABELS = "pref_desktop_show_labels"; + public static final String KEY_SHOW_DRAWER_LABELS = "pref_drawer_show_labels"; + public static final String KEY_SHOW_LABELS_LANDSCAPE = "pref_show_labels_landscape"; public static final String KEY_ICON_PATH_REF = "pref_icon_shape_path"; + public static final String KEY_WORKSPACE_EDIT = "pref_workspace_edit"; // Constants that affects the interpolation curve between statically defined device profile // buckets. @@ -132,6 +137,8 @@ public class InvariantDeviceProfile { private ConfigMonitor mConfigMonitor; private OverlayMonitor mOverlayMonitor; + private Context mContext; + @VisibleForTesting public InvariantDeviceProfile() {} @@ -156,9 +163,12 @@ public class InvariantDeviceProfile { @TargetApi(23) private InvariantDeviceProfile(Context context) { - String gridName = Utilities.getPrefs(context).getBoolean(GRID_OPTIONS_PREFERENCE_KEY, false) - ? Utilities.getPrefs(context).getString(KEY_IDP_GRID_NAME, null) - : null; + mContext = context; + + SharedPreferences prefs = Utilities.getPrefs(context); + prefs.registerOnSharedPreferenceChangeListener(this); + + String gridName = prefs.getString(KEY_IDP_GRID_NAME, null); initGrid(context, gridName); mConfigMonitor = new ConfigMonitor(context, APPLY_CONFIG_AT_RUNTIME.get() ? this::onConfigChanged : this::killProcess); @@ -175,6 +185,15 @@ public class InvariantDeviceProfile { } } + @Override + public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { + if (KEY_SHOW_DESKTOP_LABELS.equals(key) || KEY_SHOW_DRAWER_LABELS.equals(key)) { + apply(mContext, CHANGE_FLAG_ICON_PARAMS); + } else if (KEY_SHOW_LABELS_LANDSCAPE.equals(key)) { + onConfigChanged(mContext); + } + } + /** * Retrieve system defined or RRO overriden icon shape. */ @@ -348,9 +367,7 @@ public class InvariantDeviceProfile { InvariantDeviceProfile oldProfile = new InvariantDeviceProfile(this); // Re-init grid - String gridName = Utilities.getPrefs(context).getBoolean(GRID_OPTIONS_PREFERENCE_KEY, false) - ? Utilities.getPrefs(context).getString(KEY_IDP_GRID_NAME, null) - : null; + String gridName = Utilities.getPrefs(context).getString(KEY_IDP_GRID_NAME, null); initGrid(context, gridName); int changeFlags = 0; diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 1338b3062..68c8f1e25 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -102,6 +102,7 @@ import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.icons.IconCache; import com.android.launcher3.keyboard.CustomActionsPopup; import com.android.launcher3.keyboard.ViewGroupFocusHelper; +import com.android.launcher3.lineage.LineageUtils; import com.android.launcher3.logging.FileLog; import com.android.launcher3.logging.StatsLogUtils; import com.android.launcher3.logging.UserEventDispatcher; @@ -1022,6 +1023,13 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, mStateManager.onWindowFocusChanged(); } + public void startActivitySafelyAuth(View v, Intent intent, ItemInfo item, + String sourceContainer) { + LineageUtils.showLockScreen(this, getString(R.string.trust_apps_manager_name), () -> { + startActivitySafely(v, intent, item, sourceContainer); + }); + } + public interface LauncherOverlay { /** diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index ff2b40038..1fccd4f7b 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -1163,6 +1163,12 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou if (mIsBeingDragged) { final int activePointerId = mActivePointerId; final int pointerIndex = ev.findPointerIndex(activePointerId); + + if (pointerIndex == -1) { + onScrollInteractionEnd(); + return true; + } + final float x = ev.getX(pointerIndex); final VelocityTracker velocityTracker = mVelocityTracker; velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 5d0effa5f..108898e99 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -46,7 +46,6 @@ import android.os.Handler; import android.os.Message; import android.os.PowerManager; import android.os.TransactionTooLargeException; -import android.provider.Settings; import android.text.Spannable; import android.text.SpannableString; import android.text.TextUtils; @@ -68,7 +67,6 @@ import com.android.launcher3.icons.LauncherIcons; import com.android.launcher3.shortcuts.DeepShortcutManager; import com.android.launcher3.shortcuts.ShortcutKey; import com.android.launcher3.util.IntArray; -import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.views.Transposable; import com.android.launcher3.widget.PendingAddShortcutInfo; @@ -117,14 +115,7 @@ public final class Utilities { * Indicates if the device has a debug build. Should only be used to store additional info or * add extra logging and not for changing the app behavior. */ - public static final boolean IS_DEBUG_DEVICE = - Build.TYPE.toLowerCase(Locale.ROOT).contains("debug") || - Build.TYPE.toLowerCase(Locale.ROOT).equals("eng"); - - public static boolean isDevelopersOptionsEnabled(Context context) { - return Settings.Global.getInt(context.getApplicationContext().getContentResolver(), - Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0; - } + public static final boolean IS_DEBUG_DEVICE = false; // An intent extra to indicate the horizontal scroll of the wallpaper. public static final String EXTRA_WALLPAPER_OFFSET = "com.android.launcher3.WALLPAPER_OFFSET"; @@ -141,12 +132,6 @@ public final class Utilities { return Log.isLoggable(propertyName, Log.VERBOSE); } - public static boolean existsStyleWallpapers(Context context) { - ResolveInfo ri = context.getPackageManager().resolveActivity( - PackageManagerHelper.getStyleWallpapersIntent(context), 0); - return ri != null; - } - /** * Given a coordinate relative to the descendant, find the coordinate in a parent view's * coordinates. @@ -624,4 +609,9 @@ public final class Utilities { return mSize; } } + + public static boolean isWorkspaceEditAllowed(Context context) { + SharedPreferences prefs = getPrefs(context.getApplicationContext()); + return prefs.getBoolean(InvariantDeviceProfile.KEY_WORKSPACE_EDIT, true); + } } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 9eeb2866b..fcb4db78c 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -1887,8 +1887,7 @@ public class Workspace extends PagedView<WorkspacePageIndicator> final LauncherAppWidgetHostView hostView = (LauncherAppWidgetHostView) cell; AppWidgetProviderInfo pInfo = hostView.getAppWidgetInfo(); - if (pInfo != null && pInfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE - && !d.accessibleDrag) { + if (pInfo != null && !d.accessibleDrag) { onCompleteRunnable = new Runnable() { public void run() { if (!isPageInTransition()) { diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java index 31fcc8c64..bcfdf916b 100644 --- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java +++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java @@ -49,6 +49,7 @@ 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 com.android.launcher3.views.ActivityContext; import java.util.ArrayList; @@ -61,6 +62,7 @@ public class AppsSearchContainerLayout extends ExtendedEditText private final Launcher mLauncher; + private final ActivityContext mActivity; private final AllAppsSearchBarController mSearchBarController; private final SpannableStringBuilder mSearchQueryBuilder; @@ -82,16 +84,27 @@ public class AppsSearchContainerLayout extends ExtendedEditText public AppsSearchContainerLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - mLauncher = Launcher.getLauncher(context); + mActivity = ActivityContext.lookupContext(context); + mLauncher = tryGetLauncher(context); mSearchBarController = new AllAppsSearchBarController(); mSearchQueryBuilder = new SpannableStringBuilder(); Selection.setSelection(mSearchQueryBuilder, 0); - mFixedTranslationY = getTranslationY(); + mFixedTranslationY = Math.round(getTranslationY()); mMarginTopAdjusting = mFixedTranslationY - getPaddingTop(); setHint(prefixTextWithIcon(getContext(), R.drawable.ic_allapps_search, getHint())); + + setTranslationY(0); + } + + private Launcher tryGetLauncher(Context context) { + try { + return Launcher.getLauncher(context); + } catch (IllegalArgumentException e) { + return null; + } } @Override @@ -109,11 +122,11 @@ public class AppsSearchContainerLayout extends ExtendedEditText @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // Update the width to match the grid padding - DeviceProfile dp = mLauncher.getDeviceProfile(); + DeviceProfile dp = mActivity.getDeviceProfile(); int myRequestedWidth = getSize(widthMeasureSpec); - int rowWidth = myRequestedWidth - mAppsView.getActiveRecyclerView().getPaddingLeft() - - mAppsView.getActiveRecyclerView().getPaddingRight(); - + int leftRightPadding = dp.desiredWorkspaceLeftRightMarginPx + + dp.cellLayoutPaddingLeftRightPx; + int rowWidth = myRequestedWidth - leftRightPadding * 2; int cellWidth = DeviceProfile.calculateCellWidth(rowWidth, dp.inv.numHotseatIcons); int iconVisibleSize = Math.round(ICON_VISIBLE_AREA_FACTOR * dp.iconSizePx); int iconPadding = cellWidth - iconVisibleSize; @@ -133,6 +146,7 @@ public class AppsSearchContainerLayout extends ExtendedEditText int expectedLeft = parent.getPaddingLeft() + (availableWidth - myWidth) / 2; int shift = expectedLeft - left; setTranslationX(shift); + offsetTopAndBottom((int) mFixedTranslationY); } @Override @@ -207,7 +221,7 @@ public class AppsSearchContainerLayout extends ExtendedEditText @Override public float getScrollRangeDelta(Rect insets) { - if (mLauncher.getDeviceProfile().isVerticalBarLayout()) { + if (mActivity.getDeviceProfile().isVerticalBarLayout()) { return 0; } else { int topMargin = Math.round(Math.max( diff --git a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java index 26f6ec357..604ac8374 100644 --- a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java +++ b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java @@ -141,7 +141,8 @@ public class DefaultAppSearchAlgorithm implements SearchAlgorithm { // Always a break point for a symbol return true; default: - return false; + // Always a break point at first character + return prevType == Character.UNASSIGNED; } } diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java index 4b90e421f..65cf1216d 100644 --- a/src/com/android/launcher3/config/BaseFlags.java +++ b/src/com/android/launcher3/config/BaseFlags.java @@ -20,6 +20,7 @@ import static androidx.core.util.Preconditions.checkNotNull; import android.content.Context; import android.content.SharedPreferences; +import android.provider.Settings; import androidx.annotation.GuardedBy; import androidx.annotation.Keep; @@ -52,7 +53,9 @@ public abstract class BaseFlags { } public static boolean showFlagTogglerUi(Context context) { - return Utilities.IS_DEBUG_DEVICE && Utilities.isDevelopersOptionsEnabled(context); + return Utilities.IS_DEBUG_DEVICE && + Settings.Global.getInt(context.getApplicationContext().getContentResolver(), + Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0; } public static final boolean IS_DOGFOOD_BUILD = false; @@ -66,7 +69,7 @@ public abstract class BaseFlags { "Adds a promise icon to the home screen for new install sessions."); // Enable moving the QSB on the 0th screen of the workspace - public static final boolean QSB_ON_FIRST_SCREEN = true; + public static final boolean QSB_ON_FIRST_SCREEN = false; public static final TogglableFlag EXAMPLE_FLAG = new TogglableFlag("EXAMPLE_FLAG", true, "An example flag that doesn't do anything. Useful for testing"); diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java index dcdf5d616..d845ad20c 100644 --- a/src/com/android/launcher3/dragndrop/DragController.java +++ b/src/com/android/launcher3/dragndrop/DragController.java @@ -41,6 +41,7 @@ import com.android.launcher3.DropTarget; import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; import com.android.launcher3.R; +import com.android.launcher3.Utilities; import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.accessibility.DragViewStateAnnouncer; import com.android.launcher3.testing.TestProtocol; @@ -429,6 +430,11 @@ public class DragController implements DragDriver.EventListener, TouchController return false; } + if (!Utilities.isWorkspaceEditAllowed(mLauncher.getApplicationContext())) { + cancelDrag(); + return false; + } + // Update the velocity tracker mFlingToDeleteHelper.recordMotionEvent(ev); diff --git a/src/com/android/launcher3/keyboard/ViewGroupFocusHelper.java b/src/com/android/launcher3/keyboard/ViewGroupFocusHelper.java index fde220cbf..b6fcdf414 100644 --- a/src/com/android/launcher3/keyboard/ViewGroupFocusHelper.java +++ b/src/com/android/launcher3/keyboard/ViewGroupFocusHelper.java @@ -54,7 +54,7 @@ public class ViewGroupFocusHelper extends FocusIndicatorHelper { outRect.left += child.getX(); outRect.top += child.getY(); - if (parent != mContainer) { + if (parent != null && parent != mContainer) { if (parent instanceof PagedView) { PagedView page = (PagedView) parent; outRect.left -= page.getScrollForPage(page.indexOfChild(child)); diff --git a/src/com/android/launcher3/lineage/LineageLauncher.java b/src/com/android/launcher3/lineage/LineageLauncher.java new file mode 100644 index 000000000..f8aedccc7 --- /dev/null +++ b/src/com/android/launcher3/lineage/LineageLauncher.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2018 The LineageOS 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.lineage; + +import com.android.launcher3.Launcher; + +public class LineageLauncher extends Launcher { + + private final LineageLauncherCallbacks mCallbacks; + + public LineageLauncher() { + mCallbacks = new LineageLauncherCallbacks(this); + setLauncherCallbacks(mCallbacks); + } + + public LineageLauncherCallbacks getCallbacks() { + return mCallbacks; + } +} diff --git a/src/com/android/launcher3/lineage/LineageLauncherCallbacks.java b/src/com/android/launcher3/lineage/LineageLauncherCallbacks.java new file mode 100644 index 000000000..b7e662bb4 --- /dev/null +++ b/src/com/android/launcher3/lineage/LineageLauncherCallbacks.java @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2018 The LineageOS 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.lineage; + +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.SharedPreferences.OnSharedPreferenceChangeListener; +import android.os.Bundle; +import android.os.Handler; + +import com.android.launcher3.AppInfo; +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherCallbacks; +import com.android.launcher3.Utilities; + +import com.google.android.libraries.gsa.launcherclient.LauncherClient; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; + +public class LineageLauncherCallbacks implements LauncherCallbacks, + OnSharedPreferenceChangeListener, OnDeviceProfileChangeListener { + public static final String KEY_ENABLE_MINUS_ONE = "pref_enable_minus_one"; + public static final String SEARCH_PACKAGE = "com.google.android.googlequicksearchbox"; + + private final LineageLauncher mLauncher; + + private OverlayCallbackImpl mOverlayCallbacks; + private LauncherClient mLauncherClient; + private boolean mDeferCallbacks; + + public LineageLauncherCallbacks(LineageLauncher launcher) { + mLauncher = launcher; + } + + public void deferCallbacksUntilNextResumeOrStop() { + mDeferCallbacks = true; + } + + public LauncherClient getLauncherClient() { + return mLauncherClient; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + SharedPreferences prefs = Utilities.getPrefs(mLauncher); + mOverlayCallbacks = new OverlayCallbackImpl(mLauncher); + mLauncherClient = new LauncherClient(mLauncher, mOverlayCallbacks, getClientOptions(prefs)); + mOverlayCallbacks.setClient(mLauncherClient); + prefs.registerOnSharedPreferenceChangeListener(this); + + mLauncher.addOnDeviceProfileChangeListener(this); + } + + @Override + public void onDetachedFromWindow() { + mLauncherClient.onDetachedFromWindow(); + } + + @Override + public void onAttachedToWindow() { + mLauncherClient.onAttachedToWindow(); + } + + @Override + public void onHomeIntent(boolean internalStateHandled) { + mLauncherClient.hideOverlay(mLauncher.isStarted() && !mLauncher.isForceInvisible()); + } + + @Override + public void onResume() { + Handler handler = mLauncher.getDragLayer().getHandler(); + if (mDeferCallbacks) { + if (handler == null) { + // Finish defer if we are not attached to window. + checkIfStillDeferred(); + } else { + // Wait one frame before checking as we can get multiple resume-pause events + // in the same frame. + handler.post(this::checkIfStillDeferred); + } + } else { + mLauncherClient.onResume(); + } + + } + + @Override + public void onPause() { + if (!mDeferCallbacks) { + mLauncherClient.onPause(); + } + } + + @Override + public void onStart() { + if (!mDeferCallbacks) { + mLauncherClient.onStart(); + } + } + + @Override + public void onStop() { + if (mDeferCallbacks) { + checkIfStillDeferred(); + } else { + mLauncherClient.onStop(); + } + } + + @Override + public void onDeviceProfileChanged(DeviceProfile dp) { + mLauncherClient.reattachOverlay(); + } + + private void checkIfStillDeferred() { + if (!mDeferCallbacks) { + return; + } + if (!mLauncher.hasBeenResumed() && mLauncher.isStarted()) { + return; + } + mDeferCallbacks = false; + + // Move the client to the correct state. Calling the same method twice is no-op. + if (mLauncher.isStarted()) { + mLauncherClient.onStart(); + } + if (mLauncher.hasBeenResumed()) { + mLauncherClient.onResume(); + } else { + mLauncherClient.onPause(); + } + if (!mLauncher.isStarted()) { + mLauncherClient.onStop(); + } + } + @Override + public void onDestroy() { + mLauncherClient.onDestroy(); + Utilities.getPrefs(mLauncher).unregisterOnSharedPreferenceChangeListener(this); + } + + @Override + public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { + if (KEY_ENABLE_MINUS_ONE.equals(key)) { + mLauncherClient.setClientOptions(getClientOptions(prefs)); + } + } + + @Override + public void onSaveInstanceState(Bundle outState) { } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { } + + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { } + + @Override + public void dump(String prefix, FileDescriptor fd, PrintWriter w, String[] args) { + mLauncherClient.dump(prefix, w); + } + + @Override + public boolean handleBackPressed() { + return false; + } + + @Override + public void onTrimMemory(int level) { } + + @Override + public void onLauncherProviderChange() { } + + @Override + public boolean startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData) { + return false; + } + + private LauncherClient.ClientOptions getClientOptions(SharedPreferences prefs) { + return new LauncherClient.ClientOptions( + prefs.getBoolean(KEY_ENABLE_MINUS_ONE, true), + true, /* enableHotword */ + true /* enablePrewarming */ + ); + } +} diff --git a/src/com/android/launcher3/lineage/LineageUtils.java b/src/com/android/launcher3/lineage/LineageUtils.java new file mode 100644 index 000000000..75a9252dd --- /dev/null +++ b/src/com/android/launcher3/lineage/LineageUtils.java @@ -0,0 +1,78 @@ +package com.android.launcher3.lineage; + +import android.app.KeyguardManager; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.hardware.biometrics.BiometricPrompt; +import android.os.CancellationSignal; +import android.os.Handler; +import android.os.Looper; +import android.widget.Toast; + +import com.android.launcher3.R; + +public class LineageUtils { + + /** + * Shows authentication screen to confirm credentials (pin, pattern or password) for the current + * user of the device. + * + * @param context The {@code Context} used to get {@code KeyguardManager} service + * @param title the {@code String} which will be shown as the pompt title + * @param successRunnable The {@code Runnable} which will be executed if the user does not setup + * device security or if lock screen is unlocked + */ + public static void showLockScreen(Context context, String title, Runnable successRunnable) { + if (hasSecureKeyguard(context)) { + final BiometricPrompt.AuthenticationCallback authenticationCallback = + new BiometricPrompt.AuthenticationCallback() { + @Override + public void onAuthenticationSucceeded( + BiometricPrompt.AuthenticationResult result) { + successRunnable.run(); + } + + @Override + public void onAuthenticationError(int errorCode, CharSequence errString) { + //Do nothing + } + }; + + final BiometricPrompt.Builder builder = new BiometricPrompt.Builder(context) + .setTitle(title); + + final KeyguardManager keyguardManager = context.getSystemService(KeyguardManager.class); + + if (keyguardManager.isDeviceSecure()) { + builder.setDeviceCredentialAllowed(true); + } + + final BiometricPrompt bp = builder.build(); + final Handler handler = new Handler(Looper.getMainLooper()); + bp.authenticate(new CancellationSignal(), + runnable -> handler.post(runnable), + authenticationCallback); + } else { + // Notify the user a secure keyguard is required for protected apps, + // but allow to set hidden apps + Toast.makeText(context, R.string.trust_apps_no_lock_error, Toast.LENGTH_LONG) + .show(); + successRunnable.run(); + } + } + + public static boolean hasSecureKeyguard(Context context) { + final KeyguardManager keyguardManager = context.getSystemService(KeyguardManager.class); + return keyguardManager != null && keyguardManager.isKeyguardSecure(); + } + + public static boolean hasPackageInstalled(Context context, String pkgName) { + try { + ApplicationInfo ai = context.getPackageManager().getApplicationInfo(pkgName, 0); + return ai.enabled; + } catch (PackageManager.NameNotFoundException e) { + return false; + } + } +} diff --git a/src/com/android/launcher3/lineage/OverlayCallbackImpl.java b/src/com/android/launcher3/lineage/OverlayCallbackImpl.java new file mode 100644 index 000000000..d0d6756a4 --- /dev/null +++ b/src/com/android/launcher3/lineage/OverlayCallbackImpl.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2016 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.lineage; + +import com.android.launcher3.Launcher; +import com.android.launcher3.Launcher.LauncherOverlay; +import com.android.launcher3.Launcher.LauncherOverlayCallbacks; +import com.google.android.libraries.gsa.launcherclient.LauncherClient; +import com.google.android.libraries.gsa.launcherclient.LauncherClientCallbacks; + +/** + * Implements {@link LauncherOverlay} and passes all the corresponding events to {@link + * LauncherClient}. {@see setClient} + * + * <p>Implements {@link LauncherClientCallbacks} and sends all the corresponding callbacks to {@link + * Launcher}. + */ +public class OverlayCallbackImpl implements LauncherOverlay, LauncherClientCallbacks { + private final Launcher mLauncher; + + private LauncherClient mClient; + private LauncherOverlayCallbacks mLauncherOverlayCallbacks; + private boolean mWasOverlayAttached = false; + + public OverlayCallbackImpl(Launcher launcher) { + mLauncher = launcher; + } + + public void setClient(LauncherClient client) { + mClient = client; + } + + @Override + public void onServiceStateChanged(boolean overlayAttached, boolean hotwordActive) { + if (overlayAttached != mWasOverlayAttached) { + mWasOverlayAttached = overlayAttached; + mLauncher.setLauncherOverlay(overlayAttached ? this : null); + } + } + + @Override + public void onOverlayScrollChanged(float progress) { + if (mLauncherOverlayCallbacks != null) { + mLauncherOverlayCallbacks.onScrollChanged(progress); + } + } + + @Override + public void onScrollInteractionBegin() { + mClient.startMove(); + } + + @Override + public void onScrollInteractionEnd() { + mClient.endMove(); + } + + @Override + public void onScrollChange(float progress, boolean rtl) { + mClient.updateMove(progress); + } + + @Override + public void setOverlayCallbacks(Launcher.LauncherOverlayCallbacks callbacks) { + mLauncherOverlayCallbacks = callbacks; + } +} diff --git a/src/com/android/launcher3/lineage/trust/HiddenAppsFilter.java b/src/com/android/launcher3/lineage/trust/HiddenAppsFilter.java new file mode 100644 index 000000000..942887693 --- /dev/null +++ b/src/com/android/launcher3/lineage/trust/HiddenAppsFilter.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2019 The LineageOS 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.lineage.trust; + +import android.content.ComponentName; +import android.content.Context; + +import com.android.launcher3.AppFilter; +import com.android.launcher3.lineage.trust.db.TrustDatabaseHelper; + +@SuppressWarnings("unused") +public class HiddenAppsFilter extends AppFilter { + private TrustDatabaseHelper mDbHelper; + + public HiddenAppsFilter(Context context) { + if (context == null) { + throw new IllegalArgumentException("Context must not be null!"); + } + + mDbHelper = TrustDatabaseHelper.getInstance(context); + } + + @Override + public boolean shouldShowApp(ComponentName app) { + return !mDbHelper.isPackageHidden(app.getPackageName()); + } +} diff --git a/src/com/android/launcher3/lineage/trust/LoadTrustComponentsTask.java b/src/com/android/launcher3/lineage/trust/LoadTrustComponentsTask.java new file mode 100644 index 000000000..bf0a08d49 --- /dev/null +++ b/src/com/android/launcher3/lineage/trust/LoadTrustComponentsTask.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2019 The LineageOS 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.lineage.trust; + +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.graphics.drawable.Drawable; +import android.os.AsyncTask; +import android.os.Build; + +import androidx.annotation.NonNull; + +import com.android.launcher3.lineage.trust.db.TrustComponent; +import com.android.launcher3.lineage.trust.db.TrustDatabaseHelper; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class LoadTrustComponentsTask extends AsyncTask<Void, Integer, List<TrustComponent>> { + @NonNull + private TrustDatabaseHelper mDbHelper; + + @NonNull + private PackageManager mPackageManager; + + @NonNull + private Callback mCallback; + + LoadTrustComponentsTask(@NonNull TrustDatabaseHelper dbHelper, + @NonNull PackageManager packageManager, + @NonNull Callback callback) { + mDbHelper = dbHelper; + mPackageManager = packageManager; + mCallback = callback; + } + + @Override + protected List<TrustComponent> doInBackground(Void... voids) { + List<TrustComponent> list = new ArrayList<>(); + + Intent filter = new Intent(Intent.ACTION_MAIN, null); + filter.addCategory(Intent.CATEGORY_LAUNCHER); + + List<ResolveInfo> apps = mPackageManager.queryIntentActivities(filter, + PackageManager.GET_META_DATA); + + int numPackages = apps.size(); + for (int i = 0; i < numPackages; i++) { + ResolveInfo app = apps.get(i); + try { + String pkgName = app.activityInfo.packageName; + String label = mPackageManager.getApplicationLabel( + mPackageManager.getApplicationInfo(pkgName, + PackageManager.GET_META_DATA)).toString(); + Drawable icon = app.loadIcon(mPackageManager); + boolean isHidden = mDbHelper.isPackageHidden(pkgName); + boolean isProtected = mDbHelper.isPackageProtected(pkgName); + + list.add(new TrustComponent(pkgName, icon, label, isHidden, isProtected)); + + publishProgress(Math.round(i * 100f / numPackages)); + } catch (PackageManager.NameNotFoundException ignored) { + } + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + Collections.sort(list, (a, b) -> a.getLabel().compareTo(b.getLabel())); + } + + return list; + } + + @Override + protected void onProgressUpdate(Integer... values) { + if (values.length > 0) { + mCallback.onLoadListProgress(values[0]); + } + } + + @Override + protected void onPostExecute(List<TrustComponent> trustComponents) { + mCallback.onLoadCompleted(trustComponents); + } + + interface Callback { + void onLoadListProgress(int progress); + void onLoadCompleted(List<TrustComponent> result); + } +} diff --git a/src/com/android/launcher3/lineage/trust/TrustAppsActivity.java b/src/com/android/launcher3/lineage/trust/TrustAppsActivity.java new file mode 100644 index 000000000..61bcc7910 --- /dev/null +++ b/src/com/android/launcher3/lineage/trust/TrustAppsActivity.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2019 The LineageOS 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.lineage.trust; + +import android.app.ActionBar; +import android.app.Activity; +import android.app.AlertDialog; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.ProgressBar; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.DefaultItemAnimator; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.R; +import com.android.launcher3.Utilities; +import com.android.launcher3.lineage.LineageUtils; +import com.android.launcher3.lineage.trust.db.TrustComponent; +import com.android.launcher3.lineage.trust.db.TrustDatabaseHelper; + +import java.util.List; + +import static com.android.launcher3.lineage.trust.db.TrustComponent.Kind.HIDDEN; +import static com.android.launcher3.lineage.trust.db.TrustComponent.Kind.PROTECTED; + +public class TrustAppsActivity extends Activity implements + TrustAppsAdapter.Listener, + LoadTrustComponentsTask.Callback, + UpdateItemTask.UpdateCallback { + + private static final String KEY_TRUST_ONBOARDING = "pref_trust_onboarding"; + + private RecyclerView mRecyclerView; + private LinearLayout mLoadingView; + private ProgressBar mProgressBar; + + private TrustDatabaseHelper mDbHelper; + private TrustAppsAdapter mAdapter; + + @Override + protected void onCreate(@Nullable Bundle savedInstance) { + super.onCreate(savedInstance); + + ActionBar actionBar = getActionBar(); + if (actionBar != null) { + actionBar.setDisplayHomeAsUpEnabled(true); + } + + setContentView(R.layout.activity_hidden_apps); + mRecyclerView = findViewById(R.id.hidden_apps_list); + mLoadingView = findViewById(R.id.hidden_apps_loading); + mLoadingView.setVisibility(View.VISIBLE); + mProgressBar = findViewById(R.id.hidden_apps_progress_bar); + + final boolean hasSecureKeyguard = LineageUtils.hasSecureKeyguard(this); + mAdapter = new TrustAppsAdapter(this, hasSecureKeyguard); + mDbHelper = TrustDatabaseHelper.getInstance(this); + + mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); + mRecyclerView.setItemAnimator(new DefaultItemAnimator()); + mRecyclerView.setAdapter(mAdapter); + + showOnBoarding(false); + + new LoadTrustComponentsTask(mDbHelper, getPackageManager(), this).execute(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater menuInflater = getMenuInflater(); + menuInflater.inflate(R.menu.menu_trust_apps, menu); + return super.onCreateOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + int id = item.getItemId(); + if (id == android.R.id.home) { + finish(); + return true; + } else if (id == R.id.menu_trust_help) { + showOnBoarding(true); + return true; + } else { + return super.onOptionsItemSelected(item); + } + } + + @Override + public void onHiddenItemChanged(@NonNull TrustComponent component) { + new UpdateItemTask(mDbHelper, this, HIDDEN).execute(component); + } + + @Override + public void onProtectedItemChanged(@NonNull TrustComponent component) { + new UpdateItemTask(mDbHelper, this, PROTECTED).execute(component); + } + + @Override + public void onUpdated(boolean result) { + LauncherAppState state = LauncherAppState.getInstanceNoCreate(); + if (state != null) { + state.getModel().forceReload(); + } + } + + @Override + public void onLoadListProgress(int progress) { + mProgressBar.setProgress(progress); + } + + @Override + public void onLoadCompleted(List<TrustComponent> result) { + mLoadingView.setVisibility(View.GONE); + mRecyclerView.setVisibility(View.VISIBLE); + mAdapter.update(result); + } + + private void showOnBoarding(boolean forceShow) { + SharedPreferences preferenceManager = Utilities.getPrefs(this); + if (!forceShow && preferenceManager.getBoolean(KEY_TRUST_ONBOARDING, false)) { + return; + } + + preferenceManager.edit() + .putBoolean(KEY_TRUST_ONBOARDING, true) + .apply(); + + new AlertDialog.Builder(this) + .setView(R.layout.dialog_trust_welcome) + .setPositiveButton(android.R.string.ok, null) + .show(); + } +} diff --git a/src/com/android/launcher3/lineage/trust/TrustAppsAdapter.java b/src/com/android/launcher3/lineage/trust/TrustAppsAdapter.java new file mode 100644 index 000000000..6b827d6e1 --- /dev/null +++ b/src/com/android/launcher3/lineage/trust/TrustAppsAdapter.java @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2019 The LineageOS 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.lineage.trust; + +import android.graphics.drawable.Animatable2; +import android.graphics.drawable.AnimatedVectorDrawable; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.DiffUtil; +import androidx.recyclerview.widget.RecyclerView; + +import com.android.launcher3.R; +import com.android.launcher3.lineage.trust.db.TrustComponent; + +import java.util.ArrayList; +import java.util.List; + +class TrustAppsAdapter extends RecyclerView.Adapter<TrustAppsAdapter.ViewHolder> { + private List<TrustComponent> mList = new ArrayList<>(); + private Listener mListener; + private boolean mHasSecureKeyguard; + + TrustAppsAdapter(Listener listener, boolean hasSecureKeyguard) { + mListener = listener; + mHasSecureKeyguard = hasSecureKeyguard; + } + + public void update(List<TrustComponent> list) { + DiffUtil.DiffResult result = DiffUtil.calculateDiff(new Callback(mList, list)); + mList = list; + result.dispatchUpdatesTo(this); + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int type) { + return new ViewHolder(LayoutInflater.from(parent.getContext()) + .inflate(R.layout.item_hidden_app, parent, false)); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder viewHolder, int i) { + viewHolder.bind(mList.get(i), mHasSecureKeyguard); + } + + @Override + public int getItemCount() { + return mList.size(); + } + + public interface Listener { + void onHiddenItemChanged(@NonNull TrustComponent component); + + void onProtectedItemChanged(@NonNull TrustComponent component); + } + + class ViewHolder extends RecyclerView.ViewHolder { + private ImageView mIconView; + private TextView mLabelView; + private ImageView mHiddenView; + private ImageView mProtectedView; + + ViewHolder(@NonNull View itemView) { + super(itemView); + + mIconView = itemView.findViewById(R.id.item_hidden_app_icon); + mLabelView = itemView.findViewById(R.id.item_hidden_app_title); + mHiddenView = itemView.findViewById(R.id.item_hidden_app_switch); + mProtectedView = itemView.findViewById(R.id.item_protected_app_switch); + } + + void bind(TrustComponent component, boolean hasSecureKeyguard) { + mIconView.setImageDrawable(component.getIcon()); + mLabelView.setText(component.getLabel()); + + mHiddenView.setImageResource(component.isHidden() ? + R.drawable.ic_hidden_locked : R.drawable.ic_hidden_unlocked); + mProtectedView.setImageResource(component.isProtected() ? + R.drawable.ic_protected_locked : R.drawable.ic_protected_unlocked); + + mProtectedView.setVisibility(hasSecureKeyguard ? View.VISIBLE : View.GONE); + + mHiddenView.setOnClickListener(v -> { + component.invertVisibility(); + + mHiddenView.setImageResource(component.isHidden() ? + R.drawable.avd_hidden_lock : R.drawable.avd_hidden_unlock); + AnimatedVectorDrawable avd = (AnimatedVectorDrawable) mHiddenView.getDrawable(); + + int position = getAdapterPosition(); + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) { + avd.registerAnimationCallback(new Animatable2.AnimationCallback() { + @Override + public void onAnimationEnd(Drawable drawable) { + updateHiddenList(position, component); + } + }); + avd.start(); + } else { + avd.start(); + updateHiddenList(position, component); + } + }); + + mProtectedView.setOnClickListener(v -> { + component.invertProtection(); + + mProtectedView.setImageResource(component.isProtected() ? + R.drawable.avd_protected_lock : R.drawable.avd_protected_unlock); + AnimatedVectorDrawable avd = (AnimatedVectorDrawable) mProtectedView.getDrawable(); + + int position = getAdapterPosition(); + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) { + avd.registerAnimationCallback(new Animatable2.AnimationCallback() { + @Override + public void onAnimationEnd(Drawable drawable) { + updateProtectedList(position, component); + } + }); + avd.start(); + } else { + avd.start(); + updateProtectedList(position, component); + } + }); + } + + private void updateHiddenList(int position, TrustComponent component) { + mListener.onHiddenItemChanged(component); + updateList(position, component); + } + + private void updateProtectedList(int position, TrustComponent component) { + mListener.onProtectedItemChanged(component); + updateList(position, component); + } + + private void updateList(int position, TrustComponent component) { + mList.set(position, component); + notifyItemChanged(position); + } + } + + private static class Callback extends DiffUtil.Callback { + List<TrustComponent> mOldList; + List<TrustComponent> mNewList; + + public Callback(List<TrustComponent> oldList, + List<TrustComponent> newList) { + mOldList = oldList; + mNewList = newList; + } + + + @Override + public int getOldListSize() { + return mOldList.size(); + } + + @Override + public int getNewListSize() { + return mNewList.size(); + } + + @Override + public boolean areItemsTheSame(int iOld, int iNew) { + String oldPkg = mOldList.get(iOld).getPackageName(); + String newPkg = mNewList.get(iNew).getPackageName(); + return oldPkg.equals(newPkg); + } + + @Override + public boolean areContentsTheSame(int iOld, int iNew) { + return mOldList.get(iOld).equals(mNewList.get(iNew)); + } + } +} diff --git a/src/com/android/launcher3/lineage/trust/UpdateItemTask.java b/src/com/android/launcher3/lineage/trust/UpdateItemTask.java new file mode 100644 index 000000000..26ae24c6c --- /dev/null +++ b/src/com/android/launcher3/lineage/trust/UpdateItemTask.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2019 The LineageOS 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.lineage.trust; + +import android.os.AsyncTask; +import androidx.annotation.NonNull; + +import com.android.launcher3.lineage.trust.db.TrustComponent; +import com.android.launcher3.lineage.trust.db.TrustDatabaseHelper; + +public class UpdateItemTask extends AsyncTask<TrustComponent, Void, Boolean> { + @NonNull + private TrustDatabaseHelper mDbHelper; + @NonNull + private UpdateCallback mCallback; + @NonNull + private TrustComponent.Kind mKind; + + UpdateItemTask(@NonNull TrustDatabaseHelper dbHelper, + @NonNull UpdateCallback callback, + @NonNull TrustComponent.Kind kind) { + mDbHelper = dbHelper; + mCallback = callback; + mKind = kind; + } + + @Override + protected Boolean doInBackground(TrustComponent... trustComponents) { + if (trustComponents.length < 1) { + return false; + } + + TrustComponent component = trustComponents[0]; + String pkgName = component.getPackageName(); + + switch (mKind) { + case HIDDEN: + if (component.isHidden()) { + mDbHelper.addHiddenApp(pkgName); + } else { + mDbHelper.removeHiddenApp(pkgName); + } + break; + case PROTECTED: + if (component.isProtected()) { + mDbHelper.addProtectedApp(pkgName); + } else { + mDbHelper.removeProtectedApp(pkgName); + } + break; + } + return true; + } + + @Override + protected void onPostExecute(Boolean result) { + mCallback.onUpdated(result); + } + + interface UpdateCallback { + void onUpdated(boolean result); + } +} diff --git a/src/com/android/launcher3/lineage/trust/db/TrustComponent.java b/src/com/android/launcher3/lineage/trust/db/TrustComponent.java new file mode 100644 index 000000000..5342bde3d --- /dev/null +++ b/src/com/android/launcher3/lineage/trust/db/TrustComponent.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2019 The LineageOS 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.lineage.trust.db; + +import android.graphics.drawable.Drawable; +import androidx.annotation.NonNull; + +public class TrustComponent { + @NonNull + private final String mPackageName; + @NonNull + private final Drawable mIcon; + @NonNull + private final String mLabel; + + private boolean mIsHidden; + private boolean mIsProtected; + + public TrustComponent(@NonNull String packageName, @NonNull Drawable icon, + @NonNull String label, boolean isHidden, boolean isProtected) { + mPackageName = packageName; + mIcon = icon; + mLabel = label; + mIsHidden = isHidden; + mIsProtected = isProtected; + } + + @NonNull + public String getPackageName() { + return mPackageName; + } + + @NonNull + public Drawable getIcon() { + return mIcon; + } + + @NonNull + public String getLabel() { + return mLabel; + } + + public boolean isHidden() { + return mIsHidden; + } + + public boolean isProtected() { + return mIsProtected; + } + + public void invertVisibility() { + mIsHidden = !mIsHidden; + } + + public void invertProtection() { + mIsProtected = !mIsProtected; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof TrustComponent)) { + return false; + } + + TrustComponent otherComponent = (TrustComponent) other; + return otherComponent.getPackageName().equals(mPackageName) && + otherComponent.isHidden() == mIsHidden; + } + + @Override + public int hashCode() { + return mPackageName.hashCode() + (mIsHidden ? 1 : 0); + } + + public enum Kind { + HIDDEN, + PROTECTED, + } +} diff --git a/src/com/android/launcher3/lineage/trust/db/TrustDatabaseHelper.java b/src/com/android/launcher3/lineage/trust/db/TrustDatabaseHelper.java new file mode 100644 index 000000000..715355c8c --- /dev/null +++ b/src/com/android/launcher3/lineage/trust/db/TrustDatabaseHelper.java @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2019 The LineageOS 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.lineage.trust.db; + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +public class TrustDatabaseHelper extends SQLiteOpenHelper { + private static final int DATABASE_VERSION = 1; + private static final String DATABASE_NAME = "trust_apps_db"; + + private static final String TABLE_NAME = "trust_apps"; + private static final String KEY_UID = "uid"; + private static final String KEY_PKGNAME = "pkgname"; + private static final String KEY_HIDDEN = "hidden"; + private static final String KEY_PROTECTED = "protected"; + + @Nullable + private static TrustDatabaseHelper sSingleton; + + private TrustDatabaseHelper(@NonNull Context context) { + super(context, DATABASE_NAME, null, DATABASE_VERSION); + } + + public static synchronized TrustDatabaseHelper getInstance(@NonNull Context context) { + if (sSingleton == null) { + sSingleton = new TrustDatabaseHelper(context); + } + + return sSingleton; + } + + @Override + public void onCreate(SQLiteDatabase db) { + String CMD_CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + + "(" + + KEY_UID + " INTEGER PRIMARY KEY AUTOINCREMENT," + + KEY_PKGNAME + " TEXT," + + KEY_HIDDEN + " INTEGER DEFAULT 0," + + KEY_PROTECTED + " INTEGER DEFAULT 0" + + ")"; + db.execSQL(CMD_CREATE_TABLE); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + } + + public void addHiddenApp(@NonNull String packageName) { + if (isPackageHidden(packageName)) { + return; + } + + SQLiteDatabase db = getWritableDatabase(); + db.beginTransaction(); + + try { + ContentValues values = new ContentValues(); + values.put(KEY_PKGNAME, packageName); + values.put(KEY_HIDDEN, 1); + + int rows = db.update(TABLE_NAME, values, KEY_PKGNAME + " = ?", new String[]{KEY_PKGNAME}); + if (rows != 1) { + // Entry doesn't exist, create a new one + db.insertOrThrow(TABLE_NAME, null, values); + } + db.setTransactionSuccessful(); + } catch (Exception e) { + // Ignored + } finally { + db.endTransaction(); + } + } + + public void addProtectedApp(@NonNull String packageName) { + if (isPackageProtected(packageName)) { + return; + } + + SQLiteDatabase db = getWritableDatabase(); + db.beginTransaction(); + + try { + ContentValues values = new ContentValues(); + values.put(KEY_PKGNAME, packageName); + values.put(KEY_PROTECTED, 1); + + int rows = db.update(TABLE_NAME, values, KEY_PKGNAME + " = ?", new String[]{KEY_PKGNAME}); + if (rows != 1) { + // Entry doesn't exist, create a new one + db.insertOrThrow(TABLE_NAME, null, values); + } + db.setTransactionSuccessful(); + } catch (Exception e) { + // Ignored + } finally { + db.endTransaction(); + } + } + + + public void removeHiddenApp(@NonNull String packageName) { + if (!isPackageHidden(packageName)) { + return; + } + + SQLiteDatabase db = getWritableDatabase(); + db.beginTransaction(); + + try { + ContentValues values = new ContentValues(); + values.put(KEY_HIDDEN, 0); + + db.update(TABLE_NAME, values, KEY_PKGNAME + " = ?", new String[]{packageName}); + db.setTransactionSuccessful(); + } catch (Exception e) { + // Ignored + } finally { + db.endTransaction(); + } + } + + public void removeProtectedApp(@NonNull String packageName) { + if (!isPackageProtected(packageName)) { + return; + } + + SQLiteDatabase db = getWritableDatabase(); + db.beginTransaction(); + + try { + ContentValues values = new ContentValues(); + values.put(KEY_PROTECTED, 0); + + db.update(TABLE_NAME, values, KEY_PKGNAME + " = ?", new String[]{packageName}); + db.setTransactionSuccessful(); + } catch (Exception e) { + // Ignored + } finally { + db.endTransaction(); + } + } + + public boolean isPackageHidden(@NonNull String packageName) { + String query = String.format("SELECT * FROM %s WHERE %s = ? AND %s = ?", TABLE_NAME, KEY_PKGNAME, KEY_HIDDEN); + SQLiteDatabase db = getReadableDatabase(); + Cursor cursor = db.rawQuery(query, new String[]{packageName, String.valueOf(1)}); + boolean result = false; + try { + result = cursor.getCount() != 0; + } catch (Exception e) { + // Ignored + } finally { + if (cursor != null && !cursor.isClosed()) { + cursor.close(); + } + } + + return result; + } + + public boolean isPackageProtected(@NonNull String packageName) { + String query = String.format("SELECT * FROM %s WHERE %s = ? AND %s = ?", TABLE_NAME, KEY_PKGNAME, KEY_PROTECTED); + SQLiteDatabase db = getReadableDatabase(); + Cursor cursor = db.rawQuery(query, new String[]{packageName, String.valueOf(1)}); + boolean result = false; + try { + result = cursor.getCount() != 0; + } catch (Exception e) { + // Ignored + } finally { + if (cursor != null && !cursor.isClosed()) { + cursor.close(); + } + } + + return result; + } +} diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java index e8ac1d4c9..817a84c59 100644 --- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java +++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java @@ -53,6 +53,7 @@ import com.android.launcher3.ItemInfo; import com.android.launcher3.ItemInfoWithIcon; import com.android.launcher3.Launcher; import com.android.launcher3.R; +import com.android.launcher3.Utilities; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.accessibility.ShortcutMenuAccessibilityDelegate; import com.android.launcher3.dot.DotInfo; @@ -600,6 +601,8 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource, if (!ItemLongClickListener.canStartDrag(mLauncher)) return false; // Return early if not the correct view if (!(v.getParent() instanceof DeepShortcutView)) return false; + // Return early if workspace edit is disabled + if (!Utilities.isWorkspaceEditAllowed(mLauncher.getApplicationContext())) return false; // Long clicked on a shortcut. DeepShortcutView sv = (DeepShortcutView) v.getParent(); diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java index a87b7b897..cad73eb03 100644 --- a/src/com/android/launcher3/popup/SystemShortcut.java +++ b/src/com/android/launcher3/popup/SystemShortcut.java @@ -20,6 +20,7 @@ import com.android.launcher3.Launcher; import com.android.launcher3.LauncherSettings; import com.android.launcher3.R; import com.android.launcher3.WorkspaceItemInfo; +import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.AppLaunchTracker; import com.android.launcher3.model.WidgetItem; @@ -135,6 +136,7 @@ public abstract class SystemShortcut<T extends BaseDraggingActivity> @Override public View.OnClickListener getOnClickListener(final Launcher launcher, final ItemInfo itemInfo) { + if (!Utilities.isWorkspaceEditAllowed(launcher.getApplicationContext())) return null; if (itemInfo.getTargetComponent() == null) return null; final List<WidgetItem> widgets = launcher.getPopupDataProvider().getWidgetsForPackageUser(new PackageUserKey( diff --git a/src/com/android/launcher3/provider/ImportDataTask.java b/src/com/android/launcher3/provider/ImportDataTask.java index 970a03e80..0e080f2a5 100644 --- a/src/com/android/launcher3/provider/ImportDataTask.java +++ b/src/com/android/launcher3/provider/ImportDataTask.java @@ -103,7 +103,7 @@ public class ImportDataTask { String profileId = Long.toString(UserManagerCompat.getInstance(mContext) .getSerialNumberForUser(Process.myUserHandle())); - boolean createEmptyRowOnFirstScreen; + boolean createEmptyRowOnFirstScreen = false; if (FeatureFlags.QSB_ON_FIRST_SCREEN) { try (Cursor c = mContext.getContentResolver().query(mOtherFavoritesUri, null, // get items on the first row of the first screen (min screen id) diff --git a/src/com/android/launcher3/qsb/QsbContainerView.java b/src/com/android/launcher3/qsb/QsbContainerView.java index 0eb428527..3ca340a2b 100644 --- a/src/com/android/launcher3/qsb/QsbContainerView.java +++ b/src/com/android/launcher3/qsb/QsbContainerView.java @@ -30,6 +30,8 @@ import android.appwidget.AppWidgetProviderInfo; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; import android.graphics.Rect; import android.os.Bundle; import android.provider.Settings; @@ -58,6 +60,8 @@ import com.android.launcher3.graphics.FragmentWithPreview; * AppWidgetManager directly, so that it keeps working in that case. */ public class QsbContainerView extends FrameLayout { + private static final String QSB_PROVIDER_CLASS + = "com.google.android.googlequicksearchbox.SearchWidgetProvider"; public static final String SEARCH_PROVIDER_SETTINGS_KEY = "SEARCH_PROVIDER_PACKAGE_NAME"; @@ -148,6 +152,26 @@ public class QsbContainerView extends FrameLayout { super.setPadding(left, top, right, bottom); } + public static void updateDefaultLayout(Context context, AppWidgetProviderInfo info) { + ComponentName provider = info.provider; + if (QSB_PROVIDER_CLASS.equals(provider.getClassName())) { + try { + ActivityInfo activityInfo = + context.getPackageManager().getReceiverInfo(provider, + PackageManager.GET_META_DATA); + Bundle metaData = activityInfo.metaData; + int resId = metaData.getInt( + "com.google.android.gsa.searchwidget.alt_initial_layout_cqsb", -1); + if (resId != -1) { + info.initialLayout = resId; + } + } catch (Exception e) { + // Ignore the exception since if any exceptions happen + // the original initialyLayout would be used. + } + } + } + /** * A fragment to display the QSB. */ @@ -198,6 +222,11 @@ public class QsbContainerView extends FrameLayout { return getDefaultView(container, false /* show setup icon */); } Bundle opts = createBindOptions(); + // round quick search bar + opts.putString("attached-launcher-identifier", + getActivity().getPackageName()); + opts.putString("requested-widget-style", "cqsb"); + Context context = getContext(); AppWidgetManager widgetManager = AppWidgetManager.getInstance(context); diff --git a/src/com/android/launcher3/qsb/QsbWidgetHostView.java b/src/com/android/launcher3/qsb/QsbWidgetHostView.java index f5ecda3e3..660ff7d7c 100644 --- a/src/com/android/launcher3/qsb/QsbWidgetHostView.java +++ b/src/com/android/launcher3/qsb/QsbWidgetHostView.java @@ -16,6 +16,7 @@ package com.android.launcher3.qsb; +import android.appwidget.AppWidgetProviderInfo; import android.content.Context; import android.view.LayoutInflater; import android.view.View; @@ -48,6 +49,11 @@ public class QsbWidgetHostView extends NavigableAppWidgetHostView { super.updateAppWidget(remoteViews); } + @Override + public void setAppWidget(int appWidgetId, AppWidgetProviderInfo info) { + QsbContainerView.updateDefaultLayout(getContext(), info); + super.setAppWidget(appWidgetId, info); + } public boolean isReinflateRequired(int orientation) { // Re-inflate is required if the orientation has changed since last inflation. diff --git a/src/com/android/launcher3/settings/SettingsActivity.java b/src/com/android/launcher3/settings/SettingsActivity.java index 8d2db78c7..b2520e366 100644 --- a/src/com/android/launcher3/settings/SettingsActivity.java +++ b/src/com/android/launcher3/settings/SettingsActivity.java @@ -24,12 +24,10 @@ import static com.android.launcher3.states.RotationHelper.getAllowRotationDefaul import static com.android.launcher3.util.SecureSettingsObserver.newNotificationSettingsObserver; import android.app.Activity; +import android.app.ActivityManager; import android.app.DialogFragment; import android.app.Fragment; -import android.content.ComponentName; -import android.content.Context; -import android.content.SharedPreferences; -import android.content.pm.PackageManager; +import android.content.Intent; import android.os.Bundle; import android.provider.Settings; import android.text.TextUtils; @@ -48,6 +46,9 @@ import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.graphics.GridOptionsProvider; +import com.android.launcher3.lineage.LineageLauncherCallbacks; +import com.android.launcher3.lineage.LineageUtils; +import com.android.launcher3.lineage.trust.TrustAppsActivity; import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper; import com.android.launcher3.util.SecureSettingsObserver; @@ -55,8 +56,7 @@ import com.android.launcher3.util.SecureSettingsObserver; * Settings activity for Launcher. Currently implements the following setting: Allow rotation */ public class SettingsActivity extends Activity - implements OnPreferenceStartFragmentCallback, OnPreferenceStartScreenCallback, - SharedPreferences.OnSharedPreferenceChangeListener{ + implements OnPreferenceStartFragmentCallback, OnPreferenceStartScreenCallback { private static final String DEVELOPER_OPTIONS_KEY = "pref_developer_options"; private static final String FLAGS_PREFERENCE_KEY = "flag_toggler"; @@ -70,7 +70,8 @@ public class SettingsActivity extends Activity private static final int DELAY_HIGHLIGHT_DURATION_MILLIS = 600; public static final String SAVE_HIGHLIGHTED_KEY = "android:preference_highlighted"; - public static final String GRID_OPTIONS_PREFERENCE_KEY = "pref_grid_options"; + public static final String KEY_MINUS_ONE = "pref_enable_minus_one"; + public static final String KEY_TRUST_APPS = "pref_trust_apps"; @Override protected void onCreate(Bundle savedInstanceState) { @@ -90,28 +91,6 @@ public class SettingsActivity extends Activity .replace(android.R.id.content, f) .commit(); } - Utilities.getPrefs(getApplicationContext()).registerOnSharedPreferenceChangeListener(this); - } - @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - if (GRID_OPTIONS_PREFERENCE_KEY.equals(key)) { - - final ComponentName cn = new ComponentName(getApplicationContext(), - GridOptionsProvider.class); - Context c = getApplicationContext(); - int oldValue = c.getPackageManager().getComponentEnabledSetting(cn); - int newValue; - if (Utilities.getPrefs(c).getBoolean(GRID_OPTIONS_PREFERENCE_KEY, false)) { - newValue = PackageManager.COMPONENT_ENABLED_STATE_ENABLED; - } else { - newValue = PackageManager.COMPONENT_ENABLED_STATE_DISABLED; - } - - if (oldValue != newValue) { - c.getPackageManager().setComponentEnabledSetting(cn, newValue, - PackageManager.DONT_KILL_APP); - } - } } private boolean startFragment(String fragment, Bundle args, String key) { @@ -198,6 +177,7 @@ public class SettingsActivity extends Activity switch (preference.getKey()) { case NOTIFICATION_DOTS_PREFERENCE_KEY: if (!Utilities.ATLEAST_OREO || + getContext().getSystemService(ActivityManager.class).isLowRamDevice() || !getResources().getBoolean(R.bool.notification_dots_enabled)) { return false; } @@ -233,10 +213,21 @@ public class SettingsActivity extends Activity // Show if plugins are enabled or flag UI is enabled. return FeatureFlags.showFlagTogglerUi(getContext()) || PluginManagerWrapper.hasPlugins(getContext()); - case GRID_OPTIONS_PREFERENCE_KEY: - return Utilities.isDevelopersOptionsEnabled(getContext()) && - Utilities.IS_DEBUG_DEVICE && - Utilities.existsStyleWallpapers(getContext()); + + case KEY_MINUS_ONE: + return LineageUtils.hasPackageInstalled(getActivity(), + LineageLauncherCallbacks.SEARCH_PACKAGE); + + case KEY_TRUST_APPS: + preference.setOnPreferenceClickListener(p -> { + LineageUtils.showLockScreen(getActivity(), + getString(R.string.trust_apps_manager_name), () -> { + Intent intent = new Intent(getActivity(), TrustAppsActivity.class); + startActivity(intent); + }); + return true; + }); + return true; } return true; diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java index 03493a538..918b631bc 100644 --- a/src/com/android/launcher3/touch/ItemClickHandler.java +++ b/src/com/android/launcher3/touch/ItemClickHandler.java @@ -26,6 +26,7 @@ import static com.android.launcher3.model.AppLaunchTracker.CONTAINER_ALL_APPS; import android.app.AlertDialog; import android.content.ActivityNotFoundException; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.LauncherApps; @@ -55,6 +56,7 @@ import com.android.launcher3.compat.AppWidgetManagerCompat; import com.android.launcher3.compat.PackageInstallerCompat; import com.android.launcher3.folder.Folder; import com.android.launcher3.folder.FolderIcon; +import com.android.launcher3.lineage.trust.db.TrustDatabaseHelper; import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.views.FloatingIconView; import com.android.launcher3.widget.PendingAppWidgetHostView; @@ -267,6 +269,15 @@ public class ItemClickHandler { // Preload the icon to reduce latency b/w swapping the floating view with the original. FloatingIconView.fetchIcon(launcher, v, item, true /* isOpening */); } - launcher.startActivitySafely(v, intent, item, sourceContainer); + + TrustDatabaseHelper db = TrustDatabaseHelper.getInstance(launcher); + ComponentName cn = item.getTargetComponent(); + boolean isProtected = cn != null && db.isPackageProtected(cn.getPackageName()); + + if (isProtected) { + launcher.startActivitySafelyAuth(v, intent, item, sourceContainer); + } else { + launcher.startActivitySafely(v, intent, item, sourceContainer); + } } } diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java index e97adb577..78d1d3ca8 100644 --- a/src/com/android/launcher3/util/PackageManagerHelper.java +++ b/src/com/android/launcher3/util/PackageManagerHelper.java @@ -175,11 +175,6 @@ public class PackageManagerHelper { } } - public static Intent getStyleWallpapersIntent(Context context) { - return new Intent(Intent.ACTION_SET_WALLPAPER).setComponent( - new ComponentName(context.getString(R.string.wallpaper_picker_package), - "com.android.customization.picker.CustomizationPickerActivity")); - } /** * Starts the details activity for {@code info} diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java index 465df448e..c718d74e7 100644 --- a/src/com/android/launcher3/views/OptionsPopupView.java +++ b/src/com/android/launcher3/views/OptionsPopupView.java @@ -18,8 +18,10 @@ package com.android.launcher3.views; import static com.android.launcher3.Utilities.EXTRA_WALLPAPER_FLAVOR; import static com.android.launcher3.Utilities.EXTRA_WALLPAPER_OFFSET; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.pm.ResolveInfo; import android.graphics.Rect; import android.graphics.RectF; import android.text.TextUtils; @@ -152,13 +154,13 @@ public class OptionsPopupView extends ArrowPopup RectF target = new RectF(x - halfSize, y - halfSize, x + halfSize, y + halfSize); ArrayList<OptionItem> options = new ArrayList<>(); - int resString = Utilities.existsStyleWallpapers(launcher) ? + int resString = existsStyleWallpapers(launcher) ? R.string.styles_wallpaper_button_text : R.string.wallpaper_button_text; - int resDrawable = Utilities.existsStyleWallpapers(launcher) ? + int resDrawable = existsStyleWallpapers(launcher) ? R.drawable.ic_palette : R.drawable.ic_wallpaper; options.add(new OptionItem(resString, resDrawable, ControlType.WALLPAPER_BUTTON, OptionsPopupView::startWallpaperPicker)); - if (!FeatureFlags.GO_DISABLE_WIDGETS) { + if (!FeatureFlags.GO_DISABLE_WIDGETS && Utilities.isWorkspaceEditAllowed(launcher)) { options.add(new OptionItem(R.string.widget_button_text, R.drawable.ic_widget, ControlType.WIDGETS_BUTTON, OptionsPopupView::onWidgetsClicked)); } @@ -168,6 +170,14 @@ public class OptionsPopupView extends ArrowPopup show(launcher, target, options); } + private static boolean existsStyleWallpapers(Launcher launcher) { + Intent intent = new Intent(Intent.ACTION_SET_WALLPAPER); + intent.setComponent(new ComponentName(launcher.getString(R.string.wallpaper_picker_package), + "com.android.customization.picker.CustomizationPickerActivity")); + ResolveInfo ri = launcher.getPackageManager().resolveActivity(intent, 0); + return ri != null; + } + public static boolean onWidgetsClicked(View view) { return openWidgets(Launcher.getLauncher(view.getContext())) != null; } @@ -205,10 +215,13 @@ public class OptionsPopupView extends ArrowPopup .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) .putExtra(EXTRA_WALLPAPER_OFFSET, launcher.getWorkspace().getWallpaperOffsetForCenterPage()); - if (!Utilities.existsStyleWallpapers(launcher)) { + if (!existsStyleWallpapers(launcher)) { intent.putExtra(EXTRA_WALLPAPER_FLAVOR, "wallpaper_only"); } else { intent.putExtra(EXTRA_WALLPAPER_FLAVOR, "focus_wallpaper"); + intent.setComponent(new ComponentName( + launcher.getString(R.string.wallpaper_picker_package), + "com.android.customization.picker.CustomizationPickerActivity")); } String pickerPackage = launcher.getString(R.string.wallpaper_picker_package); if (!TextUtils.isEmpty(pickerPackage)) { |