Challenge Q: Trying to use interfaces to get info back to CrimeListFragment

#1

I realize this may be overkill, but I wanted to know if it was possible. So, I only wanted to update the recyclerView in CrimeListFragment for ONLY the edited CrimeFragments. Looking at the next chapter, I see we’ll be using a ViewPager to swipe to other CrimeFragments(thus perhaps editing them as well and not just the one we clicked into). The other posted challenge solutions don’t address what would happen if we were to swipe to other CrimeFragments and change them too.

I attempted to use a LinkedHashSet to store changed crime UUIDs which I wanted to populate from using an interface callback. The problem I’m running into to is, when I get into the “onAttach()” function in CrimeFragment.java, I can’t access the CrimeListFragment activity to use the interface since the calling activity for the CrimeFragment is the CrimePagerActivity. I think I would need the parent of the parent calling activity of CrimeFragment to use the interface I’ve attempted to write. I’ll attach the code below. In the debugger, I get an exception when I attempt to assign my “mCrimeUpdatedCallback” in onAttach() in CrimeFragment.java.

CrimeFragment.java

public class CrimeFragment extends Fragment {

private Crime mCrime;
private EditText mTitleField;
private Button mDateButton;
private CheckBox mSolvedCheckBox;
private CheckBox mRequiresPoliceCheckBox;
private onCrimeUpdatedListener mCrimeUpdatedCallback;

private static final String ARG_CRIME_ID = "crime_id";

public static CrimeFragment newInstance(UUID crimeId) {
    Bundle args = new Bundle();
    args.putSerializable(ARG_CRIME_ID, crimeId);
    CrimeFragment fragment = new CrimeFragment();
    fragment.setArguments(args);
    return fragment;
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    //Weird how the string ID's are slightly different, but still works
    UUID crimeId = (UUID) getArguments().getSerializable(ARG_CRIME_ID);
    mCrime = CrimeLab.get(getActivity()).getCrime(crimeId);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View v = inflater.inflate(R.layout.fragment_crime_police, container, false);

    mTitleField = (EditText)v.findViewById(R.id.crime_title);
    mTitleField.setText(mCrime.getTitle());
    mTitleField.addTextChangedListener(new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {

        }
        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            mCrime.setTitle((s.toString()));
        }
        @Override
        public void afterTextChanged(Editable s) {
        }
    });

    mDateButton = (Button)v.findViewById(R.id.crime_date);
    SimpleDateFormat dateFormat = new SimpleDateFormat("EEEE, MMM dd, yyyy");
    String date = dateFormat.format(mCrime.getDate());
    mDateButton.setText(date);
    mDateButton.setEnabled(false);

    mSolvedCheckBox = (CheckBox)v.findViewById(R.id.crime_solved);
    mSolvedCheckBox.setChecked(mCrime.isSolved());
    mSolvedCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            mCrime.setSolved(isChecked);
            mCrimeUpdatedCallback.onCrimeUpdated(mCrime.getId());
        }
    });

    mRequiresPoliceCheckBox = (CheckBox)v.findViewById(R.id.crime_requires_police);
    mRequiresPoliceCheckBox.setChecked(mCrime.isRequiresPolice());
    mRequiresPoliceCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            mCrime.setRequiresPolice(isChecked);
        }
    });

    return v;
}
@Override
public void onAttach(Context context) {
    super.onAttach(context);

    Activity activity = context instanceof Activity ? (Activity) context : null;

    try {
        mCrimeUpdatedCallback = (onCrimeUpdatedListener) activity;
    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString()
                + " must implement onCrimeUpdatedListener");
    }
}

public interface onCrimeUpdatedListener{
    public void onCrimeUpdated(UUID crimeId);
}

}

CrimeListFragment.java

public class CrimeListFragment extends Fragment implements CrimeFragment.onCrimeUpdatedListener {
private RecyclerView mCrimeRecyclerView;
private CrimeAdapter mAdapter;
private static LinkedHashSet mChangedCrimes;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment_crime_list, container, false);

    mCrimeRecyclerView = (RecyclerView) view.findViewById(R.id.crime_recycler_view);
    mCrimeRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));

    // onResume is called after onCreateView to update the
    //  whole list. No need to update twice
    //updateUI();

    return view;
}

private class CrimeHolder extends RecyclerView.ViewHolder
        implements View.OnClickListener {
    private TextView mTitleTextView;
    private TextView mDateTextView;
    private ImageView mSolvedImage;

    private Crime mCrime;

    public CrimeHolder(LayoutInflater inflater, ViewGroup parent) {
        super(inflater.inflate(R.layout.list_item_crime, parent, false));
        itemView.setOnClickListener(this);
        mTitleTextView = (TextView) itemView.findViewById(R.id.crime_title);
        mDateTextView = (TextView) itemView.findViewById(R.id.crime_date);
        mSolvedImage = (ImageView) itemView.findViewById(R.id.crime_solved);
    }
    public void bind(Crime crime) {
        mCrime = crime;
        mTitleTextView.setText(mCrime.getTitle());

        SimpleDateFormat dateFormat = new SimpleDateFormat("EEEE, MMM dd, yyyy");
        String date = dateFormat.format(mCrime.getDate());
        mDateTextView.setText(date);
        mSolvedImage.setVisibility(crime.isSolved() ? View.VISIBLE : View.GONE);
    }

    @Override
    public void onClick(View v) {
        Intent intent = CrimePagerActivity.newIntent(getActivity(), mCrime.getId());
        startActivity(intent);
    }
}

//Create a second holder for the call police button
private class CrimeHolderRequiresPolice extends RecyclerView.ViewHolder
        implements View.OnClickListener {
    private TextView mTitleTextView;
    private TextView mDateTextView;
    private Button mRequiresPolice;
    private ImageView mSolvedImage;

    private Crime mCrime;

    public CrimeHolderRequiresPolice(LayoutInflater inflater, ViewGroup parent) {
        super(inflater.inflate(R.layout.list_item_requires_police, parent, false));
        itemView.setOnClickListener(this);
        mTitleTextView = (TextView) itemView.findViewById(R.id.crime_title);
        mDateTextView = (TextView) itemView.findViewById(R.id.crime_date);
        mRequiresPolice = (Button) itemView.findViewById(R.id.contact_police);
        mSolvedImage = (ImageView) itemView.findViewById(R.id.crime_solved);
    }
    public void bind(Crime crime) {
        mCrime = crime;
        mTitleTextView.setText(mCrime.getTitle());

        SimpleDateFormat dateFormat = new SimpleDateFormat("EEEE, MMM dd, yyyy");
        String date = dateFormat.format(mCrime.getDate());
        mDateTextView.setText(date);
        mSolvedImage.setVisibility(crime.isSolved() ? View.VISIBLE : View.GONE);
    }

    @Override
    public void onClick(View v) {
        Intent intent = CrimePagerActivity.newIntent(getActivity(), mCrime.getId());
        startActivity(intent);
    }
}

public class CrimeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private List<Crime> mCrimes;
    private static final int CRIME_VIEW_TYPE_NORMAL = 1;
    private static final int CRIME_VIEW_TYPE_POLICE = 2;


    public CrimeAdapter(List<Crime> crimes) {
        mCrimes = crimes;
    }
    @Override
    public int getItemViewType (int position) {
        Crime crime = mCrimes.get(position);
        if(crime.isRequiresPolice()){
            return CRIME_VIEW_TYPE_POLICE;
        } else {
            return CRIME_VIEW_TYPE_NORMAL;
        }
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
        //Decide here which view holder to use
        switch(viewType) {
            case CRIME_VIEW_TYPE_NORMAL:
                return new CrimeHolder(layoutInflater, parent);
            case CRIME_VIEW_TYPE_POLICE:
                return new CrimeHolderRequiresPolice(layoutInflater, parent);
            default:
                //Exception here?
                return new CrimeHolder(layoutInflater, parent);
        }
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        Crime crime = mCrimes.get(position);
        //Find out which holder to use, cast appropriately, call correct bind
        if(crime.isRequiresPolice()) {
            CrimeHolderRequiresPolice policeHolder = (CrimeHolderRequiresPolice)holder;
            policeHolder.bind(crime);
        }else {
            CrimeHolder normalHolder = (CrimeHolder) holder;
            normalHolder.bind(crime);
        }
    }

    @Override
    public int getItemCount() {
        return mCrimes.size();
    }

}

    @Override
    public void onResume(){
        super.onResume();
        updateUI();
    }

    @Override
    public void onCrimeUpdated(UUID crimeId) {
        if(mChangedCrimes == null) {
            mChangedCrimes = new LinkedHashSet<>();
        }
        mChangedCrimes.add(crimeId);
    }

    private void updateUI() {
        CrimeLab crimeLab = CrimeLab.get(getActivity());
        List<Crime> crimes = crimeLab.getCrimes();

        if(mAdapter == null) {
            mAdapter = new CrimeAdapter(crimes);
            mCrimeRecyclerView.setAdapter(mAdapter);
        } else {
            mAdapter.notifyDataSetChanged();
        }

}

}

#2

You are correct. CrimeFragment’s hosting activity is CrimePagerActivity, so that would need to implement onCrimeUpdatedListener. To return the set of updated crimes to CrimeListFragment, you would probably use startActivityForResult() and related methods.