diff options
author | Jorge Ruesga <jorge@ruesga.com> | 2013-08-04 03:31:13 +0200 |
---|---|---|
committer | Jorge Ruesga <jorge@ruesga.com> | 2013-08-04 03:31:13 +0200 |
commit | 9a4d4f244d9e2d24295bf89c2bbb01610aa33899 (patch) | |
tree | 414a960db501ce996fdbbfa5adfdd0b609009d8b | |
parent | 434604db80a43302c96cca812928234528518117 (diff) | |
download | android_packages_wallpapers_PhotoPhase-9a4d4f244d9e2d24295bf89c2bbb01610aa33899.tar.gz android_packages_wallpapers_PhotoPhase-9a4d4f244d9e2d24295bf89c2bbb01610aa33899.tar.bz2 android_packages_wallpapers_PhotoPhase-9a4d4f244d9e2d24295bf89c2bbb01610aa33899.zip |
Disposition Preference Layout (#1)
Signed-off-by: Jorge Ruesga <jorge@ruesga.com>
19 files changed, 1071 insertions, 159 deletions
diff --git a/res/anim/display_with_bounce.xml b/res/anim/display_with_bounce.xml new file mode 100644 index 0000000..b9ea8c2 --- /dev/null +++ b/res/anim/display_with_bounce.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2013 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<set xmlns:android="http://schemas.android.com/apk/res/android" + android:interpolator="@android:anim/bounce_interpolator" > + + <scale + android:duration="@integer/disposition_show_anim" + android:fromXScale="0.0" + android:fromYScale="0.0" + android:toXScale="1.0" + android:toYScale="1.0" + android:pivotX="50%" + android:pivotY="50%" /> + + <alpha + android:duration="@integer/disposition_show_anim" + android:fromAlpha="0.0" + android:toAlpha="1.0" /> + +</set>
\ No newline at end of file diff --git a/res/drawable-hdpi/ic_delete.png b/res/drawable-hdpi/ic_delete.png Binary files differnew file mode 100644 index 0000000..e9ce89e --- /dev/null +++ b/res/drawable-hdpi/ic_delete.png diff --git a/res/drawable-mdpi/ic_delete.png b/res/drawable-mdpi/ic_delete.png Binary files differnew file mode 100644 index 0000000..cedb108 --- /dev/null +++ b/res/drawable-mdpi/ic_delete.png diff --git a/res/drawable-xhdpi/ic_delete.png b/res/drawable-xhdpi/ic_delete.png Binary files differnew file mode 100644 index 0000000..98c73da --- /dev/null +++ b/res/drawable-xhdpi/ic_delete.png diff --git a/res/menu/accept_restore_preference.xml b/res/menu/albums.xml index 435e4da..ec699a3 100644 --- a/res/menu/accept_restore_preference.xml +++ b/res/menu/albums.xml @@ -18,7 +18,7 @@ <item android:id="@+id/mnu_restore" android:title="@string/mnu_restore" android:icon="@drawable/ic_restore" - android:showAsAction="always|withText" /> + android:showAsAction="ifRoom|withText" /> <item android:id="@+id/mnu_ok" android:title="@string/mnu_ok" android:icon="@drawable/ic_accept" diff --git a/res/menu/dispositions.xml b/res/menu/dispositions.xml new file mode 100644 index 0000000..e167992 --- /dev/null +++ b/res/menu/dispositions.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2013 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. + --> + +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/mnu_delete" + android:title="@string/mnu_delete" + android:icon="@drawable/ic_delete" + android:showAsAction="ifRoom|withText" /> + <item android:id="@+id/mnu_restore" + android:title="@string/mnu_restore" + android:icon="@drawable/ic_restore" + android:showAsAction="ifRoom|withText" /> + <item android:id="@+id/mnu_ok" + android:title="@string/mnu_ok" + android:icon="@drawable/ic_accept" + android:showAsAction="always|withText" /> +</menu> diff --git a/res/values/integers.xml b/res/values/integers.xml index 09ee5c2..6e5eb23 100644 --- a/res/values/integers.xml +++ b/res/values/integers.xml @@ -16,4 +16,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android"> <integer name="cards_slide_anim">800</integer> + <integer name="disposition_show_anim">600</integer> + <integer name="disposition_hide_anim">400</integer> </resources> diff --git a/res/values/strings.xml b/res/values/strings.xml index 93b8824..0e4d33d 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -26,6 +26,7 @@ <!-- Menus --> <string name="mnu_ok" translatable="false">@android:string/ok</string> <string name="mnu_restore">Restore</string> + <string name="mnu_delete">Delete</string> <string name="mnu_settings">Settings</string> <string name="mnu_select_all">Select all</string> <string name="mnu_deselect_all">Deselect all</string> @@ -73,6 +74,7 @@ <string name="pref_disposition_landscape">Landscape disposition</string> <string name="pref_disposition_landscape_summary">Select how pictures are disposed on a landscape screen</string> <string name="pref_disposition_description">Long tap a frame to select it. Then drag \u0026 drop the borders of the frame to resize it.</string> + <string name="pref_disposition_unable_delete_advise">Unable to delete the selected frame</string> <string name="pref_about">About</string> <string name="pref_about_summary">PhotoPhase v<xliff:g id="version">%1$s</xliff:g>\nCopyright \u00A9 2013 The CyanogenMod Project</string> diff --git a/src/org/cyanogenmod/wallpapers/photophase/Utils.java b/src/org/cyanogenmod/wallpapers/photophase/Utils.java deleted file mode 100644 index d8331a0..0000000 --- a/src/org/cyanogenmod/wallpapers/photophase/Utils.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2013 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 org.cyanogenmod.wallpapers.photophase; - -import android.content.Context; -import android.content.res.Resources; -import android.util.DisplayMetrics; - -/** - * A helper class with utilities - */ -public class Utils { - - /** - * This method converts dp unit to equivalent device specific value in pixels. - * - * @param ctx The current context - * @param dp A value in dp (Device independent pixels) unit - * @return float A float value to represent Pixels equivalent to dp according to device - */ - public static float convertDpToPixel(Context ctx, float dp) { - Resources resources = ctx.getResources(); - DisplayMetrics metrics = resources.getDisplayMetrics(); - return dp * (metrics.densityDpi / 160f); - } - -} diff --git a/src/org/cyanogenmod/wallpapers/photophase/animations/Evaluators.java b/src/org/cyanogenmod/wallpapers/photophase/animations/Evaluators.java new file mode 100644 index 0000000..a0e52aa --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/animations/Evaluators.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2013 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 org.cyanogenmod.wallpapers.photophase.animations; + +import android.animation.IntEvaluator; +import android.view.View; +import android.view.ViewGroup; + +/** + * A class with helpful evaluators + */ +public class Evaluators { + + /** + * A width evaluator + */ + public static class WidthEvaluator extends IntEvaluator { + private View mView; + + /** + * Constructor of <code>WidthEvaluator</code> + * + * @param v The view + */ + public WidthEvaluator(View v) { + super(); + mView = v; + } + + @Override + public Integer evaluate(float fraction, Integer startValue, Integer endValue) { + Integer num = super.evaluate(fraction, startValue, endValue); + ViewGroup.LayoutParams params = mView.getLayoutParams(); + params.width = num.intValue(); + mView.setLayoutParams(params); + return num; + } + } + + /** + * A height evaluator + */ + public static class HeightEvaluator extends IntEvaluator { + private View mView; + + /** + * Constructor of <code>HeightEvaluator</code> + * + * @param v The view + */ + public HeightEvaluator(View v) { + super(); + mView = v; + } + + @Override + public Integer evaluate(float fraction, Integer startValue, Integer endValue) { + Integer num = super.evaluate(fraction, startValue, endValue); + ViewGroup.LayoutParams params = mView.getLayoutParams(); + params.height = num.intValue(); + mView.setLayoutParams(params); + return num; + } + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/model/Disposition.java b/src/org/cyanogenmod/wallpapers/photophase/model/Disposition.java index d1b798a..35cc0e9 100644 --- a/src/org/cyanogenmod/wallpapers/photophase/model/Disposition.java +++ b/src/org/cyanogenmod/wallpapers/photophase/model/Disposition.java @@ -21,7 +21,7 @@ import org.cyanogenmod.wallpapers.photophase.PhotoFrame; /** * A class that holds a {@link PhotoFrame} disposition. */ -public class Disposition { +public class Disposition implements Comparable<Disposition> { /** * Column */ @@ -83,4 +83,36 @@ public class Disposition { public String toString() { return "Disposition [x=" + x + ", y=" + y + ", w=" + w + ", h=" + h + "]"; } + + /** + * {@inheritDoc} + */ + @Override + public int compareTo(Disposition another) { + if (x == another.x && y == another.y && w == another.w && h == another.h) { + return 0; + } + if (x < another.x) { + return -1; + } + if (x > another.x) { + return 1; + } + if (y < another.y) { + return -1; + } + if (y > another.y) { + return 1; + } + if (w < another.w) { + return -1; + } + if (w > another.w) { + return 1; + } + if (h < another.h) { + return -1; + } + return 1; + } } diff --git a/src/org/cyanogenmod/wallpapers/photophase/preferences/ChoosePicturesFragment.java b/src/org/cyanogenmod/wallpapers/photophase/preferences/ChoosePicturesFragment.java index cebfd81..30a7bb9 100644 --- a/src/org/cyanogenmod/wallpapers/photophase/preferences/ChoosePicturesFragment.java +++ b/src/org/cyanogenmod/wallpapers/photophase/preferences/ChoosePicturesFragment.java @@ -242,7 +242,7 @@ public class ChoosePicturesFragment extends PreferenceFragment { */ @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - inflater.inflate(R.menu.accept_restore_preference, menu); + inflater.inflate(R.menu.albums, menu); } /** @@ -286,7 +286,7 @@ public class ChoosePicturesFragment extends PreferenceFragment { // Restore the preference PreferencesProvider.Preferences.Media.setSelectedAlbums(getActivity(), mSelectedAlbums); - mSelectionChanged = false; + mSelectionChanged = true; // Restore all the animations states for (AlbumsFlip3dAnimationController controller : mAnimationControllers) { diff --git a/src/org/cyanogenmod/wallpapers/photophase/preferences/DispositionFragment.java b/src/org/cyanogenmod/wallpapers/photophase/preferences/DispositionFragment.java index 5bfd3dd..2539e81 100644 --- a/src/org/cyanogenmod/wallpapers/photophase/preferences/DispositionFragment.java +++ b/src/org/cyanogenmod/wallpapers/photophase/preferences/DispositionFragment.java @@ -17,6 +17,7 @@ package org.cyanogenmod.wallpapers.photophase.preferences; import android.content.Context; +import android.content.Intent; import android.os.Bundle; import android.preference.PreferenceFragment; import android.view.LayoutInflater; @@ -29,6 +30,7 @@ import android.view.ViewGroup; import org.cyanogenmod.wallpapers.photophase.R; import org.cyanogenmod.wallpapers.photophase.model.Disposition; import org.cyanogenmod.wallpapers.photophase.widgets.DispositionView; +import org.cyanogenmod.wallpapers.photophase.widgets.DispositionView.OnFrameSelectedListener; import org.cyanogenmod.wallpapers.photophase.widgets.ResizeFrame; import java.util.List; @@ -36,7 +38,8 @@ import java.util.List; /** * An abstract fragment class that allow to choose the layout disposition of the wallpaper. */ -public abstract class DispositionFragment extends PreferenceFragment { +public abstract class DispositionFragment + extends PreferenceFragment implements OnFrameSelectedListener { private Runnable mRedraw = new Runnable() { @Override @@ -46,7 +49,10 @@ public abstract class DispositionFragment extends PreferenceFragment { } }; - DispositionView mDispositionView; + /*package*/ DispositionView mDispositionView; + + private boolean mRestored; + private MenuItem mDeleteMenu; /** * Constructor of <code>DispositionFragment</code> @@ -63,6 +69,20 @@ public abstract class DispositionFragment extends PreferenceFragment { public abstract List<Disposition> getUserDispositions(); /** + * Method that returns the default preference for the disposition + * + * @return List<Disposition> The default preference dispositions + */ + public abstract List<Disposition> getDefaultDispositions(); + + /** + * Method that request to save the dispositions + * + * @param dispositions The dispositions to save + */ + public abstract void saveDispositions(List<Disposition> dispositions); + + /** * Method that returns the number of rows to use * * @return int The number of rows @@ -99,6 +119,7 @@ public abstract class DispositionFragment extends PreferenceFragment { ViewGroup v = (ViewGroup)inflater.inflate(R.layout.choose_disposition_fragment, container, false); mDispositionView = (DispositionView)v.findViewById(R.id.disposition_view); mDispositionView.setResizeFrame((ResizeFrame)v.findViewById(R.id.resize_frame)); + mDispositionView.setOnFrameSelectedListener(this); mDispositionView.post(mRedraw); return v; } @@ -111,7 +132,18 @@ public abstract class DispositionFragment extends PreferenceFragment { super.onDestroyView(); if (mDispositionView != null) { mDispositionView.removeCallbacks(mRedraw); + if (mRestored || mDispositionView.isChanged()) { + saveDispositions(mDispositionView.getDispositions()); + } + } + + // Notify that the settings was changed + Intent intent = new Intent(PreferencesProvider.ACTION_SETTINGS_CHANGED); + if (mRestored || mDispositionView.isChanged()) { + intent.putExtra(PreferencesProvider.EXTRA_FLAG_REDRAW, Boolean.TRUE); + intent.putExtra(PreferencesProvider.EXTRA_FLAG_RECREATE_WORLD, Boolean.TRUE); } + getActivity().sendBroadcast(intent); } /** @@ -119,7 +151,11 @@ public abstract class DispositionFragment extends PreferenceFragment { */ @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - inflater.inflate(R.menu.accept_restore_preference, menu); + inflater.inflate(R.menu.dispositions, menu); + mDeleteMenu = menu.findItem(R.id.mnu_delete); + if (mDeleteMenu != null) { + mDeleteMenu.setVisible(false); + } } /** @@ -134,6 +170,9 @@ public abstract class DispositionFragment extends PreferenceFragment { case R.id.mnu_restore: restoreData(); return true; + case R.id.mnu_delete: + deleteFrame(); + return true; default: return super.onOptionsItemSelected(item); } @@ -143,7 +182,34 @@ public abstract class DispositionFragment extends PreferenceFragment { * Method that restores the disposition view to the default state */ private void restoreData() { - //TODO Restore disposition + mDispositionView.setDispositions(getDefaultDispositions(), getCols(), getRows()); + mRestored = true; } + /** + * Method that restores the disposition view to the default state + */ + private void deleteFrame() { + mDispositionView.deleteCurrentFrame(); + } + + /** + * {@inheritDoc} + */ + @Override + public void onFrameSelectedListener(View v) { + if (mDeleteMenu != null) { + mDeleteMenu.setVisible(true); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void onFrameUnselectedListener() { + if (mDeleteMenu != null) { + mDeleteMenu.setVisible(false); + } + } } diff --git a/src/org/cyanogenmod/wallpapers/photophase/preferences/LandscapeDispositionFragment.java b/src/org/cyanogenmod/wallpapers/photophase/preferences/LandscapeDispositionFragment.java index 1461df4..f30e291 100644 --- a/src/org/cyanogenmod/wallpapers/photophase/preferences/LandscapeDispositionFragment.java +++ b/src/org/cyanogenmod/wallpapers/photophase/preferences/LandscapeDispositionFragment.java @@ -20,6 +20,7 @@ import android.content.pm.ActivityInfo; import android.os.Bundle; import org.cyanogenmod.wallpapers.photophase.model.Disposition; +import org.cyanogenmod.wallpapers.photophase.utils.DispositionUtil; import java.util.List; @@ -57,6 +58,23 @@ public class LandscapeDispositionFragment extends DispositionFragment { * {@inheritDoc} */ @Override + public List<Disposition> getDefaultDispositions() { + return DispositionUtil.toDispositions( + PreferencesProvider.Preferences.Layout.DEFAULT_LANDSCAPE_DISPOSITION); + } + + /** + * {@inheritDoc} + */ + @Override + public void saveDispositions(List<Disposition> dispositions) { + PreferencesProvider.Preferences.Layout.setLandscapeDisposition(getActivity(), dispositions); + } + + /** + * {@inheritDoc} + */ + @Override public int getRows() { // inverted return PreferencesProvider.Preferences.Layout.getCols(); diff --git a/src/org/cyanogenmod/wallpapers/photophase/preferences/PortraitDispositionFragment.java b/src/org/cyanogenmod/wallpapers/photophase/preferences/PortraitDispositionFragment.java index a91fb96..3942093 100644 --- a/src/org/cyanogenmod/wallpapers/photophase/preferences/PortraitDispositionFragment.java +++ b/src/org/cyanogenmod/wallpapers/photophase/preferences/PortraitDispositionFragment.java @@ -20,6 +20,7 @@ import android.content.pm.ActivityInfo; import android.os.Bundle; import org.cyanogenmod.wallpapers.photophase.model.Disposition; +import org.cyanogenmod.wallpapers.photophase.utils.DispositionUtil; import java.util.List; @@ -57,6 +58,23 @@ public class PortraitDispositionFragment extends DispositionFragment { * {@inheritDoc} */ @Override + public List<Disposition> getDefaultDispositions() { + return DispositionUtil.toDispositions( + PreferencesProvider.Preferences.Layout.DEFAULT_PORTRAIT_DISPOSITION); + } + + /** + * {@inheritDoc} + */ + @Override + public void saveDispositions(List<Disposition> dispositions) { + PreferencesProvider.Preferences.Layout.setPortraitDisposition(getActivity(), dispositions); + } + + /** + * {@inheritDoc} + */ + @Override public int getRows() { return PreferencesProvider.Preferences.Layout.getRows(); } diff --git a/src/org/cyanogenmod/wallpapers/photophase/preferences/PreferencesProvider.java b/src/org/cyanogenmod/wallpapers/photophase/preferences/PreferencesProvider.java index 115e80e..811f1c8 100644 --- a/src/org/cyanogenmod/wallpapers/photophase/preferences/PreferencesProvider.java +++ b/src/org/cyanogenmod/wallpapers/photophase/preferences/PreferencesProvider.java @@ -24,8 +24,8 @@ import org.cyanogenmod.wallpapers.photophase.GLESUtil.GLColor; import org.cyanogenmod.wallpapers.photophase.effects.Effects.EFFECTS; import org.cyanogenmod.wallpapers.photophase.model.Disposition; import org.cyanogenmod.wallpapers.photophase.transitions.Transitions.TRANSITIONS; +import org.cyanogenmod.wallpapers.photophase.utils.DispositionUtil; -import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -279,7 +279,6 @@ public final class PreferencesProvider { SharedPreferences preferences = context.getSharedPreferences(PREFERENCES_FILE, Context.MODE_PRIVATE); Editor editor = preferences.edit(); - editor.remove("ui_media_selected_albums"); editor.putStringSet("ui_media_selected_albums", selection); editor.commit(); reload(context); @@ -293,8 +292,8 @@ public final class PreferencesProvider { private static final int DEFAULT_COLS = 4; private static final int DEFAULT_ROWS = 7; - private static final String DEFAULT_PORTRAIT_DISPOSITION = "0x0:2x1|3x0:3x0|3x1:3x1|0x2:1x3|2x2:3x3|0x4:3x6"; - private static final String DEFAULT_LANDSCAPE_DISPOSITION = "0x0:2x3|3x0:5x1|6x0:6x0|6x1:6x1|3x2:4x2|3x3:4x3|5x2:6x3"; + public static final String DEFAULT_PORTRAIT_DISPOSITION = "0x0:2x1|0x2:1x3|0x4:3x6|2x2:3x3|3x0:3x0|3x1:3x1"; + public static final String DEFAULT_LANDSCAPE_DISPOSITION = "0x0:2x3|3x0:5x1|3x2:4x3|5x2:6x3|6x0:6x0|6x1:6x1"; /** * Method that returns the rows of the wallpaper. @@ -322,7 +321,24 @@ public final class PreferencesProvider { * @return List<Disposition> The photo frames dispositions */ public static List<Disposition> getPortraitDisposition() { - return toDispositions(getString("ui_layout_portrait_disposition", DEFAULT_PORTRAIT_DISPOSITION)); + return DispositionUtil.toDispositions( + getString("ui_layout_portrait_disposition", DEFAULT_PORTRAIT_DISPOSITION)); + } + + /** + * Sets the disposition of the photo frames in the wallpaper on landscape screen. + * + * @param context The current context + * @param dispositions The photo frames dispositions + */ + public static void setPortraitDisposition(Context context, List<Disposition> dispositions) { + SharedPreferences preferences = + context.getSharedPreferences(PREFERENCES_FILE, Context.MODE_PRIVATE); + Editor editor = preferences.edit(); + editor.putString("ui_layout_portrait_disposition", + DispositionUtil.fromDispositions(dispositions)); + editor.commit(); + reload(context); } /** @@ -333,30 +349,24 @@ public final class PreferencesProvider { * @return List<Disposition> The photo frames dispositions */ public static List<Disposition> getLandscapeDisposition() { - return toDispositions(getString("ui_layout_landscape_disposition", DEFAULT_LANDSCAPE_DISPOSITION)); + return DispositionUtil.toDispositions( + getString("ui_layout_landscape_disposition", DEFAULT_LANDSCAPE_DISPOSITION)); } /** - * Method that converts to dispositions reference + * Sets the disposition of the photo frames in the wallpaper on landscape screen. * - * @param value The value to convert - * @return List<Disposition> The dispositions reference + * @param context The current context + * @param dispositions The photo frames dispositions */ - private static List<Disposition> toDispositions(String value) { - String[] v = value.split("\\|"); - List<Disposition> dispositions = new ArrayList<Disposition>(v.length); - for (String s : v) { - String[] s1 = s.split(":"); - String[] s2 = s1[0].split("x"); - String[] s3 = s1[1].split("x"); - Disposition disposition = new Disposition(); - disposition.x = Integer.parseInt(s2[0]); - disposition.y = Integer.parseInt(s2[1]); - disposition.w = Integer.parseInt(s3[0]) - disposition.x + 1; - disposition.h = Integer.parseInt(s3[1]) - disposition.y + 1; - dispositions.add(disposition); - } - return dispositions; + public static void setLandscapeDisposition(Context context, List<Disposition> dispositions) { + SharedPreferences preferences = + context.getSharedPreferences(PREFERENCES_FILE, Context.MODE_PRIVATE); + Editor editor = preferences.edit(); + editor.putString("ui_layout_landscape_disposition", + DispositionUtil.fromDispositions(dispositions)); + editor.commit(); + reload(context); } } diff --git a/src/org/cyanogenmod/wallpapers/photophase/utils/DispositionUtil.java b/src/org/cyanogenmod/wallpapers/photophase/utils/DispositionUtil.java new file mode 100644 index 0000000..ddd8d3a --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/utils/DispositionUtil.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2013 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 org.cyanogenmod.wallpapers.photophase.utils; + +import android.graphics.Rect; + +import org.cyanogenmod.wallpapers.photophase.model.Disposition; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * A helper class with disposition utils + */ +public final class DispositionUtil { + + /** + * Method that converts a disposition string to a disposition reference + * + * @param value The value to convert + * @return List<Disposition> The dispositions reference + */ + public static List<Disposition> toDispositions(String value) { + String[] v = value.split("\\|"); + List<Disposition> dispositions = new ArrayList<Disposition>(v.length); + for (String s : v) { + String[] s1 = s.split(":"); + String[] s2 = s1[0].split("x"); + String[] s3 = s1[1].split("x"); + Disposition disposition = new Disposition(); + disposition.x = Integer.parseInt(s2[0]); + disposition.y = Integer.parseInt(s2[1]); + disposition.w = Integer.parseInt(s3[0]) - disposition.x + 1; + disposition.h = Integer.parseInt(s3[1]) - disposition.y + 1; + dispositions.add(disposition); + } + Collections.sort(dispositions); + return dispositions; + } + + /** + * Method that converts a disposition reference to a disposition string + * + * @param dispositions The value to convert + * @return String The dispositions string + */ + public static String fromDispositions(List<Disposition> dispositions) { + Collections.sort(dispositions); + StringBuilder sb = new StringBuilder(); + int count = dispositions.size(); + for (int i = 0; i < count; i++) { + Disposition disposition = dispositions.get(i); + sb.append(disposition.x) + .append("x") + .append(disposition.y) + .append(":") + .append(disposition.x + disposition.w - 1) + .append("x") + .append(disposition.y + disposition.h - 1); + if (i < (count - 1)) { + sb.append("|"); + } + } + return sb.toString(); + } + + /** + * Method that transform the disposition to a byte matrix + * + * @param dispositions The + * @return byte[][] The boolean matrix of the disposition + */ + public static byte[][] toMatrix(List<Disposition> dispositions, int cols, int rows) { + byte[][] matrix = new byte[rows][cols]; + for (Disposition disposition : dispositions) { + int count = disposition.y + disposition.h; + for (int row = disposition.y; row < count; row++) { + int count2 = disposition.x + disposition.w; + for (int col = disposition.x; col < count2; col++) { + matrix[row][col] = 1; + } + } + } + return matrix; + } + + /** + * Method that returns a disposition from a {@link Rect} reference + * + * @return Disposition The disposition + */ + public static Disposition fromRect(Rect r) { + Disposition disposition = new Disposition(); + disposition.x = r.left; + disposition.y = r.top; + disposition.w = r.width(); + disposition.h = r.height(); + return disposition; + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/utils/MERAlgorithm.java b/src/org/cyanogenmod/wallpapers/photophase/utils/MERAlgorithm.java new file mode 100644 index 0000000..bd29235 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/utils/MERAlgorithm.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2013 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 org.cyanogenmod.wallpapers.photophase.utils; + +import android.graphics.Rect; +import java.util.Stack; + +/** + * The maximal empty rectangle algorithm that allows to find the rectangle with the maximal + * area that could be create in empty areas (in this case 0 in a byte matrix) + */ +public final class MERAlgorithm { + + /** + * Method that returns the maximal empty rectangle (MER) for a matrix of bytes (1/0) + * + * @param matrix The matrix + * @return Rect The maximal empty rectangle + */ + public static Rect getMaximalEmptyRectangle(byte[][] matrix) { + // Check matrix + int rows = matrix.length; + if (rows == 0) return null; + + // Convert to histogram + int[][] histogram = toHistogram(matrix); + + // Find the maximal area of every histogram + Rect maxRect = new Rect(); + for (int i = 0; i < rows; ++i) { + Rect rect = maximalRectangle(histogram[i], i); + if ((maxRect.width() * maxRect.height()) < (rect.width() * rect.height())) { + maxRect = rect; + } + } + return maxRect; + } + + /** + * Method that returns the maximal rectangle for an histogram of areas + * + * @return Rect The maximal rectangle histogram/area + */ + @SuppressWarnings("boxing") + private static Rect maximalRectangle(int[] histogram, int row) { + Stack<Integer> stack = new Stack<Integer>(); + int length = histogram.length; + Rect maxRect = new Rect(); + int i = 0; + while (i < length) { + if (stack.isEmpty() || histogram[i] >= histogram[stack.peek()]) { + stack.push(i++); + } else { + Rect rect = new Rect(); + rect.left = stack.pop(); + rect.right = rect.left + (stack.isEmpty() ? i : (i - stack.peek() - 1)); + rect.top = row - histogram[rect.left] + 1; + rect.bottom = rect.top + histogram[rect.left]; + if ((maxRect.width() * maxRect.height()) < (rect.width() * rect.height())) { + maxRect = rect; + } + } + } + while (!stack.isEmpty()) { + Rect rect = new Rect(); + rect.left = stack.pop(); + rect.right = rect.left + (stack.isEmpty() ? i : (i - stack.peek() - 1)); + rect.top = row - histogram[rect.left] + 1; + rect.bottom = rect.top + histogram[rect.left]; + if ((maxRect.width() * maxRect.height()) < (rect.width() * rect.height())) { + maxRect = rect; + } + } + return maxRect; + } + + /** + * Method that converts the empty areas to a histogram + * + * @param matrix The matrix where to find the MER + * return int[][] The histogram of empty areas + */ + private static int[][] toHistogram(byte[][] matrix) { + int rows = matrix.length; + int cols = matrix[0].length; + int[][] histogram = new int[rows][cols]; + for (int h=0; h < cols; h++) { + if (matrix[0][h] == 0) { + histogram[0][h] = 1; + } + } + for (int w=1; w < rows; w++) { + for (int h=0; h < cols; h++) { + if (matrix[w][h] == 1) { + continue; + } + histogram[w][h] = histogram[w-1][h] + 1; + } + } + return histogram; + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/widgets/DispositionView.java b/src/org/cyanogenmod/wallpapers/photophase/widgets/DispositionView.java index 8ee1973..55ff0b3 100644 --- a/src/org/cyanogenmod/wallpapers/photophase/widgets/DispositionView.java +++ b/src/org/cyanogenmod/wallpapers/photophase/widgets/DispositionView.java @@ -16,6 +16,11 @@ package org.cyanogenmod.wallpapers.photophase.widgets; +import android.animation.Animator; +import android.animation.Animator.AnimatorListener; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Rect; import android.os.Vibrator; @@ -23,15 +28,24 @@ import android.util.AttributeSet; import android.view.Gravity; import android.view.View; import android.view.View.OnLongClickListener; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.RelativeLayout; +import android.widget.Toast; import android.widget.ImageView.ScaleType; import org.cyanogenmod.wallpapers.photophase.R; +import org.cyanogenmod.wallpapers.photophase.animations.Evaluators; import org.cyanogenmod.wallpapers.photophase.model.Disposition; +import org.cyanogenmod.wallpapers.photophase.utils.DispositionUtil; +import org.cyanogenmod.wallpapers.photophase.utils.MERAlgorithm; import org.cyanogenmod.wallpapers.photophase.widgets.ResizeFrame.OnResizeListener; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** @@ -39,15 +53,34 @@ import java.util.List; */ public class DispositionView extends RelativeLayout implements OnLongClickListener, OnResizeListener { + /** + * An interface to communicate the selection/unselection of a frame + */ + public interface OnFrameSelectedListener { + /** + * Invoked when a frame is selected + * + * @param v The frame view selected + */ + void onFrameSelectedListener(View v); + /** + * Invoked when a frame is unselected + */ + void onFrameUnselectedListener(); + } + + private boolean mChanged; private List<Disposition> mDispositions; private int mCols; private int mRows; - private View mTarget; + /*package*/ View mTarget; private ResizeFrame mResizeFrame; private int mInternalPadding; private Rect mOldResizeFrameLocation; + private OnFrameSelectedListener mOnFrameSelectedListener; + private Vibrator mVibrator; /** @@ -95,6 +128,15 @@ public class DispositionView extends RelativeLayout implements OnLongClickListen } /** + * Method that returns the dispositions drawn on this view + * + * @return List<Disposition> The dispositions drawn + */ + public List<Disposition> getDispositions() { + return mDispositions; + } + + /** * Method that sets the disposition to draw on this view * * @param dispositions The dispositions to draw @@ -105,10 +147,9 @@ public class DispositionView extends RelativeLayout implements OnLongClickListen mRows = rows; // Remove all the current views and add the new ones - removeAllViews(); - for (Disposition disposition : mDispositions) { - createFrame(getLocationFromDisposition(disposition)); - } + recreateDispositions(); + mResizeFrame.setVisibility(View.GONE); + mChanged = false; } /** @@ -122,22 +163,211 @@ public class DispositionView extends RelativeLayout implements OnLongClickListen } /** + * Method that set the listener for listen frame selection/unselection events + * + * @param onFrameSelectedListener The callback + */ + public void setOnFrameSelectedListener(OnFrameSelectedListener onFrameSelectedListener) { + this.mOnFrameSelectedListener = onFrameSelectedListener; + } + + /** + * Method that returns if the view was changed + * + * @return boolean true if the view was changed + */ + public boolean isChanged() { + return mChanged; + } + + /** + * Method that recreates all the dispositions + */ + private void recreateDispositions() { + // Remove all the current views and add the new ones + removeAllViews(); + for (Disposition disposition : mDispositions) { + createFrame(getLocationFromDisposition(disposition), false); + } + mOldResizeFrameLocation = null; + mTarget = null; + if (mOnFrameSelectedListener != null) { + mOnFrameSelectedListener.onFrameUnselectedListener(); + } + } + + /** + * Method that request the deletion of the current selected frame + */ + @SuppressWarnings("boxing") + public void deleteCurrentFrame() { + if (mTarget == null) return; + + final Disposition targetDisposition = resizerToDisposition(); + + // Get valid dispositions to move + final List<Disposition> adjacents = findAdjacentsDispositions(targetDisposition); + if (adjacents == null) { + // Nothing to do + Toast.makeText(getContext(), + R.string.pref_disposition_unable_delete_advise, Toast.LENGTH_SHORT).show(); + return; + } + + // Hide resizer + mResizeFrame.setVisibility(View.GONE); + + // Animate adjacents views + List<Animator> animators = new ArrayList<Animator>(); + animators.add(ObjectAnimator.ofFloat(mTarget, "scaleX", 1.0f, 0.0f)); + animators.add(ObjectAnimator.ofFloat(mTarget, "scaleY", 1.0f, 0.0f)); + + Disposition first = null; + for (Disposition adjacent : adjacents) { + // Extract the view and remove from dispositions + View v = findViewFromRect(getLocationFromDisposition(adjacent)); + mDispositions.remove(adjacent); + + // Clone first disposition + if (first == null) { + first = new Disposition(); + first.x = adjacent.x; + first.y = adjacent.y; + first.w = adjacent.w; + first.h = adjacent.h; + } + + // Add animators and fix the adjacent + if (v != null) { + if (first.x < targetDisposition.x) { + // From Left to Right + int width = mTarget.getWidth() + mInternalPadding; + animators.add(ValueAnimator.ofObject( + new Evaluators.WidthEvaluator(v), v.getWidth(), v.getWidth() + width)); + + // Update the adjacent + adjacent.w += targetDisposition.w; + mDispositions.add(adjacent); + + } else if (first.x > targetDisposition.x) { + // From Right to Left + int width = mTarget.getWidth() + mInternalPadding; + animators.add(ValueAnimator.ofObject( + new Evaluators.WidthEvaluator(v), v.getWidth(), v.getWidth() + width)); + animators.add(ObjectAnimator.ofFloat(v, "x", v.getX(), mTarget.getX())); + + // Update the adjacent + adjacent.x = targetDisposition.x; + adjacent.w += targetDisposition.w; + mDispositions.add(adjacent); + + } else if (first.y < targetDisposition.y) { + // From Top to Bottom + int height = mTarget.getHeight() + mInternalPadding; + animators.add(ValueAnimator.ofObject( + new Evaluators.HeightEvaluator(v), v.getHeight(), v.getHeight() + height)); + + // Update the adjacent + adjacent.h += targetDisposition.h; + mDispositions.add(adjacent); + + } else if (first.y > targetDisposition.y) { + // From Bottom to Top + int height = mTarget.getHeight() + mInternalPadding; + animators.add(ValueAnimator.ofObject( + new Evaluators.HeightEvaluator(v), v.getHeight(), v.getHeight() + height)); + animators.add(ObjectAnimator.ofFloat(v, "y", v.getY(), mTarget.getY())); + + // Update the adjacent + adjacent.x = targetDisposition.x; + adjacent.w += targetDisposition.w; + mDispositions.add(adjacent); + } + } + } + if (animators.size() > 0) { + AnimatorSet animSet = new AnimatorSet(); + animSet.playTogether(animators); + animSet.setDuration(getResources().getInteger(R.integer.disposition_hide_anim)); + animSet.setInterpolator(new AccelerateInterpolator()); + animSet.addListener(new AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { + // Ignore + } + @Override + public void onAnimationRepeat(Animator animation) { + // Ignore + } + @Override + public void onAnimationEnd(Animator animation) { + finishDeleteAnimation(targetDisposition); + } + + @Override + public void onAnimationCancel(Animator animation) { + finishDeleteAnimation(targetDisposition); + } + }); + animSet.start(); + } + } + + /** + * Method that finalizes the delete animation + * + * @param target The disposition target + */ + /*package*/ void finishDeleteAnimation(Disposition target) { + removeView(mTarget); + mDispositions.remove(target); + Collections.sort(mDispositions); + + // Clean status + mOldResizeFrameLocation = null; + mTarget = null; + if (mOnFrameSelectedListener != null) { + mOnFrameSelectedListener.onFrameUnselectedListener(); + } + } + + /** * Method that create a new frame to be drawn in the specified location * * @param r The location relative to the parent layout + * @return v The new view */ - private void createFrame(Rect r) { + private View createFrame(Rect r, boolean animate) { int padding = (int)getResources().getDimension(R.dimen.disposition_frame_padding); - ImageView v = new ImageView(getContext()); + final ImageView v = new ImageView(getContext()); v.setImageResource(R.drawable.ic_camera); v.setScaleType(ScaleType.CENTER); v.setBackgroundColor(getResources().getColor(R.color.disposition_frame_bg_color)); RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(r.width() - padding, r.height() - padding); - params.leftMargin = r.left + padding; - params.topMargin = r.top + padding; + v.setX(r.left + padding); + v.setY(r.top + padding); v.setOnLongClickListener(this); + if (animate) { + v.setVisibility(View.INVISIBLE); + } addView(v, params); + + // Animate the view + if (animate) { + post(new Runnable() { + @Override + public void run() { + Animation anim = AnimationUtils.loadAnimation( + getContext(), R.anim.display_with_bounce); + anim.setFillBefore(true); + anim.setFillAfter(true); + v.startAnimation(anim); + } + }); + } + + return v; } /** @@ -165,26 +395,8 @@ public class DispositionView extends RelativeLayout implements OnLongClickListen */ @Override public boolean onLongClick(View v) { - // Do not do long click if we do not have a target -// if (mTarget != null) return false; - - // Show the resize frame view just in place of the current clicked view - mResizeFrame.hide(); - RelativeLayout.LayoutParams viewParams = - (RelativeLayout.LayoutParams)v.getLayoutParams(); - FrameLayout.LayoutParams frameParams = - (FrameLayout.LayoutParams)mResizeFrame.getLayoutParams(); - int padding = mInternalPadding + mResizeFrame.getNeededPadding(); - frameParams.width = viewParams.width + (padding * 2); - frameParams.height = viewParams.height + (padding * 2); - mResizeFrame.setX(v.getLeft() - padding); - mResizeFrame.setY(v.getTop() - padding); + if (!selectTarget(v)) return false; mVibrator.vibrate(300); - mResizeFrame.show(); - - // Save the new view - mTarget = v; - return true; } @@ -248,63 +460,29 @@ public class DispositionView extends RelativeLayout implements OnLongClickListen mResizeFrame.setLayoutParams(params); } + /** + * {@inheritDoc} + */ @Override - public void onEndResize(int mode) { - try { -// int w = getMeasuredWidth(); -// int h = getMeasuredHeight(); -// int cw = w / mCols; -// int ch = h / mRows; -// -// // Retrieve the new layout params -// int neededPadding = mResizeFrame.getNeededPadding(); -// int padding = (int)getResources().getDimension(R.dimen.disposition_frame_padding) -// + neededPadding; -// FrameLayout.LayoutParams params = -// (FrameLayout.LayoutParams)mResizeFrame.getLayoutParams(); -// switch (mode) { -// case Gravity.LEFT: -// int left = params.leftMargin + padding; -// if (left % cw != 0) { -// params.leftMargin = ((left / cw) * cw) - padding; -//// params.width += ((left / cw) * cw) - left + (padding * 2); -// } -// break; -// case Gravity.RIGHT: -// int right = params.rightMargin + padding; -// if (right % cw != 0) { -// params.rightMargin = ((right / cw) * cw) - padding; -//// params.width += ((right / cw) * cw) - right + (padding * 2); -// } -// break; -// case Gravity.TOP: -// int top = params.topMargin + padding; -// if (top % ch != 0) { -// params.topMargin = ((top / ch) * ch) - padding; -//// params.height += ((top / cw) * cw) - top + (padding * 2); -// } -// break; -// case Gravity.BOTTOM: -// int bottom = params.bottomMargin + padding; -// if (bottom % ch != 0) { -// params.bottomMargin = ((bottom / ch) * ch) - padding; -//// params.height += ((bottom / cw) * cw) - bottom + (padding * 2); -// } -// break; -// -// default: -// break; -// } -// mResizeFrame.setLayoutParams(params); -// mResizeFrame.invalidate(); - - // Recalculate all the dispositions in base to the new positions - - } finally { - // Reset vars -// mOldResizeFrameLocation = null; -// mTarget = null; - } + public void onEndResize(final int mode) { + if (mTarget == null) return; + + // Compute the removed dispositions + computeRemovedDispositions(mode); + recreateDispositions(); + computeNewDispositions(mode); + + // Finish resize (select the target and create the new dispositions) + post(new Runnable() { + @Override + public void run() { + // Select the target + View v = findTargetFromResizeFrame(); + if (v != null) { + selectTarget(v); + } + } + }); } /** @@ -318,7 +496,260 @@ public class DispositionView extends RelativeLayout implements OnLongClickListen mTarget.setTop(mOldResizeFrameLocation.top); mTarget.setBottom(mOldResizeFrameLocation.bottom); } -// mOldResizeFrameLocation = null; -// mTarget = null; + mOldResizeFrameLocation = null; + mTarget = null; + if (mOnFrameSelectedListener != null) { + mOnFrameSelectedListener.onFrameUnselectedListener(); + } + } + + /** + * Method that returns the target view for the current resize frame + * + * @return The target view + */ + /*package*/ View findTargetFromResizeFrame() { + int count = getChildCount(); + for (int i = 0; i < count; i++) { + View v = getChildAt(i); + if (v.getX() < (mResizeFrame.getX() + (mResizeFrame.getWidth() / 2)) && + (v.getX() + v.getWidth()) > (mResizeFrame.getX() + (mResizeFrame.getWidth() / 2)) && + v.getY() < (mResizeFrame.getY() + (mResizeFrame.getHeight() / 2)) && + (v.getY() + v.getHeight()) > (mResizeFrame.getY() + (mResizeFrame.getHeight() / 2))) { + return v; + } + } + return null; + } + + /** + * Method that returns the view under the rect + * + * @return The view + */ + private View findViewFromRect(Rect r) { + int count = getChildCount(); + for (int i = 0; i < count; i++) { + View v = getChildAt(i); + if (v.getX() < (r.left + (r.width() / 2)) && + (v.getX() + v.getWidth()) > (r.left + (r.width() / 2)) && + v.getY() < (r.top + (r.height() / 2)) && + (v.getY() + v.getHeight()) > (r.top + (r.height() / 2))) { + return v; + } + } + return null; + } + + /** + * Method that select a view as the target of to resize + * + * @param v The target view + */ + /*package*/ boolean selectTarget(View v) { + //Do not do long click if we do not have a target + if (mTarget != null && v.equals(mTarget)) return false; + + // Show the resize frame view just in place of the current clicked view + mResizeFrame.hide(); + FrameLayout.LayoutParams frameParams = + (FrameLayout.LayoutParams)mResizeFrame.getLayoutParams(); + int padding = mInternalPadding + mResizeFrame.getNeededPadding(); + frameParams.width = v.getWidth() + (padding * 2); + frameParams.height = v.getHeight() + (padding * 2); + mResizeFrame.setX(v.getX() - padding); + mResizeFrame.setY(v.getY() - padding); + mResizeFrame.show(); + + // Save the new view + mTarget = v; + if (mOnFrameSelectedListener != null) { + mOnFrameSelectedListener.onFrameSelectedListener(v); + } + return true; + } + + /** + * Computes the removed layout disposition based on the actual resize frame + * + * @param mode The resize mode + */ + private void computeRemovedDispositions(int mode) { + // Transform the resizer to a dispositions object + Disposition resizer = resizerToDisposition(); + + // Delete all overlapped + int count = mDispositions.size(); + for (int i = count - 1; i >= 0; i--) { + Disposition disposition = mDispositions.get(i); + if (!isVisible(disposition) || isOverlapped(resizer, disposition)) { + mDispositions.remove(disposition); + } + } + + // Add the new disposition + mDispositions.add(resizer); + Collections.sort(mDispositions); + + mChanged = true; + } + + /** + * Computes the new layout disposition based on the actual resize frame + * + * @param mode The resize mode + */ + private void computeNewDispositions(int mode) { + // Fill the empty areas + do { + byte[][] dispositionMatrix = DispositionUtil.toMatrix(mDispositions, mCols, mRows); + Rect rect = MERAlgorithm.getMaximalEmptyRectangle(dispositionMatrix); + if (rect.width() == 0 && rect.height() == 0) { + // No more empty areas + break; + } + Disposition disposition = DispositionUtil.fromRect(rect); + createFrame(getLocationFromDisposition(disposition), true); + mDispositions.add(disposition); + } while (true); + + // Now the view was changed and should be reported + Collections.sort(mDispositions); + mChanged = true; + } + + /** + * Method that converts the resize frame to a dispostion reference + * + * @return Disposition The disposition reference + */ + private Disposition resizerToDisposition() { + int w = getMeasuredWidth() - (getPaddingLeft() + getPaddingRight()); + int h = getMeasuredHeight() - (getPaddingTop() + getPaddingBottom()); + int cw = w / mCols; + int ch = h / mRows; + + //Remove overlapped areas + Disposition resizer = new Disposition(); + resizer.x = Math.round(mResizeFrame.getX() / cw); + resizer.y = Math.round(mResizeFrame.getY() / ch); + resizer.w = Math.round(mResizeFrame.getWidth() / cw); + resizer.h = Math.round(mResizeFrame.getHeight() / ch); + return resizer; + } + + /** + * Method that returns all dispositions that matched exactly (in one side) with + * the argument disposition. + * + * @param disposition The disposition to check + */ + private List<Disposition> findAdjacentsDispositions(Disposition disposition) { + if (mDispositions.size() <= 1) return null; + + // Check left size + if (disposition.x != 0) { + List<Disposition> dispositions = new ArrayList<Disposition>(); + for (Disposition d : mDispositions) { + if (d.compareTo(disposition) != 0) { + if ((d.x + d.w) == disposition.x && + (d.y >= disposition.y) && ((d.y + d.h) <= (disposition.y + disposition.h))) { + dispositions.add(d); + } + } + } + // Check if the sum of all the dispositions matches the disposition + int sum = 0; + for (Disposition d : dispositions) { + sum += d.h; + } + if (sum == disposition.h) { + return dispositions; + } + } + // Check top size + if (disposition.y != 0) { + List<Disposition> dispositions = new ArrayList<Disposition>(); + for (Disposition d : mDispositions) { + if (d.compareTo(disposition) != 0) { + if ((d.y + d.h) == disposition.y && + (d.x >= disposition.x) && ((d.x + d.w) <= (disposition.x + disposition.w))) { + dispositions.add(d); + } + } + } + // Check if the sum of all the dispositions matches the disposition + int sum = 0; + for (Disposition d : dispositions) { + sum += d.w; + } + if (sum == disposition.w) { + return dispositions; + } + } + // Check right size + if ((disposition.x + disposition.w) != mCols) { + List<Disposition> dispositions = new ArrayList<Disposition>(); + for (Disposition d : mDispositions) { + if (d.compareTo(disposition) != 0) { + if ((d.x) == (disposition.x + disposition.w) && + (d.y >= disposition.y) && ((d.y + d.h) <= (disposition.y + disposition.h))) { + dispositions.add(d); + } + } + } + // Check if the sum of all the dispositions matches the disposition + int sum = 0; + for (Disposition d : dispositions) { + sum += d.h; + } + if (sum == disposition.h) { + return dispositions; + } + } + // Check bottom size + if ((disposition.y + disposition.h) != mRows) { + List<Disposition> dispositions = new ArrayList<Disposition>(); + for (Disposition d : mDispositions) { + if (d.compareTo(disposition) != 0) { + if ((d.y) == (disposition.y + disposition.h) && + (d.x >= disposition.x) && ((d.x + d.w) <= (disposition.x + disposition.w))) { + dispositions.add(d); + } + } + } + // Check if the sum of all the dispositions matches the disposition + int sum = 0; + for (Disposition d : dispositions) { + sum += d.w; + } + if (sum == disposition.w) { + return dispositions; + } + } + return null; + } + + /** + * Method that checks if a dispositions overlaps another other disposition + * + * @param d1 One disposition + * @param d2 Another disposition + * @return boolean true if d1 overlaps d2 + */ + private static boolean isOverlapped(Disposition d1, Disposition d2) { + Rect r1 = new Rect(d1.x, d1.y, d1.x + d1.w, d1.y + d1.h); + Rect r2 = new Rect(d2.x, d2.y, d2.x + d2.w, d2.y + d2.h); + return r1.intersect(r2); + } + + /** + * Method that checks if a dispositions is visible + * + * @param d The disposition to check + * @return boolean true if d is visible + */ + private static boolean isVisible(Disposition d) { + return d.w > 0 && d.h > 0; } } |