/*
* Copyright (C) 2013 The CyanogenMod Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.namelessrom.devicecontrol.preferences.hardware;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.preference.DialogPreference;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.SeekBar;
import android.widget.TextView;
import org.namelessrom.devicecontrol.App;
import org.namelessrom.devicecontrol.R;
import org.namelessrom.devicecontrol.models.BootupConfig;
import org.namelessrom.devicecontrol.modules.bootup.BootupItem;
import at.amartinz.hardware.display.DisplayGammaCalibration;
/**
* Special preference type that allows configuration of Gamma settings
*/
public class DisplayGamma extends DialogPreference {
private static final int[] BAR_COLORS = new int[]{
R.string.red,
R.string.green,
R.string.blue
};
private GammaSeekBar[][] mSeekBars;
private String[][] mCurrentColors;
private String[] mOriginalColors;
private int mNumberOfControls;
private final DisplayGammaCalibration displayGammaCalibration;
public DisplayGamma(Context context, AttributeSet attrs) {
super(context, attrs);
displayGammaCalibration = new DisplayGammaCalibration(context);
if (!isSupported()) {
return;
}
mNumberOfControls = displayGammaCalibration.getNumberOfControls();
mSeekBars = new GammaSeekBar[mNumberOfControls][BAR_COLORS.length];
mOriginalColors = new String[mNumberOfControls];
mCurrentColors = new String[mNumberOfControls][];
setLayoutResource(R.layout.preference);
setDialogLayoutResource(R.layout.preference_display_gamma_calibration);
}
@SuppressLint("CommitPrefEdits") @Override protected void onBindDialogView(@NonNull View view) {
super.onBindDialogView(view);
final ViewGroup container = (ViewGroup) view.findViewById(R.id.gamma_container);
final LayoutInflater inflater = LayoutInflater.from(getContext());
final SharedPreferences prefs = getSharedPreferences();
final Resources res = container.getResources();
final String[] gammaDescriptors = displayGammaCalibration.getDescriptors();
// Create multiple sets of seekbars, depending on the
// number of controls the device has
for (int index = 0; index < mNumberOfControls; index++) {
mOriginalColors[index] = displayGammaCalibration.getCurGamma(index);
mCurrentColors[index] = mOriginalColors[index].split(" ");
final String defaultKey = "display_gamma_default_" + index;
if (!prefs.contains(defaultKey)) {
prefs.edit().putString(defaultKey, mOriginalColors[index]).commit();
}
if (mNumberOfControls != 1) {
TextView header = (TextView) inflater.inflate(
R.layout.preference_display_gamma_calibration_header, container, false);
if (index < gammaDescriptors.length) {
header.setText(gammaDescriptors[index]);
} else {
header.setText(res.getString(
R.string.gamma_tuning_control_set_header, index + 1));
}
container.addView(header);
}
for (int color = 0; color < BAR_COLORS.length; color++) {
ViewGroup item = (ViewGroup) inflater.inflate(
R.layout.preference_display_gamma_calibration_item, container, false);
mSeekBars[index][color] = new GammaSeekBar(index, color, item);
mSeekBars[index][color].setGamma(Integer.valueOf(mCurrentColors[index][color]));
// make sure to add the seekbar group to the container _after_
// creating GammaSeekBar, so that GammaSeekBar has a chance to
// get the correct subviews without getting confused by duplicate IDs
container.addView(item);
}
}
}
@Override protected void showDialog(Bundle state) {
super.showDialog(state);
// can't use onPrepareDialogBuilder for this as we want the dialog
// to be kept open on click
AlertDialog d = (AlertDialog) getDialog();
Button defaultsButton = d.getButton(DialogInterface.BUTTON_NEUTRAL);
defaultsButton.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
for (int index = 0; index < mSeekBars.length; index++) {
final SharedPreferences prefs = getSharedPreferences();
final String defaultKey = "display_gamma_default_" + index;
// this key is guaranteed to be present, as we have
// created it in onBindDialogView()
final String pref = prefs.getString(defaultKey, null);
final String[] defaultColors = pref != null ? pref.split(" ") : mOriginalColors;
for (int color = 0; color < BAR_COLORS.length; color++) {
mSeekBars[index][color].setGamma(Integer.valueOf(defaultColors[color]));
mCurrentColors[index][color] = defaultColors[color];
}
displayGammaCalibration.setGamma(index,
TextUtils.join(" ", mCurrentColors[index]));
}
}
});
}
@Override protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
if (positiveResult) {
final BootupConfig config = BootupConfig.get();
for (int i = 0; i < mNumberOfControls; i++) {
for (final String path : displayGammaCalibration.getPaths(i)) {
config.addItem(new BootupItem(
BootupConfig.CATEGORY_DEVICE, DisplayGammaCalibration.TAG + i,
path, displayGammaCalibration.getCurGamma(i), true));
}
}
config.save();
} else if (mOriginalColors != null) {
for (int i = 0; i < mNumberOfControls; i++) {
displayGammaCalibration.setGamma(i, mOriginalColors[i]);
}
}
}
@Override protected Parcelable onSaveInstanceState() {
final Parcelable superState = super.onSaveInstanceState();
if (getDialog() == null || !getDialog().isShowing()) {
return superState;
}
// Save the dialog state
final SavedState myState = new SavedState(superState);
myState.controlCount = mNumberOfControls;
myState.currentColors = mCurrentColors;
myState.originalColors = mOriginalColors;
// Restore the old state when the activity or dialog is being paused
for (int i = 0; i < mNumberOfControls; i++) {
displayGammaCalibration.setGamma(i, mOriginalColors[i]);
}
mOriginalColors = null;
return myState;
}
@Override protected void onRestoreInstanceState(Parcelable state) {
if (state == null || !state.getClass().equals(SavedState.class)) {
// Didn't save state for us in onSaveInstanceState
super.onRestoreInstanceState(state);
return;
}
SavedState myState = (SavedState) state;
super.onRestoreInstanceState(myState.getSuperState());
mNumberOfControls = myState.controlCount;
mOriginalColors = myState.originalColors;
mCurrentColors = myState.currentColors;
for (int index = 0; index < mNumberOfControls; index++) {
for (int color = 0; color < BAR_COLORS.length; color++) {
mSeekBars[index][color].setGamma(Integer.valueOf(mCurrentColors[index][color]));
}
displayGammaCalibration.setGamma(index, TextUtils.join(" ", mCurrentColors[index]));
}
}
public static boolean isSupported() {
return new DisplayGammaCalibration(App.get()).isSupported();
}
private static class SavedState extends BaseSavedState {
int controlCount;
String[] originalColors;
String[][] currentColors;
public SavedState(Parcelable superState) {
super(superState);
}
public SavedState(Parcel source) {
super(source);
controlCount = source.readInt();
originalColors = source.createStringArray();
currentColors = new String[controlCount][];
for (int i = 0; i < controlCount; i++) {
currentColors[i] = source.createStringArray();
}
}
@Override public void writeToParcel(@NonNull Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(controlCount);
dest.writeStringArray(originalColors);
for (int i = 0; i < controlCount; i++) {
dest.writeStringArray(currentColors[i]);
}
}
public static final Creator<SavedState> CREATOR =
new Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
private class GammaSeekBar implements SeekBar.OnSeekBarChangeListener {
private int mControlIndex;
private int mColorIndex;
private int mMin;
private SeekBar mSeekBar;
private TextView mValue;
public GammaSeekBar(int controlIndex, int colorIndex, ViewGroup container) {
mControlIndex = controlIndex;
mColorIndex = colorIndex;
mMin = displayGammaCalibration.getMinValue(controlIndex);
mValue = (TextView) container.findViewById(R.id.color_value);
mSeekBar = (SeekBar) container.findViewById(R.id.color_seekbar);
TextView label = (TextView) container.findViewById(R.id.color_text);
label.setText(container.getContext().getString(BAR_COLORS[colorIndex]));
mSeekBar.setMax(displayGammaCalibration.getMaxValue(controlIndex) - mMin);
mSeekBar.setProgress(0);
mValue.setText(String.valueOf(mSeekBar.getProgress() + mMin));
// this must be done last, we don't want to apply our initial value to the hardware
mSeekBar.setOnSeekBarChangeListener(this);
}
public void setGamma(int gamma) {
mSeekBar.setProgress(gamma - mMin);
}
@Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser) {
mCurrentColors[mControlIndex][mColorIndex] = String.valueOf(progress + mMin);
displayGammaCalibration.setGamma(mControlIndex,
TextUtils.join(" ", mCurrentColors[mControlIndex]));
}
mValue.setText(String.valueOf(progress + mMin));
}
@Override public void onStartTrackingTouch(SeekBar seekBar) {
// Do nothing
}
@Override public void onStopTrackingTouch(SeekBar seekBar) {
// Do nothing
}
}
}