Search code examples
javaandroidandroid-studioandroid-fragmentsandroid-activity

MainActivity cannot be cast to android.app.TimePickerDialog$OnTimeSetListener Cannot be resolved


I dont know why i am change an activity to fragment there it is not working as expected.

020-06-22 14:18:38.973 13510-13510/com.vikaskonaparthi.time E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.vikaskonaparthi.time, PID: 13510
java.lang.ClassCastException: com.vikaskonaparthi.time.MainActivity cannot be cast to android.app.TimePickerDialog$OnTimeSetListener
    at com.vikaskonaparthi.time.dialogs.TimePickerDialogFragment.onAttach(TimePickerDialogFragment.java:29)
    at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1247)
    at android.app.FragmentManagerImpl.addAddedFragments(FragmentManager.java:2431)
    at android.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2210)
    at android.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2166)
    at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:2067)
    at android.app.FragmentManagerImpl$1.run(FragmentManager.java:742)
    at android.os.Handler.handleCallback(Handler.java:883)
    at android.os.Handler.dispatchMessage(Handler.java:100)
    at android.os.Looper.loop(Looper.java:214)
    at android.app.ActivityThread.main(ActivityThread.java:7397)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:935)

Here is Timepicker dialog fragment

public class TimePickerDialogFragment extends DialogFragment  {
private int hour;
private int minute;
private TimePickerDialog.OnTimeSetListener listener;

public static final String FRAGMENT_TAG = "time_picker";
public static final String BUNDLE_KEY_ALARM_HOUR = "alarm_hour";
public static final String BUNDLE_KEY_ALARM_MINUTE = "alarm_minute";


@Override
public void onAttach(Context context) {
    super.onAttach(context);
    listener = (TimePickerDialog.OnTimeSetListener) context;
}

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    Bundle bundle = getArguments();
    hour = bundle.getInt(BUNDLE_KEY_ALARM_HOUR);
    minute = bundle.getInt(BUNDLE_KEY_ALARM_MINUTE);

    final AlarmTimePickerDialog timePickerDialog = new AlarmTimePickerDialog(getActivity(), listener, hour, minute, true);

    LayoutInflater inflater = LayoutInflater.from(getActivity());
    View customTitleView = inflater.inflate(R.layout.partial_timepicker_dialog_title, null);

    timePickerDialog.setCustomTitle(customTitleView);

    timePickerDialog.setOnShowListener(new DialogInterface.OnShowListener() {
        @Override
        public void onShow(DialogInterface dialog) {
            timePickerDialog.showTimeLeft(hour, minute);
        }
    });

    return timePickerDialog;
}
}

here is alert dialog

public class AlarmTimePickerDialog extends TimePickerDialog {

public AlarmTimePickerDialog(Context context, OnTimeSetListener listener, int hourOfDay, int minute,
                             boolean is24HourView) {
    super(context, listener, hourOfDay, minute, is24HourView);
}

@Override
public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
    super.onTimeChanged(view, hourOfDay, minute);
    showTimeLeft(hourOfDay, minute);
}

@Override
public void setCustomTitle(View customTitleView) {
    if (getContext().getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { //title is displayed in portrait orientation only
        super.setCustomTitle(customTitleView);
    }
}

public void showTimeLeft(int hour, int minute) {
    if (getContext().getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
        TextView textView = (TextView) this.findViewById(R.id.textview_timepickerdialog_timeleft);
        AlarmTime time = new AlarmTime(hour, minute);
        textView.setText(getContext().getString(R.string.all_time_left, time.getHoursLeft(), time.getMinutesLeft()));
    }
}
}

I had seen many like these and tried but nothing worked.Actually this code is working fine in as activity but when i changed activity to fragment the errors are throwing.

Thank You in advance.

public class Fragment1 extends Fragment implements
    IntervalDialogFragment.IntervalDialogListener,
    NumberOfAlarmsDialogFragment.NumberOfAlarmsDialogListener,
    TimePickerDialog.OnTimeSetListener{

SwitchCompat onOffSwitch;
ListView alarmsListView;
TextView intervalBetweenAlarmsTextView;
TextView numberOfAlarmsTextView;
TextView firstAlarmTextView;
TextView timeLeftTextView;
LinearLayout firstAlarmLayout;
LinearLayout intervalLayout;
LinearLayout numberOfAlarmsLayout;
AlarmsListHelper alarmsListHelper;
SharedPreferencesHelper sharPrefHelper;
TimerManager timerManager;
AlarmParams alarmParams;
BroadcastReceiver timeLeftReceiver;
private final String LOG_TAG = Fragment1.class.getSimpleName();

final int ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE = 45;
final float DISPLAYED_NUMBERS_SIZE_RELATIVE_TO_TEXT_PROPORTION = 2f;  // number of alarms, first alarm, interval values text size is larger than text around them

@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

    View root = inflater.inflate(R.layout.fragment1,container,false);
    alarmsListView = (ListView) root.findViewById(R.id.listview_main_alarmslist);
    onOffSwitch = (SwitchCompat) root.findViewById(R.id.switch_main);
    intervalBetweenAlarmsTextView = (TextView) root.findViewById(R.id.textview_main_interval);
    numberOfAlarmsTextView = (TextView) root.findViewById(R.id.textview_main_numberofalarms);
    firstAlarmLayout = (LinearLayout) root.findViewById(R.id.layout_main_firstalarm);
    firstAlarmTextView = (TextView) root.findViewById(R.id.textview_main_firstalarm_time);
    timeLeftTextView = (TextView) root.findViewById(R.id.textview_main_timeleft);
    intervalLayout = (LinearLayout) root.findViewById(R.id.layout_main_interval);
    numberOfAlarmsLayout = (LinearLayout) root.findViewById(R.id.layout_main_numberofalarms);

    sharPrefHelper = new SharedPreferencesHelper(getActivity());
    sharPrefHelper.printAll();

    alarmParams = sharPrefHelper.getParams();
    timerManager = new TimerManager(getActivity());
    alarmsListHelper = new AlarmsListHelper(getActivity(), alarmsListView);

    showFirstAlarmTime(alarmParams.firstAlarmTime.toString());
    showTimeLeft(alarmParams);

    showInterval(sharPrefHelper.getIntervalStr());
    showNumberOfAlarms(sharPrefHelper.getNumberOfAlarmsStr());
    onOffSwitch.setChecked(sharPrefHelper.isAlarmTurnedOn());

    alarmsListHelper.showList(alarmParams);

    onOffSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            alarmParams.turnedOn = isChecked;
            if (isChecked) {
                checkNotificationPolicy();
                checkOverlayPermission();
                timerManager.startSingleAlarmTimer(alarmParams.firstAlarmTime.toMillis());
                showToast(getString(R.string.main_alarm_turned_on_toast));
                sharPrefHelper.setNumberOfAlreadyRangAlarms(0);
            } else {
                timerManager.cancelTimer();
                showToast(getString(R.string.main_alarm_turned_off_toast));
            }
            alarmsListHelper.showList(alarmParams);
            showTimeLeft(alarmParams);
            sharPrefHelper.setAlarmState(isChecked);
        }
    });

    intervalLayout.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            IntervalDialogFragment dialog = new IntervalDialogFragment();
            Bundle intervalBundle = new Bundle();
            intervalBundle.putString(IntervalDialogFragment.BUNDLE_KEY_INTERVAL, sharPrefHelper.getIntervalStr());
            dialog.setArguments(intervalBundle);
            dialog.show(getActivity().getFragmentManager(), IntervalDialogFragment.FRAGMENT_TAG);
        }
    });

    numberOfAlarmsLayout.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            NumberOfAlarmsDialogFragment dialog = new NumberOfAlarmsDialogFragment();
            Bundle numberOfAlarmsBundle = new Bundle();
            numberOfAlarmsBundle.putString(NumberOfAlarmsDialogFragment.BUNDLE_KEY_NUMBER_OF_ALARMS, sharPrefHelper.getNumberOfAlarmsStr());
            dialog.setArguments(numberOfAlarmsBundle);
            dialog.show(getActivity().getFragmentManager(), NumberOfAlarmsDialogFragment.FRAGMENT_TAG);
        }
    });

    firstAlarmLayout.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Bundle timePickerBundle = new Bundle();
            timePickerBundle.putInt(TimePickerDialogFragment.BUNDLE_KEY_ALARM_HOUR, sharPrefHelper.getHour());
            timePickerBundle.putInt(TimePickerDialogFragment.BUNDLE_KEY_ALARM_MINUTE, sharPrefHelper.getMinute());

            TimePickerDialogFragment timePicker = new TimePickerDialogFragment();

            timePicker.setArguments(timePickerBundle);
            timePicker.show(getActivity().getFragmentManager(), TimePickerDialogFragment.FRAGMENT_TAG);
        }
    });

    return root;


}

@Override
public void onResume() {
    super.onResume();
    showTimeLeft(alarmParams);
    timeLeftReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context ctx, Intent intent) {
            if (intent.getAction().compareTo(Intent.ACTION_TIME_TICK) == 0) {  //i.e. every minute
                showTimeLeft(alarmParams);
            }
        }
    };
    getActivity().registerReceiver(timeLeftReceiver, new IntentFilter(Intent.ACTION_TIME_TICK));
}



@Override
public void onPause() {
    super.onPause();
    if (timeLeftReceiver != null) {
        getActivity().unregisterReceiver(timeLeftReceiver);
    }
}


@Override
public boolean onOptionsItemSelected(MenuItem item) {
    int id = item.getItemId();
    switch (id) {
        case R.id.action_settings: {
            Intent intent = new Intent(getActivity(), PrefActivity.class);
            startActivity(intent);
            break;
        }
    }
    return super.onOptionsItemSelected(item);
}


@Override
public void onIntervalChanged(String intervalStr) {
    showInterval(intervalStr);
    alarmParams.interval = Integer.parseInt(intervalStr);
    alarmsListHelper.showList(alarmParams);
    resetTimerIfTurnedOn();
    sharPrefHelper.setInterval(intervalStr);
}

@Override
public void onNumberOfAlarmsChanged(String numberOfAlarmsStr) {
    showNumberOfAlarms(numberOfAlarmsStr);
    alarmParams.numberOfAlarms = Integer.parseInt(numberOfAlarmsStr);
    alarmsListHelper.showList(alarmParams);
    resetTimerIfTurnedOn();
    sharPrefHelper.setNumberOfAlarms(numberOfAlarmsStr);
}

@Override
public void onTimeSet(TimePicker view, int hour, int minute) {
    AlarmTime alarmTime = new AlarmTime(hour, minute);
    alarmParams.firstAlarmTime = alarmTime;
    showFirstAlarmTime(alarmTime.toString());
    alarmsListHelper.showList(alarmParams);
    showTimeLeft(alarmParams);
    sharPrefHelper.setNumberOfAlreadyRangAlarms(0);
    resetTimerIfTurnedOn();
    sharPrefHelper.setTime(alarmTime);
}

private void showToast(String message) {
    Toast.makeText(getActivity(), message, Toast.LENGTH_SHORT).show();
}

private void resetTimerIfTurnedOn() {
    if (onOffSwitch.isChecked()) {
        timerManager.resetSingleAlarmTimer(alarmParams.firstAlarmTime.toMillis());
        showToast(getString(R.string.main_alarm_reset_toast));
    }
}

private void showInterval(String interval) {
    String wholeTitle = getString(R.string.main_interval, interval);
    SpannableString wholeTitleSpan = new SpannableString(wholeTitle);
    wholeTitleSpan.setSpan(new RelativeSizeSpan(DISPLAYED_NUMBERS_SIZE_RELATIVE_TO_TEXT_PROPORTION), wholeTitle.indexOf(interval), interval.length() + 1, 0);
    intervalBetweenAlarmsTextView.setText(wholeTitleSpan);
}

private void showNumberOfAlarms(String numberOfAlarms) {
    int numberOfAlarmsInt = Integer.parseInt(numberOfAlarms);
    String wholeTitle = this.getResources().getQuantityString(R.plurals.main_number_of_alarms, numberOfAlarmsInt, numberOfAlarmsInt);
    SpannableString wholeTitleSpan = new SpannableString(wholeTitle);
    wholeTitleSpan.setSpan(new RelativeSizeSpan(DISPLAYED_NUMBERS_SIZE_RELATIVE_TO_TEXT_PROPORTION),
            wholeTitle.indexOf(numberOfAlarms),
            numberOfAlarms.length() + 1, 0);
    numberOfAlarmsTextView.setText(wholeTitleSpan);
}

private void showFirstAlarmTime(String firstAlarmTime) {
    String wholeTitle = getString(R.string.main_firstalarm_time, firstAlarmTime);
    SpannableString wholeTitleSpan = new SpannableString(wholeTitle);
    wholeTitleSpan.setSpan(new RelativeSizeSpan(DISPLAYED_NUMBERS_SIZE_RELATIVE_TO_TEXT_PROPORTION),
            wholeTitle.indexOf(firstAlarmTime) - 1,
            wholeTitle.indexOf(firstAlarmTime) + firstAlarmTime.length(), 0);
    firstAlarmTextView.setText(wholeTitleSpan);
}

private void showTimeLeft(AlarmParams alarmParams) {
    AlarmTime alarmTime = alarmParams.firstAlarmTime;
    timeLeftTextView.setText(getString(R.string.all_time_left, alarmTime.getHoursLeft(), alarmTime.getMinutesLeft()));
    if (alarmParams.turnedOn) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            timeLeftTextView.setTextColor(getActivity().getColor(R.color.primary));
        }
    } else {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            timeLeftTextView.setTextColor(getActivity().getColor(R.color.main_disabled_textcolor));
        }
    }
    Log.d(LOG_TAG, "Time left: "+alarmTime.getHoursLeft() + ":" + alarmTime.getMinutesLeft());
}

private void checkNotificationPolicy() {
    NotificationManager notificationManager =
            (NotificationManager) getActivity().getSystemService(Context.NOTIFICATION_SERVICE);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
            && !notificationManager.isNotificationPolicyAccessGranted()) {
        Intent intent = new Intent(
                android.provider.Settings
                        .ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS);
        startActivity(intent);
    }
}

/**
 * needed for Android Q: on some devices activity doesn't show from fullScreenNotification without
 * permission SYSTEM_ALERT_WINDOW
 */
private void checkOverlayPermission() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        if ((Build.VERSION.SDK_INT > Build.VERSION_CODES.P) && (!Settings.canDrawOverlays(getActivity()))) {
            Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                    Uri.parse("package:" + getActivity().getPackageName()));
            startActivityForResult(intent, ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE);
        }
    }
}
}

Solution

  • Present your time picked dialogue using child fragment manager:

    timePicker.show(getChildFragmentManager(), TimePickerDialogFragment.FRAGMENT_TAG);
    

    What is child fragment manager for? Java docs will help us:

        /**
         * Return a private FragmentManager for placing and managing Fragments
         * inside of this Fragment.
         */
        @NonNull
        final public FragmentManager getChildFragmentManager() {
            if (mHost == null) {
                throw new IllegalStateException("Fragment " + this + " has not been attached yet.");
            }
            return mChildFragmentManager;
        }
    

    Thus your time picker dialogue will be "managed" by Fragment1 (not by the fragment directly of course).

    After changing fragment manager you now can cast parent fragment (not manager) of TimePickerDialogFragment to TimePickerDialog.OnTimeSetListener.

    public class TimePickerDialogFragment extends DialogFragment  {
        ...
        private TimePickerDialog.OnTimeSetListener listener;
        ...
    
        @Override
        public void onAttach(Context context) {
            super.onAttach(context);
            listener = (TimePickerDialog.OnTimeSetListener) getParentFragment();
        }
    
    ...
    

    You can get parent fragment by calling getParentFragment():

        /**
         * Returns the parent Fragment containing this Fragment.  If this Fragment
         * is attached directly to an Activity, returns null.
         */
        @Nullable
        final public Fragment getParentFragment() {
            return mParentFragment;
        }
    

    Note: be aware of possible memory leaks.

    Update related to the comment

    Extend TimePickerDialogFragment with DialogFragment from the androidx package:

    enter image description here