package com.dacs.vku.ui.fragments.Authentication
import android.Manifest import android.annotation.SuppressLint import android.app.DatePickerDialog import android.app.TimePickerDialog import android.content.ContentUris import android.content.ContentValues import android.content.Intent import android.content.pm.PackageManager import android.database.Cursor import android.net.Uri import android.os.Bundle import android.provider.CalendarContract import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.Button import android.widget.EditText import android.widget.Toast import androidx.appcompat.app.AlertDialog import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment import androidx.recyclerview.widget.LinearLayoutManager import com.dacs.vku.R import com.dacs.vku.adapters.ScheduleAdapter import com.dacs.vku.api.RetrofitInstance import com.dacs.vku.api.UserData import com.dacs.vku.databinding.FragmentScheduleBinding import com.dacs.vku.models.Schedule import okhttp3.ResponseBody import retrofit2.Call import retrofit2.Callback import retrofit2.Response import java.text.SimpleDateFormat import java.util.Calendar import java.util.Locale import java.util.TimeZone import java.util.UUID
class ScheduleFragment : Fragment(R.layout.fragment_schedule) {
private lateinit var binding: FragmentScheduleBinding
private lateinit var scheduleAdapter: ScheduleAdapter
private val scheduleList = mutableListOf
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
userData = it.getSerializable("userData") as? UserData
uid = userData?.userId
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentScheduleBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
checkAndRequestPermissions()
getAllSchedules()
binding.fabAddSchedule.setOnClickListener {
addSchedule()
}
// Set up RecyclerView
scheduleAdapter = ScheduleAdapter(scheduleList,
onEditClick = {
schedule -> editSchedule(schedule)
},
onDeleteClick = { schedule -> deleteSchedule(schedule) }
)
binding.rvSchedules.apply {
layoutManager = LinearLayoutManager(requireContext())
adapter = scheduleAdapter
}
}
private fun addSchedule() {
val dialogView = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_add_schedule, null)
val etDate = dialogView.findViewById<EditText>(R.id.etDate)
val etTime = dialogView.findViewById<EditText>(R.id.etTime)
val etDayOfWeek = dialogView.findViewById<EditText>(R.id.etDayOfWeek)
val etSubject = dialogView.findViewById<EditText>(R.id.etEditSubject)
val etRoom = dialogView.findViewById<EditText>(R.id.etEditRoom)
val btnAddSchedule = dialogView.findViewById<Button>(R.id.btnAddSchedule)
// Placeholder to store the captured day of the week
var dayOfWeek: Int = -1
// Set up listeners for date and time EditTexts
etDate.setOnClickListener {
showDatePickerDialog(etDate, etDayOfWeek) { selectedDayOfWeek ->
dayOfWeek = selectedDayOfWeek
}
}
etTime.setOnClickListener {
showTimePickerDialog(etTime)
}
val dialog = AlertDialog.Builder(requireContext())
.setTitle("Add Schedule")
.setView(dialogView)
.setNegativeButton("Cancel", null)
.create()
btnAddSchedule.setOnClickListener {
val subject = etSubject.text.toString()
val room = etRoom.text.toString()
val date = etDate.text.toString()
val time = etTime.text.toString()
if (subject.isEmpty() || date.isEmpty() || time.isEmpty() || room.isEmpty() || dayOfWeek == -1) {
Toast.makeText(requireContext(), "Please fill all fields", Toast.LENGTH_SHORT).show()
} else {
val scheduleId = UUID.randomUUID().toString()
val userId = uid
val dayOfWeekString = getDayOfWeekString(dayOfWeek)
val scheduleData = Schedule(scheduleId, userId, dayOfWeekString, date, time, room, subject)
// Add schedule to the list
scheduleList.add(scheduleData)
// Sort scheduleList by date and time
val sortedList = scheduleList.sortedWith(compareBy({ parseDate(it.date) }, { parseTime(it.time) }))
scheduleAdapter.updateScheduleList(sortedList.toMutableList())
sendUserDataToServer(scheduleData)
insertEventIntoCalendar(scheduleData)
Toast.makeText(requireContext(), "Schedule added successfully", Toast.LENGTH_SHORT).show()
dialog.dismiss()
}
}
dialog.show()
}
private fun showDatePickerDialog(editText: EditText, dayOfWeekEditText: EditText, onDateSelected: (Int) -> Unit) {
val calendar = Calendar.getInstance()
val year = calendar.get(Calendar.YEAR)
val month = calendar.get(Calendar.MONTH)
val day = calendar.get(Calendar.DAY_OF_MONTH)
val datePickerDialog = DatePickerDialog(requireContext(), { _, selectedYear, selectedMonth, selectedDay ->
val formattedDate = String.format("%02d/%02d/%d", selectedDay, selectedMonth + 1, selectedYear)
editText.setText(formattedDate)
// Set calendar to the selected date to get the day of the week
val selectedCalendar = Calendar.getInstance()
selectedCalendar.set(selectedYear, selectedMonth, selectedDay)
val dayOfWeek = selectedCalendar.get(Calendar.DAY_OF_WEEK)
// Set the day of the week in the edit text
dayOfWeekEditText.setText(getDayOfWeekString(dayOfWeek))
onDateSelected(dayOfWeek)
}, year, month, day)
datePickerDialog.show()
}
private fun showTimePickerDialog(editText: EditText) {
val calendar = Calendar.getInstance()
val hour = calendar.get(Calendar.HOUR_OF_DAY)
val minute = calendar.get(Calendar.MINUTE)
val timePickerDialog = TimePickerDialog(requireContext(), { _, selectedHour, selectedMinute ->
val formattedTime = String.format("%02d:%02d", selectedHour, selectedMinute)
editText.setText(formattedTime)
}, hour, minute, true)
timePickerDialog.show()
}
private fun getDayOfWeekString(dayOfWeek: Int): String {
return when (dayOfWeek) {
Calendar.SUNDAY -> "Chủ Nhật"
Calendar.MONDAY -> "Thứ 2"
Calendar.TUESDAY -> "Thứ 3"
Calendar.WEDNESDAY -> "Thứ 4"
Calendar.THURSDAY -> "Thứ 5"
Calendar.FRIDAY -> "Thứ 6"
Calendar.SATURDAY -> "Thứ 7"
else -> ""
}
}
private fun parseDate(date: String): Long {
val sdf = SimpleDateFormat("dd/MM/yyyy", Locale.getDefault())
return sdf.parse(date)?.time ?: 0L
}
private fun parseTime(time: String): Long {
val sdf = SimpleDateFormat("HH:mm", Locale.getDefault())
return sdf.parse(time)?.time ?: 0L
}
// Function to insert Event and Save Event Id
@SuppressLint("InlinedApi")
private fun insertEventIntoCalendar(schedule: Schedule) {
val cal = Calendar.getInstance()
val dateParts = schedule.date.split("/")
val day = dateParts[0].toInt()
val month = dateParts[1].toInt() - 1 // Calendar.MONTH is 0-based
val year = dateParts[2].toInt()
val timeParts = schedule.time.split(":")
val hour = timeParts[0].toInt()
val minute = timeParts[1].toInt()
cal.set(year, month, day, hour, minute)
val startMillis: Long = cal.timeInMillis
val endMillis: Long = cal.apply { add(Calendar.HOUR, 1) }.timeInMillis
val intent = Intent(Intent.ACTION_INSERT).apply {
data = CalendarContract.Events.CONTENT_URI
putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, startMillis)
putExtra(CalendarContract.EXTRA_EVENT_END_TIME, endMillis)
val description = "Subject: ${schedule.subject} Room: ${schedule.room}"
putExtra(CalendarContract.Events.TITLE, description)
Log.e("VKUUU", description)
putExtra(CalendarContract.Events.DESCRIPTION, "Subject: $description")
putExtra(CalendarContract.Events.EVENT_LOCATION, schedule.room)
putExtra(CalendarContract.Events.AVAILABILITY, CalendarContract.Events.AVAILABILITY_BUSY)
putExtra(CalendarContract.Events.RRULE, "FREQ=WEEKLY;COUNT=10")
}
startActivity(intent)
}
// Function to edit Event
private fun editEventInCalendar(eventId: Long) {
val uri: Uri = ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI, eventId)
val intent = Intent(Intent.ACTION_EDIT).apply {
data = uri
}
startActivity(intent)
}
private fun editSchedule(schedule: Schedule) {
val dialogView = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_edit_schedule, null)
val etEditSubject = dialogView.findViewById<EditText>(R.id.etEditSubject)
val etEditDate = dialogView.findViewById<EditText>(R.id.etEditDate)
val etEditTime = dialogView.findViewById<EditText>(R.id.etEditTime)
val etEditRoom = dialogView.findViewById<EditText>(R.id.etEditRoom)
val etDayOfWeek = dialogView.findViewById<EditText>(R.id.etDayOfWeek)
val btnUpdateSchedule = dialogView.findViewById<Button>(R.id.btnUpdateSchedule)
etEditSubject.setText(schedule.subject)
etEditDate.setText(schedule.date)
etEditTime.setText(schedule.time)
etEditRoom.setText(schedule.room)
etDayOfWeek.setText(schedule.dayOfWeek)
var dayOfWeek: Int = -1
etEditDate.setOnClickListener {
showDatePickerDialog(etEditDate, etDayOfWeek) { selectedDayOfWeek ->
dayOfWeek = selectedDayOfWeek
}
}
etEditTime.setOnClickListener {
showTimePickerDialog(etEditTime)
}
val dialog = AlertDialog.Builder(requireContext())
.setTitle("Edit Schedule")
.setView(dialogView)
.setNegativeButton("Cancel", null)
.create()
btnUpdateSchedule.setOnClickListener {
val updatedSubject = etEditSubject.text.toString()
val updatedDate = etEditDate.text.toString()
val updatedTime = etEditTime.text.toString()
val updatedRoom = etEditRoom.text.toString()
// Use the existing dayOfWeek if not changed
val updatedDayOfWeek = if (dayOfWeek == -1) schedule.dayOfWeek else getDayOfWeekString(dayOfWeek)
if (updatedSubject.isEmpty() || updatedDate.isEmpty() || updatedTime.isEmpty() || updatedRoom.isEmpty()) {
Toast.makeText(requireContext(), "Please fill all fields", Toast.LENGTH_SHORT).show()
} else {
val updatedSchedule = schedule.copy(
subject = updatedSubject,
dayOfWeek = updatedDayOfWeek,
date = updatedDate,
time = updatedTime,
room = updatedRoom
)
updateScheduleOnServer(updatedSchedule) { success ->
if (success) {
dialog.dismiss()
scheduleList[scheduleList.indexOfFirst { it.scheduleId == schedule.scheduleId }] = updatedSchedule
scheduleList.sortWith(compareBy({ parseDate(it.date) }, { parseTime(it.time) }))
scheduleAdapter.notifyDataSetChanged()
Toast.makeText(requireContext(), "Schedule updated successfully", Toast.LENGTH_SHORT).show()
}
}
}
}
dialog.show()
}
private fun checkAndRequestPermissions() {
if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.WRITE_CALENDAR)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(requireActivity(),
arrayOf(Manifest.permission.WRITE_CALENDAR, Manifest.permission.READ_CALENDAR),
PERMISSIONS_REQUEST_WRITE_CALENDAR
)
}
}
companion object {
private const val PERMISSIONS_REQUEST_WRITE_CALENDAR = 100
}
private fun updateScheduleOnServer(schedule: Schedule, onComplete: (Boolean) -> Unit) {
val apiService = RetrofitInstance.api
val call = apiService.updateSchedule(schedule)
call.enqueue(object : Callback<ResponseBody> {
override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
if (response.isSuccessful) {
Log.d("com.dacs.vku.ui.fragments.Authentication.ScheduleFragment", "Schedule updated successfully")
Toast.makeText(requireContext(), "Schedule updated successfully", Toast.LENGTH_SHORT).show()
val index = scheduleList.indexOfFirst { it.scheduleId == schedule.scheduleId }
if (index >= 0) {
scheduleList[index] = schedule
scheduleList.sortWith(compareBy({ parseDate(it.date) }, { parseTime(it.time) }))
scheduleAdapter.notifyDataSetChanged()
}
onComplete(true)
} else {
Log.e("com.dacs.vku.ui.fragments.Authentication.ScheduleFragment", "Failed to update schedule: ${response.errorBody()?.string()}")
Toast.makeText(requireContext(), "Failed to update schedule", Toast.LENGTH_SHORT).show()
onComplete(false)
}
}
override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
Log.e("com.dacs.vku.ui.fragments.Authentication.ScheduleFragment", "Error updating schedule", t)
Toast.makeText(requireContext(), "Network error", Toast.LENGTH_SHORT).show()
onComplete(false)
}
})
}
private fun sendUserDataToServer(schedule: Schedule) {
val apiService = RetrofitInstance.api
val call = apiService.addCalendar(schedule)
call.enqueue(object : Callback<ResponseBody> {
override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
if (response.isSuccessful) {
Log.d("com.dacs.vku.ui.fragments.Authentication.ScheduleFragment", "User data sent successfully")
} else {
Log.e("com.dacs.vku.ui.fragments.Authentication.ScheduleFragment", "Failed to send user data: ${response.errorBody()?.string()}")
Toast.makeText(requireContext(), "Failed to send user data", Toast.LENGTH_SHORT).show()
}
}
override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
Log.e("com.dacs.vku.ui.fragments.Authentication.ScheduleFragment", "Error sending user data", t)
Toast.makeText(requireContext(), "Network error", Toast.LENGTH_SHORT).show()
}
})
}
private fun deleteSchedule(schedule: Schedule) {
val apiService = RetrofitInstance.api
val deleteParams = mapOf(
"scheduleId" to schedule.scheduleId,
"userId" to uid.orEmpty()
)
val call = apiService.deleteSchedule(deleteParams)
call.enqueue(object : Callback<ResponseBody> {
override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
if (response.isSuccessful) {
Log.d("com.dacs.vku.ui.fragments.Authentication.ScheduleFragment", "Schedule deleted successfully")
Toast.makeText(requireContext(), "Schedule deleted successfully", Toast.LENGTH_SHORT).show()
// Remove schedule from the list and update the recycler view
scheduleList.remove(schedule)
val sortedList = scheduleList.sortedWith(compareBy({ parseDate(it.date) }, { parseTime(it.time) }))
scheduleAdapter.updateScheduleList(sortedList.toMutableList())
} else {
Log.e("com.dacs.vku.ui.fragments.Authentication.ScheduleFragment", "Failed to delete schedule: ${response.errorBody()?.string()}")
Toast.makeText(requireContext(), "Failed to delete schedule", Toast.LENGTH_SHORT).show()
}
}
override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
Log.e("com.dacs.vku.ui.fragments.Authentication.ScheduleFragment", "Error deleting schedule", t)
Toast.makeText(requireContext(), "Network error", Toast.LENGTH_SHORT).show()
}
})
}
private fun getAllSchedules() {
val apiService = RetrofitInstance.api
val call = apiService.getAllSchedules(uid ?: "")
call.enqueue(object : Callback<List<Schedule>> {
override fun onResponse(call: Call<List<Schedule>>, response: Response<List<Schedule>>) {
if (response.isSuccessful) {
val schedules = response.body()
schedules?.let {
scheduleList.clear()
scheduleList.addAll(it)
val sortedList = scheduleList.sortedWith(compareBy({ parseDate(it.date) }, { parseTime(it.time) }))
scheduleAdapter.updateScheduleList(sortedList.toMutableList())
}
} else {
Log.e("com.dacs.vku.ui.fragments.Authentication.ScheduleFragment", "Failed to get schedules: ${response.errorBody()?.string()}")
Toast.makeText(requireContext(), "Failed to get schedules", Toast.LENGTH_SHORT).show()
}
}
override fun onFailure(call: Call<List<Schedule>>, t: Throwable) {
Log.e("com.dacs.vku.ui.fragments.Authentication.ScheduleFragment", "Error getting schedules", t)
Toast.makeText(requireContext(), "Network error", Toast.LENGTH_SHORT).show()
}
})
}
} i want to intent to calandar a task not events
Để chuyển hướng ứng dụng Android sang ứng dụng lịch và thêm một nhiệm vụ (task) thay vì một sự kiện (event), cách thức thực hiện sẽ phức tạp hơn vì API riêng cho các nhiệm vụ không phục vụ sẵn trong gói lịch (CalendarContract). Tuy nhiên, bạn có thể tham khảo cách tiếp cận bằng cách sử dụng API của Google Tasks hoặc các ứng dụng của bên thứ ba có hỗ trợ tính năng này.
Đối với bản native của Android không có API trực tiếp để thêm nhiệm vụ, bạn có thể thử cách tiếp cận như sau:
Bạn có thể khởi động một ý định chung chỉ để mở ứng dụng lịch và người dùng sẽ phải thêm nhiệm vụ (task) bằng cách thủ công.
private fun openCalendarApp() {
val intent = Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_APP_CALENDAR)
try {
startActivity(intent)
} catch (e: ActivityNotFoundException) {
Toast.makeText(requireContext(), "No calendar app found", Toast.LENGTH_SHORT).show()
}
}
Bạn có thể tích hợp Google Tasks API, có vẻ hơi phức tạp vì cần xác thực OAuth2 và gửi yêu cầu HTTP tới Google Tasks API. Nhưng về cơ bản, quy trình như sau:
import okhttp3.*
fun addGoogleTask(title: String, notes: String, due: String) {
val client = OkHttpClient()
val json = """
{
"title": "$title",
"notes": "$notes",
"due": "$due"
}
"""
val body = RequestBody.create(MediaType.get("application/json; charset=utf-8"), json)
val request = Request.Builder()
.url("https://www.googleapis.com/tasks/v1/lists/@default/tasks")
.addHeader("Authorization", "Bearer YOUR_ACCESS_TOKEN") // Thay YOUR_ACCESS_TOKEN bằng access token thực sự
.post(body)
.build()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
e.printStackTrace()
}
override fun onResponse(call: Call, response: Response) {
if (response.isSuccessful) {
Log.d("Success", "Task added successfully")
} else {
Log.e("Error", "Failed to add task: ${response.message}")
}
}
})
}
Lưu ý rằng quá trình hoàn thiện việc này cần nghiên cứu, thử nghiệm và cấu hình các bước OAuth phức tạp hơn tùy thuộc vào cách bạn muốn trình bày các nhiệm vụ trong ứng dụng lịch hoặc quản lý nhiệm vụ nào.
Tóm lại, nếu bạn yêu công nghệ và muốn vọc thêm về AI hoặc những thứ liên quan, hãy ghé qua trituenhantao.io để cập nhật những điều mới mẻ và khuyến khích trí tuệ nhân tạo giúp bạn nhiều hơn nữa!