paulscode.android.mupen64plusae.profile.TouchscreenProfileActivity.java Source code

Java tutorial

Introduction

Here is the source code for paulscode.android.mupen64plusae.profile.TouchscreenProfileActivity.java

Source

/**
 * Mupen64PlusAE, an N64 emulator for the Android platform
 * 
 * Copyright (C) 2013 Paul Lamb
 * 
 * This file is part of Mupen64PlusAE.
 * 
 * Mupen64PlusAE is free software: you can redistribute it and/or modify it under the terms of the
 * GNU General Public License as published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * Mupen64PlusAE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along with Mupen64PlusAE. If
 * not, see <http://www.gnu.org/licenses/>.
 * 
 * Authors: littleguy77
 */
package paulscode.android.mupen64plusae.profile;

import java.io.File;

import org.apache.commons.lang.ArrayUtils;
import org.mupen64plusae.v3.alpha.R;

import paulscode.android.mupen64plusae.ActivityHelper;
import paulscode.android.mupen64plusae.MenuListView;
import paulscode.android.mupen64plusae.dialog.MenuDialogFragment;
import paulscode.android.mupen64plusae.dialog.Prompt;
import paulscode.android.mupen64plusae.dialog.MenuDialogFragment.OnDialogMenuItemSelectedListener;
import paulscode.android.mupen64plusae.dialog.Prompt.PromptFileListener;
import paulscode.android.mupen64plusae.dialog.SeekBarGroup;
import paulscode.android.mupen64plusae.game.GameOverlay;
import paulscode.android.mupen64plusae.input.AbstractController;
import paulscode.android.mupen64plusae.input.map.TouchMap;
import paulscode.android.mupen64plusae.input.map.VisibleTouchMap;
import paulscode.android.mupen64plusae.persistent.AppData;
import paulscode.android.mupen64plusae.persistent.ConfigFile;
import paulscode.android.mupen64plusae.persistent.ConfigFile.ConfigSection;
import paulscode.android.mupen64plusae.persistent.GlobalPrefs;
import android.annotation.SuppressLint;
import android.app.AlertDialog.Builder;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Bundle;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.SparseArray;
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.SoundEffectConstants;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.WindowManager.LayoutParams;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.FrameLayout;
import android.widget.ImageView;

public class TouchscreenProfileActivity extends AppCompatActivity
        implements OnTouchListener, OnDialogMenuItemSelectedListener {
    private static final String TOUCHSCREEN_AUTOHOLDABLES = "touchscreenAutoHoldables";
    private static final String AUTOHOLDABLES_DELIMITER = "~";
    private static final String STATE_MENU_DIALOG_FRAGMENT = "STATE_MENU_DIALOG_FRAGMENT";

    private static final String ANALOG = "analog";
    private static final String DPAD = "dpad";
    private static final String GROUP_AB = "groupAB";
    private static final String BUTTON_A = "buttonA";
    private static final String BUTTON_B = "buttonB";
    private static final String GROUP_C = "groupC";
    private static final String BUTTON_L = "buttonL";
    private static final String BUTTON_R = "buttonR";
    private static final String BUTTON_Z = "buttonZ";
    private static final String BUTTON_S = "buttonS";
    private static final String BUTTON_SENSOR = "buttonSen";
    private static final String TAG_X = "-x";
    private static final String TAG_Y = "-y";
    private static final String SCALE = "-scale";

    public static final SparseArray<String> READABLE_NAMES = new SparseArray<String>();

    // The inital or disabled x/y position of an asset
    private static final int INITIAL_ASSET_POS = 50;
    private static final int DISABLED_ASSET_POS = -1;

    // Touchscreen profile objects
    private ConfigFile mConfigFile;
    private Profile mProfile;

    // User preferences wrapper
    private GlobalPrefs mGlobalPrefs;
    private AppData mAppData;

    // Visual elements
    private VisibleTouchMap mTouchscreenMap;
    private GameOverlay mOverlay;
    private ImageView mSurface;

    // Live drag and drop editing
    private int initialX;
    private int initialY;
    private int dragIndex;
    private boolean dragging;
    private String dragAsset;
    private int dragX;
    private int dragY;
    private Rect dragFrame;

    // The directory of the selected touchscreen skin.
    private String touchscreenSkin;

    // This is to prevent more than one popup appearing at once
    private boolean mPopupBeingShown;

    @SuppressLint("ClickableViewAccessibility")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Get the user preferences wrapper
        mAppData = new AppData(this);
        mGlobalPrefs = new GlobalPrefs(this, mAppData);
        mGlobalPrefs.enforceLocale(this);

        // Load the profile; fail fast if there are any programmer usage errors
        Bundle extras = getIntent().getExtras();
        if (extras == null)
            throw new Error("Invalid usage: bundle must indicate profile name");
        String name = extras.getString(ActivityHelper.Keys.PROFILE_NAME);
        if (TextUtils.isEmpty(name))
            throw new Error("Invalid usage: profile name cannot be null or empty");
        mConfigFile = new ConfigFile(mGlobalPrefs.touchscreenProfiles_cfg);
        ConfigSection section = mConfigFile.get(name);
        if (section == null)
            throw new Error("Invalid usage: profile name not found in config file");
        mProfile = new Profile(false, section);

        // Define the map from N64 button indices to readable button names
        READABLE_NAMES.put(AbstractController.DPD_R, getString(R.string.controller_dpad));
        READABLE_NAMES.put(AbstractController.DPD_L, getString(R.string.controller_dpad));
        READABLE_NAMES.put(AbstractController.DPD_D, getString(R.string.controller_dpad));
        READABLE_NAMES.put(AbstractController.DPD_U, getString(R.string.controller_dpad));
        READABLE_NAMES.put(AbstractController.START, getString(R.string.controller_buttonS));
        READABLE_NAMES.put(AbstractController.BTN_Z, getString(R.string.controller_buttonZ));
        READABLE_NAMES.put(AbstractController.BTN_B, getString(R.string.controller_buttonB));
        READABLE_NAMES.put(AbstractController.BTN_A, getString(R.string.controller_buttonA));
        READABLE_NAMES.put(AbstractController.CPD_R, getString(R.string.controller_buttonCr));
        READABLE_NAMES.put(AbstractController.CPD_L, getString(R.string.controller_buttonCl));
        READABLE_NAMES.put(AbstractController.CPD_D, getString(R.string.controller_buttonCd));
        READABLE_NAMES.put(AbstractController.CPD_U, getString(R.string.controller_buttonCu));
        READABLE_NAMES.put(AbstractController.BTN_R, getString(R.string.controller_buttonR));
        READABLE_NAMES.put(AbstractController.BTN_L, getString(R.string.controller_buttonL));
        READABLE_NAMES.put(TouchMap.DPD_LU, getString(R.string.controller_dpad));
        READABLE_NAMES.put(TouchMap.DPD_LD, getString(R.string.controller_dpad));
        READABLE_NAMES.put(TouchMap.DPD_RD, getString(R.string.controller_dpad));
        READABLE_NAMES.put(TouchMap.DPD_RU, getString(R.string.controller_dpad));
        READABLE_NAMES.put(TouchMap.TOGGLE_SENSOR, getString(R.string.controller_buttonSensor));

        // Enable full-screen mode
        getWindow().setFlags(LayoutParams.FLAG_FULLSCREEN, LayoutParams.FLAG_FULLSCREEN);

        // Lay out content and get the views
        setContentView(R.layout.touchscreen_profile_activity);
        mSurface = (ImageView) findViewById(R.id.gameSurface);
        mOverlay = (GameOverlay) findViewById(R.id.gameOverlay);

        String layout = mProfile.get("touchscreenSkin", "Outline");
        if (layout.equals("Custom"))
            touchscreenSkin = mProfile.get("touchscreenCustomSkinPath", "");
        else
            touchscreenSkin = mAppData.touchscreenSkinsDir + layout;

        // Initialize the touchmap and overlay
        mTouchscreenMap = new VisibleTouchMap(getResources());
        mOverlay.setOnTouchListener(this);

        mPopupBeingShown = false;
    }

    private void refresh() {
        // Reposition the assets and refresh the overlay and options menu
        boolean isTouchscreenAnimated = Boolean.valueOf(mProfile.get("touchscreenAnimated", "False"));
        mOverlay.initialize(mTouchscreenMap, true, mGlobalPrefs.isFpsEnabled, false, isTouchscreenAnimated);
        mTouchscreenMap.load(touchscreenSkin, mProfile, isTouchscreenAnimated, true, mGlobalPrefs.touchscreenScale,
                mGlobalPrefs.touchscreenTransparency);
        mOverlay.postInvalidate();
        invalidateOptionsMenu();
    }

    @Override
    protected void onResume() {
        super.onResume();

        // Refresh in case the global settings changed
        AppData appData = new AppData(this);
        mGlobalPrefs = new GlobalPrefs(this, appData);

        // Update the dummy GameSurface size in case global settings changed
        FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) mSurface.getLayoutParams();
        params.gravity = mGlobalPrefs.displayPosition | Gravity.CENTER_HORIZONTAL;
        mSurface.setLayoutParams(params);

        // Update the screen orientation in case global settings changed
        this.setRequestedOrientation(mGlobalPrefs.displayOrientation);

        // Refresh the touchscreen controls
        refresh();
    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        if (hasFocus) {
            hideSystemBars();
        }
    }

    @Override
    protected void onPause() {
        super.onPause();

        // Lazily persist the profile data; only need to do it on pause
        mProfile.writeTo(mConfigFile);
        mConfigFile.save();
    }

    @Override
    public void onPrepareMenuList(MenuListView listView) {
        Menu menu = listView.getMenu();
        setCheckState(menu, R.id.menuItem_analog, ANALOG);
        setCheckState(menu, R.id.menuItem_dpad, DPAD);

        if (mTouchscreenMap.isABSplit()) {
            UpdateButtonMenu(listView, R.id.menuItem_groupAB);
        } else {
            UpdateButtonMenu(listView, R.id.menuItem_buttonA);
            UpdateButtonMenu(listView, R.id.menuItem_buttonB);
        }

        setCheckState(menu, R.id.menuItem_buttonA, BUTTON_A);
        setCheckState(menu, R.id.menuItem_buttonB, BUTTON_B);
        setCheckState(menu, R.id.menuItem_groupAB, GROUP_AB);
        setCheckState(menu, R.id.menuItem_groupC, GROUP_C);
        setCheckState(menu, R.id.menuItem_buttonL, BUTTON_L);
        setCheckState(menu, R.id.menuItem_buttonR, BUTTON_R);
        setCheckState(menu, R.id.menuItem_buttonZ, BUTTON_Z);
        setCheckState(menu, R.id.menuItem_buttonS, BUTTON_S);
        setCheckState(menu, R.id.menuItem_buttonSensor, BUTTON_SENSOR);
    }

    private void UpdateButtonMenu(MenuListView listView, int menuItemId) {
        MenuItem buttonGroupItem = listView.getMenu().findItem(R.id.menuItem_buttons);

        if (buttonGroupItem != null && listView.getMenu().findItem(menuItemId) != null) {
            buttonGroupItem.getSubMenu().removeItem(menuItemId);
        }
    }

    @Override
    public void onDialogMenuItemSelected(int dialogId, MenuItem item) {
        switch (item.getItemId()) {
        case R.id.menuItem_globalSettings:
            ActivityHelper.startTouchscreenPrefsActivity(this);
            return;
        case R.id.menuItem_sensorConfiguration:
            new SensorConfigurationDialog(this, mProfile).show();
            return;
        case R.id.menuItem_exit:
            finish();
            return;
        case R.id.menuItem_analog:
            toggleAsset(ANALOG);
            return;
        case R.id.menuItem_dpad:
            toggleAsset(DPAD);
            return;
        case R.id.menuItem_groupAB:
            toggleAsset(GROUP_AB);
            return;
        case R.id.menuItem_buttonA:
            toggleAsset(BUTTON_A);
            return;
        case R.id.menuItem_buttonB:
            toggleAsset(BUTTON_B);
            return;
        case R.id.menuItem_groupC:
            toggleAsset(GROUP_C);
            return;
        case R.id.menuItem_buttonL:
            toggleAsset(BUTTON_L);
            return;
        case R.id.menuItem_buttonR:
            toggleAsset(BUTTON_R);
            return;
        case R.id.menuItem_buttonZ:
            toggleAsset(BUTTON_Z);
            return;
        case R.id.menuItem_buttonS:
            toggleAsset(BUTTON_S);
            return;
        case R.id.menuItem_buttonSensor:
            toggleAsset(BUTTON_SENSOR);
            return;
        case R.id.menuItem_outline:
            touchscreenSkin = mAppData.touchscreenSkinsDir + "Outline";
            mProfile.put("touchscreenSkin", "Outline");
            refresh();
            return;
        case R.id.menuItem_shaded:
            touchscreenSkin = mAppData.touchscreenSkinsDir + "Shaded";
            mProfile.put("touchscreenSkin", "Shaded");
            refresh();
            return;
        case R.id.menuItem_custom:
            loadCustomSkinFromPrompt();
            return;
        default:
            return;
        }
    }

    private void setCheckState(Menu menu, int id, String assetName) {
        MenuItem item = menu.findItem(id);
        if (item != null)
            item.setChecked(hasAsset(assetName));
    }

    private boolean hasAsset(String assetName) {
        // Get the asset position from the profile and see if it's valid
        int x = mProfile.getInt(assetName + TAG_X, DISABLED_ASSET_POS);
        int y = mProfile.getInt(assetName + TAG_Y, DISABLED_ASSET_POS);
        return (x > DISABLED_ASSET_POS) && (y > DISABLED_ASSET_POS);
    }

    private void toggleAsset(String assetName) {
        // Change the position of the asset to show/hide
        int newPosition = hasAsset(assetName) ? DISABLED_ASSET_POS : INITIAL_ASSET_POS;
        mProfile.putInt(assetName + TAG_X, newPosition);
        mProfile.putInt(assetName + TAG_Y, newPosition);
        refresh();
    }

    private void loadCustomSkinFromPrompt() {
        CharSequence title = this.getText(R.string.touchscreenStyle_entryCustom);
        File startPath = new File(mGlobalPrefs.touchscreenCustomSkinsDir);
        Prompt.promptDirectory(this, title, null, startPath, new PromptFileListener() {
            @Override
            public void onDialogClosed(File file, int which) {
                if (which >= 0) {
                    touchscreenSkin = file.getAbsolutePath();
                    mProfile.put("touchscreenSkin", "Custom");
                    mProfile.put("touchscreenCustomSkinPath", touchscreenSkin);
                    refresh();
                }
            }
        });
    }

    private void setHoldable(int n64Index, boolean holdable) {
        String index = String.valueOf(n64Index);

        // Get the serialized list from the profile
        String serialized = mProfile.get(TOUCHSCREEN_AUTOHOLDABLES, "");
        String[] holdables = serialized.split(AUTOHOLDABLES_DELIMITER);

        // Modify the list as necessary
        if (!holdable) {
            holdables = (String[]) ArrayUtils.removeElement(holdables, index);
        } else if (!ArrayUtils.contains(holdables, index)) {
            holdables = (String[]) ArrayUtils.add(holdables, index);
        }

        // Put the serialized list back into the profile
        serialized = TextUtils.join(AUTOHOLDABLES_DELIMITER, holdables);
        mProfile.put(TOUCHSCREEN_AUTOHOLDABLES, serialized);
    }

    private boolean getHoldable(int n64Index) {
        String serialized = mProfile.get(TOUCHSCREEN_AUTOHOLDABLES, "");
        String[] holdables = serialized.split(AUTOHOLDABLES_DELIMITER);
        return ArrayUtils.contains(holdables, String.valueOf(n64Index));
    }

    @SuppressLint("InlinedApi")
    private void hideSystemBars() {
        getSupportActionBar().hide();
        View view = mSurface.getRootView();
        if (view != null) {
            if (AppData.IS_KITKAT && mGlobalPrefs.isImmersiveModeEnabled)
                view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
                        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN
                        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
            else
                view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE); // == STATUS_BAR_HIDDEN for Honeycomb
        }
    }

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();

        if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
            initialX = x;
            initialY = y;
            dragIndex = TouchMap.UNMAPPED;
            dragging = false;
            dragAsset = "";

            if (AppData.IS_KITKAT && mGlobalPrefs.isImmersiveModeEnabled) {
                // ignore edge swipes.
                // unfortunately KitKat lacks a way to do this on its own,
                // so just ignore all touches along the edges.
                // http://stackoverflow.com/questions/20530333/ignore-immersive-mode-swipe
                View view = getWindow().getDecorView();
                if (y < 10 || y > view.getHeight() - 10 || x < 10 || x > view.getWidth() - 10)
                    return false;
            }

            // Get the N64 index of the button that was pressed
            int index = mTouchscreenMap.getButtonPress(x, y);
            if (index != TouchMap.UNMAPPED) {
                dragIndex = index;
                dragAsset = TouchMap.ASSET_NAMES.get(index);
                dragFrame = mTouchscreenMap.getButtonFrame(dragAsset);
            } else {
                // See if analog was pressed
                Point point = mTouchscreenMap.getAnalogDisplacement(x, y);
                int dX = point.x;
                int dY = point.y;
                float displacement = (float) Math.sqrt((dX * dX) + (dY * dY));
                if (mTouchscreenMap.isInCaptureRange(displacement)) {
                    dragAsset = ANALOG;
                    dragFrame = mTouchscreenMap.getAnalogFrame();
                } else {
                    int resId = R.menu.touchscreen_profile_activity;
                    int stringId = R.string.touchscreenProfileActivity_menuTitle;

                    MenuDialogFragment menuDialogFragment = MenuDialogFragment.newInstance(0, getString(stringId),
                            resId);

                    FragmentManager fm = getSupportFragmentManager();
                    menuDialogFragment.show(fm, STATE_MENU_DIALOG_FRAGMENT);
                }
            }

            dragX = mProfile.getInt(dragAsset + TAG_X, INITIAL_ASSET_POS);
            dragY = mProfile.getInt(dragAsset + TAG_Y, INITIAL_ASSET_POS);

            return true;
        } else if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_MOVE) {
            if (dragIndex != TouchMap.UNMAPPED || ANALOG.equals(dragAsset)) {
                if (!dragging) {
                    int dX = x - initialX;
                    int dY = y - initialY;
                    float displacement = (float) Math.sqrt((dX * dX) + (dY * dY));
                    if (displacement >= 10)
                        dragging = true;
                }
                if (!dragging)
                    return false;

                // drag this button or analog stick around

                // calculate the X and Y percentage
                View view = getWindow().getDecorView();
                int newDragX = (x - (initialX - dragFrame.left)) * 100
                        / (view.getWidth() - (dragFrame.right - dragFrame.left));
                int newDragY = (y - (initialY - dragFrame.top)) * 100
                        / (view.getHeight() - (dragFrame.bottom - dragFrame.top));

                newDragX = Math.min(Math.max(newDragX, 0), 100);
                newDragY = Math.min(Math.max(newDragY, 0), 100);

                if (newDragX != dragX || newDragY != dragY) {
                    dragX = newDragX;
                    dragY = newDragY;
                    mProfile.put(dragAsset + TAG_X, String.valueOf(newDragX));
                    mProfile.put(dragAsset + TAG_Y, String.valueOf(newDragY));
                    mTouchscreenMap.refreshButtonPosition(mProfile, dragAsset);
                    mOverlay.postInvalidate();
                }
            }
        } else if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP) {
            // if this touch was part of a drag/swipe gesture then don't tap the button
            if (dragging)
                return false;

            // show the editor for the tapped button
            if (ANALOG.equals(dragAsset)) {
                // play the standard button sound effect
                View view = getWindow().getDecorView();
                view.playSoundEffect(SoundEffectConstants.CLICK);

                popupDialog(dragAsset, getString(R.string.controller_analog), -1);
            } else if (dragIndex != TouchMap.UNMAPPED) {
                int index = dragIndex;
                String title = READABLE_NAMES.get(dragIndex);

                // D-pad buttons and TOGGLE_SENSOR are not holdable
                if (DPAD.equals(dragAsset) || TouchMap.TOGGLE_SENSOR == index)
                    index = -1;

                // play the standard button sound effect
                View view = getWindow().getDecorView();
                view.playSoundEffect(SoundEffectConstants.CLICK);

                popupDialog(dragAsset, title, index);
            }

            return true;
        }

        return false;
    }

    private void popupDialog(final String assetName, String title, final int holdableIndex) {
        //Prevent more than one pop at a time
        if (mPopupBeingShown) {
            return;
        }

        mPopupBeingShown = true;

        // Get the original position of the asset
        final int initialX = mProfile.getInt(assetName + TAG_X, INITIAL_ASSET_POS);
        final int initialY = mProfile.getInt(assetName + TAG_Y, INITIAL_ASSET_POS);
        final int initialScale = mProfile.getInt(assetName + SCALE, 100);

        // Inflate the dialog's main view area
        View view = View.inflate(this, R.layout.touchscreen_profile_activity_popup, null);

        // Setup the dialog's compound seekbar widgets
        final SeekBarGroup posX = new SeekBarGroup(initialX, view, R.id.seekbarX, R.id.buttonXdown, R.id.buttonXup,
                R.id.textX, getString(R.string.touchscreenProfileActivity_horizontalSlider),
                new SeekBarGroup.Listener() {
                    @Override
                    public void onValueChanged(int value) {
                        mProfile.put(assetName + TAG_X, String.valueOf(value));
                        refresh();
                    }
                });

        final SeekBarGroup posY = new SeekBarGroup(initialY, view, R.id.seekbarY, R.id.buttonYdown, R.id.buttonYup,
                R.id.textY, getString(R.string.touchscreenProfileActivity_verticalSlider),
                new SeekBarGroup.Listener() {
                    @Override
                    public void onValueChanged(int value) {
                        mProfile.put(assetName + TAG_Y, String.valueOf(value));
                        refresh();
                    }
                });

        final SeekBarGroup scale = new SeekBarGroup(initialScale, view, R.id.seekbarScale, R.id.buttonScaleDown,
                R.id.buttonScaleUp, R.id.textScale, getString(R.string.touchscreenProfileActivity_ScaleSlider), 5,
                1, 0, 200, new SeekBarGroup.Listener() {
                    @Override
                    public void onValueChanged(int value) {
                        mProfile.put(assetName + SCALE, String.valueOf(value));
                        refresh();
                    }
                });

        // Setup the visual feedback checkbox
        CheckBox feedback = (CheckBox) view.findViewById(R.id.checkBox_feedback);
        CheckBox hide = (CheckBox) view.findViewById(R.id.checkBox_hideJoystick);
        if (assetName.equals("analog")) {
            feedback.setChecked(Boolean.valueOf(mProfile.get("touchscreenAnimated", "False")));
            hide.setChecked(Boolean.valueOf(mProfile.get("touchscreenHideAnalogWhenSensor")));
            feedback.setOnCheckedChangeListener(new OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                    mProfile.put("touchscreenAnimated", (isChecked ? "True" : "False"));
                    refresh();
                }
            });
            hide.setOnCheckedChangeListener(new OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                    mProfile.put("touchscreenHideAnalogWhenSensor", String.valueOf(isChecked));
                }
            });
        } else {
            feedback.setVisibility(View.GONE);
            hide.setVisibility(View.GONE);
        }

        // Setup the auto-holdability checkbox
        CheckBox holdable = (CheckBox) view.findViewById(R.id.checkBox_holdable);
        if (holdableIndex < 0) {
            // This is not a holdable button
            holdable.setVisibility(View.GONE);
        } else {
            holdable.setChecked(getHoldable(holdableIndex));
            holdable.setOnCheckedChangeListener(new OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                    setHoldable(holdableIndex, isChecked);
                }
            });
        }

        // Setup the listener for the dialog's bottom buttons (ok, cancel, etc.)
        OnClickListener listener = new OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                if (which == DialogInterface.BUTTON_NEGATIVE) {
                    // Revert asset to original position if user cancels
                    posX.revertValue();
                    posY.revertValue();
                    scale.revertValue();
                } else if (which == DialogInterface.BUTTON_NEUTRAL) {
                    // Remove the asset from this profile
                    toggleAsset(assetName);
                }

                mPopupBeingShown = false;
            }
        };

        // Create and show the popup dialog
        Builder builder = new Builder(this);
        builder.setTitle(title);
        builder.setView(view);
        builder.setNegativeButton(getString(android.R.string.cancel), listener);
        builder.setNeutralButton(getString(R.string.touchscreenProfileActivity_remove), listener);
        builder.setPositiveButton(getString(android.R.string.ok), listener);
        builder.setCancelable(false);
        builder.create().show();
    }
}