/* * Copyright 2016 Realm Inc. * * 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 com.mikepenz.fastadapter_extensions.firebase; import android.support.annotation.Nullable; import android.support.v7.widget.RecyclerView; import com.google.firebase.database.DataSnapshot; import com.google.firebase.database.DatabaseReference; import com.google.firebase.database.Query; import com.mikepenz.fastadapter.IExpandable; import com.mikepenz.fastadapter.IItem; import com.mikepenz.fastadapter.ISubItem; import com.mikepenz.fastadapter.adapters.ItemAdapter; import java.util.ArrayList; import java.util.List; /** * This class is a generic way of backing an RecyclerView with a Firebase location. * It handles all of the child events at the given Firebase location. It marshals received data into the given * class type. * <p> * To use this class in your app, subclass it passing in all required parameters and implement the * populateViewHolder method. * <p> * <blockquote><pre> * {@code * private static class ChatMessageViewHolder extends RecyclerView.ViewHolder { * TextView messageText; * TextView nameText; * <p> * public ChatMessageViewHolder(View itemView) { * super(itemView); * nameText = (TextView)itemView.findViewById(android.R.id.text1); * messageText = (TextView) itemView.findViewById(android.R.id.text2); * } * } * <p> * FirebaseRecyclerAdapter<ChatMessage, ChatMessageViewHolder> adapter; * DatabaseReference ref = FirebaseDatabase.getInstance().getReference(); * <p> * RecyclerView recycler = (RecyclerView) findViewById(R.id.messages_recycler); * recycler.setHasFixedSize(true); * recycler.setLayoutManager(new LinearLayoutManager(this)); * <p> * adapter = new FirebaseRecyclerAdapter<ChatMessage, ChatMessageViewHolder>(ChatMessage.class, android.R.layout.two_line_list_item, ChatMessageViewHolder.class, ref) { * public void populateViewHolder(ChatMessageViewHolder chatMessageViewHolder, ChatMessage chatMessage, int position) { * chatMessageViewHolder.nameText.setText(chatMessage.getName()); * chatMessageViewHolder.messageText.setText(chatMessage.getMessage()); * } * }; * recycler.setAdapter(mAdapter); * } * </pre></blockquote> */ public class FirebaseItemAdapter<Item extends IItem> extends ItemAdapter<Item> { private Class<Item> mModelClass; private FirebaseArray mSnapshots; public FirebaseItemAdapter(Class<Item> modelClass, Query ref) { mModelClass = modelClass; mSnapshots = new FirebaseArray(ref); mSnapshots.setOnChangedListener(new FirebaseArray.OnChangedListener() { @Override public void onChanged(EventType type, int index, int oldIndex) { switch (type) { case Added: notifyItemInserted(index); break; case Changed: notifyItemChanged(index); break; case Removed: notifyItemRemoved(index); break; case Moved: notifyItemMoved(oldIndex, index); break; default: throw new IllegalStateException("Incomplete case statement"); } } }); } @Override public void onAttachedToRecyclerView(final RecyclerView recyclerView) { super.onAttachedToRecyclerView(recyclerView); if (mSnapshots != null) { mSnapshots.attach(); } } @Override public void onDetachedFromRecyclerView(final RecyclerView recyclerView) { super.onDetachedFromRecyclerView(recyclerView); if (mSnapshots != null) { mSnapshots.cleanup(); } } /** * @return the count of items within this adapter */ @Override public int getAdapterItemCount() { return mSnapshots.getCount(); } /** * @return the items within this adapter */ @Override public List<Item> getAdapterItems() { List<Item> items = new ArrayList<>(getAdapterItemCount()); for (DataSnapshot snapshot : mSnapshots.getItems()) { items.add(parseSnapshot(snapshot)); } return items; } /** * Searches for the given item and calculates its relative position * * @param item the item which is searched for * @return the relative position */ @Override public int getAdapterPosition(Item item) { int count = 0; for (DataSnapshot snapshot : mSnapshots.getItems()) { if (parseSnapshot(snapshot).getIdentifier() == item.getIdentifier()) { return count; } count++; } return -1; } /** * This method parses the DataSnapshot into the requested type. You can override it in subclasses * to do custom parsing. * * @param snapshot the DataSnapshot to extract the model from * @return the model extracted from the DataSnapshot */ protected Item parseSnapshot(DataSnapshot snapshot) { return snapshot.getValue(mModelClass); } /** * @param position the relative position * @return the item inside this adapter */ @Nullable public Item getAdapterItem(int position) { return parseSnapshot(mSnapshots.getItem(position)); } /** * @param position the relative position * @return the reference of an item at the given position */ public DatabaseReference getRef(int position) { return mSnapshots.getItem(position).getRef(); } /** * removes all listeners */ public void cleanup() { mSnapshots.cleanup(); } @Override public long getItemId(int position) { // http://stackoverflow.com/questions/5100071/whats-the-purpose-of-item-ids-in-android-listview-adapter return mSnapshots.getItem(position).getKey().hashCode(); } @Override public <T extends IItem & IExpandable<T, S>, S extends IItem & ISubItem<Item, T>> T setSubItems(T collapsible, List<S> subItems) { throw new UnsupportedOperationException("this is not supported by the FirebaseItemAdapter"); } @Override public ItemAdapter<Item> set(List<Item> items) { throw new UnsupportedOperationException("this is not supported by the FirebaseItemAdapter"); } @Override public ItemAdapter<Item> setNewList(List<Item> items) { throw new UnsupportedOperationException("this is not supported by the FirebaseItemAdapter"); } @Override public ItemAdapter<Item> add(List<Item> items) { throw new UnsupportedOperationException("this is not supported by the FirebaseItemAdapter"); } @Override public ItemAdapter<Item> add(int position, List<Item> items) { throw new UnsupportedOperationException("this is not supported by the FirebaseItemAdapter"); } @Override public ItemAdapter<Item> set(int position, Item item) { throw new UnsupportedOperationException("this is not supported by the FirebaseItemAdapter"); } @Override public ItemAdapter<Item> move(int fromPosition, int toPosition) { throw new UnsupportedOperationException("this is not supported by the FirebaseItemAdapter"); } @Override public ItemAdapter<Item> remove(int position) { throw new UnsupportedOperationException("this is not supported by the FirebaseItemAdapter"); } @Override public ItemAdapter<Item> removeRange(int position, int itemCount) { throw new UnsupportedOperationException("this is not supported by the FirebaseItemAdapter"); } @Override public ItemAdapter<Item> clear() { throw new UnsupportedOperationException("this is not supported by the FirebaseItemAdapter"); } }