I am a newbie at android studio and am trying to make a simple todolist app. I have a daily alarm set up using Alarm Manager but I would like the user to be able to change the timing that this daily alarm goes off in the settings page. Therefore, I need a timepicker in preferencescreen and I have learnt that you can do this using custom DialogPreferences.
I have tried to follow this tutorial http://www.infiniteimprob.com/blog/custom-preferences/ but it shows this exceptioncom.example.todolist.TimePickerPreference cannot be cast to androidx.preference.Preference I have a feeling that this is due to compatibility issues because I used the androidx library for my other imports. I think that this will be solved if I were to import the androidx.preference.DialogPreference instead of android.preference.DialogPreference; Can someone tell me how I can go about this change? I have read this answer Difference between DialogPreference before and after AndroidX but I still do not really know how to execute it.
This is my timepickerpreference class as followed on the website:
import android.content.res.TypedArray;
import android.os.Build;
import android.preference.DialogPreference;
import android.util.AttributeSet;
import android.view.View;
import android.widget.TextView;
import android.widget.TimePicker;
public class TimePickerPreference extends DialogPreference {
private static final int DEFAULT_TIME = 0;
private TimePicker mTimePicker;
private int currentTime;
public TimePickerPreference(Context context, AttributeSet attrs){
super(context, attrs);
setDialogLayoutResource(R.layout.timepickerpreferencelayout);
setPositiveButtonText("ok");
setNegativeButtonText("cancel");
}
private static int getTime(int hour, int minute){
return hour*60*60*1000 + minute*60*1000;
}
private static int getHour(int value){
return (int)(value / (60*60*1000));
}
private static int getMinute(int value){
return (int)(value % (60*60*1000));
}
#Override
protected View onCreateDialogView() {
View view = super.onCreateDialogView();
mTimePicker = (TimePicker) view.findViewById(R.id.timePicker);
mTimePicker.setIs24HourView(true);
if (Build.VERSION.SDK_INT >= 23)
{
mTimePicker.setHour(getHour(currentTime));
mTimePicker.setMinute(getMinute(currentTime));
}
else
{
mTimePicker.setCurrentHour(getHour(currentTime));
mTimePicker.setCurrentMinute(getMinute(currentTime));
}
mTimePicker.setOnTimeChangedListener(new TimePicker.OnTimeChangedListener()
{
#Override
public void onTimeChanged(TimePicker timePicker, int hour, int minute)
{
TimePickerPreference.this.currentTime = getTime(hour, minute);
}
});
TextView messageTextView = (TextView) view.findViewById(R.id.messageTextView);
if (getDialogMessage() == null || getDialogMessage().toString().trim().length() == 0)
messageTextView.setVisibility(View.GONE);
messageTextView.setText(getDialogMessage());
return view;
}
#Override
protected Object onGetDefaultValue(TypedArray a, int index)
{
return a.getInt(index, DEFAULT_TIME);
}
#Override
protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue)
{
if(restorePersistedValue)
{
currentTime = this.getPersistedInt((int) (defaultValue == null ? DEFAULT_TIME : defaultValue));
}
else
{
currentTime = (int) defaultValue;
if (shouldPersist())
persistInt(currentTime);
}
}
#Override
protected void onDialogClosed(boolean positiveResult)
{
if (positiveResult)
persistInt(currentTime);
}
}
Related
I am making a simple alarm app that holds alarm data in a databased generated with Room, and uses a viewmodel to update the RecyclerView adapter's list. However, when I check/uncheck the switch on one item in the RecyclerView it sometimes causes another item's switch to check/uncheck itself, and I'm not sure why. Any help on what is wrong with my code is appreciated. Here is the java code:
MainActivity:
public class MainActivity extends AppCompatActivity {
private List<Alarms> mainAlarmsList;
private RecyclerView recyclerView;
private RecyclerView.LayoutManager layoutManager;
private AlarmsAdapter alarmsAdapter;
private AlarmsAdapter.alarmedInterface coolInterface;
private ImageButton imageButton, clearButton;
private AlarmsViewModel alarmsViewModel;
private AlarmManager epicAlarmManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
epicAlarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
imageButton = (ImageButton) findViewById(R.id.alarm_button);
clearButton = (ImageButton) findViewById(R.id.clear_alarms_button);
imageButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
createTimePicker(MainActivity.this);
}
});
alarmsViewModel = ViewModelProviders.of(this).get(AlarmsViewModel.class);
clearButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
alarmsViewModel.deleteAllAlarms();
}
});
recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
alarmsAdapter = new AlarmsAdapter(this);
//alarmsAdapter.setAlarms(alarmsViewModel.getAllAlarms().getValue());
coolInterface = new AlarmsAdapter.alarmedInterface() {
#Override
public void onSwitch(int position, boolean isChecked, CompoundButton compoundButton) {
Alarms currentAlarm = mainAlarmsList.get(position);
String concatenatedString;
if(isChecked){
currentAlarm.setOffOn(1);
concatenatedString = currentAlarm.getId() + " is on " + currentAlarm.getOffOn();
compoundButton.setChecked(true);
}
else {
currentAlarm.setOffOn(0);
concatenatedString = currentAlarm.getId() + " is off " + currentAlarm.getOffOn();
compoundButton.setChecked(false);
}
Toast.makeText(MainActivity.this,concatenatedString,Toast.LENGTH_LONG).show();
new updateAsync(alarmsViewModel).execute(currentAlarm);
}
};
recyclerView.setAdapter(alarmsAdapter);
alarmsViewModel.getAllAlarms().observe(this, new Observer<List<Alarms>>() {
#Override
public void onChanged(List<Alarms> alarms) {
alarmsAdapter.setAlarms(alarms);
alarmsAdapter.setAlarmInterfaceInstance(coolInterface);
alarmsAdapter.notifyDataSetChanged();
mainAlarmsList = alarms;
}
});
layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
}
public void createTimePicker(Context context){
final Calendar calendar = Calendar.getInstance();
final int hrDay = Calendar.HOUR_OF_DAY;
final int min = Calendar.MINUTE;
TimePickerDialog dialog = new TimePickerDialog(context, new TimePickerDialog.OnTimeSetListener()
{
#Override
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
Alarms myAlarm = new Alarms(hourOfDay,minute);
myAlarm.setOffOn(1);
alarmsViewModel.insertAlarms(myAlarm);
alarmsAdapter.notifyDataSetChanged();
calendar.set(Calendar.HOUR_OF_DAY, myAlarm.getHour());
calendar.set(Calendar.MINUTE, myAlarm.getMin());
alarmTime(myAlarm,calendar);
}
}, hrDay,min,true);
dialog.show();
}
public void alarmTime(Alarms alarms,Calendar calendar){
Intent intent = new Intent(MainActivity.this, AlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(MainActivity.this,
alarms.getId(),intent,0);
long hrlong = alarms.getHour();
long minlong = alarms.getMin();
epicAlarmManager.setExact(AlarmManager.RTC_WAKEUP,calendar.getTimeInMillis() ,pendingIntent);
}
class updateAsync extends AsyncTask<Alarms,Void,Void>{
private AlarmsViewModel viewModel;
updateAsync(AlarmsViewModel alarmsViewModel1){
viewModel = alarmsViewModel1;
}
#Override
protected Void doInBackground(Alarms... alarms) {
viewModel.updateAlarms(alarms[0]);
return null;
}
}
}
Alarms.java
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
#Entity(tableName = "Alarms")
public class Alarms {
#PrimaryKey(autoGenerate = true)
private int id;
#ColumnInfo(name = "hour")
private int hour;
#ColumnInfo(name = "min")
private int min;
#ColumnInfo(name = "offOn")
private int offOn;
public Alarms(int hour, int min){
this.hour = hour;
this.min = min;
offOn = 1;
}
public void setId(int id) {
this.id = id;
}
public void setMin(int min) {
this.min = min;
}
public void setHour(int hour) {
this.hour = hour;
}
public void setOffOn(int offOn) {
this.offOn = offOn;
}
public int getMin() {
return min;
}
public int getHour() {
return hour;
}
public int getOffOn() {
return offOn;
}
public int getId(){
return id;
}
}
AlarmsAdapter.java
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CompoundButton;
import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.SwitchCompat;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
public class AlarmsAdapter extends RecyclerView.Adapter<AlarmsAdapter.AlarmsViewHolder> {
private Context context;
private List<Alarms> alarms;
private alarmedInterface alarmInterfaceInstance;
interface alarmedInterface{
public void onSwitch(int position, boolean checked, CompoundButton compoundButton);
}
public AlarmsAdapter(Context context){
this.context = context;
}
public void setAlarmInterfaceInstance(alarmedInterface alarmInterfaceInstance) {
this.alarmInterfaceInstance = alarmInterfaceInstance;
}
public void setAlarms(List<Alarms> alarms) {
this.alarms = alarms;
}
#NonNull
#Override
public AlarmsViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.list_item,parent,false);
return new AlarmsViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull AlarmsAdapter.AlarmsViewHolder holder, final int position) {
if(alarms.isEmpty()){
holder.textView.setText("No alarms");
return;
}
else{
Alarms myAlarm = alarms.get(position);
if(myAlarm.getOffOn() == 1){
holder.aSwitch.setChecked(true);
}
else if(myAlarm.getOffOn() == 0){
holder.aSwitch.setChecked(false);
}
String x = alarms.get(position).getHour() + ":" + alarms.get(position).getMin();
holder.aSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
alarmInterfaceInstance.onSwitch(position, isChecked,buttonView);
}
});
holder.textView.setText(x);
}
}
#Override
public int getItemCount() {
if(alarms != null) {
return alarms.size();
}
else {
return 0;
}
}
public class AlarmsViewHolder extends RecyclerView.ViewHolder{
TextView textView;
SwitchCompat aSwitch;
public AlarmsViewHolder(View view){
super(view);
textView = view.findViewById(R.id.textView);
aSwitch = view.findViewById(R.id.switch1);
}
}
}
AlarmsViewModel.java
import android.app.Application;
import android.content.Context;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import java.util.List;
public class AlarmsViewModel extends AndroidViewModel {
private AlarmsRepository repository;
private LiveData<List<Alarms>> allAlarms;
public AlarmsViewModel(Application application){
super(application);
repository = new AlarmsRepository(application.getApplicationContext());
allAlarms = repository.getAlarmsList();
}
public void deleteAllAlarms(){
repository.deleteAllAlarms();
}
public void updateAlarms(Alarms alarms){
repository.updateAlarm(alarms);
}
public void insertAlarms(Alarms alarms){
repository.insertAlarm(alarms);
}
public LiveData<List<Alarms>> getAllAlarms() {
return allAlarms;
}
}
AlarmdDao.java
import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Update;
import java.util.List;
#Dao
public interface AlarmDao {
#Query("Select * From Alarms")
public LiveData<List<Alarms>> getAllAlarms();
#Insert
public void insertAlarm(Alarms alarms);
#Update
public void updateAlarm(Alarms alarms);
#Delete
public void deleteAlarm(Alarms alarms);
#Query("Delete From Alarms")
public void deleteAllAlarms();
}
AlarmsDatabase
import android.app.Application;
import android.content.Context;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
#Database(entities = {Alarms.class},version = 1,exportSchema = false)
public abstract class AlarmsDatabase extends RoomDatabase {
private static AlarmsDatabase alarmsDatabase;
private Context context;
public abstract AlarmDao alarmDao();
public static AlarmsDatabase AlarmsDatabaseBuilder(Context context){
if(alarmsDatabase == null){
synchronized (AlarmsDatabase.class){
if (alarmsDatabase == null){
alarmsDatabase = Room.databaseBuilder(context.getApplicationContext(),
AlarmsDatabase.class,"AlarmsDatabase").build();
}
}
}
return alarmsDatabase;
}
}
AlarmReceiver.java
package com.pbaileyapps.android.chatpal;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context,"You just recieved a toast!",Toast.LENGTH_LONG).show();
}
}
I have had similar issue with JSON objects, in my case a set of data is copied into two different JSON lists that both display differently in a recycle view. One of them displays the original data and the second has modified the data. However the modification appeared on both becuase the copies still linked to the original data behind the scences. I fixed this by making a 'deep copy'. It is possible that your alarm objects are not being deep copied into the list so two or more list elements have the same origin.
I am actually searching for an android clock view whose time can be set in the activity and then it will start working from that setted time.I saw the TextClock view but it seemed that only the timezone can be set.Can anyone help me?
Use this class, might help you, You can change date time whatever you want
import android.content.Context;
import android.database.ContentObserver;
import android.os.Handler;
import android.os.SystemClock;
import android.provider.Settings;
import android.support.v7.widget.AppCompatTextView;
import android.text.format.DateFormat;
import android.util.AttributeSet;
import java.util.Calendar;
public class DigitalClock extends AppCompatTextView {
Calendar mCalendar;
private FormatChangeObserver mFormatChangeObserver;
private Runnable mTicker;
private Handler mHandler;
private boolean mTickerStopped = false;
String mFormat;
public DigitalClock(Context context) {
super(context);
initClock();
}
public DigitalClock(Context context, AttributeSet attrs) {
super(context, attrs);
initClock();
}
private void initClock() {
if (mCalendar == null) {
mCalendar = Calendar.getInstance();
}
}
#Override
protected void onAttachedToWindow() {
mTickerStopped = false;
super.onAttachedToWindow();
mFormatChangeObserver = new FormatChangeObserver();
getContext().getContentResolver().registerContentObserver(
Settings.System.CONTENT_URI, true, mFormatChangeObserver);
setFormat();
mHandler = new Handler();
mTicker = new Runnable() {
public void run() {
if (mTickerStopped) return;
mCalendar.setTimeInMillis(System.currentTimeMillis());
setText(DateFormat.format(mFormat, mCalendar));
invalidate();
long now = SystemClock.uptimeMillis();
long next = now + (1000 - now % 1000);
mHandler.postAtTime(mTicker, next);
}
};
mTicker.run();
}
#Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mTickerStopped = true;
getContext().getContentResolver().unregisterContentObserver(
mFormatChangeObserver);
}
private void setFormat() {
mFormat = "dd MMM yy , hh:mm ss a ";
}
private class FormatChangeObserver extends ContentObserver {
public FormatChangeObserver() {
super(new Handler());
}
#Override
public void onChange(boolean selfChange) {
setFormat();
}
}
#Override
public CharSequence getAccessibilityClassName() {
//noinspection deprecation
return DigitalClock.class.getName();
}
}
I'm first using Android Preferences and encountered unexpected problem.
I'm extend DialogPreference class and all works fine except one thing: in method onDialogClosing(boolean positiveResult) I'm receiving false no matter what button I'v pressed.
What I'm doing wrong?
Whole code of the class is listed below.
package edu.kpi.ept.labwork1;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.TypedArray;
import android.preference.DialogPreference;
import android.util.AttributeSet;
import android.view.View;
import android.widget.EditText;
public class PositivePickerPreference extends DialogPreference {
private static int DEFAULT_VALUE = 0;
private int selectedValue;
private EditText intEdit;
public PositivePickerPreference(Context context, AttributeSet attrs) {
super(context, attrs);
this.setDialogLayoutResource(R.layout.int_pick_pref_dialog);
this.setPositiveButtonText(R.string.preference_ok);
this.setNegativeButtonText(R.string.preference_cancel);
}
#Override
protected void onBindDialogView(View view) {
super.onBindDialogView(view);
intEdit = (EditText) view.findViewById(R.id.intEdit);
selectedValue = getPersistedInt(DEFAULT_VALUE);
intEdit.setText(Integer.toString(selectedValue));
}
public void onClick (DialogInterface dialog, int which) {
super.onClick();
selectedValue = Integer.parseInt(intEdit.getText().toString());
}
#Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
if (positiveResult) {
persistInt(selectedValue);
}
}
#Override
protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
super.onSetInitialValue(restorePersistedValue, defaultValue);
if (restorePersistedValue) {
selectedValue = getPersistedInt(DEFAULT_VALUE);
} else {
selectedValue = (Integer) defaultValue;
persistInt(selectedValue);
}
}
#Override
protected Object onGetDefaultValue(TypedArray a, int index) {
return a.getInteger(index, DEFAULT_VALUE);
}
}
Just had this same issue. Its because of the onClick handler:
public void onClick (DialogInterface dialog, int which) {
super.onClick();
selectedValue = Integer.parseInt(intEdit.getText().toString());
}
Remove it, and you won't have the issue. If you need to know the button pressed, then just check the button type in that event handler block. For example
#Override
public void onClick(DialogInterface dialog, int which) {
buttonPress = which;
}
#Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
if (buttonPress == DialogInterface.BUTTON_NEGATIVE) {
String computerName = _etComputerName.getText().toString();
SharedPreferences computers = _context.getSharedPreferences(
"COMPUTERS", 0);
SharedPreferences.Editor editor = computers.edit();
editor.remove(computerName);
editor.commit();
this.callChangeListener(-1);
}
}
I want to remove the seconds and AM/PM indication from the digital clock widget but I don't know how to do this.
It is hard-coded in the DigitalClock class, so you can't change it programatically. The only way to change it is to re-implement DigitalClock widget. Yo can get source code from here.
You need to copy this code in your project, rename it like "MyDigitalClock" and use this one as your widget. As you will see in the implementation, there are two variables you should focus on to get what you want:
private final static String m12 = "h:mm:ss aa";
private final static String m24 = "k:mm:ss";
ss represents seconds and aa represents am/pm.
You could use addTextChangedListener() and then take out the seconds and AM/PM in afterTextChanged(). This example just removes the seconds:
DigitalClock dc = (DigitalClock) findViewById(R.id.currentTime);
dc.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
#Override
public void afterTextChanged(Editable s) {
if (s.charAt(4) == ':') {
s.delete(4, 7);
} else if (s.charAt(5) == ':') {
s.delete(5, 8);
}
}
});
Let me little extend and refresh perfect answer from Erol.
Actually you need add one file to your project and update xml layout.
Sample could be taken from here: /frameworks/base/core/java/android/widget/DigitalClock.java
Adapted DigitalClock.java:
import android.content.Context;
import android.database.ContentObserver;
import android.os.Handler;
import android.os.SystemClock;
import android.provider.Settings;
import android.text.format.DateFormat;
import android.util.AttributeSet;
import android.widget.TextView;
import java.text.SimpleDateFormat;
import java.util.Calendar;
/**
* Like AnalogClock, but digital. Shows seconds.
*
* #deprecated It is recommended you use {#link TextClock} instead.
*/
#Deprecated
public class DigitalClock extends TextView {
// FIXME: implement separate views for hours/minutes/seconds, so
// proportional fonts don't shake rendering
Calendar mCalendar;
#SuppressWarnings("FieldCanBeLocal") // We must keep a reference to this observer
private FormatChangeObserver mFormatChangeObserver;
private Runnable mTicker;
private Handler mHandler;
private boolean mTickerStopped = false;
java.text.DateFormat mFormat;
public DigitalClock(Context context) {
super(context);
initClock();
}
public DigitalClock(Context context, AttributeSet attrs) {
super(context, attrs);
initClock();
}
private void initClock() {
if (mCalendar == null) {
mCalendar = Calendar.getInstance();
}
mFormatChangeObserver = new FormatChangeObserver();
getContext().getContentResolver().registerContentObserver(
Settings.System.CONTENT_URI, true, mFormatChangeObserver);
setFormat();
}
#Override
protected void onAttachedToWindow() {
mTickerStopped = false;
super.onAttachedToWindow();
mHandler = new Handler();
/**
* requests a tick on the next hard-second boundary
*/
mTicker = new Runnable() {
public void run() {
if (mTickerStopped) return;
mCalendar.setTimeInMillis(System.currentTimeMillis());
// setText(DateFormat.format(mFormat, mCalendar));
setText(mFormat.format(mCalendar.getTime()));
invalidate();
long now = SystemClock.uptimeMillis();
long next = now + (1000 - now % 1000);
mHandler.postAtTime(mTicker, next);
}
};
mTicker.run();
}
#Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mTickerStopped = true;
}
private void setFormat() {
// mFormat = DateFormat.getTimeFormatString(getContext()); //<== ORIGINAL
// mFormat = DateFormat.getTimeFormat(getContext()); //<== Fixed with PM
// mFormat = new SimpleDateFormat("hh:mm a");
mFormat = new SimpleDateFormat("h:mm"); //<== Your variant
}
private class FormatChangeObserver extends ContentObserver {
public FormatChangeObserver() {
super(new Handler());
}
#Override
public void onChange(boolean selfChange) {
setFormat();
}
}
#Override
public CharSequence getAccessibilityClassName() {
//noinspection deprecation
return DigitalClock.class.getName();
}
}
layout.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".HomeActivity" >
<HERE_YOUR_PACKAGE_NAME.DigitalClock
android:id="#+id/digitalClock"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#android:color/background_light"
android:layout_centerInParent="true"
android:textSize="130dp"/>
</RelativeLayout>
Result:
i'm trying to work with building my own Preference classes, and having a bit of trouble. it looks like the way of saving data to a preference is via the "persist" group of methods within the Preference class. however, in my preference, i open a color picker dialog, and i need to save the preference from within the dialog's colorChanged override. whenever i run the app and try to change the color preference, i get:
06-05 10:21:46.396: ERROR/AndroidRuntime(516): FATAL EXCEPTION: main
java.lang.IllegalAccessError: tried to access method android.preference.Preference.persistInt:(IIII)V from class android.preference.ColorSelectionPreference$1
at android.preference.ColorSelectionPreference$1.colorChanged(ColorSelectionPreference.java:55)
at android.apis.graphics.ColorPickerDialog.onClick(ColorPickerDialog.java:168)
(UPDATE: 6/5/12 12:20) i tried using callChangeListener to force the onPreferenceChangeListener to trigger, but it crashes with an identical error. without callChangeListener, the preference data is (probably) saved, but the onPreferenceChangeListener doesn't get triggered:
06-05 12:20:23.691: ERROR/AndroidRuntime(2834): FATAL EXCEPTION: main
java.lang.IllegalAccessError: tried to access method android.preference.ColorSelectionPreference.callChangeListener:(IIII)V from class android.preference.ColorSelectionPreference$1
at android.preference.ColorSelectionPreference$1.colorChanged(ColorSelectionPreference.java:52)
at android.apis.graphics.ColorPickerDialog.onClick(ColorPickerDialog.java:168)
here's the actual class:
package android.preference;
import android.apis.graphics.ColorPickerDialog;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.util.AttributeSet;
public class ColorSelectionPreference extends Preference {
private Context mContext;
private int mColor;
public ColorSelectionPreference(Context context) {
super(context);
mContext = context;
}
public ColorSelectionPreference(Context context, AttributeSet attr) {
super(context, attr);
mContext = context;
}
public int getColor() {
return mColor;
}
public void setColor(int color) {
mColor = color;
}
#Override
public void onClick() {
//get original preference
//set ColorPickerDialog to original preference color or default color
ColorPickerDialog dialog = new ColorPickerDialog(mContext, new ColorPickerDialog.OnColorChangedListener() {
public void colorChanged(int a, int r, int g, int b) {
int selectedColor = Color.argb(a,r,g,b);
setColor(selectedColor);
/*** crashes on callChangeListener ***/
//SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
//SharedPreferences.Editor edit = prefs.edit();
//edit.putInt(getKey(), selectedColor);
//edit.commit();
//callChangeListener(selectedColor);
/*** the offending code, error refers to this line ***/
persistInt(selectedColor);
/*** tried this as well by request on IRC ***/
//ColorSelectionPreference.this.persistInt(selectedColor);
}
}, mColor);
dialog.show();
}
}
this is a hacky workaround that uses a Handler to call back to the main class from the inner class. it isn't pretty, but it works.
package android.preference;
import android.apis.graphics.ColorPickerDialog;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
public class ColorSelectionPreference extends Preference {
private Context mContext;
private int mColor;
private final Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
if(msg.getData().containsKey("color")) {
int color = msg.getData().getInt("color");
setColor(color);
}
}
};
public ColorSelectionPreference(Context context) {
super(context);
mContext = context;
}
public ColorSelectionPreference(Context context, AttributeSet attr) {
super(context, attr);
mContext = context;
}
public int getColor() {
return mColor;
}
public void setColor(int color) {
mColor = color;
persistInt(new Integer(color));
}
#Override
public void onClick() {
//get original preference
//set ColorPickerDialog to original preference color or default color
ColorPickerDialog dialog = new ColorPickerDialog(mContext, new ColorPickerDialog.OnColorChangedListener() {
public void colorChanged(int a, int r, int g, int b) {
int selectedColor = Color.argb(a,r,g,b);
Bundle bundle = new Bundle();
bundle.putInt("color", selectedColor);
Message msg = new Message();
msg.setData(bundle);
mHandler.sendMessage(msg);
//setColor(selectedColor);
/*** tried this, but the onPreferenceChangedListener never gets triggered, so this won't work ***/
//SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
//SharedPreferences.Editor edit = prefs.edit();
//edit.putInt(getKey(), selectedColor);
//edit.commit();
//callChangeListener(selectedColor);
/*** the offending code, error refers to this line ***/
//container.
/*** tried this as well by request on IRC ***/
//ColorSelectionPreference.this.persistInt(selectedColor);
}
}, mColor);
dialog.show();
}
}
I just encountered this problem myself. I do not pretend to understand it, but this seems like an easier fix:
// ...why is this necessary? what is special about Preference.persistInt?
#Override protected boolean persistInt(int value)
{
return super.persistInt(value);
}