/* * Copyright (C) 2015 Jorge Ruesga * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.ruesga.android.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.annotation.SuppressLint; import android.content.Context; import android.content.DialogInterface; import android.graphics.Rect; import android.support.v4.content.ContextCompat; import android.support.v7.app.AlertDialog; import android.support.v7.widget.SwitchCompat; import android.util.AttributeSet; import android.view.Gravity; import android.view.HapticFeedbackConstants; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; import android.view.ViewGroup; import android.view.animation.AccelerateInterpolator; import android.view.animation.BounceInterpolator; import android.widget.CompoundButton; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.ImageView.ScaleType; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.Toast; import com.ruesga.android.wallpapers.photophase.R; import com.ruesga.android.wallpapers.photophase.model.Disposition; import com.ruesga.android.wallpapers.photophase.model.Dispositions; import com.ruesga.android.wallpapers.photophase.utils.DispositionUtil; import com.ruesga.android.wallpapers.photophase.utils.Evaluators; import com.ruesga.android.wallpapers.photophase.utils.MERAlgorithm; import com.ruesga.android.wallpapers.photophase.widgets.ResizeFrame.OnResizeListener; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * A class that allow to select the frames disposition visually */ public class DispositionView extends RelativeLayout implements OnClickListener, 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 boolean mEditable = false; private boolean mSaved = false; private View mTarget; private ResizeFrame mResizeFrame; private int mInternalPadding; private Rect mOldResizeFrameLocation; private OnFrameSelectedListener mOnFrameSelectedListener; /** * Constructor of <code>DispositionView</code>. * * @param context The current context */ public DispositionView(Context context) { super(context); init(); } /** * Constructor of <code>DispositionView</code>. * * @param context The current context * @param attrs The attributes of the XML tag that is inflating the view. */ public DispositionView(Context context, AttributeSet attrs) { super(context, attrs); init(); } /** * Constructor of <code>DispositionView</code>. * * @param context The current context * @param attrs The attributes of the XML tag that is inflating the view. * @param defStyle The default style to apply to this view. If 0, no style * will be applied (beyond what is included in the theme). This may * either be an attribute resource, whose value will be retrieved * from the current theme, or an explicit style resource. */ public DispositionView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } /** * Initialize the view */ private void init() { mInternalPadding = (int)getResources().getDimension(R.dimen.disposition_frame_padding); } /** * 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 * @param animate If should animate the view */ public void setDispositions(Dispositions dispositions, boolean animate) { setDispositions(dispositions.getDispositions(), dispositions.getCols(), dispositions.getRows(), animate); } public boolean isEditable() { return mEditable; } public void setEditable(boolean editable) { mEditable = editable; } public boolean isSaved() { return mSaved; } public void setSaved(boolean saved) { mSaved = saved; } /** * Method that sets the disposition to draw on this view * * @param dispositions The dispositions to draw * @param cols The number of columns * @param rows The number of rows * @param animate If should animate the view */ public void setDispositions(List<Disposition> dispositions, int cols, int rows, boolean animate) { mDispositions = dispositions; mCols = cols; mRows = rows; // Remove all the current views and add the new ones recreateDispositions(animate); if (mResizeFrame != null) { mResizeFrame.setVisibility(View.GONE); } mChanged = false; } /** * Method that sets the resize frame view * * @param resizeFrame The resize frame view */ public void setResizeFrame(ResizeFrame resizeFrame) { mResizeFrame = resizeFrame; mResizeFrame.setOnResizeListener(this); } /** * 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 * * @param animate If the recreate should be done with an animation */ private void recreateDispositions(boolean animate) { // Remove all the current views and add the new ones removeAllViews(); for (Disposition disposition : mDispositions) { createFrame(disposition, getLocationFromDisposition(disposition), animate); } mOldResizeFrameLocation = null; mTarget = null; if (mOnFrameSelectedListener != null) { mOnFrameSelectedListener.onFrameUnselectedListener(); } } public void deselectCurrentFrame() { mTarget = null; if (mResizeFrame != null) { mResizeFrame.setVisibility(View.GONE); } } /** * Method that request the deletion of the current selected frame */ @SuppressWarnings("boxing") public void deleteCurrentFrame() { if (mTarget == null) return; if (mResizeFrame == 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 resize rubber mResizeFrame.setVisibility(View.GONE); // Animate adjacent views List<Animator> animators = new ArrayList<>(); 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.y = targetDisposition.y; adjacent.h += targetDisposition.h; 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 */ private void finishDeleteAnimation(Disposition target) { removeView(mTarget); mDispositions.remove(target); Collections.sort(mDispositions); mChanged = true; // 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 View createFrame(Disposition disposition, Rect r, boolean animate) { int padding = (int)getResources().getDimension(R.dimen.disposition_frame_padding); final FrameLayout v = new FrameLayout(getContext()); ViewGroup.LayoutParams params = new RelativeLayout.LayoutParams(r.width() - padding, r.height() - padding); v.setX(r.left + padding); v.setY(r.top + padding); if (mEditable) { v.setOnClickListener(this); v.setOnLongClickListener(this); } v.setTag(disposition.uid); addView(v, params); // Image final ImageView image = new ImageView(getContext()); image.setImageResource(mEditable ? R.drawable.ic_settings : R.drawable.ic_photo); image.setScaleType(ScaleType.CENTER); image.setBackgroundColor(ContextCompat.getColor(getContext(), mResizeFrame == null ? mSaved ? R.color.disposition_saved_frame_bg_color : R.color.disposition_locked_frame_bg_color : R.color.disposition_frame_bg_color)); v.addView(image); // Flags toolbar final LinearLayout toolbar = new LinearLayout(getContext()); FrameLayout.LayoutParams toolbarParams = new FrameLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); toolbarParams.gravity = Gravity.BOTTOM | Gravity.START; v.addView(toolbar, toolbarParams); int flagDimen = (int) getResources().getDimension(R.dimen.frame_settings_flag_size); padding = (int) getResources().getDimension(R.dimen.frame_settings_flag_padding); LinearLayout.LayoutParams flagParams = new LinearLayout.LayoutParams(flagDimen, flagDimen); flagParams.setMargins(padding, padding, padding, padding); if (!disposition.hasFlag(Disposition.BACKGROUND_FLAG)) { toolbar.addView(createFrameSettingFlag(R.drawable.ic_background_off), flagParams); } else { if (!disposition.hasFlag(Disposition.TRANSITION_FLAG)) { toolbar.addView(createFrameSettingFlag(R.drawable.ic_pause), flagParams); } if (!disposition.hasFlag(Disposition.EFFECT_FLAG)) { toolbar.addView(createFrameSettingFlag(R.drawable.ic_effect_off), flagParams); } if (!disposition.hasFlag(Disposition.BORDER_FLAG)) { toolbar.addView(createFrameSettingFlag(R.drawable.ic_border_off), flagParams); } } // Animate the view if (animate) { List<Animator> animators = new ArrayList<>(); animators.add(ObjectAnimator.ofFloat(v, "scaleX", 0.0f, 1.0f)); animators.add(ObjectAnimator.ofFloat(v, "scaleY", 0.0f, 1.0f)); animators.add(ObjectAnimator.ofFloat(v, "alpha", 0.0f, 1.0f)); animators.add(ObjectAnimator.ofFloat(v, "alpha", 0.0f, 1.0f)); AnimatorSet animSet = new AnimatorSet(); animSet.playTogether(animators); animSet.setDuration(getResources().getInteger(R.integer.disposition_show_anim)); animSet.setInterpolator(new BounceInterpolator()); animSet.setTarget(v); animSet.start(); } return v; } /** * Method that returns the location of the frame from its disposition * * @param disposition The source disposition * @return Rect The location on parent view */ private Rect getLocationFromDisposition(Disposition disposition) { int w = getMeasuredWidth() - (getPaddingLeft() + getPaddingRight()); int h = getMeasuredHeight() - (getPaddingTop() + getPaddingBottom()); int cw = w / mCols; int ch = h / mRows; Rect location = new Rect(); location.left = disposition.x * cw; location.top = disposition.y * ch; location.right = location.left + disposition.w * cw; location.bottom = location.top + disposition.h * ch; return location; } /** * {@inheritDoc} */ @Override public void onClick(View v) { // if there is a frame selected then unselect it if (mResizeFrame != null && mResizeFrame.getVisibility() == View.VISIBLE) { mResizeFrame.hide(); mTarget = null; if (mOnFrameSelectedListener != null) { mOnFrameSelectedListener.onFrameUnselectedListener(); } } else { // Show settings? if (mEditable) { displayFrameSettings(v); } } } /** * {@inheritDoc} */ @Override public boolean onLongClick(View v) { if (mResizeFrame != null && selectTarget(v)) { performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING | HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); } return true; } @Override public void onStartResize(int mode) { if (mResizeFrame == null) return; mOldResizeFrameLocation = new Rect( mResizeFrame.getLeft(), mResizeFrame.getTop(), mResizeFrame.getRight(), mResizeFrame.getBottom()); } @Override @SuppressLint("RtlHardcoded") public void onResize(int mode, float delta) { if (mTarget == null) return; if (mResizeFrame == null) return; int w = getMeasuredWidth() - (getPaddingLeft() + getPaddingRight()); int h = getMeasuredHeight() - (getPaddingTop() + getPaddingBottom()); int minWidth = (w / mCols) + (w / mCols) / 2; int minHeight = (h / mRows) + (h / mRows) / 2; FrameLayout.LayoutParams params = (FrameLayout.LayoutParams)mResizeFrame.getLayoutParams(); switch (mode) { case Gravity.LEFT: float newpos = mResizeFrame.getX() + delta; if ((delta < 0 && newpos < (getPaddingLeft() * -1)) || (delta > 0 && newpos > (mResizeFrame.getX() + params.width - minWidth))) { return; } mResizeFrame.setX(newpos); params.width -= delta; break; case Gravity.RIGHT: if ((delta < 0 && ((params.width + delta) < minWidth)) || (delta > 0 && (mResizeFrame.getX() + delta + params.width) > (getPaddingLeft() + getMeasuredWidth()))) { return; } params.width += delta; break; case Gravity.TOP: newpos = mResizeFrame.getY() + delta; if ((delta < 0 && newpos < (getPaddingTop() * -1)) || (delta > 0 && newpos > (mResizeFrame.getY() + params.height - minHeight))) { return; } mResizeFrame.setY(newpos); params.height -= delta; break; case Gravity.BOTTOM: if ((delta < 0 && ((params.height + delta) < minHeight)) || (delta > 0 && (mResizeFrame.getY() + delta + params.height) > (getPaddingTop() + getMeasuredHeight()))) { return; } params.height += delta; break; default: break; } mResizeFrame.setLayoutParams(params); } /** * {@inheritDoc} */ @Override public void onEndResize(final int mode) { if (mTarget == null) return; if (mResizeFrame == null) return; // Compute the removed dispositions computeRemovedDispositions(); recreateDispositions(false); computeNewDispositions(); // 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); } } }); } /** * {@inheritDoc} */ @Override public void onCancel() { if (mOldResizeFrameLocation != null) { mTarget.setLeft(mOldResizeFrameLocation.left); mTarget.setRight(mOldResizeFrameLocation.right); mTarget.setTop(mOldResizeFrameLocation.top); mTarget.setBottom(mOldResizeFrameLocation.bottom); } mOldResizeFrameLocation = null; mTarget = null; if (mOnFrameSelectedListener != null) { mOnFrameSelectedListener.onFrameUnselectedListener(); } } /** * Method that returns the target view for the current resize frame * * @return The target view */ private 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 */ private boolean selectTarget(View v) { //Do not do long click if we do not have a target if (mTarget != null && v.equals(mTarget)) return false; if (mResizeFrame == null) 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 */ private void computeRemovedDispositions() { // Transform the resize rubber to a dispositions object Disposition resizeRubber = 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(resizeRubber, disposition)) { resizeRubber.uid = disposition.uid; resizeRubber.flags = disposition.flags; mDispositions.remove(disposition); } } // Add the new disposition mDispositions.add(resizeRubber); Collections.sort(mDispositions); mChanged = true; } /** * Computes the new layout disposition based on the actual resize frame */ private void computeNewDispositions() { // Fill the empty areas do { byte[][] dispositionMatrix = DispositionUtil.toMatrix(mDispositions, mCols, mRows); Rect rect = MERAlgorithm.getMaximalEmptyRectangle(dispositionMatrix); if (rect == null || rect.width() == 0 && rect.height() == 0) { // No more empty areas break; } Disposition disposition = DispositionUtil.fromRect(rect); createFrame(disposition, 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); // Fix disposition (limits) resizer.x = Math.max(resizer.x, 0); resizer.y = Math.max(resizer.y, 0); resizer.w = Math.min(resizer.w, mCols - resizer.x); resizer.h = Math.min(resizer.h, mRows - resizer.y); 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<>(); 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<>(); 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<>(); 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<>(); 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; } @SuppressLint("InflateParams") private void displayFrameSettings(final View v) { final String uid = (String) v.getTag(); if (uid == null) { return; } final Disposition disposition = fromUid(uid); if (disposition == null) { return; } View dialogView = LayoutInflater.from( getContext()).inflate(R.layout.frame_settings, null, false); final SwitchCompat background = (SwitchCompat) dialogView.findViewById(R.id.flag_background); final SwitchCompat transition = (SwitchCompat) dialogView.findViewById(R.id.flag_transition); final SwitchCompat effect = (SwitchCompat) dialogView.findViewById(R.id.flag_effect); final SwitchCompat border = (SwitchCompat) dialogView.findViewById(R.id.flag_border); background.setChecked(disposition.hasFlag(Disposition.BACKGROUND_FLAG)); transition.setChecked(disposition.hasFlag(Disposition.TRANSITION_FLAG)); effect.setChecked(disposition.hasFlag(Disposition.EFFECT_FLAG)); border.setChecked(disposition.hasFlag(Disposition.BORDER_FLAG)); transition.setEnabled(background.isChecked()); effect.setEnabled(background.isChecked()); border.setEnabled(background.isChecked()); background.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { transition.setEnabled(background.isChecked()); effect.setEnabled(background.isChecked()); border.setEnabled(background.isChecked()); } }); AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); builder.setTitle(R.string.frame_settings_dialog_title); builder.setView(dialogView); builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { if (background.isChecked()) { disposition.addFlag(Disposition.BACKGROUND_FLAG); } else { disposition.removeFlag(Disposition.BACKGROUND_FLAG); } if (transition.isChecked()) { disposition.addFlag(Disposition.TRANSITION_FLAG); } else { disposition.removeFlag(Disposition.TRANSITION_FLAG); } if (effect.isChecked()) { disposition.addFlag(Disposition.EFFECT_FLAG); } else { disposition.removeFlag(Disposition.EFFECT_FLAG); } if (border.isChecked()) { disposition.addFlag(Disposition.BORDER_FLAG); } else { disposition.removeFlag(Disposition.BORDER_FLAG); } // Redraw recreateDispositions(false); mChanged = true; } }); AlertDialog dialog = builder.create(); dialog.show(); } private Disposition fromUid(String uid) { for (Disposition disposition : mDispositions) { if (disposition.uid.equals(uid)) { return disposition; } } return null; } private ImageView createFrameSettingFlag(int imageResId) { final ImageView image = new ImageView(getContext()); image.setImageResource(imageResId); image.setScaleType(ScaleType.FIT_CENTER); return image; } }