Commit 460e5f9a authored by Amit Kumar's avatar Amit Kumar 💻
Browse files

Add folder opening and closing feature

parent e0584e55
Pipeline #130259 passed with stage
in 8 minutes and 23 seconds
......@@ -327,7 +327,6 @@ public class LauncherPagedView extends PagedView<PageIndicatorDots> implements V
Log.i(TAG, "bindItems: " + appView);
workspaceScreen.addView(appView);
} else if (launcherItem.container == Constants.CONTAINER_HOTSEAT) {
//appView.findViewById(R.id.app_label).setVisibility(GONE);
GridLayout.Spec rowSpec = GridLayout.spec(GridLayout.UNDEFINED);
GridLayout.Spec colSpec = GridLayout.spec(GridLayout.UNDEFINED);
GridLayout.LayoutParams iconLayoutParams =
......@@ -1513,6 +1512,7 @@ public class LauncherPagedView extends PagedView<PageIndicatorDots> implements V
LauncherItem destItem = (LauncherItem) v.getTag();
Rect folderLocation = new Rect();
target.clearAnimation();
target.removeView(v);
FolderItem fi = new FolderItem();
fi.title = getResources().getString(R.string.untitled);
......
......@@ -60,7 +60,7 @@ public class ItemClickHandler {
if (tag instanceof ShortcutItem) {
onClickAppShortcut(v, (ShortcutItem) tag, launcher);
} else if (tag instanceof FolderItem) {
onClickFolderIcon(v);
onClickFolderIcon(v, launcher);
} else if (tag instanceof ApplicationItem) {
startAppShortcutOrInfoActivity(v, (ApplicationItem) tag, launcher);
}
......@@ -70,14 +70,14 @@ public class ItemClickHandler {
* Event handler for a folder icon click.
*
* @param v The view that was clicked. Must be an instance of {@link IconTextView}.
* @param launcher Launcher activity to pass actions.
*/
private static void onClickFolderIcon(View v) {
private static void onClickFolderIcon(
View v,
TestActivity launcher
) {
Log.d("ItemClick", "onClickFolderIcon() called with: v = [" + v + "]");
/*Folder folder = ((FolderIcon) v).getFolder();
if (!folder.isOpen() && !folder.isDestroyed()) {
// Open the requested folder
folder.animateOpen();
}*/
launcher.openFolder(v);
}
/**
......
package foundation.e.blisslauncher.features.folder;
import android.content.Context;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.GridLayout;
import androidx.annotation.NonNull;
import androidx.viewpager.widget.PagerAdapter;
import java.util.List;
import foundation.e.blisslauncher.R;
import foundation.e.blisslauncher.core.customviews.BlissFrameLayout;
import foundation.e.blisslauncher.core.database.model.LauncherItem;
import foundation.e.blisslauncher.core.touch.ItemClickHandler;
import foundation.e.blisslauncher.core.touch.ItemLongClickListener;
import foundation.e.blisslauncher.features.test.CellLayout;
import foundation.e.blisslauncher.features.test.IconTextView;
import foundation.e.blisslauncher.features.test.VariantDeviceProfile;
public class FolderPagerAdapter extends PagerAdapter {
private Context mContext;
private List<LauncherItem> mFolderAppItems;
private VariantDeviceProfile mDeviceProfile;
public FolderPagerAdapter(
Context context,
List<LauncherItem> items,
VariantDeviceProfile mDeviceProfile
) {
this.mContext = context;
this.mFolderAppItems = items;
this.mDeviceProfile = mDeviceProfile;
}
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
GridLayout viewGroup = (GridLayout) LayoutInflater.from(mContext).inflate(
R.layout.apps_page, container, false);
viewGroup.setRowCount(3);
viewGroup.setColumnCount(3);
viewGroup.setPadding(
mDeviceProfile.getIconDrawablePaddingPx() / 2,
mDeviceProfile.getIconDrawablePaddingPx() / 2,
mDeviceProfile.getIconDrawablePaddingPx() / 2,
mDeviceProfile.getIconDrawablePaddingPx() / 2
);
int i = 0;
while (9 * position + i < mFolderAppItems.size() && i < 9) {
LauncherItem appItem = mFolderAppItems.get(9 * position + i);
IconTextView appView = (IconTextView) LayoutInflater.from(mContext)
.inflate(R.layout.app_icon, null, false);
appView.applyFromShortcutItem(appItem);
appView.setTextVisibility(true);
appView.setOnClickListener(ItemClickHandler.INSTANCE);
appView.setOnLongClickListener(ItemLongClickListener.INSTANCE_WORKSPACE);
GridLayout.LayoutParams iconLayoutParams = new GridLayout.LayoutParams();
iconLayoutParams.height = mDeviceProfile.getCellHeightPx();
iconLayoutParams.width = mDeviceProfile.getCellHeightPx();
iconLayoutParams.setGravity(Gravity.CENTER);
appView.setLayoutParams(iconLayoutParams);
viewGroup.addView(appView);
i++;
}
container.addView(viewGroup);
return viewGroup;
}
@Override
public int getCount() {
return (int) Math.ceil((float) mFolderAppItems.size() / 9);
}
@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return view == object;
}
@Override
public void destroyItem(
@NonNull ViewGroup container, int position,
@NonNull Object object
) {
container.removeView((View) object);
}
}
......@@ -22,6 +22,7 @@ import android.content.pm.LauncherActivityInfo
import android.content.pm.LauncherApps
import android.content.res.Configuration
import android.graphics.Point
import android.graphics.Rect
import android.location.LocationManager
import android.net.Uri
import android.os.Bundle
......@@ -53,9 +54,9 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager.widget.ViewPager
import com.jakewharton.rxbinding3.widget.textChanges
import foundation.e.blisslauncher.BlissLauncher
import foundation.e.blisslauncher.BuildConfig
import foundation.e.blisslauncher.R
import foundation.e.blisslauncher.core.Preferences
import foundation.e.blisslauncher.core.Utilities
......@@ -79,12 +80,12 @@ import foundation.e.blisslauncher.core.utils.Constants
import foundation.e.blisslauncher.core.utils.ListUtil
import foundation.e.blisslauncher.core.utils.PackageUserKey
import foundation.e.blisslauncher.core.utils.UserHandle
import foundation.e.blisslauncher.features.folder.FolderPagerAdapter
import foundation.e.blisslauncher.features.launcher.Hotseat
import foundation.e.blisslauncher.features.launcher.SearchInputDisposableObserver
import foundation.e.blisslauncher.features.notification.DotInfo
import foundation.e.blisslauncher.features.notification.NotificationDataProvider
import foundation.e.blisslauncher.features.notification.NotificationListener
import foundation.e.blisslauncher.features.notification.NotificationService
import foundation.e.blisslauncher.features.shortcuts.DeepShortcutManager
import foundation.e.blisslauncher.features.shortcuts.ShortcutKey
import foundation.e.blisslauncher.features.suggestions.AutoCompleteAdapter
......@@ -112,6 +113,7 @@ import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.observers.DisposableObserver
import io.reactivex.schedulers.Schedulers
import me.relex.circleindicator.CircleIndicator
import java.net.URISyntaxException
import java.util.ArrayList
import java.util.Arrays
......@@ -122,6 +124,8 @@ import java.util.function.Predicate
class TestActivity : BaseDraggingActivity(), AutoCompleteAdapter.OnSuggestionClickListener {
// Folder start scale
private var startScaleFinal: Float = 0f
private var mCancelTouchController: Runnable? = null
private var mOnResumeCallback: OnResumeCallback? = null
private lateinit var mAppTransitionManager: LauncherAppTransitionManager
......@@ -161,6 +165,8 @@ class TestActivity : BaseDraggingActivity(), AutoCompleteAdapter.OnSuggestionCli
// UI and state for the overview panel
private lateinit var overviewPanel: View
private lateinit var folderContainer: RelativeLayout
private val mOnResumeCallbacks = ArrayList<OnResumeCallback>()
private lateinit var mAppWidgetManager: AppWidgetManager
......@@ -179,6 +185,13 @@ class TestActivity : BaseDraggingActivity(), AutoCompleteAdapter.OnSuggestionCli
private var currentAnimator: AnimatorSet? = null
private var activeFolder: FolderItem? = null
private var activeFolderView: IconTextView? = null
private var activeFolderStartBounds = Rect()
private var mFolderAppsViewPager: ViewPager? = null
private var mFolderTitleInput: BlissInput? = null
private val mWeatherReceiver: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (!intent.getBooleanExtra(WeatherUpdateService.EXTRA_UPDATE_CANCELLED, false)) {
......@@ -383,6 +396,9 @@ class TestActivity : BaseDraggingActivity(), AutoCompleteAdapter.OnSuggestionCli
workspace.initParentViews(dragLayer)
overviewPanel = findViewById(R.id.overview_panel)
hotseat = findViewById(R.id.hotseat)
folderContainer = findViewById(R.id.folder_window_container)
mFolderAppsViewPager = findViewById(R.id.folder_apps)
mFolderTitleInput = findViewById(R.id.folder_title)
launcherView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE)
......@@ -394,8 +410,6 @@ class TestActivity : BaseDraggingActivity(), AutoCompleteAdapter.OnSuggestionCli
dragController.addDragListener(workspace)
dragController.addDropTarget(workspace)
// Setup the drag controller (drop targets have to be added in reverse order in priority)
// Setup the drag controller (drop targets have to be added in reverse order in priority)
dragController.setMoveTarget(workspace)
}
......@@ -1208,6 +1222,11 @@ class TestActivity : BaseDraggingActivity(), AutoCompleteAdapter.OnSuggestionCli
if (!internalStateHandled) {
// In all these cases, only animate if we're already on home
AbstractFloatingView.closeAllOpenViews(this, isStarted)
if(folderContainer.visibility == View.VISIBLE) {
closeFolder()
return
}
if (!isInState(NORMAL)) {
// Only change state, if not already the same. This prevents cancelling any
// animations running as part of resume
......@@ -1250,9 +1269,12 @@ class TestActivity : BaseDraggingActivity(), AutoCompleteAdapter.OnSuggestionCli
// Note: There should be at most one log per method call. This is enforced implicitly
// by using if-else statements.
val topView = AbstractFloatingView.getTopOpenView(this)
if (topView != null && topView.onBackPressed()) {
// Handled by the floating view.
} else {
} else if (folderContainer.visibility == View.VISIBLE) {
closeFolder()
}else {
mStateManager.state.onBackPressed(this)
}
}
......@@ -1430,4 +1452,213 @@ class TestActivity : BaseDraggingActivity(), AutoCompleteAdapter.OnSuggestionCli
true
}
}
fun openFolder(folderView: View) {
if (currentAnimator != null) {
currentAnimator!!.cancel()
}
activeFolder = folderView.tag as FolderItem?
activeFolderView = folderView as IconTextView
// Calculate the starting and ending bounds for the zoomed-in image.
// This step involves lots of math. Yay, math.
val startBounds = Rect()
val finalBounds = Rect()
val globalOffset = Point()
// The start bounds are the global visible rectangle of the thumbnail,
// and the final bounds are the global visible rectangle of the container
// view. Also set the container view's offset as the origin for the
// bounds, since that's the origin for the positioning animation
// properties (X, Y).
folderView.getGlobalVisibleRect(startBounds)
findViewById<View>(R.id.workspace)
.getGlobalVisibleRect(finalBounds, globalOffset)
startBounds.offset(-globalOffset.x, -globalOffset.y)
finalBounds.offset(-globalOffset.x, -globalOffset.y)
activeFolderStartBounds.set(startBounds)
val startScale: Float
if (finalBounds.width() / finalBounds.height()
> startBounds.width() / startBounds.height()
) {
// Extend start bounds horizontally
startScale = (startBounds.height() / finalBounds.height()).toFloat()
val startWidth: Float = startScale * finalBounds.width()
val deltaWidth: Float = (startWidth - startBounds.width()) / 2
startBounds.left -= deltaWidth.toInt()
startBounds.right += deltaWidth.toInt()
} else {
// Extend start bounds vertically
startScale = (startBounds.width() / finalBounds.width()).toFloat()
val startHeight: Float = startScale * finalBounds.height()
val deltaHeight: Float = (startHeight - startBounds.height()) / 2
startBounds.top -= deltaHeight.toInt()
startBounds.bottom += deltaHeight.toInt()
}
// Construct and run the parallel animation of the four translation and
// scale properties (X, Y, SCALE_X, and SCALE_Y).
val set = AnimatorSet()
/*ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 18);
valueAnimator.addUpdateListener(animation ->
BlurWallpaperProvider.getInstance(this).blur((Integer) animation.getAnimatedValue()));*/
/*ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 18);
valueAnimator.addUpdateListener(animation ->
BlurWallpaperProvider.getInstance(this).blur((Integer) animation.getAnimatedValue()));*/set.play(
ObjectAnimator.ofFloat(
folderContainer, View.X,
startBounds.left.toFloat(), finalBounds.left
.toFloat()
)
)
.with(
ObjectAnimator.ofFloat(
folderContainer, View.Y,
startBounds.top.toFloat(), finalBounds.top
.toFloat()
)
)
.with(
ObjectAnimator.ofFloat(
folderContainer, View.SCALE_X,
startScale, 1f
)
)
.with(
ObjectAnimator.ofFloat(
folderContainer,
View.SCALE_Y, startScale, 1f
)
)
.with(ObjectAnimator.ofFloat<View>(getLauncherPagedView(), View.ALPHA, 0f))
//.with(ObjectAnimator.ofFloat<View>(mIndicator, View.ALPHA, 0f))
.with(ObjectAnimator.ofFloat(hotseat, View.ALPHA, 0f))
set.duration = 300
set.interpolator = LinearInterpolator()
set.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationStart(animation: Animator) {
super.onAnimationStart(animation)
folderContainer.visibility = View.VISIBLE
// Set the pivot point for SCALE_X and SCALE_Y transformations
// to the top-left corner of the zoomed-in view (the default
// is the center of the view).
folderContainer.pivotX = 0f
folderContainer.pivotY = 0f
//BlurWallpaperProvider.getInstance(LauncherActivity.this).clear();
}
override fun onAnimationEnd(animation: Animator) {
currentAnimator = null
//blurLayer.setAlpha(1f)
getLauncherPagedView().alpha = 0f
hotseat.alpha = 0f
}
override fun onAnimationCancel(animation: Animator) {
currentAnimator = null
folderContainer.visibility = View.GONE
//blurLayer.setAlpha(0f)
getLauncherPagedView().alpha = 1f
//mIndicator.setAlpha(1f)
hotseat.alpha = 1f
}
})
set.start()
currentAnimator = set
startScaleFinal = startScale
mFolderTitleInput?.setText(activeFolder?.title)
mFolderTitleInput?.isCursorVisible = false
mFolderAppsViewPager?.adapter = FolderPagerAdapter(this, activeFolder?.items, mDeviceProfile)
// We use same size for height and width as we want to look it like sqaure
val height = mDeviceProfile.cellHeightPx * 3 + resources.getDimensionPixelSize(R.dimen.folder_padding)
mFolderAppsViewPager?.layoutParams?.width =
height
mFolderAppsViewPager?.layoutParams?.height =
height
(launcherView.findViewById<View>(R.id.indicator) as CircleIndicator).setViewPager(
mFolderAppsViewPager
)
}
fun closeFolder() {
mFolderTitleInput?.clearFocus()
currentAnimator?.cancel()
// Animate the four positioning/sizing properties in parallel,
// back to their original values.
val set = AnimatorSet()
/*ValueAnimator valueAnimator = ValueAnimator.ofInt(18, 0);
valueAnimator.addUpdateListener(animation ->
BlurWallpaperProvider.getInstance(this).blurWithLauncherView(mergedView, (Integer) animation.getAnimatedValue()));*/
/*ValueAnimator valueAnimator = ValueAnimator.ofInt(18, 0);
valueAnimator.addUpdateListener(animation ->
BlurWallpaperProvider.getInstance(this).blurWithLauncherView(mergedView, (Integer) animation.getAnimatedValue()));*/set.play(
ObjectAnimator
.ofFloat<View>(folderContainer, View.X, activeFolderStartBounds.left.toFloat())
)
.with(
ObjectAnimator
.ofFloat<View>(
folderContainer,
View.Y, activeFolderStartBounds.top
.toFloat()
)
)
.with(
ObjectAnimator
.ofFloat<View>(
folderContainer,
View.SCALE_X, startScaleFinal
)
)
.with(
ObjectAnimator
.ofFloat<View>(
folderContainer,
View.SCALE_Y, startScaleFinal
)
)
//.with(ObjectAnimator.ofFloat<View>(blurLayer, View.ALPHA, 0f))
.with(ObjectAnimator.ofFloat<View>(getLauncherPagedView(), View.ALPHA, 1f))
.with(ObjectAnimator.ofFloat<View>(getLauncherPagedView().pageIndicator, View.ALPHA, 1f))
.with(ObjectAnimator.ofFloat<View>(hotseat, View.ALPHA, 1f))
//.with(valueAnimator);
set.duration = 300
set.interpolator = LinearInterpolator()
set.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationStart(animation: Animator) {
getLauncherPagedView().setVisibility(View.VISIBLE)
hotseat.setVisibility(View.VISIBLE)
getLauncherPagedView().pageIndicator.setVisibility(View.VISIBLE)
}
override fun onAnimationEnd(animation: Animator) {
folderContainer.setVisibility(View.GONE)
currentAnimator = null
//blurLayer.setAlpha(0f)
getLauncherPagedView().setAlpha(1f)
getLauncherPagedView().pageIndicator.setAlpha(1f)
hotseat.setAlpha(1f)
}
override fun onAnimationCancel(animation: Animator) {
folderContainer.setVisibility(View.GONE)
currentAnimator = null
//blurLayer.setAlpha(0f)
getLauncherPagedView().setAlpha(1f)
getLauncherPagedView().pageIndicator.setAlpha(1f)
hotseat.setAlpha(1f)
}
})
set.start()
currentAnimator = set
}
}
......@@ -41,6 +41,9 @@
layout="@layout/overview_panel"
android:visibility="gone" />
<include layout="@layout/layout_folder"
/>
</foundation.e.blisslauncher.features.test.dragndrop.DragLayer>
</foundation.e.blisslauncher.features.test.LauncherRootView>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<foundation.e.blisslauncher.features.test.CellLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:launcher="http://schemas.android.com/apk/res-auto"
android:hapticFeedbackEnabled="false"
launcher:containerType="folder">
</foundation.e.blisslauncher.features.test.CellLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/folder_window_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:focusable="true"
android:visibility="gone">
<foundation.e.blisslauncher.core.customviews.BlissInput
android:id="@+id/folder_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/folder_apps_background"
android:layout_centerHorizontal="true"
android:background="@null"
android:imeOptions="actionDone"
android:inputType="textNoSuggestions|textCapSentences"
android:paddingTop="16dp"
android:paddingBottom="16dp"
android:text="@string/untitled"
android:textAlignment="center"
android:textColor="@android:color/white"
android:textSize="18sp" />
<LinearLayout
android:id="@+id/folder_apps_background"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="@drawable/folder_window"
android:orientation="vertical">
<androidx.viewpager.widget.ViewPager
android:id="@+id/folder_apps"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<me.relex.circleindicator.CircleIndicator
android:id="@+id/indicator"
android:layout_width="match_parent"
android:layout_height="24dp"
android:paddingBottom="4dp" />
</LinearLayout>
</RelativeLayout>
\ No newline at end of file
......@@ -68,4 +68,5 @@
<dimen name="bg_popup_item_width">220dp</dimen>
<dimen name="bg_popup_item_height">56dp</dimen>
<dimen name="default_dialog_corner_radius">8dp</dimen>
<dimen name="folder_padding">16dp</dimen>
</resources>
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment