Created
August 23, 2017 18:48
-
-
Save long1eu/db237146e93e0d8d0bf7cf327134c2df to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package eu.long1.jwnotes.models.flexible | |
import android.content.Context | |
import android.os.Parcel | |
import android.os.Parcelable | |
import android.view.View | |
import android.widget.TextView | |
import eu.davidea.flexibleadapter.FlexibleAdapter | |
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem | |
import eu.davidea.flexibleadapter.items.IHolder | |
import eu.davidea.viewholders.FlexibleViewHolder | |
import eu.long1.jwnotes.Log | |
import eu.long1.jwnotes.R | |
import eu.long1.jwnotes.custom.views.FlipView | |
import eu.long1.jwnotes.data.tables.ContactsTable | |
import eu.long1.jwnotes.models.firebase.Talk | |
import eu.long1.jwnotes.models.firebase.User | |
import java.util.* | |
class ITalk(private val talk: Talk) : AbstractFlexibleItem<ITalk.TalkItemVH>(), IHolder<Talk>, Parcelable { | |
var lastPosition: Int = 0 | |
private set | |
init { | |
isSwipeable = true | |
} | |
override fun equals(other: Any?): Boolean { | |
if (this === other) return true | |
if (other == null) return false | |
if (javaClass != other.javaClass) return false | |
val iTalk = other as ITalk | |
return talk == iTalk.talk | |
} | |
override fun hashCode(): Int = talk.hashCode() | |
override fun getLayoutRes(): Int = R.layout.fragment_main_swipe_layout | |
override fun createViewHolder(view: View, adapter: FlexibleAdapter<*>): TalkItemVH = TalkItemVH(view, adapter) | |
override fun bindViewHolder(adapter: FlexibleAdapter<*>, holder: TalkItemVH, position: Int, payloads: List<*>?) { | |
val context = holder.contentView.context | |
holder.title.text = talk.title | |
holder.speaker.text = talk.speaker | |
if (User.getUserUid() == "own") { | |
if (holder.owner.visibility != View.INVISIBLE) { | |
holder.owner.visibility = View.INVISIBLE | |
} | |
} else { | |
if (holder.owner.visibility != View.VISIBLE) { | |
holder.owner.visibility = View.VISIBLE | |
} | |
holder.owner.text = ContactsTable.instance.getName(talk.ownerUid) | |
} | |
var dateAndLocation = getDate(Date(talk.date), context) | |
talk.location?.let { | |
if (it.isEmpty().not()) { | |
dateAndLocation = dateAndLocation + ", " + talk.location | |
if (dateAndLocation.isEmpty()) { | |
dateAndLocation = talk.location!! | |
} | |
} | |
} | |
holder.date.text = dateAndLocation | |
holder.icon.setFrontText((talk.title[0] + "").toUpperCase()) | |
lastPosition = position | |
} | |
private fun getDate(date: Date, context: Context): String { | |
val dateFormat = android.text.format.DateFormat.getDateFormat(context) | |
return dateFormat.format(date) | |
} | |
override fun getModel(): Talk { | |
return talk | |
} | |
class TalkItemVH(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter) { | |
val icon: FlipView by lazy { itemView.findViewById<FlipView>(R.id.icon) } | |
val title: TextView by lazy { itemView.findViewById<TextView>(R.id.title) } | |
val date: TextView by lazy { itemView.findViewById<TextView>(R.id.date) } | |
val speaker: TextView by lazy { itemView.findViewById<TextView>(R.id.speaker) } | |
val owner: TextView by lazy { itemView.findViewById<TextView>(R.id.owner) } | |
init { | |
icon.setOnClickListener { | |
adapter.mItemLongClickListener.onItemLongClick(adapterPosition) | |
toggleActivation() | |
} | |
} | |
override fun getFrontView(): View = itemView.findViewById(R.id.root) | |
override fun getRearLeftView(): View = itemView.findViewById(R.id.rear_left_view) | |
override fun getRearRightView(): View = itemView.findViewById(R.id.rear_right_view) | |
} | |
companion object { | |
private const val TAG = "ITalk" | |
private val log = Log(TAG) | |
fun getTalks(iTalks: List<ITalk>): ArrayList<Talk> = iTalks.mapTo(ArrayList()) { it.talk } | |
@JvmField | |
val CREATOR: Parcelable.Creator<ITalk> = object : Parcelable.Creator<ITalk> { | |
override fun createFromParcel(source: Parcel): ITalk = ITalk(source) | |
override fun newArray(size: Int): Array<ITalk?> = arrayOfNulls(size) | |
} | |
} | |
constructor(source: Parcel) : this( | |
source.readParcelable<Talk>(Talk::class.java.classLoader) | |
) | |
override fun describeContents() = 0 | |
override fun writeToParcel(dest: Parcel, flags: Int) { | |
dest.writeParcelable(talk, 0) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package eu.long1.jwnotes.fragments | |
import android.annotation.SuppressLint | |
import android.app.Activity | |
import android.content.Intent | |
import android.content.pm.PackageManager | |
import android.os.Bundle | |
import android.support.design.widget.Snackbar | |
import android.support.v4.app.Fragment | |
import android.support.v4.widget.SwipeRefreshLayout | |
import android.support.v7.app.AppCompatActivity | |
import android.support.v7.view.ActionMode | |
import android.support.v7.widget.DividerItemDecoration | |
import android.support.v7.widget.helper.ItemTouchHelper | |
import android.view.* | |
import android.widget.Toast | |
import com.evernote.android.state.State | |
import com.firebase.ui.auth.AuthUI | |
import com.firebase.ui.auth.ui.ExtraConstants | |
import com.firebase.ui.auth.ui.FlowParameters | |
import com.google.firebase.FirebaseApp | |
import eu.davidea.flexibleadapter.FlexibleAdapter | |
import eu.davidea.flexibleadapter.SelectableAdapter.Mode.IDLE | |
import eu.davidea.flexibleadapter.SelectableAdapter.Mode.MULTI | |
import eu.davidea.flexibleadapter.helpers.ActionModeHelper | |
import eu.long1.jwnotes.* | |
import eu.long1.jwnotes.activities.MainActivity | |
import eu.long1.jwnotes.adapters.TalkAdapter | |
import eu.long1.jwnotes.custom.views.LinearLayoutManager | |
import eu.long1.jwnotes.custom.views.RecyclerView | |
import eu.long1.jwnotes.data.tables.TalkTable | |
import eu.long1.jwnotes.events.* | |
import eu.long1.jwnotes.helpers.Generic | |
import eu.long1.jwnotes.helpers.MainActivityHelper.finishActionMode | |
import eu.long1.jwnotes.helpers.MainActivityHelper.prepareForActionMode | |
import eu.long1.jwnotes.helpers.Preferences | |
import eu.long1.jwnotes.helpers.SnackbarHelper.showDeleteTalkSnackbar | |
import eu.long1.jwnotes.helpers.firebase.DatabaseH | |
import eu.long1.jwnotes.loaders.FirebaseStorageSync | |
import eu.long1.jwnotes.models.firebase.Talk | |
import eu.long1.jwnotes.models.firebase.User | |
import eu.long1.jwnotes.models.flexible.ITalk | |
import eu.long1.jwnotes.ui.phone.PhoneVerificationActivity | |
import kotlinx.android.synthetic.main.activity_main.* | |
import kotlinx.android.synthetic.main.fragment_main.* | |
import org.greenrobot.eventbus.EventBus | |
import org.greenrobot.eventbus.Subscribe | |
import org.greenrobot.eventbus.ThreadMode | |
class MainFragment : Fragment(), SwipeRefreshLayout.OnRefreshListener, RecyclerView.EmptyViewListener, | |
ActionMode.Callback, FlexibleAdapter.OnItemClickListener, FlexibleAdapter.OnItemLongClickListener, | |
FlexibleAdapter.OnItemSwipeListener { | |
@State | |
var category = Talk.CATEGORY_MEETINGS | |
private val mActionModeHelper: ActionModeHelper by lazy { | |
ActionModeHelper(adapter, R.menu.fragment_main_action_mode_menu, this) | |
} | |
val adapter: TalkAdapter by lazy { | |
TalkAdapter(TalkTable.instance.list(category), this, category) | |
} | |
private var waitingTalks: ArrayList<Talk>? = null | |
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = | |
inflater.inflate(R.layout.fragment_main, container, false) | |
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | |
onRestoreInstanceState(savedInstanceState) | |
recycler_view.layoutManager = LinearLayoutManager(activity) | |
recycler_view.setEmptyView(empty_view) | |
recycler_view.setEmptyViewListener(this) | |
recycler_view.adapter = adapter | |
recycler_view.addItemDecoration(DividerItemDecoration(activity, 1)) | |
swipe_refresh_layout.setOnRefreshListener(this) | |
adapter.refreshAll() | |
} | |
override fun onRefresh() { | |
adapter.refreshAll() | |
if (!context.haveConnection()) { | |
Toast.makeText(context, R.string.no_connection, Toast.LENGTH_SHORT).show() | |
swipe_refresh_layout.isRefreshing = false | |
} else { | |
FirebaseStorageSync.sync() | |
} | |
} | |
private fun actionDelete(iTalks: List<ITalk>) { | |
adapter.deleteTalks(ITalk.getTalks(iTalks)) | |
recycler_view.refreshEmptyViewState() | |
showDeleteTalkSnackbar(activity.coordinator_layout, iTalks.size, SnackCallback(), iTalks, { undoDelete(iTalks) }) | |
} | |
@SuppressLint("RestrictedApi") | |
private fun actionShare(talks: ArrayList<Talk>) { | |
mActionModeHelper.destroyActionModeIfCan() | |
if (havePermission(android.Manifest.permission.READ_CONTACTS)) { | |
if (!User.getUserPhone().isNullOrEmpty()) { | |
EventBus.getDefault().postSticky(ShareTalksEvent(talks)) | |
} else { | |
startActivityForResult( | |
PhoneVerificationActivity.createIntent( | |
context, | |
FlowParameters( | |
FirebaseApp.DEFAULT_APP_NAME, | |
arrayListOf(AuthUI.IdpConfig.Builder(AuthUI.PHONE_VERIFICATION_PROVIDER).build()), | |
AuthUI.getDefaultTheme(), | |
AuthUI.NO_LOGO, | |
null, | |
null, | |
true, | |
true, | |
true | |
) | |
), | |
PHONE_NUMBER_RC) | |
} | |
} else { | |
waitingTalks = talks | |
requestPermissions(arrayOf(android.Manifest.permission.READ_CONTACTS), CONTACTS_RC) | |
} | |
} | |
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { | |
when (requestCode) { | |
CONTACTS_RC -> { | |
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { | |
waitingTalks?.let { actionShare(it) } | |
} else { | |
waitingTalks = null | |
context.longToast(getString(R.string.fragment_main_contacts_permission)) | |
adapter.refreshAll() | |
} | |
return | |
} | |
} | |
} | |
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { | |
log.e("onActivityResult $requestCode $resultCode $data") | |
when (requestCode) { | |
PHONE_NUMBER_RC -> if (resultCode == Activity.RESULT_OK) { | |
data?.getStringExtra(ExtraConstants.EXTRA_PHONE).let { | |
Preferences.getUserPreferences().edit().putString(Preferences.USER_PHONE_NUMBER, it).apply() | |
DatabaseH.userPhoneNumber.setValue(it) | |
waitingTalks?.let { actionShare(it) } | |
} | |
} | |
} | |
} | |
private fun startActionMode() { | |
mActionModeHelper.withDefaultMode(MULTI) | |
} | |
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean { | |
adapter.isSwipeEnabled = false | |
swipe_refresh_layout.isEnabled = false | |
prepareForActionMode(activity) | |
(activity as MainActivity).lockDrawer(false) | |
return true | |
} | |
override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean = false | |
override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean { | |
val iTalks = adapter.selectedTalks | |
mActionModeHelper.destroyActionModeIfCan() | |
when (item.itemId) { | |
R.id.item_share -> actionShare(ITalk.getTalks(iTalks)) | |
R.id.item_delete -> actionDelete(iTalks) | |
} | |
return true | |
} | |
override fun onDestroyActionMode(mode: ActionMode) { | |
swipe_refresh_layout.isEnabled = true | |
adapter.isSwipeEnabled = true | |
adapter.refreshSelected() | |
finishActionMode(activity) | |
(activity as MainActivity).unlockDrawer() | |
} | |
override fun onItemClick(position: Int): Boolean { | |
return if (adapter.mode != IDLE) { | |
mActionModeHelper.onClick(position) | |
} else { | |
val talk = adapter.getItem(position)!!.model | |
EventBus.getDefault().post(OpenTalkEvent(talk)) | |
false | |
} | |
} | |
override fun onItemLongClick(position: Int) { | |
startActionMode() | |
mActionModeHelper.onLongClick(activity as AppCompatActivity, position) | |
} | |
override fun onActionStateChanged(viewHolder: android.support.v7.widget.RecyclerView.ViewHolder?, actionState: Int) { | |
} | |
override fun onItemSwipe(position: Int, direction: Int) { | |
when (direction) { | |
ItemTouchHelper.LEFT -> showDeleteSnackBar(position) | |
ItemTouchHelper.RIGHT -> { | |
val talks = ArrayList<Talk>() | |
talks.add(adapter.getItem(position)!!.model) | |
actionShare(talks) | |
} | |
} | |
} | |
private fun showDeleteSnackBar(position: Int) { | |
val iTalks = ArrayList<ITalk>() | |
iTalks.add(adapter.getItem(position)!!) | |
adapter.removeItem(position) | |
recycler_view!!.refreshEmptyViewState() | |
showDeleteTalkSnackbar(activity.findViewById(R.id.coordinator_layout), 1, | |
SnackCallback(), iTalks) { undoDelete(iTalks) } | |
} | |
private fun undoDelete(iTalks: List<ITalk>) { | |
adapter.resetTalksToPosition(iTalks) | |
recycler_view.refreshEmptyViewState() | |
recycler_view.scrollToPosition(0) | |
} | |
override fun onEmptyView(visible: Boolean) { | |
if (!visible) return | |
empty_text.text = Generic.getCategoryEmptyText(context, category) | |
empty_image.setImageResource(Generic.getCategoryEmptyImage(category)) | |
} | |
override fun onSaveInstanceState(outState: Bundle?) { | |
adapter.onSaveInstanceState(outState) | |
super.onSaveInstanceState(outState) | |
} | |
fun onRestoreInstanceState(savedInstanceState: Bundle?) { | |
if (savedInstanceState != null) { | |
adapter.onRestoreInstanceState(savedInstanceState) | |
if (adapter.selectedItemCount > 0) { | |
startActionMode() | |
mActionModeHelper.restoreSelection(activity as AppCompatActivity) | |
} | |
} | |
} | |
inner class SnackCallback : Snackbar.Callback() { | |
@Suppress("UNCHECKED_CAST") | |
override fun onDismissed(snackbar: Snackbar, event: Int) = when (event) { | |
Snackbar.Callback.DISMISS_EVENT_SWIPE, Snackbar.Callback.DISMISS_EVENT_TIMEOUT -> { | |
val talks = (snackbar.view.tag as ArrayList<ITalk>).map { it.model } as ArrayList<Talk> | |
FirebaseStorageSync.delete(talks) | |
Unit | |
} | |
else -> Unit | |
} | |
} | |
companion object { | |
val TAG = "MainFragment" | |
private val log = Log(TAG) | |
private const val CONTACTS_RC = 100 | |
private const val PHONE_NUMBER_RC = 300 | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package eu.long1.jwnotes.adapters | |
import android.support.v7.widget.RecyclerView | |
import eu.davidea.flexibleadapter.FlexibleAdapter | |
import eu.long1.jwnotes.Log | |
import eu.long1.jwnotes.data.tables.TalkTable | |
import eu.long1.jwnotes.models.firebase.Talk | |
import eu.long1.jwnotes.models.flexible.ITalk | |
import java.util.* | |
class TalkAdapter(items: List<ITalk>, listeners: Any, private var currentCategory: Long) : | |
FlexibleAdapter<ITalk>(items, listeners) { | |
override fun onAttachedToRecyclerView(recyclerView: RecyclerView?) { | |
super.onAttachedToRecyclerView(recyclerView) | |
isSwipeEnabled = true | |
} | |
fun addTalk(talk: Talk) { | |
val iTalk = ITalk(talk) | |
if (contains(iTalk)) { | |
log.d("Dataset already contains " + talk) | |
return | |
} | |
if (currentCategory != talk.category) { | |
log.d("Talk not from this category") | |
return | |
} | |
addItem(0, iTalk) | |
} | |
fun resetTalksToPosition(talks: List<ITalk>) { | |
talks.forEach { addItem(it.lastPosition, it) } | |
} | |
fun deleteTalk(talk: Talk) { | |
val iTalk = ITalk(talk) | |
if (currentCategory != talk.category) { | |
log.d("Talk not from this category") | |
return | |
} | |
val index = getGlobalPositionOf(iTalk) | |
if (index == -1) return | |
removeItem(index) | |
} | |
fun updateTalk(talk: Talk) { | |
val iTalk = ITalk(talk) | |
if (currentCategory != talk.category) { | |
log.d("Talk not from this category") | |
val index = getGlobalPositionOf(iTalk) | |
if (index != -1) { | |
log.d("But it was in this list and we removed it.") | |
removeItem(index) | |
} | |
return | |
} | |
updateItem(0, iTalk, null) | |
} | |
fun addTalks(talks: ArrayList<Talk>) { | |
for (talk in talks) addTalk(talk) | |
} | |
fun deleteTalks(talks: ArrayList<Talk>) { | |
for (talk in talks) deleteTalk(talk) | |
} | |
fun updateTalks(talks: ArrayList<Talk>) { | |
for (talk in talks) updateTalk(talk) | |
} | |
fun refreshAll() { | |
updateDataSet(TalkTable.instance.list(currentCategory), false) | |
} | |
fun refreshCategory(category: Long) { | |
currentCategory = category | |
refreshAll() | |
} | |
fun refreshSelected() { | |
selectedPositions.forEach { | |
toggleSelection(it) | |
notifyItemChanged(it, true) | |
log.d(it) | |
} | |
} | |
override fun onCreateBubbleText(position: Int): String { | |
return getItem(position)!!.model.fileName[0].toString() | |
} | |
val selectedTalks: List<ITalk> get() = selectedPositions.map { getItem(it)!! } | |
companion object { | |
private val TAG = TalkAdapter::class.java.simpleName | |
private val log = Log(TAG) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment