/*
* Copyright (C) 2015 AChep@xda <artemchep@gmail.com>
*
* This program 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 2
* of the License, or (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package com.achep.base.ui.fragments;
import android.content.Context;
import android.os.Bundle;
import android.preference.ListPreference;
import android.preference.MultiSelectListPreference;
import android.preference.Preference;
import android.preference.PreferenceScreen;
import android.preference.TwoStatePreference;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.util.Log;
import android.view.View;
import android.view.ViewStub;
import com.achep.acdisplay.R;
import com.achep.base.content.ConfigBase;
import com.achep.base.interfaces.ICheckable;
import com.achep.base.permissions.Permission;
import com.achep.base.ui.SwitchBarPermissible;
import com.achep.base.ui.activities.ActivityBase;
import com.achep.base.ui.preferences.Enabler;
import com.achep.base.ui.widgets.SwitchBar;
import com.achep.base.utils.Operator;
import com.achep.base.utils.ResUtils;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import static com.achep.base.Build.DEBUG;
public abstract class PreferenceFragment extends PreferenceFragmentBase {
private static final String TAG = "PreferenceFragment";
private SwitchBar mSwitch;
private SwitchBarPermissible mSwitchPermissible;
private Permission[] mEnablerPermissions;
private String mEnablerKey;
private Enabler mEnabler;
private ConfigBase mConfig;
private ConfigBase.Syncer mSyncer;
private TwoStatePreferenceSetter mTwoStatePreferenceSetter;
public abstract ConfigBase getConfig();
/**
* Requests the fragment to setup master switch from
* the corresponding key.
*
* @param key the key of one of the {@link com.achep.base.content.ConfigBase}'s options
* @see com.achep.base.content.ConfigBase
* @see com.achep.base.ui.preferences.Enabler
*/
public void requestMasterSwitch(@NonNull String key) {
requestMasterSwitch(key, null);
}
public void requestMasterSwitch(@NonNull String key, @Nullable Permission[] permissions) {
mEnablerKey = key;
mEnablerPermissions = permissions;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
mConfig = getConfig();
mSyncer = new ConfigBase.Syncer(context, mConfig);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (mEnablerKey != null) {
if (DEBUG) Log.d(TAG, "Creating the enabler for #" + mEnablerKey + " key.");
// Setup enabler to switch bar.
mEnabler = new Enabler(getActivity(), mConfig, mEnablerKey, createPermissionSwitch());
}
}
@NonNull
private ICheckable createPermissionSwitch() {
ActivityBase activity = (ActivityBase) getActivity();
SwitchBar switchBar = getSwitchBar();
assert switchBar != null;
mSwitchPermissible = new SwitchBarPermissible(activity, switchBar, mEnablerPermissions);
return mSwitchPermissible;
}
@Override
public void onResume() {
super.onResume();
if (mEnabler != null) {
mSwitchPermissible.resume();
mEnabler.start();
}
mSyncer.start();
}
@Override
public void onPause() {
super.onPause();
if (mEnabler != null) {
mEnabler.stop();
mSwitchPermissible.pause();
}
mSyncer.stop();
}
/**
* Synchronizes simple checkbox preference with the config.
*
* @param key the key of preference & config's parameter.
* @see com.achep.acdisplay.Config#getMap()
*/
protected void syncPreference(@NonNull String key) {
if (mTwoStatePreferenceSetter == null)
mTwoStatePreferenceSetter = new TwoStatePreferenceSetter();
syncPreference(key, mTwoStatePreferenceSetter);
}
/**
* Synchronizes any preference with the config.
*
* @param key the key of preference & config's parameter.
* @param setter preference's setter
* @see com.achep.acdisplay.Config#getMap()
* @see ListPreferenceSetter
* @see TwoStatePreferenceSetter
*/
protected void syncPreference(@NonNull String key, @NonNull ConfigBase.Syncer.Setter setter) {
Preference preference = findPreference(key);
PreferenceScreen preferenceScreen = getPreferenceScreen();
if (preference == null) {
if (DEBUG) Log.d(TAG, "Tried to sync non-existent preference with config.");
return;
}
mSyncer.syncPreference(preferenceScreen, preference, setter);
}
@Nullable
public SwitchBar getSwitchBar() {
if (mSwitch == null) {
if (getView() == null) {
return null;
}
ViewStub stub = (ViewStub) getView().findViewById(R.id.switch_bar_stub);
mSwitch = (SwitchBar) stub.inflate().findViewById(R.id.switch_bar);
}
return mSwitch;
}
/**
* The setter for a {@link TwoStatePreference}.
*
* @author Artem Chepurnoy
*/
protected static class TwoStatePreferenceSetter implements ConfigBase.Syncer.Setter {
/**
* {@inheritDoc}
*/
@Override
public final void updateSummary(@NonNull Preference preference,
@NonNull ConfigBase.Option option,
@NonNull Object value) {
// This is unneeded, because you should always use
// android:summaryOn=""
// android:summaryOff=""
// attributes.
}
/**
* {@inheritDoc}
*/
@Override
public void setValue(@NonNull Preference preference,
@NonNull ConfigBase.Option option,
@NonNull Object value) {
TwoStatePreference cbp = (TwoStatePreference) preference;
cbp.setChecked((Boolean) value);
}
/**
* {@inheritDoc}
*/
@NonNull
@Override
public Object getValue(@NonNull Object value) {
return value;
}
}
/**
* The setter for a {@link ListPreference}.
*
* @author Artem Chepurnoy
*/
protected static class ListPreferenceSetter implements ConfigBase.Syncer.Setter {
/**
* {@inheritDoc}
*/
@Override
public void updateSummary(@NonNull Preference preference,
@NonNull ConfigBase.Option option,
@NonNull Object value) { /* unused */ }
/**
* {@inheritDoc}
*/
@Override
public void setValue(@NonNull Preference preference,
@NonNull ConfigBase.Option option,
@NonNull Object value) {
ListPreference cbp = (ListPreference) preference;
cbp.setValue(Integer.toString((Integer) value));
}
/**
* {@inheritDoc}
*/
@NonNull
@Override
public Object getValue(@NonNull Object value) {
return Integer.parseInt((String) value);
}
}
/**
* The setter for a {@link MultiSelectListPreference}. To work currently,
* its data must be in a bit-mask (bit per selectable item) format.
*
* @author Artem Chepurnoy
*/
protected static class MultiSelectListPreferenceSetter implements ConfigBase.Syncer.Setter {
@NonNull
private final Context mContext;
private final int mStrResSummary;
private final int mStrResDisabled;
public MultiSelectListPreferenceSetter(@NonNull Context context,
@StringRes int summaryStrRes,
@StringRes int disabledStrRes) {
mContext = context;
mStrResSummary = summaryStrRes;
mStrResDisabled = disabledStrRes;
}
/**
* {@inheritDoc}
*/
@Override
public void updateSummary(@NonNull Preference preference,
@NonNull ConfigBase.Option option,
@NonNull Object value) {
MultiSelectListPreference mslp = (MultiSelectListPreference) preference;
int mode = (int) value;
CharSequence summary;
if (mode != 0) {
CharSequence[] entries = mslp.getEntries();
CharSequence[] values = mslp.getEntryValues();
String divider = mContext.getString(R.string.settings_multi_list_divider);
StringBuilder sb = new StringBuilder();
boolean empty = true;
assert entries != null;
assert values != null;
// Append selected items.
for (int i = 0; i < values.length; i++) {
int a = Integer.parseInt(values[i].toString());
if (Operator.bitAnd(mode, a)) {
if (!empty) {
sb.append(divider);
}
sb.append(entries[i]);
empty = false;
}
}
String itemsText = sb.toString().toLowerCase();
summary = mStrResSummary != 0
? ResUtils.getString(mContext, mStrResSummary, itemsText)
: sb.charAt(0) + itemsText.substring(1, itemsText.length());
} else {
summary = mContext.getString(mStrResDisabled);
}
mslp.setSummary(summary);
}
/**
* {@inheritDoc}
*/
@Override
public void setValue(@NonNull Preference preference,
@NonNull ConfigBase.Option option,
@NonNull Object value) {
int mode = (int) value;
String[] values = new String[Integer.bitCount(mode)];
for (int i = 1, j = 0; j < values.length; i <<= 1) {
if (Operator.bitAnd(mode, i)) {
values[j++] = Integer.toString(i);
}
}
Set<String> valuesSet = new HashSet<>();
Collections.addAll(valuesSet, values);
MultiSelectListPreference mslp = (MultiSelectListPreference) preference;
mslp.setValues(valuesSet);
}
/**
* {@inheritDoc}
*/
@NonNull
@Override
public Object getValue(@NonNull Object value) {
int mode = 0;
Set<String> values = (Set<String>) value;
for (String v : values) {
mode |= Integer.parseInt(v);
}
return mode;
}
}
}