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

Java tutorial

Introduction

Here is the source code for paulscode.android.mupen64plusae.profile.ManageProfilesActivity.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.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.mupen64plusae.v3.alpha.BuildConfig;
import org.mupen64plusae.v3.alpha.R;

import paulscode.android.mupen64plusae.MenuListView;
import paulscode.android.mupen64plusae.compat.AppCompatListActivity;
import paulscode.android.mupen64plusae.dialog.ConfirmationDialog;
import paulscode.android.mupen64plusae.dialog.ConfirmationDialog.PromptConfirmListener;
import paulscode.android.mupen64plusae.dialog.MenuDialogFragment;
import paulscode.android.mupen64plusae.dialog.MenuDialogFragment.OnDialogMenuItemSelectedListener;
import paulscode.android.mupen64plusae.dialog.ProfileNameEditDialog;
import paulscode.android.mupen64plusae.dialog.ProfileNameEditDialog.OnProfileNameDialogButtonListener;
import paulscode.android.mupen64plusae.persistent.AppData;
import paulscode.android.mupen64plusae.persistent.ConfigFile;
import paulscode.android.mupen64plusae.persistent.GlobalPrefs;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.v4.app.FragmentManager;
import android.support.v7.preference.PreferenceManager;
import android.support.v7.widget.Toolbar;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;

abstract public class ManageProfilesActivity extends AppCompatListActivity
        implements OnDialogMenuItemSelectedListener, OnProfileNameDialogButtonListener, PromptConfirmListener {
    /**
     * Gets the absolute path of the {@link ConfigFile} that backs this profile. Subclasses should
     * implement this method to define the locations of the subclass-specific built-in or custom
     * config files.
     * 
     * @param isBuiltin true to return the built-in config file path; false to return the custom
     *            config file path
     * @return the absolute path of the requested config file
     */
    abstract protected ConfigFile getConfigFile(boolean isBuiltin);

    /**
     * Gets the name of the profile to use if the user unsets the default. If a profile can be
     * "disabled", subclasses should return an empty string. Otherwise, subclasses should return the
     * name of a builtin profile that is guaranteed to exist (typically the default profile at
     * installation).
     * 
     * @return the default name of the default profile
     */
    abstract protected String getNoDefaultProfile();

    /**
     * Gets the name of the default profile. Subclasses should implement this method to retrieve the
     * persisted subclass-specific profile specified by the user.
     * 
     * @return the name of the default profile
     */
    abstract protected String getDefaultProfile();

    /**
     * Sets the name of the default profile. Subclasses should implement this method to persist the
     * subclass-specific profile specified by the user.
     * 
     * @param name the name of the new default profile
     */
    abstract protected void putDefaultProfile(String name);

    /**
     * Edits a profile using a subclass-specific UI. Subclasses should implement this method to
     * launch a dialog or activity, to allow the user to modify the given profile. Subclasses are
     * responsible for persisting the profile data to disk when the dialog or activity finishes.
     * 
     * @param profile the profile to be edited
     */
    abstract protected void onEditProfile(Profile profile);

    /**
     * Returns the title of the activity resource id
     * @return title of the activity resource id
     */
    abstract protected int getWindowTitleResource();

    private static final String STATE_MENU_DIALOG_FRAGMENT = "STATE_MENU_DIALOG_FRAGMENT";
    private static final String STATE_PROFILE_EDIT_DIALOG_FRAGMENT = "STATE_PROFILE_EDIT_DIALOG_FRAGMENT";
    private static final String STATE_CURRENT_SELECTED_ITEM = "STATE_CURRENT_SELECTED_ITEM";
    private static final String STATE_CURRENT_SELECTED_OPERATION = "STATE_CURRENT_SELECTED_OPERATION";
    private static final int DELETE_PROFILE_CONFIRM_DIALOG_ID = 0;
    private static final String DELETE_PROFILE_CONFIRM_DIALOG_STATE = "DELETE_PROFILE_CONFIRM_DIALOG_STATE";

    /** The back-end store for the built-in profiles, which subclasses should read from. */
    protected ConfigFile mConfigBuiltin;

    /** The back-end store for the custom profiles, which subclasses should read from and write to. */
    protected ConfigFile mConfigCustom;

    /** The application data wrapper, available as a convenience to subclasses. */
    protected AppData mAppData;

    /** The user preferences wrapper, available as a convenience to subclasses. */
    protected GlobalPrefs mGlobalPrefs;

    private final List<String> mProfileNames = new ArrayList<String>();

    /** Profile list adapter */
    private ProfileListAdapter mProfileListAdapter = null;

    /** Profile list **/
    List<Profile> mProfileList = new ArrayList<Profile>();

    /** Current listview position */
    private int mListViewPosition = 0;

    /** Current selectedOperation */
    private int mSelectedOperation = 0;

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

        setContentView(R.layout.manage_profiles_activity);

        // Add the toolbar to the activity (which supports the fancy menu/arrow animation)
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        toolbar.setTitle(getWindowTitleResource());
        setSupportActionBar(toolbar);

        mAppData = new AppData(this);
        mGlobalPrefs = new GlobalPrefs(this, mAppData);
        mGlobalPrefs.enforceLocale(this);

        // Get the config files from the subclass-specified paths
        mConfigBuiltin = getConfigFile(true);
        mConfigCustom = getConfigFile(false);

        if (savedInstanceState != null) {
            mListViewPosition = savedInstanceState.getInt(STATE_CURRENT_SELECTED_ITEM);
            mSelectedOperation = savedInstanceState.getInt(STATE_CURRENT_SELECTED_OPERATION);
        }

        refreshList();
    }

    @Override
    public void onSaveInstanceState(Bundle savedInstanceState) {
        savedInstanceState.putInt(STATE_CURRENT_SELECTED_ITEM, mListViewPosition);
        savedInstanceState.putInt(STATE_CURRENT_SELECTED_OPERATION, mSelectedOperation);

        super.onSaveInstanceState(savedInstanceState);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.profile_activity, menu);
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        menu.findItem(R.id.menuItem_toggleBuiltins)
                .setTitle(getBuiltinVisibility() ? R.string.menuItem_hideBuiltins : R.string.menuItem_showBuiltins);
        return super.onPrepareOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        mSelectedOperation = item.getItemId();

        switch (item.getItemId()) {
        case R.id.menuItem_new:
            promptNameComment(R.string.menuItem_new, "", "", false);
            return true;
        case R.id.menuItem_toggleBuiltins:
            setBuiltinVisibility(!getBuiltinVisibility());
            invalidateOptionsMenu();
            refreshList();
            return true;
        default:
            return super.onOptionsItemSelected(item);
        }
    }

    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        mListViewPosition = position;
        // Popup a dialog with a context-sensitive list of options for the profile
        final Profile profile = (Profile) getListView().getItemAtPosition(position);
        if (profile != null) {
            int resId = profile.isBuiltin ? R.menu.profile_click_menu_builtin : R.menu.profile_click_menu_custom;

            int stringId = profile.isBuiltin ? R.string.popup_titleBuiltin : R.string.popup_titleCustom;

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

            FragmentManager fm = getSupportFragmentManager();
            menuDialogFragment.show(fm, STATE_MENU_DIALOG_FRAGMENT);
        }
        super.onListItemClick(l, v, position, id);
    }

    @Override
    public void onPrepareMenuList(MenuListView listView) {

        // Popup a dialog with a context-sensitive list of options for the profile
        final Profile profile = (Profile) getListView().getItemAtPosition(mListViewPosition);

        if (profile.name.equals(getDefaultProfile())) {
            MenuItem defaultProfileItem = profile.isBuiltin
                    ? listView.getMenu().findItem(R.id.menuItem_setUnsetDefaultBuiltinProfile)
                    : listView.getMenu().findItem(R.id.menuItem_setUnsetDefaultCustomProfile);
            defaultProfileItem.setTitle(getString(R.string.listItem_unsetDefault));
        }
    }

    @Override
    public void onDialogMenuItemSelected(int dialogId, MenuItem item) {
        //We can only get here if mListViewPosition is valid, so profile shouldn't be null
        final Profile profile = (Profile) getListView().getItemAtPosition(mListViewPosition);
        final boolean isDefault = profile.name.equals(getDefaultProfile());

        mSelectedOperation = item.getItemId();

        switch (mSelectedOperation) {
        case R.id.menuItem_setUnsetDefaultCustomProfile:
            putDefaultProfile(isDefault ? getNoDefaultProfile() : profile.name);
            refreshList();
            break;
        case R.id.menuItem_editCustomProfile:
            editProfile(profile);
            break;
        case R.id.menuItem_copyCustomProfile:
            promptNameComment(R.string.listItem_copy, profile.name, profile.comment, false);
            break;
        case R.id.menuItem_renameCustomProfile:
            promptNameComment(R.string.listItem_rename, profile.name, profile.comment, true);
            break;
        case R.id.menuItem_deleteCustomProfile:
            deleteProfile(profile, isDefault);
            break;
        case R.id.menuItem_setUnsetDefaultBuiltinProfile:
            putDefaultProfile(isDefault ? getNoDefaultProfile() : profile.name);
            refreshList();
            break;
        case R.id.menuItem_copyBUiltinProfile:
            promptNameComment(R.string.listItem_copy, profile.name, profile.comment, false);
            break;
        }
    }

    private void editProfile(Profile profile) {
        if (BuildConfig.DEBUG && profile.isBuiltin)
            throw new RuntimeException();

        onEditProfile(profile);
    }

    private void addProfile(String name, String comment) {
        if (BuildConfig.DEBUG && mConfigCustom.keySet().contains(name))
            throw new RuntimeException();

        Profile profile = new Profile(false, name, comment);
        profile.writeTo(mConfigCustom);
        mConfigCustom.save();
        refreshList();
        editProfile(profile);
    }

    private void copyProfile(String name, String comment) {
        final Profile profile = (Profile) getListView().getItemAtPosition(mListViewPosition);

        if (BuildConfig.DEBUG && mConfigCustom.keySet().contains(name))
            throw new RuntimeException();

        Profile newProfile = profile.copy(name, comment);
        newProfile.writeTo(mConfigCustom);
        mConfigCustom.save();
        refreshList();
        editProfile(newProfile);
    }

    private void renameProfile(String name, String comment) {
        Profile profile = (Profile) getListView().getItemAtPosition(mListViewPosition);

        mConfigCustom.remove(profile.name);
        Profile newProfile = profile.copy(name, comment);
        newProfile.writeTo(mConfigCustom);
        mConfigCustom.save();
        refreshList();
    }

    private void deleteProfile(Profile profile, final boolean isDefault) {
        if (BuildConfig.DEBUG && profile.isBuiltin)
            throw new RuntimeException();

        String title = getString(R.string.confirm_title);
        String message = getString(R.string.confirmDeleteProfile_message, profile.name);

        ConfirmationDialog confirmationDialog = ConfirmationDialog.newInstance(DELETE_PROFILE_CONFIRM_DIALOG_ID,
                title, message);

        FragmentManager fm = getSupportFragmentManager();
        confirmationDialog.show(fm, DELETE_PROFILE_CONFIRM_DIALOG_STATE);
    }

    @Override
    public void onPromptDialogClosed(int id, int which) {
        if (id == DELETE_PROFILE_CONFIRM_DIALOG_ID && which == DialogInterface.BUTTON_POSITIVE) {
            Profile profile = (Profile) getListView().getItemAtPosition(mListViewPosition);
            boolean isDefault = profile.name.equals(getDefaultProfile());

            if (BuildConfig.DEBUG && !mConfigCustom.keySet().contains(profile.name))
                throw new RuntimeException();

            //If this was the default profile, pick another default profile
            if (isDefault) {
                putDefaultProfile(getNoDefaultProfile());
            }

            mConfigCustom.remove(profile.name);
            mConfigCustom.save();
            refreshList();
        }
    }

    private void promptNameComment(int titleId, final String name, String comment, final boolean allowSameName) {

        ProfileNameEditDialog profileNameEditDialogFragment = ProfileNameEditDialog.newInstance(0,
                getString(titleId), name, comment, mProfileNames, allowSameName);

        FragmentManager fm = getSupportFragmentManager();
        profileNameEditDialogFragment.show(fm, STATE_PROFILE_EDIT_DIALOG_FRAGMENT);
    }

    @Override
    public void onProfileNameDialogButton(int dialogId, int selectedButton, String name, String comment) {
        if (selectedButton == DialogInterface.BUTTON_POSITIVE) {
            switch (mSelectedOperation) {
            case R.id.menuItem_new:
                addProfile(name, comment);
                break;
            case R.id.menuItem_copyCustomProfile:
                copyProfile(name, comment);
                break;
            case R.id.menuItem_renameCustomProfile:
                renameProfile(name, comment);
                break;
            case R.id.menuItem_copyBUiltinProfile:
                copyProfile(name, comment);
                break;
            }
        }
    }

    private void setBuiltinVisibility(boolean visible) {
        // Persist builtin visibility for this specific subclass
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
        prefs.edit().putBoolean(getBuiltinVisibilityKey(), visible).commit();
    }

    private boolean getBuiltinVisibility() {
        // Retrieve builtin visibility for this specific subclass
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
        return prefs.getBoolean(getBuiltinVisibilityKey(), true);
    }

    private String getBuiltinVisibilityKey() {
        // Get the subclass-specific key for persisting builtin visibility
        return "builtinsVisible" + getClass().getSimpleName();
    }

    protected void refreshList() {
        // Get the profiles to be shown to the user
        mProfileList.clear();
        mProfileList.addAll(Profile.getProfiles(mConfigCustom, false));
        if (getBuiltinVisibility())
            mProfileList.addAll(Profile.getProfiles(mConfigBuiltin, true));
        Collections.sort(mProfileList);

        if (mProfileListAdapter == null) {
            mProfileListAdapter = new ProfileListAdapter(this, mProfileList);
            setListAdapter(mProfileListAdapter);
        }

        mProfileListAdapter.notifyDataSetChanged();

        // Get all profiles, for validating unique names
        List<Profile> profiles2 = Profile.getProfiles(mConfigCustom, false);
        profiles2.addAll(Profile.getProfiles(mConfigBuiltin, true));

        // Add reserved profile names
        CharSequence defaultProfileTitle = getText(R.string.default_profile_title);
        profiles2.add(new Profile(true, defaultProfileTitle.toString(), null));

        mProfileNames.clear();
        for (Profile profile : profiles2)
            mProfileNames.add(profile.name);
    }

    private class ProfileListAdapter extends ArrayAdapter<Profile> {
        private static final int RESID = R.layout.list_item_two_text_icon;

        public ProfileListAdapter(Context context, List<Profile> profiles) {
            super(context, RESID, profiles);
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            Context context = getContext();
            LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            View view = convertView;
            if (view == null)
                view = inflater.inflate(RESID, null);

            Profile item = getItem(position);
            if (item != null) {
                TextView text1 = (TextView) view.findViewById(R.id.text1);
                TextView text2 = (TextView) view.findViewById(R.id.text2);
                ImageView icon = (ImageView) view.findViewById(R.id.icon);

                text1.setText(item.name);
                text2.setText(item.comment);
                if (item.name.equals(getDefaultProfile()))
                    icon.setImageResource(R.drawable.ic_sliders2);
                else
                    icon.setImageResource(R.drawable.ic_sliders);
            }
            return view;
        }
    }

    protected void RemoveProfile(String profileName) {
        Profile profile = new Profile(true, profileName, null);
        mProfileList.remove(profile);
    }
}