org.computeforcancer.android.PrefsFragment.java Source code

Java tutorial

Introduction

Here is the source code for org.computeforcancer.android.PrefsFragment.java

Source

/*******************************************************************************
 * This file is part of BOINC.
 * http://boinc.berkeley.edu
 * Copyright (C) 2016 University of California
 * 
 * BOINC is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation,
 * either version 3 of the License, or (at your option) any later version.
 * 
 * BOINC 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 Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with BOINC.  If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/
package org.computeforcancer.android;

import android.app.Dialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.RemoteException;
import android.support.v4.app.Fragment;
import android.support.v7.widget.AppCompatRadioButton;
import android.support.v7.widget.SwitchCompat;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;
import org.computeforcancer.android.adapter.PrefsListAdapter;
import org.computeforcancer.android.adapter.PrefsListItemWrapper;
import org.computeforcancer.android.adapter.PrefsListItemWrapperBool;
import org.computeforcancer.android.adapter.PrefsListItemWrapperValue;
import org.computeforcancer.android.adapter.PrefsSelectionDialogListAdapter;
import org.computeforcancer.android.fragments.AbstractBaseFragment;
import org.computeforcancer.android.utils.BOINCDefs;
import org.computeforcancer.android.utils.Logging;
import org.computeforcancer.android.utils.SharedPrefs;

import java.text.NumberFormat;
import java.util.ArrayList;

public class PrefsFragment extends AbstractBaseFragment {

    private static final long DAY = 1000 * 60 * 60 * 24;
    private static final long WEEK = DAY * 7;
    private static final long MONTH = DAY * 30;

    private ListView lv;
    private PrefsListAdapter listAdapter;
    private SwitchCompat mAutoStart;
    private AppCompatRadioButton mDailyButton;
    private AppCompatRadioButton mWeeklyButton;
    private AppCompatRadioButton mMonthlyButton;

    private ArrayList<PrefsListItemWrapper> data = new ArrayList<PrefsListItemWrapper>(); //Adapter for list data
    private GlobalPreferences clientPrefs = null; //preferences of the client, read on every onResume via RPC
    //private AppPreferences appPrefs = null; //Android specific preferences, singleton of monitor
    private HostInfo hostinfo = null;

    private boolean layoutSuccessful = false;

    private BroadcastReceiver mClientStatusChangeRec = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (Logging.VERBOSE)
                Log.d(Logging.TAG, "PrefsFragment ClientStatusChange - onReceive()");
            try {
                if (!layoutSuccessful)
                    populateLayout();
            } catch (RemoteException e) {
            }
        }
    };
    private IntentFilter ifcsc = new IntentFilter("org.computeforcancer.android.clientstatuschange");

    // fragment lifecycle: 2.
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        if (Logging.VERBOSE)
            Log.d(Logging.TAG, "ProjectsFragment onCreateView");
        // Inflate the layout for this fragment
        View layout = inflater.inflate(R.layout.prefs_layout, container, false);
        lv = (ListView) layout.findViewById(R.id.listview);
        listAdapter = new PrefsListAdapter(getActivity(), this, R.id.listview, data);
        lv.setAdapter(listAdapter);
        final SharedPreferences mSharedPreferences = getContext().getSharedPreferences(
                "org.computeforcancer.android", Context.MODE_PRIVATE | Context.MODE_MULTI_PROCESS);
        mAutoStart = (SwitchCompat) layout.findViewById(R.id.switchRun);
        mAutoStart.setChecked(mSharedPreferences.getBoolean(SharedPrefs.AUTO_START, true));
        mAutoStart.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
                mSharedPreferences.edit().putBoolean(SharedPrefs.AUTO_START, b).commit();
                try {
                    BOINCActivity.monitor.setAutostart(b);
                    BOINCActivity.monitor.setRunMode(b ? BOINCDefs.RUN_MODE_AUTO : BOINCDefs.RUN_MODE_NEVER);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });

        mDailyButton = (AppCompatRadioButton) layout.findViewById(R.id.checkBtnDaily);
        mDailyButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                mDailyButton.setChecked(true);
                mWeeklyButton.setChecked(false);
                mMonthlyButton.setChecked(false);
                mSharedPreferences.edit().putLong(SharedPrefs.NOTIFICATION_DELAY, DAY).commit();
            }
        });
        mWeeklyButton = (AppCompatRadioButton) layout.findViewById(R.id.checkBtnWeekly);
        mWeeklyButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                mDailyButton.setChecked(false);
                mWeeklyButton.setChecked(true);
                mMonthlyButton.setChecked(false);
                mSharedPreferences.edit().putLong(SharedPrefs.NOTIFICATION_DELAY, WEEK).commit();
            }
        });
        mMonthlyButton = (AppCompatRadioButton) layout.findViewById(R.id.checkBtnMonthly);
        mMonthlyButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                mDailyButton.setChecked(false);
                mWeeklyButton.setChecked(false);
                mMonthlyButton.setChecked(true);
                mSharedPreferences.edit().putLong(SharedPrefs.NOTIFICATION_DELAY, MONTH).commit();
            }
        });
        final SwitchCompat notificationSwitch = (SwitchCompat) layout.findViewById(R.id.switchNotifications);

        notificationSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
                if (b) {
                    mDailyButton.setChecked(true);
                    mWeeklyButton.setChecked(false);
                    mMonthlyButton.setChecked(false);
                    mDailyButton.setEnabled(true);
                    mWeeklyButton.setEnabled(true);
                    mMonthlyButton.setEnabled(true);
                    if (mSharedPreferences.getLong(SharedPrefs.NOTIFICATION_DELAY,
                            Long.MAX_VALUE) == Long.MAX_VALUE) {
                        mSharedPreferences.edit().putLong(SharedPrefs.NOTIFICATION_DELAY, DAY).commit();
                    }
                } else {
                    mDailyButton.setChecked(true);
                    mWeeklyButton.setChecked(false);
                    mMonthlyButton.setChecked(false);
                    mDailyButton.setEnabled(false);
                    mWeeklyButton.setEnabled(false);
                    mMonthlyButton.setEnabled(false);
                    mSharedPreferences.edit().putLong(SharedPrefs.NOTIFICATION_DELAY, Long.MAX_VALUE).commit();
                }
            }
        });

        if (mSharedPreferences.getLong(SharedPrefs.NOTIFICATION_DELAY, Long.MAX_VALUE) == DAY) {
            notificationSwitch.setChecked(true);
            mDailyButton.setChecked(true);
            mWeeklyButton.setChecked(false);
            mMonthlyButton.setChecked(false);
            mDailyButton.setEnabled(true);
            mWeeklyButton.setEnabled(true);
            mMonthlyButton.setEnabled(true);
        } else if (mSharedPreferences.getLong(SharedPrefs.NOTIFICATION_DELAY, Long.MAX_VALUE) == WEEK) {
            notificationSwitch.setChecked(true);
            mDailyButton.setChecked(false);
            mWeeklyButton.setChecked(true);
            mMonthlyButton.setChecked(false);
            mDailyButton.setEnabled(true);
            mWeeklyButton.setEnabled(true);
            mMonthlyButton.setEnabled(true);
        } else if (mSharedPreferences.getLong(SharedPrefs.NOTIFICATION_DELAY, Long.MAX_VALUE) == MONTH) {
            notificationSwitch.setChecked(true);
            mDailyButton.setChecked(false);
            mWeeklyButton.setChecked(false);
            mMonthlyButton.setChecked(true);
            mDailyButton.setEnabled(true);
            mWeeklyButton.setEnabled(true);
            mMonthlyButton.setEnabled(true);
        } else {
            notificationSwitch.setChecked(false);
            mDailyButton.setChecked(true);
            mWeeklyButton.setChecked(false);
            mMonthlyButton.setChecked(false);
            mDailyButton.setEnabled(false);
            mWeeklyButton.setEnabled(false);
            mMonthlyButton.setEnabled(false);
        }

        return layout;
    }

    // fragment lifecycle: 1.
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    // fragment lifecycle: 3.
    @Override
    public void onResume() {
        try {
            populateLayout();
        } catch (RemoteException e) {
        }
        getActivity().registerReceiver(mClientStatusChangeRec, ifcsc);
        super.onResume();
    }

    @Override
    public void onPause() {
        getActivity().unregisterReceiver(mClientStatusChangeRec);
        super.onPause();
    }

    private Boolean getPrefs() {
        // try to get current client status from monitor
        //ClientStatus status;
        try {
            //status  = Monitor.getClientStatus();
            clientPrefs = BOINCActivity.monitor.getPrefs();
        } catch (Exception e) {
            if (Logging.WARNING)
                Log.w(Logging.TAG, "PrefsActivity: Could not load data, clientStatus not initialized.");
            e.printStackTrace();
            return false;
        }
        //clientPrefs = status.getPrefs(); //read prefs from client via rpc
        if (clientPrefs == null) {
            if (Logging.DEBUG)
                Log.d(Logging.TAG, "readPrefs: null, return false");
            return false;
        }
        //if(Logging.DEBUG) Log.d(Logging.TAG, "readPrefs done");
        return true;
    }

    private Boolean getHostInfo() {
        // try to get current client status from monitor
        //ClientStatus status;

        try {
            //status  = Monitor.getClientStatus();
            hostinfo = BOINCActivity.monitor.getHostInfo();
        } catch (Exception e) {
            if (Logging.WARNING)
                Log.w(Logging.TAG, "PrefsActivity: Could not load data, clientStatus not initialized.");
            e.printStackTrace();
            return false;
        }
        //hostinfo = status.getHostInfo(); //Get the hostinfo from client via rpc
        if (hostinfo == null) {
            if (Logging.DEBUG)
                Log.d(Logging.TAG, "getHostInfo: null, return false");
            return false;
        }
        return true;
    }

    private void populateLayout() throws RemoteException {

        if (!getPrefs() || BOINCActivity.monitor == null || !getHostInfo()) {
            if (Logging.ERROR)
                Log.e(Logging.TAG, "PrefsFragment.populateLayout returns, data is not present");
            return;
        }

        data.clear();

        Boolean advanced = BOINCActivity.monitor.getShowAdvanced();
        Boolean stationaryDeviceMode = BOINCActivity.monitor.getStationaryDeviceMode();
        Boolean stationaryDeviceSuspected = BOINCActivity.monitor.isStationaryDeviceSuspected();

        // general
        data.add(new PrefsListItemWrapper(getActivity(), R.string.prefs_category_general, true));
        data.add(new PrefsListItemWrapperBool(getActivity(), R.string.prefs_autostart_header,
                R.string.prefs_category_general, BOINCActivity.monitor.getAutostart()));
        data.add(new PrefsListItemWrapperBool(getActivity(), R.string.prefs_show_notification_notices_header,
                R.string.prefs_category_general, BOINCActivity.monitor.getShowNotificationForNotices()));
        data.add(new PrefsListItemWrapperBool(getActivity(), R.string.prefs_show_notification_suspended_header,
                R.string.prefs_category_general, BOINCActivity.monitor.getShowNotificationDuringSuspend()));
        data.add(new PrefsListItemWrapperBool(getActivity(), R.string.prefs_show_advanced_header,
                R.string.prefs_category_general, BOINCActivity.monitor.getShowAdvanced()));
        if (!stationaryDeviceMode)
            data.add(new PrefsListItemWrapperBool(getActivity(), R.string.prefs_suspend_when_screen_on,
                    R.string.prefs_category_general, BOINCActivity.monitor.getSuspendWhenScreenOn()));
        // network
        data.add(new PrefsListItemWrapper(getActivity(), R.string.prefs_category_network, true));
        data.add(new PrefsListItemWrapperBool(getActivity(), R.string.prefs_network_wifi_only_header,
                R.string.prefs_category_network, clientPrefs.network_wifi_only));
        if (advanced)
            data.add(new PrefsListItemWrapperValue(getActivity(), R.string.prefs_network_daily_xfer_limit_mb_header,
                    R.string.prefs_category_network, clientPrefs.daily_xfer_limit_mb));
        // power
        data.add(new PrefsListItemWrapper(getActivity(), R.string.prefs_category_power, true));
        if (stationaryDeviceSuspected) { // API indicates that there is no battery, offer opt-in preference for stationary device mode
            data.add(new PrefsListItemWrapperBool(getActivity(), R.string.prefs_stationary_device_mode_header,
                    R.string.prefs_category_power, BOINCActivity.monitor.getStationaryDeviceMode()));
        }
        if (!stationaryDeviceMode) { // client would compute regardless of battery preferences, so only show if that is not the case
            data.add(new PrefsListItemWrapper(getActivity(), R.string.prefs_power_source_header,
                    R.string.prefs_category_power));
            data.add(new PrefsListItemWrapperValue(getActivity(), R.string.battery_charge_min_pct_header,
                    R.string.prefs_category_power, clientPrefs.battery_charge_min_pct));
            if (advanced)
                data.add(new PrefsListItemWrapperValue(getActivity(), R.string.battery_temperature_max_header,
                        R.string.prefs_category_power, clientPrefs.battery_max_temperature));
        }
        // cpu
        if (advanced)
            data.add(new PrefsListItemWrapper(getActivity(), R.string.prefs_category_cpu, true));
        if (advanced && hostinfo.p_ncpus > 1)
            data.add(new PrefsListItemWrapperValue(getActivity(), R.string.prefs_cpu_number_cpus_header,
                    R.string.prefs_category_cpu, pctCpuCoresToNumber(clientPrefs.max_ncpus_pct)));
        if (advanced)
            data.add(new PrefsListItemWrapperValue(getActivity(), R.string.prefs_cpu_time_max_header,
                    R.string.prefs_category_cpu, clientPrefs.cpu_usage_limit));
        if (advanced)
            data.add(new PrefsListItemWrapperValue(getActivity(), R.string.prefs_cpu_other_load_suspension_header,
                    R.string.prefs_category_cpu, clientPrefs.suspend_cpu_usage));
        // storage
        if (advanced)
            data.add(new PrefsListItemWrapper(getActivity(), R.string.prefs_category_storage, true));
        if (advanced)
            data.add(new PrefsListItemWrapperValue(getActivity(), R.string.prefs_disk_max_pct_header,
                    R.string.prefs_category_storage, clientPrefs.disk_max_used_pct));
        if (advanced)
            data.add(new PrefsListItemWrapperValue(getActivity(), R.string.prefs_disk_min_free_gb_header,
                    R.string.prefs_category_storage, clientPrefs.disk_min_free_gb));
        if (advanced)
            data.add(new PrefsListItemWrapperValue(getActivity(), R.string.prefs_disk_access_interval_header,
                    R.string.prefs_category_storage, clientPrefs.disk_interval));
        // memory
        if (advanced)
            data.add(new PrefsListItemWrapper(getActivity(), R.string.prefs_category_memory, true));
        if (advanced)
            data.add(new PrefsListItemWrapperValue(getActivity(), R.string.prefs_memory_max_idle_header,
                    R.string.prefs_category_memory, clientPrefs.ram_max_used_idle_frac));
        // debug
        if (advanced)
            data.add(new PrefsListItemWrapper(getActivity(), R.string.prefs_category_debug, true));
        if (advanced)
            data.add(new PrefsListItemWrapper(getActivity(), R.string.prefs_client_log_flags_header,
                    R.string.prefs_category_debug));
        if (advanced)
            data.add(new PrefsListItemWrapperValue(getActivity(), R.string.prefs_gui_log_level_header,
                    R.string.prefs_category_debug, (double) BOINCActivity.monitor.getLogLevel()));

        updateLayout();
        layoutSuccessful = true;
    }

    private void updateLayout() {
        listAdapter.notifyDataSetChanged();
    }

    // updates list item of boolean preference
    // requires updateLayout to be called afterwards
    private void updateBoolPref(int ID, Boolean newValue) {
        if (Logging.DEBUG)
            Log.d(Logging.TAG, "updateBoolPref for ID: " + ID + " value: " + newValue);
        for (PrefsListItemWrapper item : data) {
            if (item.ID == ID) {
                ((PrefsListItemWrapperBool) item).setStatus(newValue);
                continue;
            }
        }
    }

    // updates list item of value preference
    // requires updateLayout to be called afterwards
    private void updateValuePref(int ID, Double newValue) {
        if (Logging.DEBUG)
            Log.d(Logging.TAG, "updateValuePref for ID: " + ID + " value: " + newValue);
        for (PrefsListItemWrapper item : data) {
            if (item.ID == ID) {
                ((PrefsListItemWrapperValue) item).status = newValue;
                continue;
            }
        }
    }

    private void setupSliderDialog(PrefsListItemWrapper item, final Dialog dialog) {
        final PrefsListItemWrapperValue valueWrapper = (PrefsListItemWrapperValue) item;
        dialog.setContentView(R.layout.prefs_layout_dialog_pct);
        TextView sliderProgress = (TextView) dialog.findViewById(R.id.seekbar_status);
        SeekBar slider = (SeekBar) dialog.findViewById(R.id.seekbar);

        if (valueWrapper.ID == R.string.battery_charge_min_pct_header
                || valueWrapper.ID == R.string.prefs_disk_max_pct_header
                || valueWrapper.ID == R.string.prefs_cpu_time_max_header
                || valueWrapper.ID == R.string.prefs_cpu_other_load_suspension_header
                || valueWrapper.ID == R.string.prefs_memory_max_idle_header) {
            Double seekBarDefault = valueWrapper.status / 10;
            slider.setProgress(seekBarDefault.intValue());
            final SeekBar.OnSeekBarChangeListener onSeekBarChangeListener;
            slider.setOnSeekBarChangeListener(onSeekBarChangeListener = new SeekBar.OnSeekBarChangeListener() {
                public void onProgressChanged(final SeekBar seekBar, final int progress, final boolean fromUser) {
                    final String progressString = NumberFormat.getPercentInstance().format(progress / 10.0);
                    TextView sliderProgress = (TextView) dialog.findViewById(R.id.seekbar_status);
                    sliderProgress.setText(progressString);
                }

                @Override
                public void onStartTrackingTouch(SeekBar seekBar) {
                }

                @Override
                public void onStopTrackingTouch(SeekBar seekBar) {
                }
            });
            onSeekBarChangeListener.onProgressChanged(slider, seekBarDefault.intValue(), false);
        } else if (valueWrapper.ID == R.string.prefs_cpu_number_cpus_header) {
            if (!getHostInfo()) {
                if (Logging.WARNING)
                    Log.w(Logging.TAG, "onItemClick missing hostInfo");
                return;
            }
            slider.setMax(hostinfo.p_ncpus <= 1 ? 0 : hostinfo.p_ncpus - 1);
            final int statusValue;
            slider.setProgress((statusValue = valueWrapper.status.intValue()) <= 0 ? 0
                    : statusValue - 1 > slider.getMax() ? slider.getMax() : statusValue - 1);
            Log.d(Logging.TAG, String.format("statusValue == %,d", statusValue));
            final SeekBar.OnSeekBarChangeListener onSeekBarChangeListener;
            slider.setOnSeekBarChangeListener(onSeekBarChangeListener = new SeekBar.OnSeekBarChangeListener() {
                public void onProgressChanged(final SeekBar seekBar, final int progress, final boolean fromUser) {
                    final String progressString = NumberFormat.getIntegerInstance()
                            .format(progress <= 0 ? 1 : progress + 1); // do not allow 0 cpus
                    TextView sliderProgress = (TextView) dialog.findViewById(R.id.seekbar_status);
                    sliderProgress.setText(progressString);
                }

                @Override
                public void onStartTrackingTouch(SeekBar seekBar) {
                }

                @Override
                public void onStopTrackingTouch(SeekBar seekBar) {
                }
            });
            onSeekBarChangeListener.onProgressChanged(slider, statusValue - 1, false);
        } else if (valueWrapper.ID == R.string.prefs_gui_log_level_header) {
            slider.setMax(5);
            slider.setProgress(valueWrapper.status.intValue());
            final SeekBar.OnSeekBarChangeListener onSeekBarChangeListener;
            slider.setOnSeekBarChangeListener(onSeekBarChangeListener = new SeekBar.OnSeekBarChangeListener() {
                public void onProgressChanged(final SeekBar seekBar, final int progress, final boolean fromUser) {
                    String progressString = NumberFormat.getIntegerInstance().format(progress);
                    TextView sliderProgress = (TextView) dialog.findViewById(R.id.seekbar_status);
                    sliderProgress.setText(progressString);
                }

                @Override
                public void onStartTrackingTouch(SeekBar seekBar) {
                }

                @Override
                public void onStopTrackingTouch(SeekBar seekBar) {
                }
            });
            onSeekBarChangeListener.onProgressChanged(slider, valueWrapper.status.intValue(), false);
        }

        setupDialogButtons(item, dialog);
    }

    private void setupSelectionListDialog(final PrefsListItemWrapper item, final Dialog dialog)
            throws RemoteException {
        dialog.setContentView(R.layout.prefs_layout_dialog_selection);

        if (item.ID == R.string.prefs_client_log_flags_header) {
            final ArrayList<SelectionDialogOption> options = new ArrayList<SelectionDialogOption>();
            String[] array = getResources().getStringArray(R.array.prefs_client_log_flags);
            for (String option : array)
                options.add(new SelectionDialogOption(option));
            ListView lv = (ListView) dialog.findViewById(R.id.selection);
            new PrefsSelectionDialogListAdapter(getActivity(), lv, R.id.selection, options);

            // setup confirm button action
            Button confirm = (Button) dialog.findViewById(R.id.confirm);
            confirm.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    ArrayList<String> selectedOptions = new ArrayList<String>();
                    for (SelectionDialogOption option : options)
                        if (option.selected)
                            selectedOptions.add(option.name);
                    if (Logging.DEBUG)
                        Log.d(Logging.TAG, selectedOptions.size() + " log flags selected");
                    new SetCcConfigAsync().execute(formatOptionsToCcConfig(selectedOptions));
                    dialog.dismiss();
                }
            });
        } else if (item.ID == R.string.prefs_power_source_header) {
            final ArrayList<SelectionDialogOption> options = new ArrayList<SelectionDialogOption>();
            options.add(new SelectionDialogOption(getResources().getString(R.string.prefs_power_source_ac),
                    BOINCActivity.monitor.getPowerSourceAc()));
            options.add(new SelectionDialogOption(getResources().getString(R.string.prefs_power_source_usb),
                    BOINCActivity.monitor.getPowerSourceUsb()));
            options.add(new SelectionDialogOption(getResources().getString(R.string.prefs_power_source_wireless),
                    BOINCActivity.monitor.getPowerSourceWireless()));
            options.add(new SelectionDialogOption(getResources().getString(R.string.prefs_power_source_battery),
                    clientPrefs.run_on_batteries, true));
            ListView lv = (ListView) dialog.findViewById(R.id.selection);
            new PrefsSelectionDialogListAdapter(getActivity(), lv, R.id.selection, options);

            // setup confirm button action
            Button confirm = (Button) dialog.findViewById(R.id.confirm);
            confirm.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    try {
                        for (SelectionDialogOption option : options) {
                            if (option.name == getResources().getString(R.string.prefs_power_source_ac))
                                BOINCActivity.monitor.setPowerSourceAc(option.selected);
                            if (option.name == getResources().getString(R.string.prefs_power_source_usb))
                                BOINCActivity.monitor.setPowerSourceUsb(option.selected);
                            if (option.name == getResources().getString(R.string.prefs_power_source_wireless))
                                BOINCActivity.monitor.setPowerSourceWireless(option.selected);
                            if (option.name == getResources().getString(R.string.prefs_power_source_battery)) {
                                clientPrefs.run_on_batteries = option.selected;
                                new WriteClientPrefsAsync().execute(clientPrefs); //async task triggers layout update
                            }
                        }
                        dialog.dismiss();
                    } catch (RemoteException e) {
                    }
                }
            });
        }

        // generic cancel button
        Button cancel = (Button) dialog.findViewById(R.id.cancel);
        cancel.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                dialog.dismiss();
            }
        });
    }

    private void setupDialogButtons(final PrefsListItemWrapper item, final Dialog dialog) {
        // confirm
        Button confirm = (Button) dialog.findViewById(R.id.confirm);
        confirm.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                if (item.ID == R.string.battery_charge_min_pct_header
                        || item.ID == R.string.prefs_disk_max_pct_header
                        || item.ID == R.string.prefs_cpu_time_max_header
                        || item.ID == R.string.prefs_cpu_other_load_suspension_header
                        || item.ID == R.string.prefs_memory_max_idle_header) {
                    SeekBar slider = (SeekBar) dialog.findViewById(R.id.seekbar);
                    double value = slider.getProgress() * 10;
                    writeClientValuePreference(item.ID, value);
                } else if (item.ID == R.string.prefs_cpu_number_cpus_header) {
                    SeekBar slider = (SeekBar) dialog.findViewById(R.id.seekbar);
                    int sbProgress = slider.getProgress();
                    double value = numberCpuCoresToPct(sbProgress <= 0 ? 1 : sbProgress + 1);
                    writeClientValuePreference(item.ID, value);
                } else if (item.ID == R.string.prefs_gui_log_level_header) {
                    SeekBar slider = (SeekBar) dialog.findViewById(R.id.seekbar);
                    int sbProgress = slider.getProgress();
                    try {
                        // monitor and UI in two different processes. set static variable in both
                        Logging.setLogLevel(sbProgress);
                        BOINCActivity.monitor.setLogLevel(sbProgress);
                    } catch (RemoteException e) {
                    }
                    updateValuePref(item.ID, (double) sbProgress);
                    updateLayout();
                } else if (item.ID == R.string.prefs_network_daily_xfer_limit_mb_header
                        || item.ID == R.string.battery_temperature_max_header
                        || item.ID == R.string.prefs_disk_min_free_gb_header
                        || item.ID == R.string.prefs_disk_access_interval_header) {
                    EditText edit = (EditText) dialog.findViewById(R.id.Input);
                    String input = edit.getText().toString();
                    Double valueTmp = parseInputValueToDouble(input);
                    if (valueTmp == null)
                        return;
                    double value = valueTmp;
                    writeClientValuePreference(item.ID, value);
                }
                dialog.dismiss();
            }
        });
        // cancel
        Button cancel = (Button) dialog.findViewById(R.id.cancel);
        cancel.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                dialog.dismiss();
            }
        });
    }

    private void writeClientValuePreference(int id, double value) {
        // update preferences
        switch (id) {
        case R.string.prefs_disk_max_pct_header:
            clientPrefs.disk_max_used_pct = value;
            break;
        case R.string.prefs_disk_min_free_gb_header:
            clientPrefs.disk_min_free_gb = value;
            break;
        case R.string.prefs_disk_access_interval_header:
            clientPrefs.disk_interval = value;
            break;
        case R.string.prefs_network_daily_xfer_limit_mb_header:
            clientPrefs.daily_xfer_limit_mb = value;
            // also need to set the period!
            clientPrefs.daily_xfer_period_days = 1;
            break;
        case R.string.battery_charge_min_pct_header:
            clientPrefs.battery_charge_min_pct = value;
            break;
        case R.string.battery_temperature_max_header:
            clientPrefs.battery_max_temperature = value;
            break;
        case R.string.prefs_cpu_number_cpus_header:
            clientPrefs.max_ncpus_pct = value;
            //convert value back to number for layout update
            value = pctCpuCoresToNumber(value);
            break;
        case R.string.prefs_cpu_time_max_header:
            clientPrefs.cpu_usage_limit = value;
            break;
        case R.string.prefs_cpu_other_load_suspension_header:
            clientPrefs.suspend_cpu_usage = value;
            break;
        case R.string.prefs_memory_max_idle_header:
            clientPrefs.ram_max_used_idle_frac = value;
            break;
        default:
            if (Logging.DEBUG)
                Log.d(Logging.TAG, "onClick (dialog submit button), couldnt match ID");
            Toast toast = Toast.makeText(getActivity(), "ooops! something went wrong...", Toast.LENGTH_SHORT);
            toast.show();
            return;
        }
        // update list item
        updateValuePref(id, value);
        // preferences adapted, write preferences to client
        new WriteClientPrefsAsync().execute(clientPrefs);
    }

    private double numberCpuCoresToPct(double ncpus) {
        double pct = (ncpus / (double) hostinfo.p_ncpus) * 100;
        if (Logging.DEBUG)
            Log.d(Logging.TAG, "numberCpuCoresToPct: " + ncpus + hostinfo.p_ncpus + pct);
        return pct;
    }

    private double pctCpuCoresToNumber(double pct) {
        double ncpus = (double) hostinfo.p_ncpus * (pct / 100.0);
        if (ncpus < 1.0)
            ncpus = 1.0;
        return ncpus;
    }

    public Double parseInputValueToDouble(String input) {
        // parse value
        Double value = 0.0;
        try {
            input = input.replaceAll(",", "."); //replace e.g. European decimal seperator "," by "."
            value = Double.parseDouble(input);
            if (Logging.DEBUG)
                Log.d(Logging.TAG, "parseInputValueToDouble: " + value);
            return value;
        } catch (Exception e) {
            if (Logging.WARNING)
                Log.w(Logging.TAG, e);
            Toast toast = Toast.makeText(getActivity(), "wrong format!", Toast.LENGTH_SHORT);
            toast.show();
            return null;
        }
    }

    private String formatOptionsToCcConfig(ArrayList<String> options) {
        StringBuilder builder = new StringBuilder();
        builder.append("<cc_config>\n <log_flags>\n");
        for (String option : options)
            builder.append("  <" + option + "/>\n");
        builder.append(" </log_flags>\n <options>\n </options>\n</cc_config>");
        return builder.toString();
    }

    @Override
    public String getTAG() {
        return PrefsFragment.class.getSimpleName();
    }

    public class BoolOnClick implements OnClickListener {

        private Integer ID;
        private CheckBox cb;

        public BoolOnClick(Integer ID, CheckBox cb) {
            this.ID = ID;
            this.cb = cb;
        }

        @Override
        public void onClick(View view) {
            if (Logging.DEBUG)
                Log.d(Logging.TAG, "onCbClick");
            Boolean previousState = cb.isChecked();
            cb.setChecked(!previousState);
            Boolean isSet = cb.isChecked();
            try {
                switch (ID) {
                case R.string.prefs_autostart_header: //app pref
                    BOINCActivity.monitor.setAutostart(isSet);
                    updateBoolPref(ID, isSet);
                    updateLayout();
                    break;
                case R.string.prefs_show_notification_notices_header: //app pref
                    BOINCActivity.monitor.setShowNotificationForNotices(isSet);
                    updateBoolPref(ID, isSet);
                    updateLayout();
                    break;
                case R.string.prefs_show_notification_suspended_header: //app pref
                    BOINCActivity.monitor.setShowNotificationDuringSuspend(isSet);
                    updateBoolPref(ID, isSet);
                    updateLayout();
                    break;
                case R.string.prefs_show_advanced_header: //app pref
                    BOINCActivity.monitor.setShowAdvanced(isSet);
                    // reload complete layout to remove/add advanced elements
                    populateLayout();
                    break;
                case R.string.prefs_suspend_when_screen_on: //app pref
                    BOINCActivity.monitor.setSuspendWhenScreenOn(isSet);
                    updateBoolPref(ID, isSet);
                    updateLayout();
                    break;
                case R.string.prefs_network_wifi_only_header: //client pref
                    clientPrefs.network_wifi_only = isSet;
                    updateBoolPref(ID, isSet);
                    new WriteClientPrefsAsync().execute(clientPrefs); //async task triggers layout update
                    break;
                case R.string.prefs_stationary_device_mode_header: //app pref
                    BOINCActivity.monitor.setStationaryDeviceMode(isSet);
                    // reload complete layout to remove/add power preference elements
                    populateLayout();
                    break;
                }
            } catch (RemoteException e) {
            }
        }

    }

    public class ValueOnClick implements OnClickListener {

        private PrefsListItemWrapper item;

        public ValueOnClick(PrefsListItemWrapper wrapper) {
            this.item = wrapper;
        }

        @Override
        public void onClick(View view) {
            final Dialog dialog = new Dialog(getActivity());
            dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);

            // setup dialog layout
            switch (item.ID) {
            case R.string.prefs_network_daily_xfer_limit_mb_header:
                dialog.setContentView(R.layout.prefs_layout_dialog);
                ((TextView) dialog.findViewById(R.id.pref)).setText(item.ID);
                setupDialogButtons(item, dialog);
                break;
            case R.string.prefs_power_source_header:
                try {
                    setupSelectionListDialog(item, dialog);
                } catch (RemoteException e) {
                }
                break;
            case R.string.battery_charge_min_pct_header:
                setupSliderDialog(item, dialog);
                ((TextView) dialog.findViewById(R.id.pref)).setText(item.ID);
                break;
            case R.string.battery_temperature_max_header:
                dialog.setContentView(R.layout.prefs_layout_dialog);
                ((TextView) dialog.findViewById(R.id.pref)).setText(item.ID);
                setupDialogButtons(item, dialog);
                break;
            case R.string.prefs_cpu_number_cpus_header:
                setupSliderDialog(item, dialog);
                ((TextView) dialog.findViewById(R.id.pref)).setText(item.ID);
                break;
            case R.string.prefs_cpu_time_max_header:
                setupSliderDialog(item, dialog);
                ((TextView) dialog.findViewById(R.id.pref)).setText(item.ID);
                break;
            case R.string.prefs_cpu_other_load_suspension_header:
                setupSliderDialog(item, dialog);
                ((TextView) dialog.findViewById(R.id.pref)).setText(item.ID);
                break;
            case R.string.prefs_disk_max_pct_header:
                setupSliderDialog(item, dialog);
                ((TextView) dialog.findViewById(R.id.pref)).setText(item.ID);
                break;
            case R.string.prefs_disk_min_free_gb_header:
                dialog.setContentView(R.layout.prefs_layout_dialog);
                ((TextView) dialog.findViewById(R.id.pref)).setText(item.ID);
                setupDialogButtons(item, dialog);
                break;
            case R.string.prefs_disk_access_interval_header:
                dialog.setContentView(R.layout.prefs_layout_dialog);
                ((TextView) dialog.findViewById(R.id.pref)).setText(item.ID);
                setupDialogButtons(item, dialog);
                break;
            case R.string.prefs_memory_max_idle_header:
                setupSliderDialog(item, dialog);
                ((TextView) dialog.findViewById(R.id.pref)).setText(item.ID);
                break;
            case R.string.prefs_client_log_flags_header:
                try {
                    setupSelectionListDialog(item, dialog);
                } catch (RemoteException e) {
                }
                break;
            case R.string.prefs_gui_log_level_header:
                setupSliderDialog(item, dialog);
                ((TextView) dialog.findViewById(R.id.pref)).setText(item.ID);
                break;
            default:
                if (Logging.ERROR)
                    Log.d(Logging.TAG, "PrefsActivity onItemClick: could not map ID: " + item.ID);
                return;
            }

            // show dialog
            dialog.show();
        }
    }

    private final class WriteClientPrefsAsync extends AsyncTask<GlobalPreferences, Void, Boolean> {
        @Override
        protected Boolean doInBackground(GlobalPreferences... params) {
            try {
                return BOINCActivity.monitor.setGlobalPreferences(params[0]);
            } catch (RemoteException e) {
                return false;
            }
        }

        @Override
        protected void onPostExecute(Boolean success) {
            if (Logging.DEBUG)
                Log.d(Logging.TAG, "WriteClientPrefsAsync returned: " + success);
            updateLayout();
        }
    }

    private final class SetCcConfigAsync extends AsyncTask<String, Void, Boolean> {
        @Override
        protected Boolean doInBackground(String... params) {
            if (Logging.DEBUG)
                Log.d(Logging.TAG, "SetCcConfigAsync with: " + params[0]);
            try {
                return BOINCActivity.monitor.setCcConfig(params[0]);
            } catch (RemoteException e) {
                return false;
            }
        }
    }

    public class SelectionDialogOption {
        public String name;
        public Boolean selected = false;
        public Boolean highlighted = false;

        public SelectionDialogOption(String name) {
            this.name = name;
        }

        public SelectionDialogOption(String name, Boolean selected) {
            this.name = name;
            this.selected = selected;
        }

        public SelectionDialogOption(String name, Boolean selected, Boolean highlighted) {
            this.name = name;
            this.selected = selected;
            this.highlighted = highlighted;
        }
    }
}