OSDN Git Service

Reimplement CM Settings Overview Panel Part 3
authorYvonne Wong <ywong@cyngn.com>
Wed, 2 Dec 2015 01:04:07 +0000 (17:04 -0800)
committerYvonne Wong <ywong@cyngn.com>
Thu, 3 Dec 2015 10:05:08 +0000 (02:05 -0800)
- Enable dynamic grid resizing

Change-Id: I95a7f20da48e037a94ce5b6191c5597490d91d9d

20 files changed:
res/drawable/ic_navigation_next.xml [new file with mode: 0644]
res/drawable/ic_navigation_prev.xml [new file with mode: 0644]
res/drawable/listitem_bg.xml
res/drawable/listitem_text.xml
res/layout-land/launcher.xml
res/layout/custom_grid_size_dialog.xml [new file with mode: 0644]
res/layout/dynamic_grid_size_screen.xml [new file with mode: 0644]
res/layout/overview_panel.xml
res/layout/settings_pane_list_header.xml
res/values/cm_strings.xml
res/values/colors.xml
res/values/dimens.xml
src/com/android/launcher3/DeviceProfile.java
src/com/android/launcher3/DynamicGridSizeFragment.java [new file with mode: 0644]
src/com/android/launcher3/InsettableLinearLayout.java [new file with mode: 0644]
src/com/android/launcher3/InvariantDeviceProfile.java
src/com/android/launcher3/ItemInfo.java
src/com/android/launcher3/Launcher.java
src/com/android/launcher3/LauncherModel.java
src/com/android/launcher3/list/SettingsPinnedHeaderAdapter.java

diff --git a/res/drawable/ic_navigation_next.xml b/res/drawable/ic_navigation_next.xml
new file mode 100644 (file)
index 0000000..571280b
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
+
+    <path
+            android:fillColor="#FFFFFF"
+            android:pathData="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z" />
+    <path
+            android:pathData="M0 0h24v24H0z" />
+</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_navigation_prev.xml b/res/drawable/ic_navigation_prev.xml
new file mode 100644 (file)
index 0000000..f0a6a3b
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
+
+    <path
+            android:fillColor="#FFFFFF"
+            android:pathData="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z" />
+    <path
+            android:pathData="M0 0h24v24H0z" />
+</vector>
\ No newline at end of file
index 1a1e93d..55dbfcf 100644 (file)
@@ -16,6 +16,6 @@
 -->
 <selector xmlns:android="http://schemas.android.com/apk/res/android" >
     <item android:state_enabled="true"
-        android:state_pressed="true" android:drawable="@android:color/white" />
+          android:state_pressed="true" android:drawable="@android:color/white" />
     <item android:drawable="@color/settings_bg_color" />
 </selector>
index 9637fd3..d6c2503 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <selector xmlns:android="http://schemas.android.com/apk/res/android" >
     <item android:state_enabled="true"
-          android:state_pressed="true" android:color="@color/slideup_panel_bg_color" />
+          android:state_pressed="true" android:color="@color/settings_bg_color" />
     <item android:color="@android:color/white" />
 </selector>
index 3a93365..3fe6f89 100644 (file)
@@ -18,6 +18,7 @@
 <com.android.launcher3.LauncherRootView
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:launcher="http://schemas.android.com/apk/res-auto"
+    xmlns:insettable="http://schemas.android.com/apk/res-auto"
     android:id="@+id/launcher"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
@@ -30,6 +31,7 @@
         android:layout_height="match_parent"
         android:visibility="invisible"
         android:alpha="1.0"
+        insettable:layout_ignoreInsets="true"
         android:clipToPadding="false">
 
         <ImageView
diff --git a/res/layout/custom_grid_size_dialog.xml b/res/layout/custom_grid_size_dialog.xml
new file mode 100644 (file)
index 0000000..59a5db4
--- /dev/null
@@ -0,0 +1,48 @@
+<!--
+     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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content" >
+
+    <LinearLayout
+        android:id="@+id/grid_number_pickers"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerHorizontal="true"
+        android:orientation="horizontal">
+
+        <NumberPicker
+            android:id="@+id/custom_rows"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginRight="@dimen/dialog_padding"/>
+
+        <NumberPicker
+            android:id="@+id/custom_columns"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="@dimen/dialog_padding"/>
+
+    </LinearLayout>
+
+    <Button
+        android:id="@+id/dialog_confirm_button"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/dialog_confirm"
+        android:layout_below="@id/grid_number_pickers"
+        android:layout_marginTop="@dimen/dialog_padding"/>
+</RelativeLayout>
\ No newline at end of file
diff --git a/res/layout/dynamic_grid_size_screen.xml b/res/layout/dynamic_grid_size_screen.xml
new file mode 100644 (file)
index 0000000..85dc992
--- /dev/null
@@ -0,0 +1,69 @@
+<!--
+     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.
+-->
+<com.android.launcher3.InsettableLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:insettable="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_gravity="center_horizontal|bottom"
+    android:background="@color/settings_bg_color"
+    android:orientation="vertical"
+    android:clickable="true" >
+
+    <LinearLayout
+        android:id="@+id/dynamic_grid_title"
+        insettable:layout_ignoreBottomInsets="true"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:background="@drawable/listitem_bg"
+        android:clickable="true" >
+
+        <ImageView
+            android:id="@+id/nav_prev"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:adjustViewBounds="true"
+            android:src="@drawable/ic_navigation_prev" />
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/grid_size_text"
+            android:textAllCaps="true"
+            android:fontFamily="sans-serif-condensed"
+            android:textColor="@drawable/listitem_text"
+            android:layout_gravity="center_vertical"
+            android:textSize="16sp" />
+    </LinearLayout>
+
+    <view
+        insettable:layout_ignoreInsets="true"
+        class="com.android.launcher3.DynamicGridSizeFragment$GridSizeView"
+        android:id="@+id/dynamic_grid_size_image"
+        android:layout_width="150dp"
+        android:layout_height="150dp"
+        android:layout_gravity="center_horizontal"
+        android:layout_marginBottom="@dimen/grid_padding"/>
+
+    <ListView
+        insettable:layout_ignoreTopInsets="true"
+        android:id="@+id/dynamic_grid_list"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:listSelector="@android:color/transparent"
+        android:splitMotionEvents="false"/>
+</com.android.launcher3.InsettableLinearLayout>
index 5698c86..636e36b 100644 (file)
@@ -28,7 +28,7 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_gravity="center_horizontal"
-        android:background="@color/slideup_panel_bg_color"
+        android:background="@color/settings_bg_color"
         android:paddingTop="@dimen/overview_panel_top_padding" >
 
         <ImageView
@@ -45,7 +45,7 @@
 
     <FrameLayout android:orientation="vertical"
          android:layout_width="match_parent"
-         android:background="@color/slideup_panel_bg_color"
+         android:background="@color/settings_bg_color"
          android:layout_height="match_parent" >
 
         <LinearLayout
index 2429b9b..a7d04e3 100644 (file)
@@ -1,8 +1,23 @@
+<!--
+     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.
+-->
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:layout_gravity="center_horizontal|bottom"
-    android:background="@color/slideup_panel_bg_color"
+    android:background="@color/settings_bg_color"
     android:paddingLeft="@dimen/overview_panel_list_padding"
     android:paddingRight="@dimen/overview_panel_list_padding"
     android:orientation="horizontal" >
index e31fed1..9072824 100644 (file)
@@ -43,6 +43,9 @@
     <string name="grid_size_custom">Custom (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
     <string name="preferences_interface_homescreen_custom">Select custom size</string>
 
+    <!-- Dialog -->
+    <string name="dialog_confirm">Confirm</string>
+
     <!-- Home screen search bar -->
     <string name="home_screen_search_text">Search bar</string>
 
index 80c0d6f..1b89621 100644 (file)
@@ -36,7 +36,6 @@
     <color name="quantum_panel_text_color_dark">#FFF</color>
     <color name="quantum_panel_bg_color">#FFF5F5F5</color>
     <color name="quantum_panel_bg_color_dark">#76000000</color>
-    <color name="slideup_panel_bg_color">#FF374248</color>
 
     <color name="outline_color">#FFFFFFFF</color>
 
     <color name="widgets_cell_color">#263238</color>
 
     <!-- CM Settings -->
-    <color name="settings_header_text">#FF6cd2ea</color>
+    <color name="settings_bg_color">#424242</color>
+    <color name="settings_header_text">#00B1E5</color>
+
+    <color name="dynamic_grid_preview_background">#FFFFFFFF</color>
+    <color name="dynamic_grid_preview_foreground">#FF000000</color>
 
     <color name="app_scrubber_highlight_color">@android:color/white</color>
     <color name="app_scrubber_gray_color">@android:color/darker_gray</color>
index 95557d9..d73bd33 100644 (file)
@@ -28,6 +28,9 @@
     <dimen name="dynamic_grid_overview_max_icon_zone_height">120dp</dimen>
     <dimen name="dynamic_grid_overview_bar_item_width">80dp</dimen>
     <dimen name="dynamic_grid_overview_bar_spacer_width">20dp</dimen>
+    <dimen name="dialog_padding">10dp</dimen>
+    <dimen name="grid_padding">15dp</dimen>
+    <dimen name="grid_custom_text">50dp</dimen>
 
 <!-- App Widget resize frame -->
     <dimen name="default_widget_padding">8dp</dimen>
index 7e9d8f8..6115df6 100644 (file)
@@ -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 (file)
index 0000000..103d660
--- /dev/null
@@ -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 (file)
index 0000000..8f64713
--- /dev/null
@@ -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) {
+    }
+
+}
index 6d4d952..a03cc31 100644 (file)
@@ -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;
index f7e0ea4..c7729ff 100644 (file)
@@ -115,6 +115,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.
      */
     public int[] dropPos = null;
index 15373c2..8f3bbf5 100644 (file)
@@ -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
index c3ad6a8..4f2491f 100644 (file)
@@ -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<? extends ItemInfo> 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<ItemInfo[][]> occupied, ItemInfo item,
-                   ArrayList<Long> workspaceScreens) {
+                   ArrayList<Long> 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: ");
index 1dcc91d..533e64d 100644 (file)
@@ -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();