From 4a4827ba502c73e0c486b04d553dddc2601c98a7 Mon Sep 17 00:00:00 2001 From: Ken Yee Date: Mon, 26 Mar 2018 18:27:48 -0400 Subject: [PATCH] make push notification clickable to jump you into agenda detail --- .../droidcon_boston/data/FirebaseDatabase.kt | 6 ++- .../utils/NotificationUtils.kt | 31 ++++++++---- .../droidcon_boston/views/MainActivity.kt | 28 ++++++++++- .../views/agenda/AgendaDayFragment.kt | 13 ++--- .../views/detail/AgendaDetailFragment.kt | 50 +++++++++++++------ 5 files changed, 90 insertions(+), 38 deletions(-) diff --git a/Droidcon-Boston/app/src/main/java/com/mentalmachines/droidcon_boston/data/FirebaseDatabase.kt b/Droidcon-Boston/app/src/main/java/com/mentalmachines/droidcon_boston/data/FirebaseDatabase.kt index 8f1e5c0..aa62750 100644 --- a/Droidcon-Boston/app/src/main/java/com/mentalmachines/droidcon_boston/data/FirebaseDatabase.kt +++ b/Droidcon-Boston/app/src/main/java/com/mentalmachines/droidcon_boston/data/FirebaseDatabase.kt @@ -5,6 +5,7 @@ import com.mentalmachines.droidcon_boston.R import com.mentalmachines.droidcon_boston.data.Schedule.ScheduleDetail import com.mentalmachines.droidcon_boston.data.Schedule.ScheduleRow import com.mentalmachines.droidcon_boston.utils.NotificationUtils +import com.mentalmachines.droidcon_boston.utils.ServiceLocator import com.mentalmachines.droidcon_boston.utils.getHtmlFormattedSpanned import org.threeten.bp.LocalDateTime import org.threeten.bp.ZoneId @@ -37,9 +38,10 @@ open class FirebaseDatabase { return ZonedDateTime.parse(startTime).withZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime() } - fun scheduleNotification(context: Context, eventId: String) { + fun scheduleNotification(context: Context, eventId: String, sessionDetail: ScheduleRow) { NotificationUtils(context).scheduleNotificationAlarm(getLocalStartTime().minusMinutes(SESSION_REMINDER_MINUTES_BEFORE), - eventId, context.getString(R.string.str_session_start_soon, name), description.getHtmlFormattedSpanned().toString()) + eventId, context.getString(R.string.str_session_start_soon, name), description.getHtmlFormattedSpanned().toString(), + ServiceLocator.gson.toJson(sessionDetail, ScheduleRow::class.java)) } fun toScheduleRow(scheduleId: String): ScheduleRow { diff --git a/Droidcon-Boston/app/src/main/java/com/mentalmachines/droidcon_boston/utils/NotificationUtils.kt b/Droidcon-Boston/app/src/main/java/com/mentalmachines/droidcon_boston/utils/NotificationUtils.kt index f88228f..cd8777b 100644 --- a/Droidcon-Boston/app/src/main/java/com/mentalmachines/droidcon_boston/utils/NotificationUtils.kt +++ b/Droidcon-Boston/app/src/main/java/com/mentalmachines/droidcon_boston/utils/NotificationUtils.kt @@ -1,29 +1,34 @@ package com.mentalmachines.droidcon_boston.utils import android.annotation.TargetApi -import android.app.* +import android.app.AlarmManager +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent +import android.content.ComponentName import android.content.Context import android.content.ContextWrapper import android.content.Intent +import android.content.pm.PackageManager import android.graphics.Color import android.os.Build import android.os.Build.VERSION_CODES import android.support.v4.app.NotificationCompat +import android.text.TextUtils import android.util.Log import com.google.firebase.database.DataSnapshot import com.google.firebase.database.DatabaseError import com.google.firebase.database.ValueEventListener -import com.mentalmachines.droidcon_boston.receivers.NotificationPublisher import com.mentalmachines.droidcon_boston.R import com.mentalmachines.droidcon_boston.data.FirebaseDatabase import com.mentalmachines.droidcon_boston.data.UserAgendaRepo import com.mentalmachines.droidcon_boston.firebase.FirebaseHelper +import com.mentalmachines.droidcon_boston.receivers.BootReceiver +import com.mentalmachines.droidcon_boston.receivers.NotificationPublisher import com.mentalmachines.droidcon_boston.views.MainActivity import org.threeten.bp.LocalDateTime import org.threeten.bp.ZoneId -import android.content.pm.PackageManager -import android.content.ComponentName -import com.mentalmachines.droidcon_boston.receivers.BootReceiver class NotificationUtils(context: Context) : ContextWrapper(context) { @@ -91,7 +96,7 @@ class NotificationUtils(context: Context) : ContextWrapper(context) { scheduleEvent?.let { if (userRepo.isSessionBookmarked(eventId) && scheduleEvent.getLocalStartTime().isAfter(LocalDateTime.now())) { - scheduleEvent.scheduleNotification(context, eventId) + scheduleEvent.scheduleNotification(context, eventId, scheduleEvent.toScheduleRow(eventId)) hasBookmarkedEvents = true } } @@ -109,9 +114,9 @@ class NotificationUtils(context: Context) : ContextWrapper(context) { }) } - fun scheduleNotificationAlarm(alarmTime: LocalDateTime, sessionId: String, title: String, body: String) { + fun scheduleNotificationAlarm(alarmTime: LocalDateTime, sessionId: String, title: String, body: String, sessionDetail: String) { if (alarmTime.isAfter(LocalDateTime.now())) { - val pendingIntent = getAgendaSessionNotificationPendingIntent(sessionId, title, body) + val pendingIntent = getAgendaSessionNotificationPendingIntent(sessionId, title, body, sessionDetail) val utcInMillis = alarmTime.atZone(ZoneId.systemDefault()).toEpochSecond() * 1000 val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager alarmManager.set(AlarmManager.RTC_WAKEUP, utcInMillis, pendingIntent) @@ -134,7 +139,7 @@ class NotificationUtils(context: Context) : ContextWrapper(context) { PackageManager.DONT_KILL_APP) } - private fun getAgendaSessionNotificationPendingIntent(sessionId: String, title: String = "", body: String = ""): PendingIntent { + private fun getAgendaSessionNotificationPendingIntent(sessionId: String, title: String = "", body: String = "", sessionDetail: String = ""): PendingIntent { val builder = NotificationCompat.Builder(this, ANDROID_CHANNEL_ID) .setContentText(title) .setStyle(NotificationCompat.BigTextStyle() @@ -143,6 +148,14 @@ class NotificationUtils(context: Context) : ContextWrapper(context) { .setSmallIcon(R.drawable.ic_notification_session_start) .setAutoCancel(true) + if (!TextUtils.isEmpty(sessionDetail)) { + val sessionIntent = MainActivity.getSessionDetailIntent(this, sessionId, sessionDetail) + val contentIntent = PendingIntent.getActivity(this, 0, + sessionIntent, PendingIntent.FLAG_UPDATE_CURRENT) + + builder.setContentIntent(contentIntent) + } + val notificationIntent = Intent(this, NotificationPublisher::class.java).apply { putExtra(NotificationPublisher.NOTIFICATION_ID, sessionId.hashCode()) putExtra(NotificationPublisher.SESSION_ID, sessionId) diff --git a/Droidcon-Boston/app/src/main/java/com/mentalmachines/droidcon_boston/views/MainActivity.kt b/Droidcon-Boston/app/src/main/java/com/mentalmachines/droidcon_boston/views/MainActivity.kt index 113f4d5..4324785 100644 --- a/Droidcon-Boston/app/src/main/java/com/mentalmachines/droidcon_boston/views/MainActivity.kt +++ b/Droidcon-Boston/app/src/main/java/com/mentalmachines/droidcon_boston/views/MainActivity.kt @@ -1,15 +1,21 @@ package com.mentalmachines.droidcon_boston.views +import android.content.Context +import android.content.Intent import android.content.res.Configuration import android.os.Bundle import android.support.v4.app.Fragment import android.support.v4.view.GravityCompat import android.support.v7.app.ActionBarDrawerToggle import android.support.v7.app.AppCompatActivity +import android.text.TextUtils import android.view.Gravity import android.view.MenuItem import com.mentalmachines.droidcon_boston.R +import com.mentalmachines.droidcon_boston.data.Schedule.ScheduleRow +import com.mentalmachines.droidcon_boston.utils.ServiceLocator import com.mentalmachines.droidcon_boston.views.agenda.AgendaFragment +import com.mentalmachines.droidcon_boston.views.detail.AgendaDetailFragment import com.mentalmachines.droidcon_boston.views.social.SocialFragment import com.mentalmachines.droidcon_boston.views.speaker.SpeakerFragment import com.mentalmachines.droidcon_boston.views.volunteer.VolunteerFragment @@ -29,7 +35,14 @@ class MainActivity : AppCompatActivity() { initNavDrawerToggle() replaceFragment(getString(R.string.str_agenda)) - navView.setCheckedItem(R.id.nav_agenda) + + val sessionDetails = intent.extras?.getString(EXTRA_SESSION_DETAILS) + if (!TextUtils.isEmpty(sessionDetails)) { + AgendaDetailFragment.addDetailFragmentToStack(supportFragmentManager, + ServiceLocator.gson.fromJson(sessionDetails, ScheduleRow::class.java)) + } else { + navView.setCheckedItem(R.id.nav_agenda) + } } @@ -220,4 +233,17 @@ class MainActivity : AppCompatActivity() { supportActionBar?.title = title } } + + companion object { + private const val EXTRA_SESSIONID = "MainActivity.EXTRA_SESSIONID" + private const val EXTRA_SESSION_DETAILS = "MainActivity.EXTRA_SESSION_DETAILS" + + fun getSessionDetailIntent(context: Context, sessionId: String, sessionDetail: String): Intent { + val intent = Intent(context, MainActivity::class.java).apply { + putExtra(EXTRA_SESSIONID, sessionId) + putExtra(EXTRA_SESSION_DETAILS, sessionDetail) + } + return intent + } + } } diff --git a/Droidcon-Boston/app/src/main/java/com/mentalmachines/droidcon_boston/views/agenda/AgendaDayFragment.kt b/Droidcon-Boston/app/src/main/java/com/mentalmachines/droidcon_boston/views/agenda/AgendaDayFragment.kt index e6cdef9..0d8ff5c 100644 --- a/Droidcon-Boston/app/src/main/java/com/mentalmachines/droidcon_boston/views/agenda/AgendaDayFragment.kt +++ b/Droidcon-Boston/app/src/main/java/com/mentalmachines/droidcon_boston/views/agenda/AgendaDayFragment.kt @@ -177,17 +177,10 @@ class AgendaDayFragment : Fragment(), FlexibleAdapter.OnItemClickListener { return false } } - val arguments = Bundle() - arguments.putString(Schedule.SCHEDULE_ITEM_ROW, gson.toJson(itemData, ScheduleRow::class.java)) - val agendaDetailFragment = AgendaDetailFragment() - agendaDetailFragment.arguments = arguments - - val fragmentManager = activity?.supportFragmentManager - fragmentManager?.beginTransaction() - ?.add(R.id.fragment_container, agendaDetailFragment) - ?.addToBackStack(null) - ?.commit() + activity?.let { + AgendaDetailFragment.addDetailFragmentToStack(it.supportFragmentManager, itemData); + } } return true diff --git a/Droidcon-Boston/app/src/main/java/com/mentalmachines/droidcon_boston/views/detail/AgendaDetailFragment.kt b/Droidcon-Boston/app/src/main/java/com/mentalmachines/droidcon_boston/views/detail/AgendaDetailFragment.kt index 2870597..7fca566 100644 --- a/Droidcon-Boston/app/src/main/java/com/mentalmachines/droidcon_boston/views/detail/AgendaDetailFragment.kt +++ b/Droidcon-Boston/app/src/main/java/com/mentalmachines/droidcon_boston/views/detail/AgendaDetailFragment.kt @@ -4,6 +4,7 @@ import android.content.res.ColorStateList import android.os.Bundle import android.support.design.widget.Snackbar import android.support.v4.app.Fragment +import android.support.v4.app.FragmentManager import android.support.v4.content.ContextCompat import android.text.method.LinkMovementMethod import android.util.Log @@ -42,7 +43,7 @@ import kotlinx.android.synthetic.main.agenda_detail_fragment.v_agenda_detail_spe class AgendaDetailFragment : Fragment() { - private lateinit var scheduleDetail: ScheduleDetail + private var scheduleDetail: ScheduleDetail? = null private lateinit var scheduleRowItem: ScheduleRow private val eventSpeakers = HashMap() @@ -78,22 +79,24 @@ class AgendaDetailFragment : Fragment() { fab_agenda_detail_bookmark.setOnClickListener({ - val nextBookmarkStatus = !userAgendaRepo.isSessionBookmarked(scheduleDetail.id) - userAgendaRepo.bookmarkSession(scheduleDetail.id, nextBookmarkStatus) - val context = tv_agenda_detail_title.context - if (nextBookmarkStatus) { - NotificationUtils(context).scheduleMySessionNotifications() - } else { - NotificationUtils(context).cancelNotificationAlarm(scheduleRowItem.id) - } + if (scheduleDetail != null) { + val nextBookmarkStatus = !userAgendaRepo.isSessionBookmarked(scheduleDetail!!.id) + userAgendaRepo.bookmarkSession(scheduleDetail!!.id, nextBookmarkStatus) + val context = tv_agenda_detail_title.context + if (nextBookmarkStatus) { + NotificationUtils(context).scheduleMySessionNotifications() + } else { + NotificationUtils(context).cancelNotificationAlarm(scheduleRowItem.id) + } - Snackbar.make(agendaDetailView, - if (nextBookmarkStatus) - getString(R.string.saved_agenda_item) - else getString(R.string.removed_agenda_item), - Snackbar.LENGTH_SHORT).show() + Snackbar.make(agendaDetailView, + if (nextBookmarkStatus) + getString(R.string.saved_agenda_item) + else getString(R.string.removed_agenda_item), + Snackbar.LENGTH_SHORT).show() - showBookmarkStatus(scheduleDetail) + showBookmarkStatus(scheduleDetail!!) + } }) populateSpeakersInformation(scheduleRowItem) @@ -108,7 +111,7 @@ class AgendaDetailFragment : Fragment() { if (scheduleRowItem.primarySpeakerName == speaker.name) { scheduleDetail = speaker.toScheduleDetail(scheduleRowItem) - showAgendaDetail(scheduleDetail) + showAgendaDetail(scheduleDetail!!) } } @@ -228,4 +231,19 @@ class AgendaDetailFragment : Fragment() { else ColorStateList.valueOf(ContextCompat.getColor(context, R.color.colorLightGray)) } + + companion object { + fun addDetailFragmentToStack(supportFragmentManager: FragmentManager, itemData: Schedule.ScheduleRow) { + val arguments = Bundle() + arguments.putString(Schedule.SCHEDULE_ITEM_ROW, gson.toJson(itemData, ScheduleRow::class.java)) + + val agendaDetailFragment = AgendaDetailFragment() + agendaDetailFragment.arguments = arguments + + supportFragmentManager.beginTransaction() + ?.add(R.id.fragment_container, agendaDetailFragment) + ?.addToBackStack(null) + ?.commit() + } + } }