Easy Challenge Problem


#1

If I first change the date, say Jun 21 2015, go back to CrimeFragment I can see time is 02:56. When I click into the time, it’s now 12:00 AM.

So t’s seems that changing the date resets the time to 00:00:00. :confused:

Time

public class TimePickerFragment extends DialogFragment {

	public static final String EXTRA_TIME = "com.bignerdranch.android.criminalIntent.TIME";
	
	private Date mTime;
	
	@Override
	public Dialog onCreateDialog(Bundle savedInstanceState)
	{
		mTime = (Date)getArguments().getSerializable(EXTRA_TIME);
		
		// Create calendar to get the day, month and year
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(mTime);
		
		int hour = calendar.get(Calendar.HOUR);
		int min = calendar.get(Calendar.MINUTE);
		int sec = calendar.get(Calendar.SECOND);
		
		View v = getActivity().getLayoutInflater().inflate(R.layout.dialog_time, null);
		
		TimePicker timePicker = (TimePicker)v.findViewById(R.id.dialog_time_timePicker);
		
		timePicker.setCurrentHour(hour);
		timePicker.setCurrentMinute(min);
		
		timePicker.setOnTimeChangedListener(new TimePicker.OnTimeChangedListener() {
			
			@Override
	        public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
	        	mTime.setHours(hourOfDay);
	        	mTime.setMinutes(minute);
	            // Update argument to preserve selected value on rotation
	            getArguments().putSerializable(EXTRA_TIME, mTime);
	        }
		});

		return new AlertDialog.Builder(getActivity())
			.setView(v)
			.setTitle(R.string.time_picker_title)
			.setPositiveButton(
					android.R.string.ok,
					new DialogInterface.OnClickListener() {
						
						public void onClick(DialogInterface dialog, int which) {
							sendResult(Activity.RESULT_OK);
							
						}
					})
			.create();
	}
	
	public static TimePickerFragment newInstance(Date date)
	{
		Bundle args = new Bundle();
		args.putSerializable(EXTRA_TIME, date);
		
		TimePickerFragment fragment = new TimePickerFragment();
		fragment.setArguments(args);
		return fragment;
	}
	
	private void sendResult(int resultCode)
	{
		if (getTargetFragment() == null)
			return;
		
		Intent i = new Intent();
		i.putExtra(EXTRA_TIME, mTime);
		
		getTargetFragment().onActivityResult(getTargetRequestCode(), resultCode, i);
	}
}

CrimeListFragment


public class CrimeListFragment extends ListFragment {
	//private static final String TAG = "CrimeListFragment";
	private ArrayList<Crime> mCrimes;

	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		getActivity().setTitle(R.string.crimes_title);
		mCrimes = CrimeLab.get(getActivity()).getCrimes();
	
		CrimeAdapter adapter = new CrimeAdapter(mCrimes);
		setListAdapter(adapter);
	}
	
	@Override
	public void onListItemClick(ListView l, View v, int position,long id)
	{
		Crime c = ((CrimeAdapter)getListAdapter()).getItem(position);
		//Log.d(TAG, c.getTitle() + " was clicked");
		
		// Start CrimeActivity
		Intent i = new Intent(getActivity(), CrimePagerActivity.class);
		i.putExtra(CrimeFragment.EXTRA_CRIME_ID, c.getId());
		startActivity(i);
	}
	
	private class CrimeAdapter extends ArrayAdapter<Crime>
	{
		public CrimeAdapter(ArrayList<Crime> crimes)
		{
			super(getActivity(), android.R.layout.simple_list_item_1, crimes);
		}
		
		@Override
		public View getView(int position, View convertView, ViewGroup parent)
		{
			if (convertView == null)
			{
				convertView = getActivity().getLayoutInflater()
				.inflate(R.layout.list_item_crime, null);
			}
			Crime c = getItem(position);
			
			TextView titleTextView = (TextView)convertView.findViewById(R.id.crime_list_item_titleTextView);
			
			titleTextView.setText(c.getTitle());
			
			TextView dateTextView = (TextView)convertView.findViewById(R.id.crime_list_item_dateTextView);
			//dateTextView.setText(c.getDate().toString());
			dateTextView.setText(updateDate(c));
			CheckBox solvedCheckBox = (CheckBox)convertView.findViewById(R.id.crime_list_item_solvedCheckBox);
			solvedCheckBox.setChecked(c.isSolved());
			
			return convertView;
				
		}
		
	    public String updateDate(Crime c)
	    {
	    	Date date = c.getDate();
	    	SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy @ HH:mm");
	    	return sdf.format(date).toString();
	    }
		
	}
	
	

	@Override
	public void onResume()
	{
		super.onResume();
		((CrimeAdapter)getListAdapter()).notifyDataSetChanged();
	}

}

CrimeFragment


public class CrimeFragment extends Fragment {
	private Crime mCrime;
	private EditText mTitleField;
	
	private Button mDateButton;
	private Button mTimeButton;
	private CheckBox mSolvedCheckBox;
	
	public static final String EXTRA_CRIME_ID = "criminalintent.CRIME_ID";
	public static final String DIALOG_DATE = "date";
	
	public static final String DIALOG_TIME = "time";
	
	public static final int REQUEST_DATE = 0;
	public static final int REQUEST_TIME = 1;
	
    public static CrimeFragment newInstance(UUID crimeId)
    {
    	Bundle args = new Bundle();
    	args.putSerializable(EXTRA_CRIME_ID, crimeId);
    	CrimeFragment fragment = new CrimeFragment();
    	fragment.setArguments(args);
    	return fragment;
    }
    
	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		
		UUID crimeId = (UUID)getArguments().getSerializable(CrimeFragment.EXTRA_CRIME_ID);
		mCrime = CrimeLab.get(getActivity()).getCrime(crimeId);
	}
	
	
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.fragment_crime, parent, false);

        mTitleField = (EditText)v.findViewById(R.id.crime_title);
        mTitleField.setText(mCrime.getTitle());
        
        mTitleField.addTextChangedListener(new TextWatcher() {
            public void onTextChanged(CharSequence c, int start, int before, int count) {
                mCrime.setTitle(c.toString());
            }

            public void beforeTextChanged(CharSequence c, int start, int count, int after) {
                // this space intentionally left blank
            }

            public void afterTextChanged(Editable c) {
                // this one too
            }
        });    
        
        mDateButton = (Button)v.findViewById(R.id.crime_date);
        updateDate();
        
        mDateButton.setOnClickListener(new View.OnClickListener() {
        	public void onClick(View v)
        	{
        		FragmentManager fm = getActivity().getSupportFragmentManager();
        		DatePickerFragment dialog = DatePickerFragment.newInstance(mCrime.getDate());
        		dialog.setTargetFragment(CrimeFragment.this, REQUEST_DATE);
        		dialog.show(fm, DIALOG_DATE);
        	}
        });
        
        mTimeButton = (Button)v.findViewById(R.id.crime_time);
        updateTime();
        
        mTimeButton.setOnClickListener(new View.OnClickListener() {
        	public void onClick(View v)
        	{
        		FragmentManager fm = getActivity().getSupportFragmentManager();
        		TimePickerFragment dialog = TimePickerFragment.newInstance(mCrime.getDate());
        		dialog.setTargetFragment(CrimeFragment.this, REQUEST_TIME);
        		dialog.show(fm, DIALOG_TIME);
        	}
        });
        
        mSolvedCheckBox = (CheckBox)v.findViewById(R.id.crime_solved);
        mSolvedCheckBox.setChecked(mCrime.isSolved());
        
        mSolvedCheckBox.setOnCheckedChangeListener(new OnCheckedChangeListener () {
        	public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
        	{
        		mCrime.setSolved(isChecked);
        	}
        	
        });
        
        return v; 
    }
    

    
    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data)
    {
    	if (resultCode != Activity.RESULT_OK) return;
    	
    	if (requestCode == REQUEST_DATE)
    	{
    		Date date = (Date)data.getSerializableExtra(DatePickerFragment.EXTRA_DATE);
    		mCrime.setDate(date);
    		updateDate();
    	}
    	else if (requestCode == REQUEST_TIME)
    	{
    		Date date = (Date)data.getSerializableExtra(TimePickerFragment.EXTRA_TIME);
    		mCrime.setDate(date);
    		updateTime();
    	}
    	
    }
	
    public void updateDate()
    {
    	Date date = mCrime.getDate();
    	SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy");
    	mDateButton.setText(sdf.format(date).toString());
    }
    
    public void updateTime()
    {
    	Date date = mCrime.getDate();
    	SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
    	mTimeButton.setText(sdf.format(date).toString());
    }
}

#2

I had the same issue. This happens because in DatePickerFragment.java, we have

datePicker.init(year, month, day, new OnDateChangedListener(){
        public void onDateChanged(DatePicker view, int year,
                                  int month, int day){
            mDate = new GregorianCalendar(year, month, day).getTime();
            getArguments().putSerializable(EXTRA_DATE, mDate);
        }
    });

which creates a new instance of mDate as YEAR-MONTH-DAY 00:00:00.

The way I fixed it was to augment it as follows:

datePicker.init(year, month, day, new OnDateChangedListener(){

        public void onDateChanged(DatePicker view, int year,
                                  int month, int day){

            Calendar calendar = Calendar.getInstance();
            calendar.setTime(mDate);
            int hour   = calendar.get(Calendar.HOUR);
            int minute = calendar.get(Calendar.MINUTE);
            int second = calendar.get(Calendar.SECOND);

            mDate = new GregorianCalendar(year, month, day, hour,
                                          minute, second).getTime();
                    
            getArguments().putSerializable(EXTRA_DATE, mDate);
        }
    });

If anyone has a more elegant solution, please share.


#3

You should try to find another way to successfully modify the time inside the date since the methods you are using for mTime, a Date object are deprecated.

I found a solution by using a Calendar object and it’s methods. Thanks to the fact that mTime is the current crime’s date retrieved as an EXTRA, you can just modify the fields you’re interested on inside a Calendar object with it’s time set with the crime’s date.

Talking in code terms, this is the way the onCreateDialog (Bundle) method would look like inside your TimePickerFragment class:

        @Override
	@NonNull
	public Dialog onCreateDialog(Bundle savedInstanceState) {
		
		mTime = (Date) getArguments().getSerializable(EXTRA_TIME);
		//Creates a Calendar object and extracts hour and minutes from crime's date
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(mTime);
		int hourOfDay = calendar.get(Calendar.HOUR_OF_DAY);
		int minute = calendar.get(Calendar.MINUTE);
		
		//Creates the Time view based on the dialog_time layout
		View v = getActivity().getLayoutInflater().inflate(R.layout.dialog_time, null);
		
		//Inflating the timePicker with it's listener (in it anonymous class form) inside the view
		TimePicker timePicker = (TimePicker) v.findViewById(R.id.dialog_time_picker);
		timePicker.setCurrentHour(hourOfDay);
		timePicker.setCurrentMinute(minute);
		timePicker.setIs24HourView(true);
		timePicker.setOnTimeChangedListener(new OnTimeChangedListener() {

			@Override
			public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
				
				//retrieving the original crime date from the mTime value with a calendar 
				Calendar calendar = Calendar.getInstance();
				calendar.setTime(mTime);
				calendar.set(Calendar.HOUR_OF_DAY, hourOfDay);
				calendar.set(Calendar.MINUTE, minute);
				
				// Translating hourOfDay & minute into a Date object using a calendar, date keeps the same
				mTime = calendar.getTime();
				
				//Update argument to preserve selected value on rotation
				getArguments().putSerializable(EXTRA_TIME, mTime);
				Log.d(TAG, "Time set is: " + mTime.toString());
			}
			
		});
		
		return new AlertDialog.Builder(getActivity())
					.setView(v)
					.setTitle(R.string.time_picker_title)
					.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
						
						@Override
						public void onClick(DialogInterface dialog, int which) {
							sendResult(Activity.RESULT_OK);
							
						}
					})
					.create();
	}

This way you’re delegating a Calendar object to do all the work and then set a brand new date on your mTime variable instead of modifying it directly using deprecated methods.

Hope this was of some help.