Commit c726070b authored by Amit Kumar's avatar Amit Kumar 💻
Browse files

Add overlay panel for widget page

parent 462543b3
Pipeline #126107 passed with stage
in 5 minutes and 34 seconds
......@@ -3,7 +3,9 @@ package foundation.e.blisslauncher.core.customviews;
import static foundation.e.blisslauncher.core.utils.Constants.ITEM_TYPE_APPLICATION;
import static foundation.e.blisslauncher.features.test.LauncherState.NORMAL;
import static foundation.e.blisslauncher.features.test.anim.LauncherAnimUtils.SPRING_LOADED_TRANSITION_MS;
import static foundation.e.blisslauncher.features.test.dragndrop.DragLayer.ALPHA_INDEX_OVERLAY;
import android.Manifest;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.LayoutTransition;
......@@ -12,14 +14,21 @@ import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.app.WallpaperManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Bitmap;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.location.LocationManager;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
......@@ -29,22 +38,38 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.WindowInsets;
import android.view.inputmethod.EditorInfo;
import android.widget.GridLayout;
import android.widget.ImageView;
import android.widget.Toast;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.jakewharton.rxbinding3.widget.RxTextView;
import foundation.e.blisslauncher.BuildConfig;
import foundation.e.blisslauncher.R;
import foundation.e.blisslauncher.core.Preferences;
import foundation.e.blisslauncher.core.Utilities;
import foundation.e.blisslauncher.core.customviews.pageindicators.PageIndicatorDots;
import foundation.e.blisslauncher.core.database.DatabaseManager;
import foundation.e.blisslauncher.core.database.model.ApplicationItem;
import foundation.e.blisslauncher.core.database.model.FolderItem;
import foundation.e.blisslauncher.core.database.model.LauncherItem;
import foundation.e.blisslauncher.core.database.model.ShortcutItem;
import foundation.e.blisslauncher.core.executors.AppExecutors;
import foundation.e.blisslauncher.core.touch.ItemClickHandler;
import foundation.e.blisslauncher.core.touch.ItemLongClickListener;
import foundation.e.blisslauncher.core.utils.Constants;
import foundation.e.blisslauncher.core.utils.GraphicsUtil;
import foundation.e.blisslauncher.core.utils.LongArrayMap;
import foundation.e.blisslauncher.features.launcher.Hotseat;
import foundation.e.blisslauncher.features.launcher.SearchInputDisposableObserver;
import foundation.e.blisslauncher.features.suggestions.AutoCompleteAdapter;
import foundation.e.blisslauncher.features.suggestions.SuggestionsResult;
import foundation.e.blisslauncher.features.test.Alarm;
import foundation.e.blisslauncher.features.test.CellLayout;
import foundation.e.blisslauncher.features.test.IconTextView;
......@@ -55,6 +80,7 @@ import foundation.e.blisslauncher.features.test.TestActivity;
import foundation.e.blisslauncher.features.test.VariantDeviceProfile;
import foundation.e.blisslauncher.features.test.WorkspaceStateTransitionAnimation;
import foundation.e.blisslauncher.features.test.anim.AnimatorSetBuilder;
import foundation.e.blisslauncher.features.test.anim.Interpolators;
import foundation.e.blisslauncher.features.test.dragndrop.DragController;
import foundation.e.blisslauncher.features.test.dragndrop.DragOptions;
import foundation.e.blisslauncher.features.test.dragndrop.DragSource;
......@@ -62,8 +88,17 @@ import foundation.e.blisslauncher.features.test.dragndrop.DragView;
import foundation.e.blisslauncher.features.test.dragndrop.DropTarget;
import foundation.e.blisslauncher.features.test.dragndrop.SpringLoadedDragController;
import foundation.e.blisslauncher.features.test.graphics.DragPreviewProvider;
import foundation.e.blisslauncher.features.widgets.WidgetViewBuilder;
import foundation.e.blisslauncher.features.widgets.WidgetsActivity;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.jetbrains.annotations.NotNull;
public class LauncherPagedView extends PagedView<PageIndicatorDots> implements View.OnTouchListener,
......@@ -75,8 +110,10 @@ public class LauncherPagedView extends PagedView<PageIndicatorDots> implements V
private static final int SNAP_OFF_EMPTY_SCREEN_DURATION = 400;
private static final int FADE_EMPTY_SCREEN_DURATION = 150;
/** The value that {@link #mTransitionProgress} must be greater than for
* {@link #isFinishedSwitchingState()} ()} to return true. */
/**
* The value that {@link #mTransitionProgress} must be greater than for
* {@link #isFinishedSwitchingState()} ()} to return true.
*/
private static final float FINISHED_SWITCHING_STATE_TRANSITION_PROGRESS = 0.5f;
private static final boolean MAP_NO_RECURSE = false;
......@@ -150,9 +187,15 @@ public class LauncherPagedView extends PagedView<PageIndicatorDots> implements V
private boolean mForceDrawAdjacentPages;
private WorkspaceStateTransitionAnimation mStateTransitionAnimation;
// State related to Launcher Overlay
TestActivity.LauncherOverlay mLauncherOverlay;
boolean mScrollInteractionBegan;
boolean mStartedSendingScrollEvents;
float mLastOverlayScroll = 0;
boolean mOverlayShown = false;
private Runnable mOnOverlayHiddenCallback;
// Total over scrollX in the overlay direction.
private float mOverlayTranslation;
public LauncherPagedView(Context context, AttributeSet attributeSet) {
this(context, attributeSet, 0);
......@@ -906,7 +949,7 @@ public class LauncherPagedView extends PagedView<PageIndicatorDots> implements V
}
}
public void moveToDefaultScreen() {
public void moveToDefaultScreen() {
int page = DEFAULT_PAGE;
if (getNextPage() != page) {
snapToPage(page);
......@@ -988,21 +1031,47 @@ public class LauncherPagedView extends PagedView<PageIndicatorDots> implements V
updateChildrenLayersEnabled();
}
protected void onScrollInteractionBegin() {
super.onScrollInteractionBegin();
mScrollInteractionBegan = true;
}
protected void onScrollInteractionEnd() {
super.onScrollInteractionEnd();
mScrollInteractionBegan = false;
if (mStartedSendingScrollEvents) {
mStartedSendingScrollEvents = false;
mLauncherOverlay.onScrollInteractionEnd();
}
}
public void setLauncherOverlay(TestActivity.LauncherOverlay overlay) {
mLauncherOverlay = overlay;
// A new overlay has been set. Reset event tracking
mStartedSendingScrollEvents = false;
onOverlayScrollChanged(0);
}
private boolean isScrollingOverlay() {
return mLauncherOverlay != null &&
((mIsRtl && getUnboundedScrollX() > mMaxScroll)
|| (!mIsRtl && getUnboundedScrollX() <mMinScroll));
}
@Override
protected void snapToDestination() {
// If we're overscrolling the overlay, we make sure to immediately reset the PagedView
// to it's baseline position instead of letting the overscroll settle. The overlay handles
// it's own settling, and every gesture to the overlay should be self-contained and start
// from 0, so we zero it out here.
/*if (isScrollingOverlay()) {
if (isScrollingOverlay()) {
// We reset mWasInOverscroll so that PagedView doesn't zero out the overscroll
// interaction when we call snapToPageImmediately.
mWasInOverscroll = false;
snapToPageImmediately(0);
} else {
}*/
super.snapToDestination();
super.snapToDestination();
}
}
@Override
......@@ -1055,6 +1124,38 @@ public class LauncherPagedView extends PagedView<PageIndicatorDots> implements V
}
}
@Override
protected void overScroll(int amount) {
boolean shouldScrollOverlay = mLauncherOverlay != null && !mScroller.isSpringing() &&
((amount <= 0 && !mIsRtl) || (amount >= 0 && mIsRtl));
boolean shouldZeroOverlay = mLauncherOverlay != null && mLastOverlayScroll != 0 &&
((amount >= 0 && !mIsRtl) || (amount <= 0 && mIsRtl));
if (shouldScrollOverlay) {
if (!mStartedSendingScrollEvents && mScrollInteractionBegan) {
mStartedSendingScrollEvents = true;
mLauncherOverlay.onScrollInteractionBegin();
}
mLastOverlayScroll = Math.abs(((float) amount) / getMeasuredWidth());
mLauncherOverlay.onScrollChange(mLastOverlayScroll, mIsRtl);
} else {
dampedOverScroll(amount);
}
if (shouldZeroOverlay) {
mLauncherOverlay.onScrollChange(0, mIsRtl);
}
}
@Override
protected boolean shouldFlingForVelocity(int velocityX) {
// When the overlay is moving, the fling or settle transition is controlled by the overlay.
return Float.compare(Math.abs(mOverlayTranslation), 0) == 0 &&
super.shouldFlingForVelocity(velocityX);
}
@Override
public void onDragStart(
DragObject dragObject, DragOptions options
......@@ -1224,7 +1325,8 @@ public class LauncherPagedView extends PagedView<PageIndicatorDots> implements V
if (tryRunOverlayCallback() && observer.isAlive()) {
observer.removeOnWindowFocusChangeListener(this);
}
}});
}
});
}
return true;
}
......@@ -2096,25 +2198,29 @@ public class LauncherPagedView extends PagedView<PageIndicatorDots> implements V
* animation.
*
* @param packageName The package name of the app to match.
* @param user The user of the app to match.
* @param user The user of the app to match.
*/
public View getFirstMatchForAppClose(String packageName, UserHandle user) {
final int curPage = getCurrentPage();
final CellLayout currentPage = (CellLayout) getPageAt(curPage);
final LauncherPagedView.ItemOperator packageAndUser = (LauncherItem info, View view) -> info != null
&& info.getTargetComponent() != null
&& TextUtils.equals(info.getTargetComponent().getPackageName(), packageName)
&& info.user.equals(user);
final LauncherPagedView.ItemOperator packageAndUserAndApp = (LauncherItem info, View view) ->
packageAndUser.evaluate(info, view) && info.itemType == ITEM_TYPE_APPLICATION;
return getFirstMatch(new CellLayout[] { mLauncher.getHotseat(), currentPage },
packageAndUserAndApp);
final LauncherPagedView.ItemOperator packageAndUser =
(LauncherItem info, View view) -> info != null
&& info.getTargetComponent() != null
&& TextUtils.equals(info.getTargetComponent().getPackageName(), packageName)
&& info.user.equals(user);
final LauncherPagedView.ItemOperator packageAndUserAndApp =
(LauncherItem info, View view) ->
packageAndUser.evaluate(info, view) && info.itemType == ITEM_TYPE_APPLICATION;
return getFirstMatch(
new CellLayout[]{mLauncher.getHotseat(), currentPage},
packageAndUserAndApp
);
}
/**
* @param cellLayouts List of CellLayouts to scan, in order of preference.
* @param operators List of operators, in order starting from best matching operator.
* @param operators List of operators, in order starting from best matching operator.
* @return
*/
private View getFirstMatch(CellLayout[] cellLayouts, final ItemOperator... operators) {
......@@ -2176,6 +2282,56 @@ public class LauncherPagedView extends PagedView<PageIndicatorDots> implements V
return false;
}
/**
* The overlay scroll is being controlled locally, just update our overlay effect
*/
public void onOverlayScrollChanged(float scroll) {
if (Float.compare(scroll, 1f) == 0) {
mOverlayShown = true;
// Not announcing the overlay page for accessibility since it announces itself.
} else if (Float.compare(scroll, 0f) == 0) {
if (Float.compare(mOverlayTranslation, 0f) != 0) {
// When arriving to 0 overscroll from non-zero overscroll, announce page for
// accessibility since default announcements were disabled while in overscroll
// state.
// Not doing this if mOverlayShown because in that case the accessibility service
// will announce the launcher window description upon regaining focus after
// switching from the overlay screen.
announcePageForAccessibility();
}
mOverlayShown = false;
tryRunOverlayCallback();
}
float offset = 0f;
scroll = Math.max(scroll - offset, 0);
scroll = Math.min(1, scroll / (1 - offset));
float alpha = 1 - Interpolators.DEACCEL_3.getInterpolation(scroll);
float transX = mLauncher.getDragLayer().getMeasuredWidth() * scroll;
if (mIsRtl) {
transX = -transX;
}
mOverlayTranslation = transX;
Log.d(TAG, "onOverlayScrollChanged() called with: scroll = [" + scroll + "]");
// TODO(adamcohen): figure out a final effect here. We may need to recommend
// different effects based on device performance. On at least one relatively high-end
// device I've tried, translating the launcher causes things to get quite laggy.
mLauncher.getDragLayer().setTranslationX(transX);
mLauncher.getDragLayer().getAlphaProperty(ALPHA_INDEX_OVERLAY).setValue(alpha);
}
protected void announcePageForAccessibility() {
// TODO: Enable it when supporting accessibility.
/*if (isAccessibilityEnabled(getContext())) {
// Notify the user when the page changes
announceForAccessibility(getCurrentPageDescription());
}*/
}
public Hotseat getHotseat() {
return mLauncher.getHotseat();
}
......
......@@ -87,7 +87,7 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
@ViewDebug.ExportedProperty(category = "launcher")
protected int mNextPage = INVALID_PAGE;
protected int mMaxScroll;
private int mMinScroll;
protected int mMinScroll;
public OverScroller mScroller;
private Interpolator mDefaultInterpolator;
private VelocityTracker mVelocityTracker;
......@@ -119,6 +119,8 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
protected boolean mIsPageInTransition = false;
protected float mSpringOverScrollX;
protected boolean mWasInOverscroll = false;
// mOverScrollX is equal to getScrollX() when we're within the normal scroll range. Otherwise
......@@ -360,6 +362,11 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
boolean isXBeforeFirstPage = mIsRtl ? (x > mMaxScroll) : (x < 0);
boolean isXAfterLastPage = mIsRtl ? (x < 0) : (x > mMaxScroll);
if (!isXBeforeFirstPage && !isXAfterLastPage) {
mSpringOverScrollX = 0;
}
if (isXBeforeFirstPage) {
super.scrollTo(mIsRtl ? mMaxScroll : 0, y);
if (mAllowOverScroll) {
......@@ -367,7 +374,7 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
if (mIsRtl) {
overScroll(x - mMaxScroll);
} else {
overScroll(x);
overScroll(x - mMinScroll);
}
}
} else if (isXAfterLastPage) {
......@@ -375,7 +382,7 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
if (mAllowOverScroll) {
mWasInOverscroll = true;
if (mIsRtl) {
overScroll(x);
overScroll(x - mMinScroll);
} else {
overScroll(x - mMaxScroll);
}
......@@ -385,7 +392,6 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
overScroll(0);
mWasInOverscroll = false;
}
mOverScrollX = x;
super.scrollTo(x, y);
}
}
......@@ -1004,9 +1010,24 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
invalidate();
}
protected void overScroll(float amount) {
dampedOverScroll(amount);
}
protected void overScroll(int amount) {
mSpringOverScrollX = amount;
if (mScroller.isSpringing()) {
invalidate();
return;
}
if (amount == 0) return;
if (mFreeScroll && !mScroller.isFinished()) {
if (amount < 0) {
super.scrollTo(mMinScroll + amount, getScrollY());
} else {
super.scrollTo(mMaxScroll + amount, getScrollY());
}
} else {
dampedOverScroll(amount);
} }
protected void enableFreeScroll(boolean settleOnPageInFreeScroll) {
setEnableFreeScroll(true);
......
......@@ -24,6 +24,7 @@ import foundation.e.blisslauncher.features.test.RotationHelper.REQUEST_NONE
import foundation.e.blisslauncher.features.test.dragndrop.DragController
import foundation.e.blisslauncher.features.test.dragndrop.DragLayer
import foundation.e.blisslauncher.features.test.graphics.RotationMode
import foundation.e.blisslauncher.uioverrides.OverlayCallbackImpl
import foundation.e.blisslauncher.uioverrides.UiFactory
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
......@@ -135,6 +136,7 @@ class TestActivity : BaseDraggingActivity() {
}
})
createOrUpdateIconGrid()
setLauncherOverlay(OverlayCallbackImpl(this))
}
/**
......@@ -351,6 +353,14 @@ class TestActivity : BaseDraggingActivity() {
mOnResumeCallback = callback
}
/**
* Call this after onCreate to set or clear overlay.
*/
fun setLauncherOverlay(overlay: LauncherOverlay) {
overlay.setOverlayCallbacks(LauncherOverlayCallbacksImpl())
workspace.setLauncherOverlay(overlay)
}
fun isInState(state: LauncherState): Boolean {
return mStateManager.getState() === state
}
......@@ -446,4 +456,40 @@ class TestActivity : BaseDraggingActivity() {
interface OnResumeCallback {
fun onLauncherResume()
}
interface LauncherOverlay {
/**
* Touch interaction leading to overscroll has begun
*/
fun onScrollInteractionBegin()
/**
* Touch interaction related to overscroll has ended
*/
fun onScrollInteractionEnd()
/**
* Scroll progress, between 0 and 100, when the user scrolls beyond the leftmost
* screen (or in the case of RTL, the rightmost screen).
*/
fun onScrollChange(progress: Float, rtl: Boolean)
/**
* Called when the launcher is ready to use the overlay
* @param callbacks A set of callbacks provided by Launcher in relation to the overlay
*/
fun setOverlayCallbacks(callbacks: LauncherOverlayCallbacks?)
}
interface LauncherOverlayCallbacks {
fun onScrollChanged(progress: Float)
}
inner class LauncherOverlayCallbacksImpl : LauncherOverlayCallbacks {
override fun onScrollChanged(progress: Float) {
if (workspace != null) {
workspace.onOverlayScrollChanged(progress)
}
}
}
}
......@@ -8,7 +8,7 @@
launcher:numRows="4"
launcher:numColumns="4"
launcher:numFolderRows="3"
launcher:numFolderColumns="4"
launcher:numFolderColumns="3"
launcher:numHotseatIcons="4"
launcher:iconSize="56"
launcher:iconTextSize="13.0"
......@@ -22,7 +22,7 @@
launcher:numRows="4"
launcher:numColumns="4"
launcher:numFolderRows="3"
launcher:numFolderColumns="4"
launcher:numFolderColumns="3"
launcher:numHotseatIcons="4"
launcher:iconSize="56"
launcher:iconTextSize="13.0"
......@@ -34,24 +34,24 @@
launcher:minWidthDps="296"
launcher:minHeightDps="568"
launcher:numRows="5"
launcher:numColumns="5"
launcher:numFolderRows="4"
launcher:numFolderColumns="4"
launcher:numHotseatIcons="5"
launcher:numColumns="4"
launcher:numFolderRows="3"
launcher:numFolderColumns="3"
launcher:numHotseatIcons="4"
launcher:iconSize="54"
launcher:iconTextSize="13.0"
launcher:defaultLayoutId="@xml/dw_hotseat_5"
/>
<profile
launcher:name="High DPI (4x5)"
launcher:name="High DPI (4x4)"
launcher:minWidthDps="322"
launcher:minHeightDps="557"
launcher:numRows="4"
launcher:numColumns="5"
launcher:numFolderRows="4"
launcher:numFolderColumns="4"
launcher:numHotseatIcons="5"
launcher:numColumns="4"
launcher:numFolderRows="3"
launcher:numFolderColumns="3"
launcher:numHotseatIcons="4"
launcher:iconSize="56"
launcher:iconTextSize="13.0"
launcher:defaultLayoutId="@xml/dw_hotseat_5"
......@@ -62,10 +62,10 @@
launcher:minWidthDps="387"
launcher:minHeightDps="659"
launcher:numRows="5"
launcher:numColumns="5"
launcher:numFolderRows="4"
launcher:numFolderColumns="4"
launcher:numHotseatIcons="5"
launcher:numColumns="4"
launcher:numFolderRows="3"
launcher:numFolderColumns="3"
launcher:numHotseatIcons="4"
launcher:iconSize="56"
launcher:iconTextSize="13.0"
launcher:defaultLayoutId="@xml/dw_hotseat_5"
......@@ -76,10 +76,10 @@
launcher:minWidthDps="387"
launcher:minHeightDps="750"
launcher:numRows="5"
launcher:numColumns="5"
launcher:numFolderRows="4"
launcher:numFolderColumns="4"
launcher:numHotseatIcons="5"
launcher:numColumns="4"
launcher:numFolderRows="3"
launcher:numFolderColumns="3"
launcher:numHotseatIcons="4"
launcher:iconSize="54"
launcher:iconTextSize="13.0"
launcher:defaultLayoutId="@xml/dw_hotseat_5"
......@@ -90,10 +90,10 @@
launcher:minWidthDps="525"
launcher:minHeightDps="750"
launcher:numRows="6"
launcher:numColumns="6"
launcher:numFolderRows="4"
launcher:numFolderColumns="4"
launcher:numHotseatIcons="6"
launcher:numColumns="4"
launcher:numFolderRows="3"
launcher:numFolderColumns="3"
launcher:numHotseatIcons="4"
launcher:iconSize="52"
launcher:iconTextSize="13.0"
launcher:defaultLayoutId="@xml/dw_hotseat_6"
......@@ -105,8 +105,8 @@
launcher:minHeightDps="1200"
launcher:numRows="5"
launcher:numColumns="6"
launcher:numFolderRows="4"
launcher:numFolderColumns="4"
launcher:numFolderRows="3"
launcher:numFolderColumns="3"
launcher:numHotseatIcons="6"
launcher:iconSize="64"
launcher:iconTextSize="13.0"
......
package foundation.e.blisslauncher.uioverrides;
import android.util.Log;