aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--res/anim/display_with_bounce.xml34
-rw-r--r--res/drawable-hdpi/ic_delete.pngbin0 -> 1624 bytes
-rw-r--r--res/drawable-mdpi/ic_delete.pngbin0 -> 1359 bytes
-rw-r--r--res/drawable-xhdpi/ic_delete.pngbin0 -> 1848 bytes
-rw-r--r--res/menu/albums.xml (renamed from res/menu/accept_restore_preference.xml)2
-rw-r--r--res/menu/dispositions.xml30
-rw-r--r--res/values/integers.xml2
-rw-r--r--res/values/strings.xml2
-rw-r--r--src/org/cyanogenmod/wallpapers/photophase/Utils.java41
-rw-r--r--src/org/cyanogenmod/wallpapers/photophase/animations/Evaluators.java79
-rw-r--r--src/org/cyanogenmod/wallpapers/photophase/model/Disposition.java34
-rw-r--r--src/org/cyanogenmod/wallpapers/photophase/preferences/ChoosePicturesFragment.java4
-rw-r--r--src/org/cyanogenmod/wallpapers/photophase/preferences/DispositionFragment.java74
-rw-r--r--src/org/cyanogenmod/wallpapers/photophase/preferences/LandscapeDispositionFragment.java18
-rw-r--r--src/org/cyanogenmod/wallpapers/photophase/preferences/PortraitDispositionFragment.java18
-rw-r--r--src/org/cyanogenmod/wallpapers/photophase/preferences/PreferencesProvider.java58
-rw-r--r--src/org/cyanogenmod/wallpapers/photophase/utils/DispositionUtil.java115
-rw-r--r--src/org/cyanogenmod/wallpapers/photophase/utils/MERAlgorithm.java116
-rw-r--r--src/org/cyanogenmod/wallpapers/photophase/widgets/DispositionView.java603
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
new file mode 100644
index 0000000..e9ce89e
--- /dev/null
+++ b/res/drawable-hdpi/ic_delete.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_delete.png b/res/drawable-mdpi/ic_delete.png
new file mode 100644
index 0000000..cedb108
--- /dev/null
+++ b/res/drawable-mdpi/ic_delete.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_delete.png b/res/drawable-xhdpi/ic_delete.png
new file mode 100644
index 0000000..98c73da
--- /dev/null
+++ b/res/drawable-xhdpi/ic_delete.png
Binary files differ
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;
}
}