From 9c9418bc626d36dce33f2ac6513e7407f07298d4 Mon Sep 17 00:00:00 2001 From: Yvonne Wong Date: Tue, 1 Dec 2015 17:04:07 -0800 Subject: Reimplement CM Settings Overview Panel Part 3 - Enable dynamic grid resizing Change-Id: I95a7f20da48e037a94ce5b6191c5597490d91d9d --- src/com/android/launcher3/DeviceProfile.java | 1 - .../android/launcher3/DynamicGridSizeFragment.java | 381 +++++++++++++++++++++ .../android/launcher3/InsettableLinearLayout.java | 122 +++++++ .../android/launcher3/InvariantDeviceProfile.java | 57 +++ src/com/android/launcher3/ItemInfo.java | 6 + src/com/android/launcher3/Launcher.java | 130 ++++++- src/com/android/launcher3/LauncherModel.java | 103 +++++- .../list/SettingsPinnedHeaderAdapter.java | 27 +- 8 files changed, 781 insertions(+), 46 deletions(-) create mode 100644 src/com/android/launcher3/DynamicGridSizeFragment.java create mode 100644 src/com/android/launcher3/InsettableLinearLayout.java (limited to 'src') diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 7e9d8f8a7..6115df62f 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -37,7 +37,6 @@ import com.android.launcher3.settings.SettingsProvider; import com.android.launcher3.allapps.AllAppsContainerView; public class DeviceProfile { - public final InvariantDeviceProfile inv; // Device properties diff --git a/src/com/android/launcher3/DynamicGridSizeFragment.java b/src/com/android/launcher3/DynamicGridSizeFragment.java new file mode 100644 index 000000000..103d660dd --- /dev/null +++ b/src/com/android/launcher3/DynamicGridSizeFragment.java @@ -0,0 +1,381 @@ +/* + * Copyright (C) 2014 The CyanogenMod 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; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; +import android.app.Dialog; +import android.app.Fragment; +import android.content.Context; +import android.content.DialogInterface; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.os.Bundle; +import android.util.AttributeSet; +import android.util.DisplayMetrics; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.Button; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ListView; +import android.widget.NumberPicker; +import android.widget.TextView; +import com.android.launcher3.settings.SettingsProvider; + +public class DynamicGridSizeFragment extends Fragment + implements NumberPicker.OnValueChangeListener, Dialog.OnDismissListener { + public static final String DYNAMIC_GRID_SIZE_FRAGMENT = "DynamicGridSizeFragment"; + + public static final int MIN_DYNAMIC_GRID_ROWS = 2; + public static final int MIN_DYNAMIC_GRID_COLUMNS = 3; + + GridSizeView mDynamicGrid; + + ListView mListView; + View mCurrentSelection; + GridSizeAdapter mAdapter; + InvariantDeviceProfile.GridSize mCurrentSize; + + Dialog mDialog; + + int mCustomGridRows = 0; + int mCustomGridColumns = 0; + + View.OnClickListener mSettingsItemListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + mCurrentSize = InvariantDeviceProfile.GridSize.getModeForValue((Integer) v.getTag()); + + setCleared(mCurrentSelection); + setSelected(v); + mCurrentSelection = v; + + if (mCurrentSize == InvariantDeviceProfile.GridSize.Custom) { + showNumberPicker(); + } + + ((GridSizeAdapter) mListView.getAdapter()).notifyDataSetChanged(); + + mAdapter.notifyDataSetInvalidated(); + updateGridMetrics(); + } + }; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.dynamic_grid_size_screen, container, false); + mDynamicGrid = (GridSizeView) v.findViewById(R.id.dynamic_grid_size_image); + mListView = (ListView) v.findViewById(R.id.dynamic_grid_list); + + Launcher launcher = (Launcher) getActivity(); + LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) + mListView.getLayoutParams(); + lp.bottomMargin = ((FrameLayout.LayoutParams) launcher.getOverviewPanel() + .findViewById(R.id.settings_container).getLayoutParams()).bottomMargin; + mListView.setLayoutParams(lp); + + LinearLayout titleLayout = (LinearLayout) v.findViewById(R.id.dynamic_grid_title); + titleLayout.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + setSize(); + } + }); + + mCurrentSize = InvariantDeviceProfile.GridSize.getModeForValue( + SettingsProvider.getIntCustomDefault(getActivity(), + SettingsProvider.SETTINGS_UI_DYNAMIC_GRID_SIZE, 0)); + + InvariantDeviceProfile grid = getInvariantDeviceProfile(); + mCustomGridRows = grid.numRows; + mCustomGridColumns = grid.numColumns; + + updateGridMetrics(); + + Resources res = getResources(); + int[] valueResIds = { + R.string.grid_size_comfortable, + R.string.grid_size_cozy, + R.string.grid_size_condensed, + R.string.grid_size_custom + }; + mAdapter = new GridSizeAdapter(getActivity(), valueResIds); + mListView.setAdapter(mAdapter); + + // RTL + ImageView navPrev = (ImageView) v.findViewById(R.id.nav_prev); + Configuration config = getResources().getConfiguration(); + if (config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) { + navPrev.setImageResource(R.drawable.ic_navigation_next); + } + + return v; + } + + private void updateGridMetrics() { + if (mCurrentSize == InvariantDeviceProfile.GridSize.Custom) { + mDynamicGrid.setMetrics(mCustomGridRows, mCustomGridColumns); + } else { + InvariantDeviceProfile grid = getInvariantDeviceProfile(); + mDynamicGrid.setMetrics(grid.numRowsBase + mCurrentSize.getValue(), + grid.numColumnsBase + mCurrentSize.getValue()); + } + } + + @Override + public Animator onCreateAnimator(int transit, boolean enter, int nextAnim) { + if (enter) { + DisplayMetrics displaymetrics = new DisplayMetrics(); + getActivity().getWindowManager().getDefaultDisplay().getMetrics(displaymetrics); + int width = displaymetrics.widthPixels; + Configuration config = getResources().getConfiguration(); + final ObjectAnimator anim; + if (config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) { + anim = ObjectAnimator.ofFloat(this, "translationX", -width, 0); + } else { + anim = ObjectAnimator.ofFloat(this, "translationX", width, 0); + } + + final View darkPanel = ((Launcher) getActivity()).getDarkPanel(); + darkPanel.setVisibility(View.VISIBLE); + ObjectAnimator anim2 = ObjectAnimator.ofFloat(darkPanel, "alpha", 0.0f, 0.3f); + anim2.start(); + + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd (Animator animation) { + darkPanel.setVisibility(View.GONE); + } + }); + + return anim; + } else { + return super.onCreateAnimator(transit, enter, nextAnim); + } + } + + public void setSize() { + ((Launcher) getActivity()).setDynamicGridSize(mCurrentSize); + } + + private void setSelected(View v) { + v.setBackgroundColor(Color.WHITE); + TextView t = (TextView) v.findViewById(R.id.item_name); + t.setTextColor(getResources().getColor(R.color.settings_bg_color)); + } + + private void setCleared(View v) { + v.setBackgroundColor(getResources().getColor(R.color.settings_bg_color)); + TextView t = (TextView) v.findViewById(R.id.item_name); + t.setTextColor(Color.WHITE); + } + + private void showNumberPicker() { + mDialog = new Dialog(getActivity()); + mDialog.setTitle(getResources().getString( + R.string.preferences_interface_homescreen_custom)); + mDialog.setContentView(R.layout.custom_grid_size_dialog); + + NumberPicker nPRows = (NumberPicker) mDialog.findViewById(R.id.custom_rows); + NumberPicker nPColumns = (NumberPicker) mDialog.findViewById(R.id.custom_columns); + + InvariantDeviceProfile grid = getInvariantDeviceProfile(); + int rows = grid.numRowsBase; + int columns = grid.numColumnsBase; + + nPRows.setMinValue(Math.max(MIN_DYNAMIC_GRID_ROWS, rows - InvariantDeviceProfile.GRID_SIZE_MIN)); + nPRows.setMaxValue(rows + InvariantDeviceProfile.GRID_SIZE_MAX); + nPRows.setValue(mCustomGridRows); + nPRows.setWrapSelectorWheel(false); + nPRows.setOnValueChangedListener(this); + nPRows.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS); + + nPColumns.setMinValue(Math.max(MIN_DYNAMIC_GRID_COLUMNS, + columns - InvariantDeviceProfile.GRID_SIZE_MIN)); + nPColumns.setMaxValue(columns + InvariantDeviceProfile.GRID_SIZE_MAX); + nPColumns.setValue(mCustomGridColumns); + nPColumns.setWrapSelectorWheel(false); + nPColumns.setOnValueChangedListener(this); + nPColumns.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS); + + Button button = (Button) mDialog.findViewById(R.id.dialog_confirm_button); + button.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (mDialog != null) { + mDialog.dismiss(); + } + } + }); + + mDialog.setOnDismissListener(this); + mDialog.show(); + } + + @Override + public void onPause() { + super.onPause(); + if (mDialog != null) { + mDialog.dismiss(); + } + } + + @Override + public void onValueChange(NumberPicker picker, int oldVal, int newVal) { + if (picker.getId() == R.id.custom_rows) { + mCustomGridRows = newVal; + } else if (picker.getId() == R.id.custom_columns) { + mCustomGridColumns = newVal; + } + } + + @Override + public void onDismiss(DialogInterface dialog) { + SettingsProvider.putInt(getActivity(), + SettingsProvider.SETTINGS_UI_HOMESCREEN_ROWS, mCustomGridRows); + SettingsProvider.putInt(getActivity(), + SettingsProvider.SETTINGS_UI_HOMESCREEN_COLUMNS, mCustomGridColumns); + + mAdapter.notifyDataSetInvalidated(); + mDynamicGrid.setMetrics(mCustomGridRows, mCustomGridColumns); + } + + private class GridSizeAdapter extends BaseAdapter { + Context mContext; + int[] mTitleResIds; + + public GridSizeAdapter(Context context, int[] resIds) { + mContext = context; + mTitleResIds = resIds; + } + + @Override + public int getCount() { + return mTitleResIds.length; + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public Object getItem(int position) { + return mContext.getString(mTitleResIds[position]); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + if (convertView == null) { + LayoutInflater inflater = (LayoutInflater) + mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + convertView = inflater.inflate(R.layout.settings_pane_list_item, parent, false); + } + + TextView textView = (TextView) convertView.findViewById(R.id.item_name); + + // RTL + Configuration config = getResources().getConfiguration(); + if (config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) { + textView.setGravity(Gravity.RIGHT); + } + + // Set selected state + if (position == mCurrentSize.getValue()) { + if (mCurrentSelection != null) { + setCleared(mCurrentSelection); + } + mCurrentSelection = convertView; + setSelected(mCurrentSelection); + } + + if (position == InvariantDeviceProfile.GridSize.Custom.getValue()) { + InvariantDeviceProfile grid = getInvariantDeviceProfile(); + + int rows = SettingsProvider.getIntCustomDefault(getActivity(), + SettingsProvider.SETTINGS_UI_HOMESCREEN_ROWS, grid.numRowsBase); + int columns = SettingsProvider.getIntCustomDefault(getActivity(), + SettingsProvider.SETTINGS_UI_HOMESCREEN_COLUMNS, grid.numColumnsBase); + textView.setText(mContext.getString(mTitleResIds[position], rows, columns)); + } else { + textView.setText(mTitleResIds[position]); + } + + convertView.setOnClickListener(mSettingsItemListener); + convertView.setTag(position); + return convertView; + } + } + + private InvariantDeviceProfile getInvariantDeviceProfile() { + LauncherAppState app = LauncherAppState.getInstance(); + return app.getInvariantDeviceProfile(); + } + + private static class GridSizeView extends View { + private int mRows = 0, mColumns = 0; + private Paint mForegroundPaint; + private int mBackgroundColor; + + public GridSizeView(Context context, AttributeSet attrs) { + super(context, attrs); + Resources res = context.getResources(); + + mForegroundPaint = new Paint(); + mForegroundPaint.setColor(res.getColor(R.color.dynamic_grid_preview_foreground)); + mBackgroundColor = res.getColor(R.color.dynamic_grid_preview_background); + } + + public void setMetrics(int rows, int columns) { + mRows = rows; + mColumns = columns; + invalidate(); + } + + @Override + protected void onDraw(Canvas canvas) { + float width = getWidth() - getPaddingLeft() - getPaddingRight(); + float height = getHeight() - getPaddingTop() - getPaddingBottom(); + float xOffset = getPaddingLeft(); + float yOffset = getPaddingTop(); + + canvas.drawColor(mBackgroundColor); + + // Draw rows + for (int i = 1; i < mRows; i++) { + float yPos = yOffset + height / mRows * i; + canvas.drawLine(xOffset, yPos, xOffset + width, yPos, mForegroundPaint); + } + + // Draw columns + for (int j = 1; j < mColumns; j++) { + float xPos = xOffset + width / mColumns * j; + canvas.drawLine(xPos, yOffset, xPos, yOffset + height, mForegroundPaint); + } + } + } +} diff --git a/src/com/android/launcher3/InsettableLinearLayout.java b/src/com/android/launcher3/InsettableLinearLayout.java new file mode 100644 index 000000000..8f64713e5 --- /dev/null +++ b/src/com/android/launcher3/InsettableLinearLayout.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2015 The CyanogenMod 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; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; + +public class InsettableLinearLayout extends LinearLayout implements + ViewGroup.OnHierarchyChangeListener, Insettable { + + protected Rect mInsets = new Rect(); + + public InsettableLinearLayout(Context context, AttributeSet attrs) { + super(context, attrs); + setOnHierarchyChangeListener(this); + } + + public void setLinearLayoutChildInsets(View child, Rect newInsets, Rect oldInsets) { + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + + if (child instanceof Insettable) { + ((Insettable) child).setInsets(newInsets); + } else if (!lp.ignoreInsets) { + if (!lp.ignoreTopInsets) { + lp.topMargin += (newInsets.top - oldInsets.top); + } + lp.leftMargin += (newInsets.left - oldInsets.left); + lp.rightMargin += (newInsets.right - oldInsets.right); + if (!lp.ignoreBottomInsets) { + lp.bottomMargin += (newInsets.bottom - oldInsets.bottom); + } + } + child.setLayoutParams(lp); + } + + @Override + public void setInsets(Rect insets) { + final int n = getChildCount(); + for (int i = 0; i < n; i++) { + final View child = getChildAt(i); + setLinearLayoutChildInsets(child, insets, mInsets); + } + mInsets.set(insets); + } + + @Override + public LayoutParams generateLayoutParams(AttributeSet attrs) { + return new LayoutParams(getContext(), attrs); + } + + @Override + protected LayoutParams generateDefaultLayoutParams() { + return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + } + + // Override to allow type-checking of LayoutParams. + @Override + protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { + return p instanceof LayoutParams; + } + + @Override + protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { + return new LayoutParams(p); + } + + public static class LayoutParams extends LinearLayout.LayoutParams { + boolean ignoreInsets = false; + boolean ignoreTopInsets = false; + boolean ignoreBottomInsets = false; + + public LayoutParams(Context c, AttributeSet attrs) { + super(c, attrs); + TypedArray a = c.obtainStyledAttributes(attrs, + R.styleable.InsettableLinearLayout_Layout); + ignoreInsets = a.getBoolean( + R.styleable.InsettableLinearLayout_Layout_layout_ignoreInsets, false); + ignoreTopInsets = a.getBoolean( + R.styleable.InsettableLinearLayout_Layout_layout_ignoreTopInsets, false); + ignoreBottomInsets = a.getBoolean( + R.styleable.InsettableLinearLayout_Layout_layout_ignoreBottomInsets, false); + a.recycle(); + } + + public LayoutParams(int width, int height) { + super(width, height); + } + + public LayoutParams(ViewGroup.LayoutParams lp) { + super(lp); + } + } + + @Override + public void onChildViewAdded(View parent, View child) { + setLinearLayoutChildInsets(child, mInsets, new Rect()); + } + + @Override + public void onChildViewRemoved(View parent, View child) { + } + +} diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java index 6d4d95292..a03cc31f2 100644 --- a/src/com/android/launcher3/InvariantDeviceProfile.java +++ b/src/com/android/launcher3/InvariantDeviceProfile.java @@ -33,6 +33,38 @@ import java.util.Comparator; public class InvariantDeviceProfile { + public enum GridSize { + Comfortable(0), + Cozy(1), + Condensed(2), + Custom(3); + + private final int mValue; + GridSize(int value) { + mValue = value; + } + + public int getValue() { + return mValue; + } + + public static GridSize getModeForValue(int value) { + switch (value) { + case 1: + return Cozy; + case 2: + return Condensed; + case 3: + return Custom; + default : + return Comfortable; + } + } + } + + public final static int GRID_SIZE_MAX = 3; + public final static int GRID_SIZE_MIN = 2; + // This is a static that we use for the default icon size on a 4/5-inch phone private static float DEFAULT_ICON_SIZE_DP = 60; @@ -56,6 +88,8 @@ public class InvariantDeviceProfile { */ public int numRows; public int numColumns; + public int numRowsBase; + public int numColumnsBase; /** * The minimum number of predicted apps in all apps. @@ -148,6 +182,29 @@ public class InvariantDeviceProfile { numFolderColumns = closestProfile.numFolderColumns; minAllAppsPredictionColumns = closestProfile.minAllAppsPredictionColumns; + numRowsBase = numRows; + int gridResize = SettingsProvider.getIntCustomDefault(context, + SettingsProvider.SETTINGS_UI_DYNAMIC_GRID_SIZE, 0); + if (GridSize.getModeForValue(gridResize) != GridSize.Custom) { + numRows += gridResize; + } else { + int iTempNumberOfRows = SettingsProvider.getIntCustomDefault(context, + SettingsProvider.SETTINGS_UI_HOMESCREEN_ROWS, numRows); + if (iTempNumberOfRows > 0) { + numRows = iTempNumberOfRows; + } + } + numColumnsBase = numColumns; + if (GridSize.getModeForValue(gridResize) != GridSize.Custom) { + numColumns += gridResize; + } else { + int iTempNumberOfColumns = SettingsProvider.getIntCustomDefault(context, + SettingsProvider.SETTINGS_UI_HOMESCREEN_COLUMNS, numColumns); + if (iTempNumberOfColumns > 0) { + numColumns = iTempNumberOfColumns; + } + } + iconSize = interpolatedDeviceProfileOut.iconSize; iconBitmapSize = Utilities.pxFromDp(iconSize, dm); iconTextSize = interpolatedDeviceProfileOut.iconTextSize; diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java index f7e0ea488..c7729ffe6 100644 --- a/src/com/android/launcher3/ItemInfo.java +++ b/src/com/android/launcher3/ItemInfo.java @@ -114,6 +114,12 @@ public class ItemInfo { */ public CharSequence contentDescription; + /** + * Indicates that this item has had it's position changed + * because the grid size was made smaller and it could no longer fit. + */ + public boolean wasMovedDueToReducedSpace = false; + /** * The position of the item in a drag-and-drop operation. */ diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 15373c20b..8f3bbf578 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -32,7 +32,6 @@ import android.app.AlertDialog; import android.app.Fragment; import android.app.FragmentManager; import android.app.FragmentTransaction; -import android.app.Dialog; import android.app.SearchManager; import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetManager; @@ -258,6 +257,7 @@ public class Launcher extends Activity private DragController mDragController; private View mWeightWatcher; protected HiddenFolderFragment mHiddenFolderFragment; + private DynamicGridSizeFragment mDynamicGridSizeFragment; private AppWidgetManagerCompat mAppWidgetManager; private LauncherAppWidgetHost mAppWidgetHost; @@ -273,6 +273,7 @@ public class Launcher extends Activity @Thunk Hotseat mHotseat; private ViewGroup mOverviewPanel; + private View mDarkPanel; OverviewSettingsPanel mOverviewSettingsPanel; private View mAllAppsButton; @@ -369,6 +370,20 @@ public class Launcher extends Activity private BubbleTextView mWaitingForResume; private boolean mReloadLauncher; + private boolean mResizeGridRequired; + + public Animator.AnimatorListener mAnimatorListener = new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator arg0) {} + @Override + public void onAnimationRepeat(Animator arg0) {} + @Override + public void onAnimationEnd(Animator arg0) { + mDarkPanel.setVisibility(View.GONE); + } + @Override + public void onAnimationCancel(Animator arg0) {} + }; // Preferences private boolean mHideIconLabels; @@ -1120,6 +1135,13 @@ public class Launcher extends Activity mLauncherCallbacks.onResume(); } + // Close out fragments + Fragment gridFragment = getFragmentManager().findFragmentByTag( + DynamicGridSizeFragment.DYNAMIC_GRID_SIZE_FRAGMENT); + if (gridFragment != null) { + mDynamicGridSizeFragment.setSize(); + } + reloadLauncherIfNeeded(); //Close out Fragments @@ -1439,6 +1461,8 @@ public class Launcher extends Activity mOverviewSettingsPanel = new OverviewSettingsPanel(this); mOverviewSettingsPanel.initializeAdapter(); + mDarkPanel = mOverviewPanel.findViewById(R.id.dark_panel); + mWidgetsButton = findViewById(R.id.widget_button); mWidgetsButton.setOnClickListener(new OnClickListener() { @Override @@ -1759,11 +1783,13 @@ public class Launcher extends Activity } /** - * Sets the reload launcher flag to true, which will reload the launcher at the next appropriate - * time. + * Sets the reload launcher flag to true and the resize grid flag to the parameter value, + * which will reload the launcher/grid size at the next appropriate time. + * @param shouldResizeGrid Indicates whether the grid needs to be resized. */ - public void setReloadLauncher() { + public void setReloadLauncher(boolean shouldResizeGrid) { mReloadLauncher = true; + mResizeGridRequired = shouldResizeGrid; } /** @@ -1774,6 +1800,7 @@ public class Launcher extends Activity if (mReloadLauncher) { reloadLauncher(mWorkspace.getCurrentPage()); mReloadLauncher = false; + mResizeGridRequired = false; return true; } @@ -1797,12 +1824,76 @@ public class Launcher extends Activity // Reload mModel.resetLoadedState(true, true); - mModel.startLoader(page, LauncherModel.LOADER_FLAG_NONE); + int flag = mResizeGridRequired ? LauncherModel.LOADER_FLAG_RESIZE_GRID : + LauncherModel.LOADER_FLAG_NONE; + mModel.startLoader(page, flag); mWorkspace.updateCustomContentVisibility(); mAppsView.reset(); } + /** + * Replaces currently added fragments in the launcher layout with a + * {@link DynamicGridSizeFragment}. + */ + public void onClickDynamicGridSizeButton() { + FragmentManager fragmentManager = getFragmentManager(); + FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); + + mDynamicGridSizeFragment = new DynamicGridSizeFragment(); + fragmentTransaction.replace(R.id.launcher, mDynamicGridSizeFragment, + DynamicGridSizeFragment.DYNAMIC_GRID_SIZE_FRAGMENT); + fragmentTransaction.commit(); + } + + /** + * If the new grid size is different from the current grid size, the launcher will be reloaded + * and the overview settings panel updated with the new grid size value. + * @param size The new grid size to set the workspace to. + */ + public void setDynamicGridSize(InvariantDeviceProfile.GridSize size) { + int gridSize = SettingsProvider.getIntCustomDefault(this, + SettingsProvider.SETTINGS_UI_DYNAMIC_GRID_SIZE, 0); + boolean customValuesChanged = false; + if (gridSize == size.getValue() && size == InvariantDeviceProfile.GridSize.Custom) { + int tempRows = SettingsProvider.getIntCustomDefault(this, + SettingsProvider.SETTINGS_UI_HOMESCREEN_ROWS, mDeviceProfile.inv.numRows); + int tempColumns = SettingsProvider.getIntCustomDefault(this, + SettingsProvider.SETTINGS_UI_HOMESCREEN_COLUMNS, mDeviceProfile.inv.numColumns); + if (tempColumns != mDeviceProfile.inv.numColumns || + tempRows != mDeviceProfile.inv.numRows) { + customValuesChanged = true; + } + } + + if (gridSize != size.getValue() || customValuesChanged) { + SettingsProvider.putInt(this, + SettingsProvider.SETTINGS_UI_DYNAMIC_GRID_SIZE, size.getValue()); + + setReloadLauncher(true); + } + + mOverviewSettingsPanel.notifyDataSetInvalidated(); + + FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction(); + Configuration config = getResources().getConfiguration(); + if(config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) { + fragmentTransaction + .setCustomAnimations(0, R.anim.exit_out_left); + } else { + fragmentTransaction + .setCustomAnimations(0, R.anim.exit_out_right); + } + fragmentTransaction + .remove(mDynamicGridSizeFragment).commit(); + + mDarkPanel.setVisibility(View.VISIBLE); + ObjectAnimator anim = ObjectAnimator.ofFloat( + mDarkPanel, "alpha", 0.3f, 0.0f); + anim.start(); + anim.addListener(mAnimatorListener); + } + @Override public void onAttachedToWindow() { super.onAttachedToWindow(); @@ -2000,6 +2091,10 @@ public class Launcher extends Activity return mOverviewPanel; } + public View getDarkPanel() { + return mDarkPanel; + } + public SearchDropTargetBar getSearchDropTargetBar() { return mSearchDropTargetBar; } @@ -2627,7 +2722,14 @@ public class Launcher extends Activity } else if (isWidgetsViewVisible()) { showOverviewMode(true); } else if (mWorkspace.isInOverviewMode()) { - showWorkspace(true); + Fragment gridFragment = getFragmentManager().findFragmentByTag( + DynamicGridSizeFragment.DYNAMIC_GRID_SIZE_FRAGMENT); + if (gridFragment != null) { + mDynamicGridSizeFragment.setSize(); + } + else { + showWorkspace(true); + } } else if (mWorkspace.getOpenFolder() != null) { Folder openFolder = mWorkspace.getOpenFolder(); if (openFolder.isEditingName()) { @@ -3516,6 +3618,8 @@ public class Launcher extends Activity } void showWorkspace(int snapToPage, boolean animated, Runnable onCompleteRunnable) { + reloadLauncherIfNeeded(); + boolean changed = mState != State.WORKSPACE || mWorkspace.getState() != Workspace.State.NORMAL; if (changed) { @@ -3544,6 +3648,8 @@ public class Launcher extends Activity } void showOverviewMode(boolean animated) { + reloadLauncherIfNeeded(); + mWorkspace.setVisibility(View.VISIBLE); mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(), Workspace.State.OVERVIEW, @@ -4293,6 +4399,10 @@ public class Launcher extends Activity if (mLauncherCallbacks != null) { mLauncherCallbacks.finishBindingItems(false); } + + if (mWorkspace.isInOverviewMode()) { + reloadLauncherIfNeeded(); + } } private void sendLoadingCompleteBroadcastIfNecessary() { @@ -4978,14 +5088,6 @@ public class Launcher extends Activity AnimationDrawable frameAnimation = (AnimationDrawable) mAnimatedArrow.getBackground(); frameAnimation.start(); - - /*if (mLauncher.updateGridIfNeeded()) { - Workspace workspace = mLauncher.getWorkspace(); - if (workspace.isInOverviewMode()) { - workspace.setChildrenOutlineAlpha(1.0f); - mLauncher.mSearchDropTargetBar.hideSearchBar(false); - } - }*/ } @Override diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index c3ad6a8a1..4f2491f8d 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -1112,7 +1112,7 @@ public class LauncherModel extends BroadcastReceiver /** * Removes the specified items from the database * @param context - * @param item + * @param items */ static void deleteItemsFromDatabase(Context context, final ArrayList items) { final ContentResolver cr = context.getContentResolver(); @@ -1644,7 +1644,7 @@ public class LauncherModel extends BroadcastReceiver // check & update map of what's occupied; used to discard overlapping/invalid items private boolean checkItemPlacement(LongArrayMap occupied, ItemInfo item, - ArrayList workspaceScreens) { + ArrayList workspaceScreens, boolean shouldResizeAndUpdateDB) { LauncherAppState app = LauncherAppState.getInstance(); InvariantDeviceProfile profile = app.getInvariantDeviceProfile(); final int countX = profile.numColumns; @@ -1700,26 +1700,67 @@ public class LauncherModel extends BroadcastReceiver return true; } - if (!occupied.containsKey(item.screenId)) { - ItemInfo[][] items = new ItemInfo[countX + 1][countY + 1]; - occupied.put(item.screenId, items); + // If the current item's position lies outside of the bounds + // of the current grid size, attempt to place it in the next + // available position. + if (item.cellX < 0 || item.cellY < 0 || item.cellX + item.spanX > countX + || item.cellY + item.spanY > countY) { + // If we won't be resizing the grid, then just return, this item does not fit. + if (!shouldResizeAndUpdateDB) { + Log.e(TAG, "Error loading shortcut " + item + + " into cell (" + containerIndex + "-" + item.screenId + ":" + + item.cellX + "," + item.cellY + + ") out of screen bounds ( " + countX + "x" + countY + ")"); + return false; + } + + if (item.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET) { + // Place the item at 0 0 of screen 1 + // if items overlap here, they will be moved later on + item.cellX = 0; + item.cellY = 0; + item.screenId = 1; + item.wasMovedDueToReducedSpace = true; + item.requiresDbUpdate = true; + } else { + // see if widget can be shrunk to fit a screen, if not, just remove it + if (item.minSpanX > countX || item.minSpanY > countY) { + return false; + } + // if the widget is larger than the grid, shrink it down + if (item.cellX + item.spanX > countX) { + item.cellX = 0; + item.spanY = (item.spanY / 2) > 0 ? item.spanY / 2 : 1; + item.spanX = item.minSpanX; + item.requiresDbUpdate = true; + item.wasMovedDueToReducedSpace = true; + } + if (item.cellY + item.spanY > countY) { + item.cellY = 0; + item.spanY = countY; + item.requiresDbUpdate = true; + item.wasMovedDueToReducedSpace = true; + } + if (item.cellY + item.spanY == countY && item.cellX + item.spanX == countX) { + // if the widget is the size of the grid, make a screen all it's own. + item.screenId = sBgWorkspaceScreens.size() + 1; + } + } + } else { + item.wasMovedDueToReducedSpace = false; + item.requiresDbUpdate = true; } - final ItemInfo[][] screens = occupied.get(item.screenId); - if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP && - item.cellX < 0 || item.cellY < 0 || - item.cellX + item.spanX > countX || item.cellY + item.spanY > countY) { - Log.e(TAG, "Error loading shortcut " + item - + " into cell (" + containerIndex + "-" + item.screenId + ":" - + item.cellX + "," + item.cellY - + ") out of screen bounds ( " + countX + "x" + countY + ")"); - return false; + if (!occupied.containsKey(item.screenId)) { + ItemInfo[][] items = new ItemInfo[countX][countY]; + occupied.put(item.screenId, items); } + ItemInfo[][] screens = occupied.get(item.screenId); // Check if any workspace icons overlap with each other for (int x = item.cellX; x < (item.cellX+item.spanX); x++) { for (int y = item.cellY; y < (item.cellY+item.spanY); y++) { - if (screens[x][y] != null) { + if (!shouldResizeAndUpdateDB && screens[x][y] != null) { Log.e(TAG, "Error loading shortcut " + item + " into cell (" + containerIndex + "-" + item.screenId + ":" + x + "," + y @@ -1732,6 +1773,16 @@ public class LauncherModel extends BroadcastReceiver for (int x = item.cellX; x < (item.cellX+item.spanX); x++) { for (int y = item.cellY; y < (item.cellY+item.spanY); y++) { screens[x][y] = item; + if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET + && shouldResizeAndUpdateDB) { + // fill up the entire grid where the widget technically is + for (int spanX = x; spanX < item.spanX; spanX++) { + screens[spanX][y] = item; + for (int spanY = y; spanY < item.spanX; spanY++) { + screens[spanX][spanY] = item; + } + } + } } } @@ -1765,6 +1816,8 @@ public class LauncherModel extends BroadcastReceiver int countX = profile.numColumns; int countY = profile.numRows; + boolean shouldResize = ((mFlags & LOADER_FLAG_RESIZE_GRID) != 0); + if (MigrateFromRestoreTask.ENABLED && MigrateFromRestoreTask.shouldRunTask(mContext)) { long migrationStartTime = System.currentTimeMillis(); Log.v(TAG, "Starting workspace migration after restore"); @@ -2076,7 +2129,8 @@ public class LauncherModel extends BroadcastReceiver } // check & update map of what's occupied - if (!checkItemPlacement(occupied, info, sBgWorkspaceScreens)) { + if (!checkItemPlacement(occupied, info, sBgWorkspaceScreens, + shouldResize)) { itemsToRemove.add(id); break; } @@ -2127,7 +2181,8 @@ public class LauncherModel extends BroadcastReceiver folderInfo.options = c.getInt(optionsIndex); // check & update map of what's occupied - if (!checkItemPlacement(occupied, folderInfo, sBgWorkspaceScreens)) { + if (!checkItemPlacement(occupied, folderInfo, sBgWorkspaceScreens, + shouldResize)) { itemsToRemove.add(id); break; } @@ -2254,7 +2309,8 @@ public class LauncherModel extends BroadcastReceiver appWidgetInfo.container = container; // check & update map of what's occupied - if (!checkItemPlacement(occupied, appWidgetInfo, sBgWorkspaceScreens)) { + if (!checkItemPlacement(occupied, appWidgetInfo, + sBgWorkspaceScreens, shouldResize)) { itemsToRemove.add(id); break; } @@ -2359,6 +2415,17 @@ public class LauncherModel extends BroadcastReceiver updateWorkspaceScreenOrder(context, sBgWorkspaceScreens); } + // If any items have been shifted and require a DB update, update them in the DB. + if (shouldResize) { + for (ItemInfo info : sBgWorkspaceItems) { + if (info != null && info.requiresDbUpdate) { + info.requiresDbUpdate = false; + LauncherModel.modifyItemInDatabase(mContext, info, info.container, + info.screenId, info.cellX, info.cellY, info.spanX, info.spanY); + } + } + } + if (DEBUG_LOADERS) { Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms"); Log.d(TAG, "workspace layout: "); diff --git a/src/com/android/launcher3/list/SettingsPinnedHeaderAdapter.java b/src/com/android/launcher3/list/SettingsPinnedHeaderAdapter.java index 1dcc91db6..533e64d20 100644 --- a/src/com/android/launcher3/list/SettingsPinnedHeaderAdapter.java +++ b/src/com/android/launcher3/list/SettingsPinnedHeaderAdapter.java @@ -16,6 +16,8 @@ import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.TextView; import android.widget.Toast; +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.OverviewSettingsPanel; import com.android.launcher3.R; @@ -88,7 +90,6 @@ public class SettingsPinnedHeaderAdapter extends PinnedHeaderListAdapter { Resources res = mLauncher.getResources(); - boolean current; String state; @@ -119,9 +120,9 @@ public class SettingsPinnedHeaderAdapter extends PinnedHeaderListAdapter { : res.getString(R.string.setting_state_off); ((TextView) v.findViewById(R.id.item_state)).setText(state); break; - /*case 3: + case 3: updateDynamicGridSizeSettingsItem(v); - break;*/ + break; default: ((TextView) v.findViewById(R.id.item_state)).setText(""); } @@ -173,8 +174,8 @@ public class SettingsPinnedHeaderAdapter extends PinnedHeaderListAdapter { return mPinnedHeaderCount; } - /*public void updateDynamicGridSizeSettingsItem(View v) { - DeviceProfile.GridSize gridSize = DeviceProfile.GridSize.getModeForValue( + public void updateDynamicGridSizeSettingsItem(View v) { + InvariantDeviceProfile.GridSize gridSize = InvariantDeviceProfile.GridSize.getModeForValue( SettingsProvider.getIntCustomDefault(mLauncher, SettingsProvider.SETTINGS_UI_DYNAMIC_GRID_SIZE, 0)); String state = ""; @@ -198,7 +199,7 @@ public class SettingsPinnedHeaderAdapter extends PinnedHeaderListAdapter { break; } ((TextView) v.findViewById(R.id.item_state)).setText(state); - }*/ + } OnClickListener mSettingsItemListener = new OnClickListener() { @@ -212,23 +213,23 @@ public class SettingsPinnedHeaderAdapter extends PinnedHeaderListAdapter { switch (position) { case 0: updateSearchBarVisibility(v); - mLauncher.setReloadLauncher(); + mLauncher.setReloadLauncher(false); break; case 1: onIconLabelsBooleanChanged(v, SettingsProvider.SETTINGS_UI_HOMESCREEN_HIDE_ICON_LABELS, R.bool.preferences_interface_homescreen_hide_icon_labels_default); - mLauncher.setReloadLauncher(); + mLauncher.setReloadLauncher(false); break; case 2: onSettingsBooleanChanged(v, SettingsProvider.SETTINGS_UI_HOMESCREEN_SCROLLING_WALLPAPER_SCROLL, R.bool.preferences_interface_homescreen_scrolling_wallpaper_scroll_default); - mLauncher.setReloadLauncher(); + mLauncher.setReloadLauncher(false); break; - /*case 3: + case 3: mLauncher.onClickDynamicGridSizeButton(); - break;*/ + break; } break; case OverviewSettingsPanel.DRAWER_SETTINGS_POSITION: @@ -237,7 +238,7 @@ public class SettingsPinnedHeaderAdapter extends PinnedHeaderListAdapter { onIconLabelsBooleanChanged(v, SettingsProvider.SETTINGS_UI_DRAWER_HIDE_ICON_LABELS, R.bool.preferences_interface_drawer_hide_icon_labels_default); - mLauncher.setReloadLauncher(); + mLauncher.setReloadLauncher(false); break; } break; @@ -247,7 +248,7 @@ public class SettingsPinnedHeaderAdapter extends PinnedHeaderListAdapter { onSettingsBooleanChanged(v, SettingsProvider.SETTINGS_UI_GENERAL_ICONS_LARGE, R.bool.preferences_interface_general_icons_large_default); - mLauncher.setReloadLauncher(); + mLauncher.setReloadLauncher(false); break; /*case 1: Intent intent = new Intent(); -- cgit v1.2.3