commit 923379a43bc1a190046501f9615e6cb3bfe4d08a Author: InsaneTrashRU Date: Fri Jun 6 15:16:47 2025 +0300 Upload diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aa724b7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..c321988 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +Doctor \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..b589d56 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml new file mode 100644 index 0000000..f60ae34 --- /dev/null +++ b/.idea/deploymentTargetDropDown.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml new file mode 100644 index 0000000..b268ef3 --- /dev/null +++ b/.idea/deploymentTargetSelector.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..0897082 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..053b7aa --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml new file mode 100644 index 0000000..2b8a50f --- /dev/null +++ b/.idea/kotlinc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/migrations.xml b/.idea/migrations.xml new file mode 100644 index 0000000..f8051a6 --- /dev/null +++ b/.idea/migrations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..8978d23 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/other.xml b/.idea/other.xml new file mode 100644 index 0000000..0312e8a --- /dev/null +++ b/.idea/other.xml @@ -0,0 +1,714 @@ + + + + + + \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..e27a301 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,102 @@ +plugins { + id 'com.android.application' + id 'org.jetbrains.kotlin.android' +} + +android { + namespace 'com.example.doctor' + compileSdk 33 + + defaultConfig { + applicationId "com.example.doctor" + minSdk 28 + targetSdk 33 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + } + buildFeatures{ + viewBinding true + } +} + +dependencies { + + implementation 'com.google.code.gson:gson:2.8.6' + + implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0" + + implementation "com.mikepenz:materialdrawer:7.0.0" + implementation "com.mikepenz:materialdrawer-nav:7.0.0" + + implementation 'pl.droidsonroids.gif:android-gif-drawable:1.1.17' + + implementation platform('com.google.firebase:firebase-bom:31.2.0') + implementation 'com.google.firebase:firebase-database-ktx:20.1.0' + implementation 'com.google.firebase:firebase-auth-ktx:21.1.0' + + implementation 'com.android.volley:volley:1.2.1' + implementation 'com.squareup.picasso:picasso:2.71828' + implementation 'androidx.fragment:fragment-ktx:1.5.5' + implementation 'com.google.android.gms:play-services-location:21.0.1' + implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1' + + implementation 'androidx.core:core-ktx:1.7.0' + implementation 'androidx.appcompat:appcompat:1.5.0' + implementation 'com.google.android.material:material:1.7.0' + implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + implementation 'androidx.test:core-ktx:1.4.0' + implementation 'androidx.games:games-activity:1.1.0' + implementation 'com.google.mlkit:common:18.1.0' + implementation 'com.google.firebase:firebase-messaging-ktx:23.1.2' + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.5' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' + implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.6.1" + + coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3' + implementation 'com.kizitonwose.calendar:view:2.0.0' + + //Lifecycle + implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.6.2' + implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2' + implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.6.1" + + //Retrofit + implementation "com.squareup.retrofit2:retrofit:2.9.0" + implementation "com.squareup.retrofit2:converter-gson:2.9.0" + + //OkHttp + implementation "com.squareup.okhttp3:logging-interceptor:4.7.2" + implementation "com.squareup.okhttp3:okhttp:4.7.2" + + implementation 'com.squareup.picasso:picasso:2.71828' + + //Свайп + implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0" + + implementation "androidx.work:work-runtime-ktx:2.8.1" + + + +// // Kotlin +// implementation "androidx.navigation:navigation-fragment-ktx:2.5.2" +// implementation "androidx.navigation:navigation-ui-ktx:2.5.2" + + +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/src/androidTest/java/com/example/doctor/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/example/doctor/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..740eed1 --- /dev/null +++ b/app/src/androidTest/java/com/example/doctor/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.example.doctor + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.example.doctor", appContext.packageName) + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..7c659b1 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/Adapter/VpAdapter.kt b/app/src/main/java/com/example/doctor/Adapter/VpAdapter.kt new file mode 100644 index 0000000..274f4cd --- /dev/null +++ b/app/src/main/java/com/example/doctor/Adapter/VpAdapter.kt @@ -0,0 +1,18 @@ +package com.example.doctor.Adapter + +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity +import androidx.viewpager2.adapter.FragmentStateAdapter + +// Адаптер для ViewPager2, который управляет списком фрагментов +class VpAdapter(fr_act:FragmentActivity, private val list:List):FragmentStateAdapter(fr_act) { + // Возвращает количество фрагментов в списке + override fun getItemCount(): Int { + return list.size // возвращаем количество фрагментов + } + + // Возвращает фрагмент по указанной позиции + override fun createFragment(position: Int): Fragment { + return list[position] // возвращаем фрагмент из списка по позиции + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/Adapter/VpAdapterPatientList.kt b/app/src/main/java/com/example/doctor/Adapter/VpAdapterPatientList.kt new file mode 100644 index 0000000..57a6688 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Adapter/VpAdapterPatientList.kt @@ -0,0 +1,18 @@ +package com.example.doctor.Adapter + +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity +import androidx.viewpager2.adapter.FragmentStateAdapter + +// Адаптер для ViewPager2, который управляет списком фрагментов пациентов +class VpAdapterPatientList(fr_act:FragmentActivity, private val list:List):FragmentStateAdapter(fr_act) { + // Возвращает количество фрагментов в списке + override fun getItemCount(): Int { + return list.size // возвращаем количество фрагментов + } + + // Возвращает фрагмент по указанной позиции + override fun createFragment(position: Int): Fragment { + return list[position] // возвращаем фрагмент из списка по позиции + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/Adapter/VpEditSportAdapter.kt b/app/src/main/java/com/example/doctor/Adapter/VpEditSportAdapter.kt new file mode 100644 index 0000000..92dc46f --- /dev/null +++ b/app/src/main/java/com/example/doctor/Adapter/VpEditSportAdapter.kt @@ -0,0 +1,18 @@ +package com.example.doctor.Adapter + +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity +import androidx.viewpager2.adapter.FragmentStateAdapter + +// Адаптер для ViewPager2, который управляет списком фрагментов для редактирования спортивных упражнений +class VpEditSportAdapter(fr_act:FragmentActivity, private val list: List):FragmentStateAdapter(fr_act) { + // Возвращает количество фрагментов в списке + override fun getItemCount(): Int { + return list.size // возвращаем количество фрагментов + } + + // Возвращает фрагмент по указанной позиции (приведение типа к Fragment) + override fun createFragment(position: Int): Fragment { + return list[position] as Fragment // возвращаем фрагмент из списка по позиции + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/AddSportPatientModel.kt b/app/src/main/java/com/example/doctor/AddSportPatientModel.kt new file mode 100644 index 0000000..5228465 --- /dev/null +++ b/app/src/main/java/com/example/doctor/AddSportPatientModel.kt @@ -0,0 +1,20 @@ +package com.example.doctor + +// Модель данных для добавления спортивного курса пациенту +data class AddSportPatientModel( + val id: Int, // Идентификатор добавленного курса пациента + val status: String, // Статус операции добавления + val one: Int, // Поле 1 (вероятно, для хранения данных о выполнении упражнений по дням) + val two: Int, // Поле 2 + val three: Int, // Поле 3 + val four: Int, // Поле 4 + val five: Int, // Поле 5 + val six: Int, // Поле 6 + val seven: Int, // Поле 7 + val eight: Int, // Поле 8 + val nine: Int, // Поле 9 + val ten: Int, // Поле 10 + val eleven: Int, // Поле 11 + val twelve: Int, // Поле 12 + ) + diff --git a/app/src/main/java/com/example/doctor/Appeals/AppealsCreateMessageActivity.kt b/app/src/main/java/com/example/doctor/Appeals/AppealsCreateMessageActivity.kt new file mode 100644 index 0000000..a6eb944 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Appeals/AppealsCreateMessageActivity.kt @@ -0,0 +1,20 @@ +package com.example.doctor.Appeals + +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import com.example.doctor.R +import com.example.doctor.databinding.ActivityAppealsBinding + +// Активити для создания нового обращения (сообщения) +class AppealsCreateMessageActivity : AppCompatActivity() { + private lateinit var binding: ActivityAppealsBinding + // Функция жизненного цикла Activity, инициализирует интерфейс и запускает фрагмент создания сообщения + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityAppealsBinding.inflate(layoutInflater) + setContentView(binding.root) + // Заменяем контейнер на фрагмент создания сообщения + supportFragmentManager.beginTransaction().replace(R.id.CLAppealsActivity, CreateMessageFragment.newInstance()).commit()//Заменяем наш экран на фрагмент (используем наш экран как основу)//R.id.placeHolder - куда всталяем //MainFragment.newInstance() - это то что мы вставляем + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/Appeals/AppealsFragment.kt b/app/src/main/java/com/example/doctor/Appeals/AppealsFragment.kt new file mode 100644 index 0000000..dc4b986 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Appeals/AppealsFragment.kt @@ -0,0 +1,235 @@ +package com.example.doctor.Appeals + +import android.content.Intent +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.fragment.app.FragmentActivity +import androidx.fragment.app.activityViewModels +import com.example.doctor.Adapter.VpAdapter +import com.example.doctor.R +import com.example.doctor.Appeals.TabLayout.OldAppealsFragment +import com.example.doctor.Appeals.TabLayout.NewAppealsFragment +import com.example.doctor.Auth.AuthActivity +import com.example.doctor.CodeError.Code429Activity +import com.example.doctor.CodeError.Code500Activity +import com.example.doctor.DataModel +import com.example.doctor.DoctorViewModel +import com.example.doctor.Enternet.EnternetActivity +import com.example.doctor.Enternet.EnternetCheck +import com.example.doctor.Pref.ClearPref +import com.example.doctor.Pref.ConclusionPref +import com.example.doctor.Pref.SavePref +import com.example.doctor.Retrofit.DoctorApi +import com.example.doctor.Toast.showCustomInfoToast +import com.example.doctor.databinding.FragmentAppealsBinding +import com.google.android.material.tabs.TabLayoutMediator +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import java.util.Timer +import kotlin.concurrent.fixedRateTimer + + +class AppealsFragment : Fragment() { + private lateinit var binding: FragmentAppealsBinding + private lateinit var doctorApi: DoctorApi + private lateinit var timer: Timer + + + private val model: DoctorViewModel by activityViewModels() + + val prefDoctorConclusion = ConclusionPref() + private val dataModel: DataModel by activityViewModels()//Для передачи данных + val prefDoctorClear = ClearPref() + val prefDoctorSave = SavePref() + + //Класс проверки интеренета + val enternetCheck = EnternetCheck() + + private val tList = listOf( + "новые", + "обработанные", + ) + + //Список с фрагментами для переключения + private val flist = listOf( + NewAppealsFragment.newInstance(), + OldAppealsFragment.newInstance(), + ) + + // Функция жизненного цикла, создаёт и возвращает view фрагмента + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentAppealsBinding.inflate(layoutInflater, container, false) + return binding.root + } + + // Функция жизненного цикла, вызывается после создания view. Инициализирует интерфейс и обработчики + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + init() // Инициализация ViewPager и TabLayout + binding.btnAddMessage.setOnClickListener { + val intetn = Intent(requireContext(), AppealsCreateMessageActivity::class.java) + startActivity(intetn) + } + } + + // Получение обработанных обращений с сервера и обновление ViewModel + private fun getOldAppeals() { + if (enternetCheck.isOnline(requireContext())) { + initRetrofit() + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) + CoroutineScope(Dispatchers.IO).launch { + val listAppeals = doctorApi.GetAppealsDoctorOld("Bearer $Tokens") + requireActivity().runOnUiThread { + //Фиксируем полученные данные + val List = listAppeals.body() + val Nice = listAppeals.isSuccessful + val Code = listAppeals.code() + if(Code==429){ + val intetn = Intent(requireContext(), Code429Activity::class.java) + startActivity(intetn) + } + else if(Code==200) { + //Если нету ошибок + if (Nice) { + if (List != null) { + if (List?.appeals_old != null) { + model.appealsOldList.value = List.appeals_old + } + } + } + } + else if (Code == 500) { + val intetn = Intent(requireContext(), Code500Activity::class.java) + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(requireContext(), AuthActivity::class.java) + activity?.finish() + startActivity(intetn) + } + } + } + } else { + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() + startActivity(intetn) + } + } + + // Получение новых (необработанных) обращений с сервера и обновление ViewModel + private fun getNewAppeals() { + if (enternetCheck.isOnline(requireContext())) { + initRetrofit() + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) + CoroutineScope(Dispatchers.IO).launch { + val listAppeals = doctorApi.GetAppealsDoctorNew("Bearer $Tokens") + requireActivity().runOnUiThread { + //Фиксируем полученные данные + val List = listAppeals.body() + val Nice = listAppeals.isSuccessful + val Code = listAppeals.code() + if(Code==429){ + val intetn = Intent(requireContext(), Code429Activity::class.java) + startActivity(intetn) + } + else if(Code==200) { + //Если нету ошибок + if (Nice) { + if (List != null) { + if(List.appeals_new != null){ + model.appealsNewList.value = List.appeals_new + } + } + } + } + else if (Code == 500) { + val intetn = Intent(requireContext(), Code500Activity::class.java) + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(requireContext(), AuthActivity::class.java) + activity?.finish() + startActivity(intetn) + } + } + } + } else { + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() + startActivity(intetn) + } + } + + // Инициализация ViewPager и TabLayout для переключения между вкладками обращений + private fun init() = with(binding) { + val adapter = VpAdapter(activity as FragmentActivity, flist) + vpProduct.adapter = adapter + //Переключения (связываем таблаяут(переключатель) с viewpager, чтобы переключать фрагменты) + TabLayoutMediator(tabLayoutProduct, vpProduct) { tab, pos -> + tab.text = + tList[pos]//tab - нажатая кнопка, pos - позиция кнопки, tList[pos] - передаем название по полученной позиции + }.attach()// attach() - чтобы все переключалось, а не вывадило постоянно один экран + //Изменения цвета в зависомости на каком из tabLayout вы находитесь + binding.tabLayoutProduct.setTabTextColors( + getResources().getColor(R.color.black), + getResources().getColor(R.color.white) + ); + } + + // Инициализация клиента Retrofit для сетевых запросов + private fun initRetrofit() { + val interceptor = HttpLoggingInterceptor() + interceptor.level = HttpLoggingInterceptor.Level.BODY + val client = OkHttpClient + .Builder() + .addInterceptor(interceptor) + .build() + val retrofit = Retrofit.Builder() + .baseUrl("https://rehabilitation.vmeda.org/api/") + .client(client) + .addConverterFactory(GsonConverterFactory.create()) + .build() + doctorApi = retrofit.create(DoctorApi::class.java) + } + + companion object { + // Фабричный метод для создания экземпляра AppealsFragment + fun newInstance() = AppealsFragment() + } + + // Функция жизненного цикла, запускает таймер для периодического обновления обращений + override fun onStart() { + super.onStart() + checkForUpdates(true) + } + + // Функция жизненного цикла, останавливает таймер при остановке фрагмента + override fun onStop() { + super.onStop() + timer.cancel() + timer.purge() + } + + // Запускает таймер, который каждые 10 секунд обновляет списки обращений + private fun checkForUpdates(daemonIsTrue: Boolean) { + timer = fixedRateTimer("default", daemonIsTrue, 0, 10000) { + activity?.runOnUiThread { + getOldAppeals() + getNewAppeals() + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/Appeals/AppealsNewAdapter.kt b/app/src/main/java/com/example/doctor/Appeals/AppealsNewAdapter.kt new file mode 100644 index 0000000..25a36ff --- /dev/null +++ b/app/src/main/java/com/example/doctor/Appeals/AppealsNewAdapter.kt @@ -0,0 +1,109 @@ +package com.example.doctor.Appeals + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import com.example.doctor.Appeals.TabLayout.Model.AppealsNewModel +import com.example.doctor.Appeals.TabLayout.Model.AppealsOldModel +import com.example.doctor.R +import com.example.doctor.databinding.ItemAppealsNewBinding + + +class AppealsNewAdapter(val listener_check_apeals: Listener, val listener_patient_apeals: Listener2) : + ListAdapter( + Comparator() + ) {//Productitem - по этой форме будем заполнять.//ProductAdapter.holder - это создаваемый holder который хранит логику как нужно заполнять карточку + + // ViewHolder для элемента списка обращений + class Holder(view: View, val listener_check_apeals: Listener, val listener_patient_apeals: Listener2) : + RecyclerView.ViewHolder(view) { + // Класс, который хранит ссылки на элементы и обрабатывает нажатия + val binding = + ItemAppealsNewBinding.bind(view) + + var itemTemp: AppealsNewModel? = + null//Глобальная переменная для нашего item, чтобы можно было передать данные для нажатия + + // Инициализация обработчиков нажатий на кнопки в карточке + init { + binding.btnAdd.setOnClickListener { + itemTemp?.let { it1 -> listener_check_apeals.onClickAppeals(it1) } + } + binding.btnPatient.setOnClickListener { + itemTemp?.let { it2 -> listener_patient_apeals.onClickAppealsPatient(it2) } + } + } + + // Привязка данных к элементу списка + @SuppressLint("SuspiciousIndentation") + fun bind(item: AppealsNewModel) = with(binding) { + itemTemp = item + txtNumber.text = item.number.toString()+"." + txtAppeals.text = item.text + txtLoginPatient.text = item.login + + if (item.expand) { + txtAppeals.maxLines = 30 + } else { + txtAppeals.maxLines = 1 + } + + CardViewNew.setOnClickListener { + if (item.expand == false) { + txtAppeals.maxLines = 100 + item.expand = true + } else { + txtAppeals.maxLines = 1 + item.expand = false + } + } + } + } + + // Создание ViewHolder для элемента списка + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.item_appeals_new, parent, false) + return Holder(view, listener_check_apeals,listener_patient_apeals) + } + + // Привязка данных к ViewHolder + override fun onBindViewHolder(holder: Holder, position: Int) { + val view = holder.bind(getItem(position)) + } + + // Comparator - сравнивает старый список и новый, чтобы обновлять только изменённые элементы + class Comparator : DiffUtil.ItemCallback() { + override fun areItemsTheSame( + oldItem: AppealsNewModel, + newItem: AppealsNewModel + ): Boolean { + return oldItem.id == newItem.id + } + + override fun areContentsTheSame( + oldItem: AppealsNewModel, + newItem: AppealsNewModel + ): Boolean { + return oldItem == newItem + } + } + + // Интерфейс для обработки нажатия на кнопку "Обработать обращение" + interface Listener { + fun onClickAppeals(item: AppealsNewModel) + } + + // Интерфейс для обработки нажатия на кнопку "Открыть пациента" + interface Listener2 { + fun onClickAppealsPatient(item: AppealsNewModel) + } +} + + + + diff --git a/app/src/main/java/com/example/doctor/Appeals/AppealsOldAdapter.kt b/app/src/main/java/com/example/doctor/Appeals/AppealsOldAdapter.kt new file mode 100644 index 0000000..ef27b7e --- /dev/null +++ b/app/src/main/java/com/example/doctor/Appeals/AppealsOldAdapter.kt @@ -0,0 +1,109 @@ +package com.example.doctor.Appeals + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import com.example.doctor.Appeals.TabLayout.Model.AppealsOldModel +import com.example.doctor.Appeals.TabLayout.OldAppealsFragment +import com.example.doctor.R +import com.example.doctor.databinding.ItemAppealsOldBinding + + +class AppealsOldAdapter(val listener_appeal: Listener, val listener_patient_apeals: OldAppealsFragment) : + ListAdapter( + Comparator() + ) { + // Адаптер для отображения обработанных обращений в RecyclerView + + // ViewHolder для элемента списка обработанных обращений + class Holder(view: View, val listener_appeal: Listener, val listener_patient_apeals: OldAppealsFragment) : + RecyclerView.ViewHolder(view) { + // Класс, который хранит ссылки на элементы и обрабатывает нажатия + val binding = + ItemAppealsOldBinding.bind(view) + + var itemTemp: AppealsOldModel? = + null // Глобальная переменная для текущего элемента, чтобы можно было передать данные при нажатии + + // Инициализация обработчиков нажатий на карточку и кнопку пациента + init { + itemView.setOnClickListener { + itemTemp?.let { it1 -> listener_appeal.onClickAppeals(it1) } + } + binding.btnPatient.setOnClickListener { + itemTemp?.let { it2 -> listener_patient_apeals.onClickAppealsPatient(it2) } + } + } + + // Привязка данных к элементу списка + @SuppressLint("SuspiciousIndentation") + fun bind(item: AppealsOldModel) = with(binding) { + itemTemp = item + txtNumber.text = item.number.toString()+"." + txtAppeals.text = item.text + txtLoginPatient.text = item.login + + if (item.expand) { + binding.txtAppeals.maxLines = 100 + } else { + binding.txtAppeals.maxLines = 1 + } + + binding.CardViewOld.setOnClickListener { + if (item.expand == false) { + binding.txtAppeals.maxLines = 100 + item.expand = true + } else { + binding.txtAppeals.maxLines = 1 + item.expand = false + } + } + } + } + + // Создание ViewHolder для элемента списка + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.item_appeals_old, parent, false) + return Holder(view, listener_appeal,listener_patient_apeals) + } + + // Привязка данных к ViewHolder + override fun onBindViewHolder(holder: Holder, position: Int) { + val view = holder.bind(getItem(position)) + } + + // Comparator - сравнивает старый и новый списки, чтобы обновлять только изменённые элементы + class Comparator : DiffUtil.ItemCallback() { + override fun areItemsTheSame( + oldItem: AppealsOldModel, + newItem: AppealsOldModel + ): Boolean { + return oldItem.id == newItem.id + } + + override fun areContentsTheSame( + oldItem: AppealsOldModel, + newItem: AppealsOldModel + ): Boolean { + return oldItem == newItem + } + } + + // Интерфейс для обработки нажатия на карточку обращения + interface Listener { + fun onClickAppeals(item: AppealsOldModel) + } + // Интерфейс для обработки нажатия на кнопку "Открыть пациента" + interface Listener2 { + fun onClickAppealsPatient(item: AppealsOldModel) + } +} + + + + diff --git a/app/src/main/java/com/example/doctor/Appeals/CreateMessageFragment.kt b/app/src/main/java/com/example/doctor/Appeals/CreateMessageFragment.kt new file mode 100644 index 0000000..d682fc5 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Appeals/CreateMessageFragment.kt @@ -0,0 +1,281 @@ +package com.example.doctor.Appeals + +import android.content.Intent +import android.os.Bundle +import android.transition.TransitionInflater +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ArrayAdapter +import android.widget.Toast +import androidx.fragment.app.activityViewModels +import com.example.doctor.Auth.AuthActivity +import com.example.doctor.CodeError.Code429Activity +import com.example.doctor.CodeError.Code500Activity +import com.example.doctor.DataModel +import com.example.doctor.DoctorViewModel +import com.example.doctor.Enternet.EnternetActivity +import com.example.doctor.Enternet.EnternetCheck +import com.example.doctor.Patients.Model.PatientAllModel +import com.example.doctor.Patients.Reports.PatientFragment +import com.example.doctor.Pref.ClearPref +import com.example.doctor.Pref.ConclusionPref +import com.example.doctor.R +import com.example.doctor.Retrofit.DoctorApi +import com.example.doctor.Toast.showCustomInfoToast +import com.example.doctor.databinding.FragmentCreateMessageBinding +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import java.util.Timer +import kotlin.concurrent.fixedRateTimer + + +class CreateMessageFragment : Fragment() { + private lateinit var binding: FragmentCreateMessageBinding + private val dataModel: DataModel by activityViewModels()//Для передачи данных + private val modelDoctor: DoctorViewModel by activityViewModels() + + private lateinit var doctorApi: DoctorApi + private lateinit var timer: Timer + + + val prefDoctorConclusion = ConclusionPref() + val prefDoctorClear = ClearPref() + + var patientList:List?=null + //Класс проверки интеренета + val enternetCheck = EnternetCheck() + // Функция жизненного цикла, создаёт и возвращает view фрагмента + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentCreateMessageBinding.inflate(layoutInflater,container,false) + return binding.root + + } + + // Функция жизненного цикла, вызывается после создания view. Инициализирует интерфейс и подписки на данные + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + val inflater = TransitionInflater.from(requireContext()) + enterTransition = inflater.inflateTransition(R.transition.slide_right) + exitTransition = inflater.inflateTransition(R.transition.slide_right) + + GetPatientList() // Загрузка списка пациентов + + binding.btnAddMessagePatient.setOnClickListener{ + val login = binding.searchPatient.text.toString() + val text = binding.edTextMessagePatient.text.toString() + if(login!="" && text!=""){ + AddMessageDoctor() // Отправка сообщения + } + else{ + Toast(requireContext()).showCustomInfoToast("Не все данные заполнены", requireActivity()) + + } + } + +// binding.btnExit.setOnClickListener{ +// dataModel.patientAppeal.value = 1 +// } + + binding.btnExit.setOnClickListener{ + activity?.finish() + } + + modelDoctor.patientListSearch.observe(viewLifecycleOwner){ + if(patientList!=it){ + patientList =it + addPatientLogin(it) // Обновление автодополнения по пациентам + } + } + +// fixedRateTimer("timer", false, 0, 10000) { +// activity?.runOnUiThread { +// +// } +// } + } + + // Добавляет автодополнение по логинам пациентов + private fun addPatientLogin(patient: List) { + val listPatient = arrayListOf("") + for(i in 0 .. patient.count()-1){ + listPatient.add(patient[i].login.toString()) + } + + val adapter = ArrayAdapter(requireContext(), android.R.layout.simple_list_item_1, listPatient) + + binding.searchPatient.threshold = 1 + binding.searchPatient.setAdapter(adapter) + + binding.searchPatient.setOnItemClickListener { parent, arg1, pos, id -> + //val list = ArrayList() + val serpat = binding.searchPatient.text.toString() + binding.searchPatient.setText(serpat) + + } + } + + + // Получение списка всех пациентов с сервера и обновление ViewModel + fun GetPatientList() { + if (enternetCheck.isOnline(requireContext())) { + initRetrofit() + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) + CoroutineScope(Dispatchers.IO).launch { + val listProduct = doctorApi.GetPatientAll("Bearer $Tokens") + requireActivity().runOnUiThread { + + + //Фиксируем полученные данные + val List = listProduct.body() + val Nice = listProduct.isSuccessful + val Code = listProduct.code() + if(Code==429){ + val intetn = Intent(requireContext(), Code429Activity::class.java) + + startActivity(intetn) + } + else if(Code==200) { + //Если нету ошибок + if (Nice) { + if (List != null) { + //Если нету ошибок + if (List != null) { + modelDoctor.patientListSearch.value = List.patient + } + } + } + } + else if (Code == 500) { + val intetn = Intent(requireContext(), Code500Activity::class.java) + + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(requireContext(), AuthActivity::class.java) + activity?.finish() + startActivity(intetn) + } + } + + } + } else { + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() + startActivity(intetn) + } + + } + + // Отправка сообщения врачу (POST-запрос) + fun AddMessageDoctor() { + if (enternetCheck.isOnline(requireContext())) { + initRetrofit() + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) + CoroutineScope(Dispatchers.IO).launch { + val listProduct = doctorApi.AddMessageDoctor("Bearer $Tokens",binding.searchPatient.text.toString(),binding.edTextMessagePatient.text.toString()) + requireActivity().runOnUiThread { + + //Фиксируем полученные данные + val List = listProduct.body() + val Nice = listProduct.isSuccessful + val Code = listProduct.code() + if(Code==429){ + val intetn = Intent(requireContext(), Code429Activity::class.java) + + startActivity(intetn) + } + else if(Code==200) { + + if (Nice) { + if (List != null) { + //Если нету ошибок + if (List != null) { + Toast(requireContext()).showCustomInfoToast(List.message, requireActivity()) + binding.edTextMessagePatient.setText("") + } + + } + } + } + else if (Code == 500) { + val intetn = Intent(requireContext(), Code500Activity::class.java) + + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(requireContext(), AuthActivity::class.java) + activity?.finish() + startActivity(intetn) + } + } + + } + } else { + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() + startActivity(intetn) + } + + } + + + // Инициализация клиента Retrofit для сетевых запросов + private fun initRetrofit() { + val interceptor = HttpLoggingInterceptor() + interceptor.level = HttpLoggingInterceptor.Level.BODY + + val client = OkHttpClient + .Builder() + .addInterceptor(interceptor) + .build() + + val retrofit = Retrofit.Builder() + .baseUrl("https://rehabilitation.vmeda.org/api/") + .client(client) + .addConverterFactory(GsonConverterFactory.create()) + .build() + + doctorApi = retrofit.create(DoctorApi::class.java) + + } + + companion object { + + fun newInstance() = CreateMessageFragment() + } + + + // Функция жизненного цикла, запускает таймер для периодического обновления списка пациентов + override fun onResume() { + super.onResume() + checkForUpdates(true) + } + + // Функция жизненного цикла, останавливает таймер при остановке фрагмента + override fun onStop() { + super.onStop() + timer.cancel() + timer.purge() + } + + // Запускает таймер, который каждые 10 секунд обновляет список пациентов + private fun checkForUpdates(daemonIsTrue: Boolean) { + + timer = fixedRateTimer("default", daemonIsTrue, 0, 10000) { + activity?.runOnUiThread { + GetPatientList() + } + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/Appeals/TabLayout/Model/AppealsNewListModel.kt b/app/src/main/java/com/example/doctor/Appeals/TabLayout/Model/AppealsNewListModel.kt new file mode 100644 index 0000000..4d7c7e1 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Appeals/TabLayout/Model/AppealsNewListModel.kt @@ -0,0 +1,7 @@ +package com.example.doctor.Appeals.TabLayout.Model + +// Модель данных для списка новых обращений (необработанных) +data class AppealsNewListModel( + val appeals_new: List // Список новых обращений +) + diff --git a/app/src/main/java/com/example/doctor/Appeals/TabLayout/Model/AppealsNewModel.kt b/app/src/main/java/com/example/doctor/Appeals/TabLayout/Model/AppealsNewModel.kt new file mode 100644 index 0000000..da3581a --- /dev/null +++ b/app/src/main/java/com/example/doctor/Appeals/TabLayout/Model/AppealsNewModel.kt @@ -0,0 +1,16 @@ +package com.example.doctor.Appeals.TabLayout.Model + +// Модель данных для нового обращения (необработанного) +data class AppealsNewModel( + val id: Int, // Уникальный идентификатор обращения + val number: Int, // Порядковый номер обращения + val text: String, // Текст обращения + val id_patient: Int, // ID пациента + val id_doctor: Int, // ID доктора + val check: Int, // Статус проверки обращения + val login: String, // Логин пациента + val created_at: String, // Дата создания + val updated_at: String, // Дата обновления + var expand : Boolean = false, // Флаг для раскрытия текста обращения в UI +) + diff --git a/app/src/main/java/com/example/doctor/Appeals/TabLayout/Model/AppealsOldListModel.kt b/app/src/main/java/com/example/doctor/Appeals/TabLayout/Model/AppealsOldListModel.kt new file mode 100644 index 0000000..a5f7a2b --- /dev/null +++ b/app/src/main/java/com/example/doctor/Appeals/TabLayout/Model/AppealsOldListModel.kt @@ -0,0 +1,7 @@ +package com.example.doctor.Appeals.TabLayout.Model + +// Модель данных для списка обработанных обращений +data class AppealsOldListModel( + val appeals_old: List // Список обработанных обращений +) + diff --git a/app/src/main/java/com/example/doctor/Appeals/TabLayout/Model/AppealsOldModel.kt b/app/src/main/java/com/example/doctor/Appeals/TabLayout/Model/AppealsOldModel.kt new file mode 100644 index 0000000..d2693fb --- /dev/null +++ b/app/src/main/java/com/example/doctor/Appeals/TabLayout/Model/AppealsOldModel.kt @@ -0,0 +1,16 @@ +package com.example.doctor.Appeals.TabLayout.Model + +// Модель данных для обработанного обращения +data class AppealsOldModel( + val id: Int, // Уникальный идентификатор обращения + val number: Int, // Порядковый номер обращения + val text: String, // Текст обращения + val id_patient: Int, // ID пациента + val id_doctor: Int, // ID доктора + val check: Int, // Статус проверки обращения + val login: String, // Логин пациента + val created_at: String, // Дата создания + val updated_at: String, // Дата обновления + var expand : Boolean = false, // Флаг для раскрытия текста обращения в UI +) + diff --git a/app/src/main/java/com/example/doctor/Appeals/TabLayout/NewAppealsFragment.kt b/app/src/main/java/com/example/doctor/Appeals/TabLayout/NewAppealsFragment.kt new file mode 100644 index 0000000..c0aa75b --- /dev/null +++ b/app/src/main/java/com/example/doctor/Appeals/TabLayout/NewAppealsFragment.kt @@ -0,0 +1,257 @@ +package com.example.doctor.Appeals.TabLayout + +import android.content.Intent +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.fragment.app.activityViewModels +import androidx.recyclerview.widget.GridLayoutManager +import com.example.doctor.Appeals.AppealsNewAdapter +import com.example.doctor.Appeals.TabLayout.Model.AppealsNewModel +import com.example.doctor.Appeals.TabLayout.Model.AppealsOldModel +import com.example.doctor.Auth.AuthActivity +import com.example.doctor.CodeError.Code429Activity +import com.example.doctor.CodeError.Code500Activity +import com.example.doctor.DataModel +import com.example.doctor.DoctorViewModel +import com.example.doctor.Enternet.EnternetActivity +import com.example.doctor.Enternet.EnternetCheck +import com.example.doctor.Patients.Model.PatientIdModel +import com.example.doctor.Patients.PatientActivity +import com.example.doctor.Patients.PatientsListFragment +import com.example.doctor.Pref.ConclusionPref +import com.example.doctor.Pref.SavePref +import com.example.doctor.R +import com.example.doctor.Retrofit.DoctorApi +import com.example.doctor.Toast.showCustomInfoToast +import com.example.doctor.databinding.FragmentNewAppealsBinding +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory + +// Фрагмент для отображения списка новых (необработанных) обращений +class NewAppealsFragment : Fragment(), AppealsNewAdapter.Listener,AppealsNewAdapter.Listener2 { + private lateinit var binding:FragmentNewAppealsBinding + private val model: DoctorViewModel by activityViewModels() + private val dataModel: DataModel by activityViewModels()//Для передачи данных между фрагментами + lateinit var adapter: AppealsNewAdapter + private lateinit var doctorApi: DoctorApi + val prefDoctorConclusion = ConclusionPref() + val prefDoctorSave = SavePref() + + // Класс для проверки наличия интернета + val enternetCheck = EnternetCheck() + + // Список новых обращений + var newAppeals: List? = null + // Функция жизненного цикла, создаёт и возвращает view фрагмента + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentNewAppealsBinding.inflate(layoutInflater,container,false) + return binding.root + } + + // Функция жизненного цикла, вызывается после создания view. Инициализирует интерфейс и подписки на данные + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + initRcViewAppeals() // Инициализация списка обращений (RecyclerView) + patientListCurrent() // (зарезервировано для будущего) + // Подписка на обновление списка обращений + model.appealsNewList.observe(viewLifecycleOwner) { + if(newAppeals != it){ + newAppeals = it + adapter.submitList(it) + if(newAppeals !=null){ + binding.CLNull.visibility = View.GONE + binding.txtNull.visibility = View.GONE + } + } + } + getNewAppeals() // Загрузка новых обращений с сервера + } + + // Получение необработанных обращений с сервера и обновление ViewModel + private fun getNewAppeals() { + if (enternetCheck.isOnline(requireContext())) { + initRetrofit() + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) + CoroutineScope(Dispatchers.IO).launch { + val listAppeals = doctorApi.GetAppealsDoctorNew("Bearer $Tokens") + requireActivity().runOnUiThread { + // Обработка ответа сервера + val List = listAppeals.body() + val Nice = listAppeals.isSuccessful + val Code = listAppeals.code() + if(Code==429){ + val intetn = Intent(requireContext(), Code429Activity::class.java) + startActivity(intetn) + } + else if(Code==200) { + if (Nice) { + if(List?.appeals_new != null){ + binding.txtNull.visibility = View.GONE + model.appealsNewList.value = List.appeals_new + } + else{ + binding.txtNull.visibility = View.VISIBLE + } + } + } + else if (Code == 500) { + val intetn = Intent(requireContext(), Code500Activity::class.java) + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(requireContext(), AuthActivity::class.java) + activity?.finish() + startActivity(intetn) + } + } + } + } else { + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() + startActivity(intetn) + } + } + + // Получение обработанных обращений с сервера и обновление ViewModel (для синхронизации) + private fun getOldAppeals() { + if (enternetCheck.isOnline(requireContext())) { + initRetrofit() + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) + CoroutineScope(Dispatchers.IO).launch { + val listAppeals = doctorApi.GetAppealsDoctorOld("Bearer $Tokens") + requireActivity().runOnUiThread { + val List = listAppeals.body() + val Nice = listAppeals.isSuccessful + val Code = listAppeals.code() + if(Code==429){ + val intetn = Intent(requireContext(), Code429Activity::class.java) + startActivity(intetn) + } + else if(Code==200) { + if (Nice) { + if(List?.appeals_old != null){ + model.appealsOldList.value = List.appeals_old + } + } + } + else if (Code == 500) { + val intetn = Intent(requireContext(), Code500Activity::class.java) + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(requireContext(), AuthActivity::class.java) + activity?.finish() + startActivity(intetn) + } + } + } + } else { + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() + startActivity(intetn) + } + } + + // Отметить обращение как обработанное (отправка на сервер) + private fun addCheckAppeals(id:Int,id_patient:Int) { + if (enternetCheck.isOnline(requireContext())) { + initRetrofit() + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) + CoroutineScope(Dispatchers.IO).launch { + val listAppeals = doctorApi.UpdateMessageDoctor("Bearer $Tokens",id,id_patient) + requireActivity().runOnUiThread { + val List = listAppeals.body() + val Nice = listAppeals.isSuccessful + val Code = listAppeals.code() + if(Code==429){ + val intetn = Intent(requireContext(), Code429Activity::class.java) + startActivity(intetn) + } + else if(Code==200) { + if (Nice) { + if (List != null) { + Toast(requireContext()).showCustomInfoToast(List.message, requireActivity()) + } + } + getNewAppeals() + getOldAppeals() + } + else if (Code == 500) { + val intetn = Intent(requireContext(), Code500Activity::class.java) + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(requireContext(), AuthActivity::class.java) + activity?.finish() + startActivity(intetn) + } + } + } + } else { + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() + startActivity(intetn) + } + } + + // Инициализация клиента Retrofit для сетевых запросов + private fun initRetrofit() { + val interceptor = HttpLoggingInterceptor() + interceptor.level = HttpLoggingInterceptor.Level.BODY + val client = OkHttpClient + .Builder() + .addInterceptor(interceptor) + .build() + val retrofit = Retrofit.Builder() + .baseUrl("https://rehabilitation.vmeda.org/api/") + .client(client) + .addConverterFactory(GsonConverterFactory.create()) + .build() + doctorApi = retrofit.create(DoctorApi::class.java) + } + + // (Зарезервировано для будущего, например, для вывода прогресса) + private fun patientListCurrent() = with(binding) { +// model.appealsAllCurrent.observe(viewLifecycleOwner) {//viewLifecycleOwner - следит за циклом жизни fragment +// +// } + } + + // Инициализация списка обращений (RecyclerView) + private fun initRcViewAppeals() = with(binding) { + rcView.layoutManager = GridLayoutManager(requireContext(),1) + adapter = AppealsNewAdapter(this@NewAppealsFragment,this@NewAppealsFragment) + rcView.adapter = adapter + } + + companion object { + // Фабричный метод для создания экземпляра NewAppealsFragment + fun newInstance() = NewAppealsFragment() + } + + // Обработчик нажатия на кнопку "Обработать обращение" + override fun onClickAppeals(item: AppealsNewModel) { + addCheckAppeals(item.id,item.id_patient) + } + + // Обработчик нажатия на кнопку "Открыть пациента" + override fun onClickAppealsPatient(item: AppealsNewModel) { + prefDoctorSave.saveIdPatient(requireContext(),item.id_patient) + prefDoctorSave.saveViewPatient(requireContext(),1) + model.patientId.value = PatientIdModel(item.id) + val intetn = Intent(requireContext(), PatientActivity::class.java) + startActivity(intetn) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/Appeals/TabLayout/OldAppealsFragment.kt b/app/src/main/java/com/example/doctor/Appeals/TabLayout/OldAppealsFragment.kt new file mode 100644 index 0000000..128d2cc --- /dev/null +++ b/app/src/main/java/com/example/doctor/Appeals/TabLayout/OldAppealsFragment.kt @@ -0,0 +1,176 @@ +package com.example.doctor.Appeals.TabLayout + +import android.content.Intent +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.activityViewModels +import androidx.recyclerview.widget.GridLayoutManager +import com.example.doctor.Appeals.AppealsOldAdapter +import com.example.doctor.Appeals.TabLayout.Model.AppealsNewModel +import com.example.doctor.Appeals.TabLayout.Model.AppealsOldModel +import com.example.doctor.Auth.AuthActivity +import com.example.doctor.CodeError.Code429Activity +import com.example.doctor.CodeError.Code500Activity +import com.example.doctor.DataModel +import com.example.doctor.DoctorViewModel +import com.example.doctor.Enternet.EnternetActivity +import com.example.doctor.Enternet.EnternetCheck +import com.example.doctor.Patients.Model.PatientIdModel +import com.example.doctor.Patients.PatientActivity +import com.example.doctor.Patients.PatientsListFragment +import com.example.doctor.Pref.ConclusionPref +import com.example.doctor.Pref.SavePref +import com.example.doctor.R +import com.example.doctor.Retrofit.DoctorApi +import com.example.doctor.databinding.FragmentOldAppealsBinding +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import kotlin.concurrent.fixedRateTimer + +// Фрагмент для отображения списка обработанных обращений +class OldAppealsFragment : Fragment(), AppealsOldAdapter.Listener,AppealsOldAdapter.Listener2 { + private lateinit var binding:FragmentOldAppealsBinding + private val model: DoctorViewModel by activityViewModels() + private val dataModel: DataModel by activityViewModels()//Для передачи данных между фрагментами + + lateinit var adapter: AppealsOldAdapter + private lateinit var doctorApi: DoctorApi + val prefDoctorConclusion = ConclusionPref() + val prefDoctorSave = SavePref() + + // Класс для проверки наличия интернета + val enternetCheck = EnternetCheck() + + // Список обработанных обращений + var oldAppeals: List? = null + + // Функция жизненного цикла, создаёт и возвращает view фрагмента + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentOldAppealsBinding.inflate(layoutInflater,container,false) + return binding.root + } + + // Функция жизненного цикла, вызывается после создания view. Инициализирует интерфейс и подписки на данные + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + initRcViewAppeals() // Инициализация списка обращений (RecyclerView) + appealsAllCurrent() // (зарезервировано для будущего) + // Подписка на обновление списка обработанных обращений + model.appealsOldList.observe(viewLifecycleOwner) { + if(oldAppeals != it){ + oldAppeals = it + adapter.submitList(it) + if(oldAppeals !=null){ + binding.CLNull.visibility = View.GONE + binding.txtNull.visibility = View.GONE + } + } + } + getOldAppeals() // Загрузка обработанных обращений с сервера + } + + // Получение обработанных обращений с сервера и обновление ViewModel + private fun getOldAppeals() { + if (enternetCheck.isOnline(requireContext())) { + initRetrofit() + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) + CoroutineScope(Dispatchers.IO).launch { + val listAppeals = doctorApi.GetAppealsDoctorOld("Bearer $Tokens") + requireActivity().runOnUiThread { + val List = listAppeals.body() + val Nice = listAppeals.isSuccessful + val Code = listAppeals.code() + if(Code==429){ + val intetn = Intent(requireContext(), Code429Activity::class.java) + startActivity(intetn) + } + else if(Code==200) { + if (Nice) { + if (List != null) { + if(List.appeals_old != null){ + binding.txtNull.visibility = View.GONE + model.appealsOldList.value = List.appeals_old + } + else{ + binding.txtNull.visibility = View.VISIBLE + } + }else{ + binding.txtNull.visibility = View.VISIBLE + } + } + } + else if (Code == 500) { + val intetn = Intent(requireContext(), Code500Activity::class.java) + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(requireContext(), AuthActivity::class.java) + activity?.finish() + startActivity(intetn) + } + } + } + } else { + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() + startActivity(intetn) + } + } + + // Инициализация клиента Retrofit для сетевых запросов + private fun initRetrofit() { + val interceptor = HttpLoggingInterceptor() + interceptor.level = HttpLoggingInterceptor.Level.BODY + val client = OkHttpClient + .Builder() + .addInterceptor(interceptor) + .build() + val retrofit = Retrofit.Builder() + .baseUrl("https://rehabilitation.vmeda.org/api/") + .client(client) + .addConverterFactory(GsonConverterFactory.create()) + .build() + doctorApi = retrofit.create(DoctorApi::class.java) + } + + // (Зарезервировано для будущего, например, для вывода прогресса) + private fun appealsAllCurrent() = with(binding) { +// model.appealsAllCurrent.observe(viewLifecycleOwner) {//viewLifecycleOwner - следит за циклом жизни fragment +// +// } + } + + // Инициализация списка обращений (RecyclerView) + private fun initRcViewAppeals() = with(binding) { + rcView.layoutManager = GridLayoutManager(requireContext(),1) + adapter = AppealsOldAdapter(this@OldAppealsFragment,this@OldAppealsFragment) + rcView.adapter = adapter + } + companion object { + // Фабричный метод для создания экземпляра OldAppealsFragment + fun newInstance() = OldAppealsFragment() + } + + // Обработчик нажатия на обращение (пустой, можно реализовать при необходимости) + override fun onClickAppeals(item: AppealsOldModel) { + // Здесь можно реализовать дополнительную логику при нажатии на обращение + } + + // Обработчик нажатия на кнопку "Открыть пациента" + override fun onClickAppealsPatient(item: AppealsOldModel) { + model.patientId.value = PatientIdModel(item.id) + val intetn = Intent(requireContext(), PatientActivity::class.java) + startActivity(intetn) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/Auth/AuthActivity.kt b/app/src/main/java/com/example/doctor/Auth/AuthActivity.kt new file mode 100644 index 0000000..dfe9849 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Auth/AuthActivity.kt @@ -0,0 +1,24 @@ +package com.example.doctor.Auth + +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import com.example.doctor.R +import com.example.doctor.databinding.ActivityAuthBinding + +// Активити для аутентификации пользователя (доктора) +class AuthActivity : AppCompatActivity() { + private lateinit var binding:ActivityAuthBinding + + // Функция жизненного цикла Activity: инициализация интерфейса и отображение фрагмента аутентификации + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityAuthBinding.inflate(layoutInflater) + setContentView(binding.root) + + // Отображение фрагмента аутентификации при создании активити + supportFragmentManager.beginTransaction() + .replace(R.id.CLAuth, AuthFragment.newInstance()) + .commit() + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/Auth/AuthFragment.kt b/app/src/main/java/com/example/doctor/Auth/AuthFragment.kt new file mode 100644 index 0000000..0694022 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Auth/AuthFragment.kt @@ -0,0 +1,200 @@ +package com.example.doctor.Auth + +import android.annotation.SuppressLint +import android.content.Intent +import android.os.Bundle +import android.util.Log +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.fragment.app.activityViewModels +import com.example.doctor.Auth.Model.AuthModel +import com.example.doctor.Auth.Model.UserModel +import com.example.doctor.CodeError.Code429Activity +import com.example.doctor.CodeError.Code500Activity +import com.example.doctor.DoctorViewModel +import com.example.doctor.Enternet.EnternetActivity +import com.example.doctor.Enternet.EnternetCheck +import com.example.doctor.MainActivity +import com.example.doctor.Pref.ClearPref +import com.example.doctor.Pref.ConclusionPref +import com.example.doctor.R +import com.example.doctor.Retrofit.DoctorApi +import com.example.doctor.Pref.SavePref +import com.example.doctor.Toast.showCustomDangerToast +import com.example.doctor.Toast.showCustomInfoToast +import com.example.doctor.databinding.FragmentAuthBinding +import com.squareup.picasso.Picasso +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import org.json.JSONObject +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import retrofit2.create + + +// Фрагмент для аутентификации пользователя (доктора) +class AuthFragment : Fragment() { + + private lateinit var binding: FragmentAuthBinding + private lateinit var doctorApi: DoctorApi + private val DoctorviewModel: DoctorViewModel by activityViewModels() + + // Переменная для хранения токена + private var Token = "" + // Вспомогательные классы для работы с SharedPreferences + val prefDoctorSave = SavePref() + val prefDoctorConclusion = ConclusionPref() + val prefDoctorClear = ClearPref() + + // Класс для проверки наличия интернета + val enternetCheck = EnternetCheck() + // Функция жизненного цикла Fragment: создание и возврат View + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentAuthBinding.inflate(layoutInflater, container, false) + return binding.root + } + + // Функция жизненного цикла Fragment: инициализация View и обработчиков + @SuppressLint("SuspiciousIndentation") + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + initRetrofit() // Инициализация Retrofit клиента + + prefDoctorClear.clearToken(requireContext()) // Очистка сохраненного токена + Token = prefDoctorConclusion.conclusionToken(requireContext()) // Получение токена (после очистки должен быть пустым) + + binding.apply { + binding.textError.text = "" // Очистка текста ошибки + + // Обработчик нажатия на кнопку "Авторизоваться" + binding.btnAuth.setOnClickListener { + // Проверка на заполненность полей логина и пароля + if(edLogin.text.toString() == ""||edPassword.text.toString() == ""){ + Toast(requireContext()).showCustomInfoToast( + "Не все поля заполнены", + requireActivity() + ) + } + else{ + // Вызов функции аутентификации с введенными данными + Auth( + AuthModel( + edLogin.text.toString(), + edPassword.text.toString(), + ) + ) + } + } + } + } + + + + // Функция для выполнения запроса аутентификации к серверу + private fun Auth(authModel: AuthModel) { + // Проверка интернет-соединения + if (enternetCheck.isOnline(requireContext())) { + initRetrofit() // Повторная инициализация Retrofit (возможно, избыточна здесь) + // Запуск корутины для выполнения сетевого запроса в фоновом потоке + CoroutineScope(Dispatchers.IO).launch { + val response = doctorApi.LoginDoctor(authModel) // Выполнение POST-запроса аутентификации + // Обработка сообщения об ошибке из тела ответа при его наличии + val errorMessage = response.errorBody()?.string() + ?.let { JSONObject(it).getString("error") } + // Переключение на главный поток для обновления UI + requireActivity().runOnUiThread { + + // Фиксируем полученные данные из ответа + val List = response.body() + val Nice = response.isSuccessful + val Code = response.code() + // Обработка различных кодов ответа сервера + if(Code==429){ + val intetn = Intent(requireContext(), Code429Activity::class.java) + + startActivity(intetn) + } + else if(Code==200) { + // Если ответ успешный (код 200) + if (Nice) { + if (List != null) { + // Сохранение токена в ViewModel и SharedPreferences + DoctorviewModel.token.value = List.token + prefDoctorSave.saveToken(requireContext(), List.token) + + // Переход на главный экран приложения + val intetn = Intent(requireContext(), MainActivity::class.java) + startActivity(intetn) + activity?.finish() // Закрытие текущей активити аутентификации + } + else{ + // Отображение сообщения об ошибке, если пользователь не найден + Toast(requireContext()).showCustomInfoToast( + "Такого пользователя нету", + requireActivity() + ) + } + } + } + else if (Code == 500) { + // Обработка внутренней ошибки сервера + val intetn = Intent(requireContext(), Code500Activity::class.java) + + startActivity(intetn) + } + else if (Code == 401) { + // Обработка ошибки аутентификации (неверные учетные данные или истек токен) + val intetn = Intent(requireContext(), AuthActivity::class.java) + activity?.finish() + startActivity(intetn) + } + + + } + } + } else { + // Переход на экран отсутствия интернета + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() + startActivity(intetn) + } + + } + + + // Инициализация клиента Retrofit для выполнения сетевых запросов + private fun initRetrofit() { + val interceptor = HttpLoggingInterceptor() + interceptor.level = HttpLoggingInterceptor.Level.BODY // Уровень логирования HTTP-запросов + + val client = OkHttpClient + .Builder() + .addInterceptor(interceptor) + .build() + + val retrofit = Retrofit.Builder() + .baseUrl("https://rehabilitation.vmeda.org/api/") // Базовый URL API + .client(client) // Установка OkHttpClient + .addConverterFactory(GsonConverterFactory.create()) // Добавление конвертера JSON + .build() + + doctorApi = retrofit.create(DoctorApi::class.java) // Создание экземпляра API + + } + + + + companion object { + // Фабричный метод для создания экземпляра AuthFragment + fun newInstance() = AuthFragment() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/Auth/Model/AuthModel.kt b/app/src/main/java/com/example/doctor/Auth/Model/AuthModel.kt new file mode 100644 index 0000000..8d3df95 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Auth/Model/AuthModel.kt @@ -0,0 +1,8 @@ +package com.example.doctor.Auth.Model + +// Модель данных для запроса аутентификации (логин и пароль) +data class AuthModel( + //val login:String, + val login:String, // Логин пользователя (доктора) + val password:String, // Пароль пользователя (доктора) +) diff --git a/app/src/main/java/com/example/doctor/Auth/Model/CheckTokenModel.kt b/app/src/main/java/com/example/doctor/Auth/Model/CheckTokenModel.kt new file mode 100644 index 0000000..83a844a --- /dev/null +++ b/app/src/main/java/com/example/doctor/Auth/Model/CheckTokenModel.kt @@ -0,0 +1,6 @@ +package com.example.doctor.Auth.Model + +// Модель данных для проверки статуса токена +data class CheckTokenModel( + val status:String, // Статус токена (например, "active") +) diff --git a/app/src/main/java/com/example/doctor/Auth/Model/UserModel.kt b/app/src/main/java/com/example/doctor/Auth/Model/UserModel.kt new file mode 100644 index 0000000..97723a2 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Auth/Model/UserModel.kt @@ -0,0 +1,8 @@ +package com.example.doctor.Auth.Model + +// Модель данных для ответа аутентификации (информация о пользователе) +data class UserModel( + val id:String, // Идентификатор пользователя (доктора) + val token:String, // Токен аутентификации + +) diff --git a/app/src/main/java/com/example/doctor/BottomSheetMenu/AccountBottomSheetMenu.kt b/app/src/main/java/com/example/doctor/BottomSheetMenu/AccountBottomSheetMenu.kt new file mode 100644 index 0000000..03bbe5d --- /dev/null +++ b/app/src/main/java/com/example/doctor/BottomSheetMenu/AccountBottomSheetMenu.kt @@ -0,0 +1,173 @@ +//package com.example.doctor.BottomSheetMenu +// +//import android.annotation.SuppressLint +//import android.app.Dialog +//import android.graphics.Color +//import android.os.Bundle +//import android.support.annotation.Nullable +//import android.view.KeyEvent +//import android.view.LayoutInflater +//import android.view.View +//import android.view.ViewGroup +//import android.widget.FrameLayout +//import android.widget.Toast +//import androidx.coordinatorlayout.widget.CoordinatorLayout +//import androidx.fragment.app.activityViewModels +//import com.example.doctor.DoctorViewModel +//import com.example.doctor.R +//import com.example.doctor.databinding.ItemButtomMenu2Binding +//import com.google.android.material.bottomsheet.BottomSheetBehavior +//import com.google.android.material.bottomsheet.BottomSheetDialogFragment +// +//// Этот класс представляет собой нижнее всплывающее меню (BottomSheet) для аккаунта. +//// В настоящее время весь код внутри класса закомментирован. +//class AccountBottomSheetMenu: BottomSheetDialogFragment() { +// private lateinit var binding: ItemButtomMenu2Binding +// private val model: DoctorViewModel by activityViewModels()//Инициализировали класс +// +// @SuppressLint("RestrictedApi") +// override fun setupDialog(dialog: Dialog, style: Int) { +// super.setupDialog(dialog, style) +// +// val view = LayoutInflater.from(context).inflate(R.layout.item_buttom_menu, null) +// dialog.setContentView(view) +// +// val param = (view.parent as View).layoutParams as CoordinatorLayout.LayoutParams +// +// val behavior = param.behavior +// +// if(behavior is BottomSheetBehavior<*>){ +// behavior.setBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback(){ +// override fun onStateChanged(bottomSheet: View, newState: Int) { +// var state = "" +// when(newState){ +// BottomSheetBehavior.STATE_DRAGGING->{ +// state = "DRAGGING" +// } +// BottomSheetBehavior.STATE_SETTLING->{ +// state = "SETTLING" +// } +// BottomSheetBehavior.STATE_EXPANDED->{ +// state = "EXPANDED" +// } +// BottomSheetBehavior.STATE_COLLAPSED->{ +// state = "COLLAPSED" +// } +// BottomSheetBehavior.STATE_HIDDEN->{ +// state = "HIDDEN" +// dismiss() +// behavior.state = BottomSheetBehavior.STATE_COLLAPSED +// } +// +// } +// } +// +// override fun onSlide(bottomSheet: View, slideOffset: Float) { +// +// } +// +// }) +// } +// } +// +// //Данная функция необходима чтобы убрать белый фон в нижнем всплывающем меню +// override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { +// +// return super.onCreateDialog(savedInstanceState).apply { +// // window?.setDimAmount(0.2f) // Set dim amount here +// setOnShowListener { +// val bottomSheet = findViewById(com.google.android.material.R.id.design_bottom_sheet) as FrameLayout +// bottomSheet.setBackgroundResource(android.R.color.transparent) +// } +// } +// } +// +// +// override fun onCreateView( +// inflater: LayoutInflater, +// container: ViewGroup?, +// savedInstanceState: Bundle? +// ): View? { +// binding = ItemButtomMenu2Binding.inflate(inflater,container,false) +// return binding.root +// } +// +// +// +// +// companion object { +// +// fun newInstance(): AccountBottomSheetMenu { +// val args = Bundle() +// val fragment = AccountBottomSheetMenu() +// fragment.arguments = args +// return fragment +// } +// } +// +// +// override fun onViewCreated(view: View, savedInstanceState: Bundle?) { +// super.onViewCreated(view, savedInstanceState) +// +// /* //Ввод адреса вручную +// binding.btnAdresAdd.setOnClickListener{ +// binding.CardViewStart.visibility = View.GONE +// binding.CardView1.visibility = View.VISIBLE +// } +// //Вернутся из ввода адреса +// binding.btnBack.setOnClickListener{ +// binding.CardViewStart.visibility = View.VISIBLE +// binding.CardView1.visibility = View.GONE +// }*/ +// //Оформления заказа по введенному адресу +// /* binding.btnAddZakaz.setOnClickListener{ +// setListener() +// }*/ +// /*//Оформления заказа по адресу из профиля +// binding.btnZakazAdresUserProfile.setOnClickListener{ +// setListener() +// }*/ +// //Вызов фунции +// setListener() +// +// +// //binding.textView34.setText(binding.editTextAdres.getText().toString()) +// } +// +// //Функция оформления заказа +// private fun setListener(){ +// /* binding.btnAddZakaz.setOnClickListener{ +// adresUser = binding.editTextAdres.text.toString() +// if(adresUser == ""){ +// Toast.makeText(requireContext(), "Нужен адрес", Toast.LENGTH_SHORT).show() +// } +// else{ +// //Данная модель нужна только для того чтобы передать сигнал для BasketFragmen и в случае если true, то он вызовет запрос и прогрузочный экран +// val itemStatus = StatusModel(true) +// model.addZakaz.value = itemStatus +// dismiss()//После нажатия закрываем +// } +// //requestWithSomeHttpHeadersZakaz(requireContext(), adresUser) +// } +// binding.editTextAdres.setOnKeyListener { v, keyCode, event -> +// +// when { +// //Проверяем нажалили мы на "ввод" +// //Check if it is the Enter-Key, Check if the Enter Key was pressed down +// ((keyCode == KeyEvent.KEYCODE_ENTER) && (event.action == KeyEvent.ACTION_DOWN)) -> { +// +// //Если да, то выполняем нажати на кнопку отправки сообщения +// binding.btnAddZakaz.performClick() +// +// //return true +// return@setOnKeyListener true +// } +// else -> false +// } +// +// +// }*/ +// +// } +// +//} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/BottomSheetMenu/NoteBottomSheetMenu.kt b/app/src/main/java/com/example/doctor/BottomSheetMenu/NoteBottomSheetMenu.kt new file mode 100644 index 0000000..02e474e --- /dev/null +++ b/app/src/main/java/com/example/doctor/BottomSheetMenu/NoteBottomSheetMenu.kt @@ -0,0 +1,182 @@ +package com.example.user.BottomSheetMenu + +import android.annotation.SuppressLint +import android.app.Dialog +import android.graphics.Color +import android.os.Bundle +import android.support.annotation.Nullable +import android.view.KeyEvent +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.FrameLayout +import android.widget.Toast +import androidx.coordinatorlayout.widget.CoordinatorLayout +import androidx.fragment.app.activityViewModels +import com.example.doctor.DoctorViewModel +import com.example.doctor.Patients.Model.CreatePatientModel +import com.example.doctor.Pref.ConclusionPref +import com.example.doctor.R +import com.example.doctor.Retrofit.DoctorApi +import com.example.doctor.Toast.showCustomInfoToast +import com.example.doctor.databinding.ItemButtomMenuBinding +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory + +// Этот класс представляет собой нижнее всплывающее меню (BottomSheet) для добавления пациента. +class NoteBottomSheetMenu: BottomSheetDialogFragment() { + private lateinit var binding: ItemButtomMenuBinding + private lateinit var doctorApi: DoctorApi + // Класс для получения данных из SharedPreferences + val prefDoctorConclusion = ConclusionPref() + + // Метод для настройки диалога BottomSheet + @SuppressLint("RestrictedApi") + override fun setupDialog(dialog: Dialog, style: Int) { + super.setupDialog(dialog, style) + + val view = LayoutInflater.from(context).inflate(R.layout.item_buttom_menu, null) + dialog.setContentView(view) + + val param = (view.parent as View).layoutParams as CoordinatorLayout.LayoutParams + + val behavior = param.behavior + + if(behavior is BottomSheetBehavior<*>){ + behavior.setBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback(){ + // Обработчик изменения состояния BottomSheet + override fun onStateChanged(bottomSheet: View, newState: Int) { + var state = "" + when(newState){ + BottomSheetBehavior.STATE_DRAGGING->{ + state = "DRAGGING" + } + BottomSheetBehavior.STATE_SETTLING->{ + state = "SETTLING" + } + BottomSheetBehavior.STATE_EXPANDED->{ + state = "EXPANDED" + } + BottomSheetBehavior.STATE_COLLAPSED->{ + state = "COLLAPSED" + } + BottomSheetBehavior.STATE_HIDDEN->{ + state = "HIDDEN" + dismiss() // Закрыть диалог при скрытии + behavior.state = BottomSheetBehavior.STATE_COLLAPSED + } + + } + } + + // Обработчик сдвига BottomSheet + override fun onSlide(bottomSheet: View, slideOffset: Float) { + + } + + }) + } + } + + // Данная функция необходима чтобы убрать белый фон в нижнем всплывающем меню + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + + return super.onCreateDialog(savedInstanceState).apply { + // window?.setDimAmount(0.2f) // Установка затемнения фона + setOnShowListener { + val bottomSheet = findViewById(com.google.android.material.R.id.design_bottom_sheet) as FrameLayout + bottomSheet.setBackgroundResource(android.R.color.transparent) // Установка прозрачного фона + } + } + } + + // Функция жизненного цикла Fragment: создание и возврат View + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = ItemButtomMenuBinding.inflate(inflater,container,false) + return binding.root + } + + companion object { + // Фабричный метод для создания экземпляра NoteBottomSheetMenu + fun newInstance(): NoteBottomSheetMenu { + val args = Bundle() + val fragment = NoteBottomSheetMenu() + fragment.arguments = args + return fragment + } + } + + // Функция жизненного цикла Fragment: инициализация View и обработчиков + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + // Обработчик нажатия на кнопку добавления пациента + binding.btnAddPatient.setOnClickListener{ + // Проверка на заполненность полей email и пароля + if(binding.edEmail.text.toString() == "" && binding.edPassword.text.toString() == ""){ + Toast(requireContext()).showCustomInfoToast("Не все поля заполнены", requireActivity()) + } + else{ + // Вызов функции создания аккаунта пациента + CreateAccountPatient(CreatePatientModel(binding.edEmail.text.toString(),binding.edPassword.text.toString())) + } + } + } + + // Функция для создания аккаунта пациента через API + fun CreateAccountPatient(createPatientModel: CreatePatientModel) { + initRetrofit() // Инициализация Retrofit + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) // Получение токена + // Запуск корутины для выполнения сетевого запроса в фоновом потоке + CoroutineScope(Dispatchers.IO).launch { + val listProduct = doctorApi.CreateAccountPatient("Bearer $Tokens",createPatientModel) // Выполнение POST-запроса создания пациента + // Переключение на главный поток для обновления UI + requireActivity().runOnUiThread { + + // Фиксируем полученные данные из ответа + val patientList = listProduct.body() + + // Обработка ответа сервера + if (patientList != null) { + Toast(requireContext()).showCustomInfoToast("Аккаунт для пациента создан", requireActivity()) + dismiss()// Закрыть диалог после успешного создания + } + else{ + Toast(requireContext()).showCustomInfoToast("Ошибка валидации", requireActivity()) + } + } + + } + } + + // Инициализация клиента Retrofit для выполнения сетевых запросов + private fun initRetrofit() { + val interceptor = HttpLoggingInterceptor() + interceptor.level = HttpLoggingInterceptor.Level.BODY // Уровень логирования HTTP-запросов + + val client = OkHttpClient + .Builder() + .addInterceptor(interceptor) + .build() + + val retrofit = Retrofit.Builder() + .baseUrl("https://rehabilitation.vmeda.org/api/") // Базовый URL API + .client(client) // Установка OkHttpClient + .addConverterFactory(GsonConverterFactory.create()) // Добавление конвертера JSON + .build() + + doctorApi = retrofit.create(DoctorApi::class.java) // Создание экземпляра API + + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/BottomSheetMenu/StatusModel.kt b/app/src/main/java/com/example/doctor/BottomSheetMenu/StatusModel.kt new file mode 100644 index 0000000..1d0cdb4 --- /dev/null +++ b/app/src/main/java/com/example/doctor/BottomSheetMenu/StatusModel.kt @@ -0,0 +1,6 @@ +package com.example.doctor.BottomSheetMenu + +// Модель данных для передачи статуса (булево значение) +data class StatusModel( + val status: Boolean = false // Булево поле для индикации статуса (по умолчанию false) + ) diff --git a/app/src/main/java/com/example/doctor/CodeError/Code429Activity.kt b/app/src/main/java/com/example/doctor/CodeError/Code429Activity.kt new file mode 100644 index 0000000..bc4f4ba --- /dev/null +++ b/app/src/main/java/com/example/doctor/CodeError/Code429Activity.kt @@ -0,0 +1,141 @@ +package com.example.doctor.CodeError + +import android.annotation.SuppressLint +import android.content.Intent +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import android.os.CountDownTimer +import android.util.Log +import com.example.doctor.Auth.AuthActivity +import com.example.doctor.Enternet.EnternetActivity +import com.example.doctor.Enternet.EnternetCheck +import com.example.doctor.MainActivity +import com.example.doctor.Pref.ClearPref +import com.example.doctor.Pref.ConclusionPref +import com.example.doctor.Pref.SavePref +import com.example.doctor.R +import com.example.doctor.Retrofit.DoctorApi +import com.example.doctor.databinding.ActivityCode429Binding +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import java.util.Timer +import kotlin.concurrent.fixedRateTimer + +// Активити для обработки ошибки с кодом 429 (слишком много запросов) +// При появлении этой ошибки пользователь перенаправляется на этот экран с ожиданием +class Code429Activity : AppCompatActivity() { + private lateinit var binding:ActivityCode429Binding // Объект привязки для доступа к элементам UI + private lateinit var doctorApi: DoctorApi // Экземпляр Retrofit API + private lateinit var timer: Timer // Таймер для периодической проверки + + // Вспомогательные классы для работы с SharedPreferences + val prefDoctorConclusion = ConclusionPref() + val prefDoctorClear = ClearPref() + val prefDoctorSave = SavePref() + + // Класс для проверки наличия интернета + val enternetCheck = EnternetCheck() + + // Функция жизненного цикла Activity: создание Activity и инициализация UI + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityCode429Binding.inflate(layoutInflater) // Надуваем макет + setContentView(binding.root) // Устанавливаем корневой View + } + + // Функция для периодической проверки токена и состояния соединения + fun CheckToken(){ + // Проверка интернет-соединения + if (enternetCheck.isOnline(this@Code429Activity)) { + initRetrofit() // Инициализация Retrofit + val Tokens = prefDoctorConclusion.conclusionToken(this@Code429Activity) // Получение токена + // Запуск корутины для выполнения сетевого запроса в фоновом потоке + CoroutineScope(Dispatchers.IO).launch { + val list = doctorApi.CheckToken("Bearer $Tokens") // Запрос на проверку токена + // Переключение на главный поток для обновления UI и обработки ответа + runOnUiThread { + // Фиксируем полученные данные из ответа + val List = list.body() + val Nice = list.isSuccessful + val Code = list.code() + // Обработка различных кодов ответа сервера + if(Code==500){ + // Переход на экран ошибки 500 + val intetn = Intent(this@Code429Activity, Code500Activity::class.java) + finish() // Закрываем текущую активити + startActivity(intetn) + } + else if(Code==200) { + // Если ответ успешный (код 200) + //Если нету ошибок + if (Nice) { + //Если нету ошибок + if (List != null) { + // Логируем успешную проверку токена + Log.i("clogon1231111","clogon1231111") + finish() // Закрываем текущую активити (ошибка устранена) + } + } + } + else if (Code == 401) { + // Переход на экран аутентификации при ошибке 401 (неавторизованный) + val intetn = Intent(this@Code429Activity, AuthActivity::class.java) + finish() // Закрываем текущую активити + startActivity(intetn) + } + } + } + } else { + // Переход на экран отсутствия интернета + val intetn = Intent(this, EnternetActivity::class.java) + finish() // Закрываем текущую активити + startActivity(intetn) + } + } + + // Инициализация клиента Retrofit для выполнения сетевых запросов + private fun initRetrofit() { + val interceptor = HttpLoggingInterceptor() + interceptor.level = HttpLoggingInterceptor.Level.BODY // Уровень логирования HTTP-запросов + + val client = OkHttpClient + .Builder() + .addInterceptor(interceptor) + .build() + + val retrofit = Retrofit.Builder() + .baseUrl("https://rehabilitation.vmeda.org/api/") // Базовый URL API + .client(client) // Установка OkHttpClient + .addConverterFactory(GsonConverterFactory.create()) // Добавление конвертера JSON + .build() + + doctorApi = retrofit.create(DoctorApi::class.java) // Создание экземпляра API + } + + // Функция жизненного цикла Activity: возобновление работы, запуск таймера + override fun onResume() { + super.onResume() + checkForUpdates(true) // Запускаем периодическую проверку + } + + // Функция жизненного цикла Activity: остановка работы, отмена таймера + override fun onStop() { + super.onStop() + timer.cancel() // Отмена таймера + timer.purge() // Очистка задач таймера + } + + // Запускает таймер, который периодически (каждые 15 секунд) вызывает CheckToken() + private fun checkForUpdates(daemonIsTrue: Boolean) { + timer = fixedRateTimer("default", daemonIsTrue, 0, 15000) { // Таймер с фиксированной задержкой и интервалом 15 секунд + this@Code429Activity.runOnUiThread { // Выполнение кода в основном потоке UI + CheckToken() // Вызываем проверку токена + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/CodeError/Code500Activity.kt b/app/src/main/java/com/example/doctor/CodeError/Code500Activity.kt new file mode 100644 index 0000000..d05a947 --- /dev/null +++ b/app/src/main/java/com/example/doctor/CodeError/Code500Activity.kt @@ -0,0 +1,193 @@ +package com.example.doctor.CodeError + +import android.annotation.SuppressLint +import android.content.Intent +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import android.os.CountDownTimer +import android.util.Log +import android.view.View +import android.widget.Toast +import androidx.core.content.ContentProviderCompat.requireContext +import com.example.doctor.Auth.AuthActivity +import com.example.doctor.Enternet.EnternetActivity +import com.example.doctor.Enternet.EnternetCheck +import com.example.doctor.MainActivity +import com.example.doctor.Pref.ClearPref +import com.example.doctor.Pref.ConclusionPref +import com.example.doctor.Pref.SavePref +import com.example.doctor.R +import com.example.doctor.Retrofit.DoctorApi +import com.example.doctor.Toast.showCustomInfoToast +import com.example.doctor.databinding.ActivityCode500Binding +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import java.util.Timer +import kotlin.concurrent.fixedRateTimer + +// Активити для обработки внутренней ошибки сервера с кодом 500 +// Отображает пользователю экран с информацией об ошибке и таймером до перенаправления +class Code500Activity : AppCompatActivity() { + private lateinit var binding: ActivityCode500Binding // Объект привязки для доступа к элементам UI + private lateinit var doctorApi: DoctorApi // Экземпляр Retrofit API + private lateinit var timer: Timer // Таймер для периодической проверки + + // Вспомогательные классы для работы с SharedPreferences + val prefDoctorConclusion = ConclusionPref() + val prefDoctorClear = ClearPref() + val prefDoctorSave = SavePref() + + // Класс для проверки наличия интернета + val enternetCheck = EnternetCheck() + + var checkClose = false // Флаг для контроля закрытия активити (не используется в текущем коде) + + // Функция жизненного цикла Activity: создание Activity и инициализация UI, запуск таймера обратного отсчета + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityCode500Binding.inflate(layoutInflater) // Надуваем макет + setContentView(binding.root) // Устанавливаем корневой View + + +// val fixedRateTimer = fixedRateTimer("hello-timer", +// false,0,5000) { +// +// } +// try { +// this@Code500Activity.runOnUiThread { +// CheckToken() +// } +// } +// finally { +// fixedRateTimer.cancel(); +// } +//ПОСТОЯННО ВЫЗЫВАЕТСЯ НУЖНО КАК-ТО ОТКЛЮЧАТЬ +// fixedRateTimer("timer", false, 0, 10000) { +// this@Code500Activity?.runOnUiThread { +// } +// } + + binding.apply { + // Запуск таймера обратного отсчета на 30 секунд + val timer = object: CountDownTimer(30000, 1000) { // 30000 мс = 30 секунд, интервал 1000 мс = 1 секунда + override fun onTick(millisUntilFinished: Long) { + // Этот метод вызывается каждую секунду во время отсчета (логика не реализована) + } + + override fun onFinish() { + // Этот метод вызывается по окончании таймера + // Переход на экран аутентификации + val intetn = Intent(this@Code500Activity, AuthActivity::class.java) + finish() // Закрываем текущую активити + startActivity(intetn) + } + } + timer.start() // Запуск таймера + + } + + } + + // Функция для периодической проверки токена и состояния соединения + fun CheckToken(){ + // Проверка интернет-соединения + if (enternetCheck.isOnline(this@Code500Activity)) { + initRetrofit() // Инициализация Retrofit + val Tokens = prefDoctorConclusion.conclusionToken(this@Code500Activity) // Получение токена + // Запуск корутины для выполнения сетевого запроса в фоновом потоке + CoroutineScope(Dispatchers.IO).launch { + val list = doctorApi.CheckToken("Bearer $Tokens") // Запрос на проверку токена + // Переключение на главный поток для обновления UI и обработки ответа + runOnUiThread { + + // Фиксируем полученные данные из ответа + val List = list.body() + val Nice = list.isSuccessful + val Code = list.code() + // Обработка различных кодов ответа сервера + if(Code==429){ + // Переход на экран ошибки 429 + val intetn = Intent(this@Code500Activity, Code429Activity::class.java) + finish() // Закрываем текущую активити + startActivity(intetn) + } + else if(Code==200) { + // Если ответ успешный (код 200) + //Если нету ошибок + if (Nice) { + //Если нету ошибок + if (List != null) { + // Логируем успешную проверку токена + Log.i("clogon1231111","clogon1231111") + finish() // Закрываем текущую активити (ошибка устранена) + } + + } + } + else if (Code == 401) { + // Переход на экран аутентификации при ошибке 401 (неавторизованный) + val intetn = Intent(this@Code500Activity, AuthActivity::class.java) + finish() // Закрываем текущую активити + startActivity(intetn) + } + } + } + } else { + // Переход на экран отсутствия интернета + val intetn = Intent(this, EnternetActivity::class.java) + finish() // Закрываем текущую активити + startActivity(intetn) + } + + } + + // Инициализация клиента Retrofit для выполнения сетевых запросов + private fun initRetrofit() { + val interceptor = HttpLoggingInterceptor() + interceptor.level = HttpLoggingInterceptor.Level.BODY // Уровень логирования HTTP-запросов + + val client = OkHttpClient + .Builder() + .addInterceptor(interceptor) + .build() + + val retrofit = Retrofit.Builder() + .baseUrl("https://rehabilitation.vmeda.org/api/") // Базовый URL API + .client(client) // Установка OkHttpClient + .addConverterFactory(GsonConverterFactory.create()) // Добавление конвертера JSON + .build() + + doctorApi = retrofit.create(DoctorApi::class.java) // Создание экземпляра API + + } + + // Функция жизненного цикла Activity: возобновление работы, запуск таймера + override fun onResume() { + super.onResume() + checkForUpdates(true) // Запускаем периодическую проверку + } + + // Функция жизненного цикла Activity: остановка работы, отмена таймера + override fun onStop() { + super.onStop() + timer.cancel() // Отмена таймера + timer.purge() // Очистка задач таймера + } + + // Запускает таймер, который периодически (каждые 15 секунд) вызывает CheckToken() + private fun checkForUpdates(daemonIsTrue: Boolean) { + + timer = fixedRateTimer("default", daemonIsTrue, 0, 15000) { // Таймер с фиксированной задержкой и интервалом 15 секунд + this@Code500Activity.runOnUiThread { // Выполнение кода в основном потоке UI + CheckToken() // Вызываем проверку токена + } + } + + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/DataModel.kt b/app/src/main/java/com/example/doctor/DataModel.kt new file mode 100644 index 0000000..b5b9afd --- /dev/null +++ b/app/src/main/java/com/example/doctor/DataModel.kt @@ -0,0 +1,30 @@ +package com.example.doctor + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel + +//Ослеживание за обновлением подробной продуктовой карточкой +open class DataModel: ViewModel() { + /*//Для отлеживания обновлений активити + val messageForActivity: MutableLiveData by lazy{//by lazy - чтобы каждый раз не создавался MutableLiveData(MutableLiveData()), а он один раз при загрузке создаться и дальше будет пользоваться уже созданным + MutableLiveData()//LiveData - означает что будет обновляться в нужынй момент, не постоянна, а в зависимоти от жизни активити + }*/ + //Для отлеживания обновлений фрагммента 1 + val messageForFrag1: MutableLiveData by lazy{//by lazy - чтобы каждый раз не создавался MutableLiveData(MutableLiveData()), а он один раз при загрузке создаться и дальше будет пользоваться уже созданным + MutableLiveData()//LiveData - означает что будет обновляться в нужынй момент, не постоянна, а в зависимоти от жизни активити + } + + val fragmentMenu: MutableLiveData by lazy{//by lazy - чтобы каждый раз не создавался MutableLiveData(MutableLiveData()), а он один раз при загрузке создаться и дальше будет пользоваться уже созданным + MutableLiveData()//LiveData - означает что будет обновляться в нужынй момент, не постоянна, а в зависимоти от жизни активити + } + val patientList: MutableLiveData by lazy{//by lazy - чтобы каждый раз не создавался MutableLiveData(MutableLiveData()), а он один раз при загрузке создаться и дальше будет пользоваться уже созданным + MutableLiveData()//LiveData - означает что будет обновляться в нужынй момент, не постоянна, а в зависимоти от жизни активити + } + val patientAppeal: MutableLiveData by lazy{//by lazy - чтобы каждый раз не создавался MutableLiveData(MutableLiveData()), а он один раз при загрузке создаться и дальше будет пользоваться уже созданным + MutableLiveData()//LiveData - означает что будет обновляться в нужынй момент, не постоянна, а в зависимоти от жизни активити + } + /*//Для отлеживания обновлений фрагммента 2 + val messageForFrag2: MutableLiveData by lazy{//by lazy - чтобы каждый раз не создавался MutableLiveData(MutableLiveData()), а он один раз при загрузке создаться и дальше будет пользоваться уже созданным + MutableLiveData()//LiveData - означает что будет обновляться в нужынй момент, не постоянна, а в зависимоти от жизни активити + }*/ +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/DoctorViewModel.kt b/app/src/main/java/com/example/doctor/DoctorViewModel.kt new file mode 100644 index 0000000..6ce7381 --- /dev/null +++ b/app/src/main/java/com/example/doctor/DoctorViewModel.kt @@ -0,0 +1,93 @@ +package com.example.doctor + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.example.doctor.Appeals.TabLayout.Model.AppealsNewModel +import com.example.doctor.Appeals.TabLayout.Model.AppealsOldModel +import com.example.doctor.Patients.Model.PatientAllModel +import com.example.doctor.Patients.Model.PatientIdModel +import com.example.doctor.Patients.Model.PatientModel +import com.example.doctor.Patients.Reports.Courses.SportCoursModel +import com.example.doctor.Patients.Reports.Edit.EditSportModel +import com.example.doctor.Patients.Reports.QBAModel +import com.example.doctor.Setting.Courses.EditCourses.TabLayoutEditCourses.Model.EditCoursesDoctorModel +import com.example.doctor.Setting.Courses.EditCourses.TabLayoutEditCourses.Model.CoursesDoctorCA +import com.example.doctor.Setting.Courses.Model.CoursesDoctorModel + +//import com.example.retrofit.model.Product + +class DoctorViewModel: ViewModel() { + + //Токен + val token = MutableLiveData() + + //Пациенты + val patientId = MutableLiveData() + val patientCurrent = MutableLiveData() + val patientListList= MutableLiveData() + + //Обращения необработанные + val appealsNewCurrent = MutableLiveData() + val appealsNewList= MutableLiveData>() + + //Обращения обработанные + val appealsOldCurrent = MutableLiveData() + val appealsOldList= MutableLiveData>() + + //val productList = MutableLiveData>() + val patientOneCurrent = MutableLiveData() + + //Отчеты + val qbaCurrent = MutableLiveData() + val qbaList= MutableLiveData>() + + //КУрсы + val SportCoursCurrent = MutableLiveData() + val SportCoursList= MutableLiveData>() + val SportCoursDoctorList= MutableLiveData>() + + val BtnSportCoursDoctorCurrent = MutableLiveData() + + //Редактирование занятий + val EditSportCurrentYes = MutableLiveData() + val EditSportListYes= MutableLiveData>() + + //Редактирование занятий + val EditSportCurrentNO = MutableLiveData() + val EditSportListNo= MutableLiveData>() + + //Список пациентов + val PatientActiveCurrent = MutableLiveData() + val PatientActiveList= MutableLiveData>() + val PatientNotCurrent = MutableLiveData() + val PatientNotList= MutableLiveData>() + + //Список со всеми пациентами + val PatientAllModel= MutableLiveData>() + + //id пациента + val id_patient = MutableLiveData() + + //Курсы созданный доктором + val CoursesDoctorCurrent = MutableLiveData() + val CoursesDoctorList= MutableLiveData>() + + + //Упражнения не входяшие в курс доктора + val EditCoursesDoctorAllCurrent = MutableLiveData() + val EditCoursesDoctorAllList= MutableLiveData>() + + //Упражнения входяшие в курс доктора + val EditCoursesDoctorYourCurrent = MutableLiveData() + val EditCoursesDoctorYourList= MutableLiveData>() + + val CoursesDoctorCA = MutableLiveData() + + + //Список пациентов для поисковой строки + val patientListSearch= MutableLiveData>() + + //Для передачи данных редактируемого курса + val CoursesCustomDoctor = MutableLiveData() + +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/Enternet/EnternetActivity.kt b/app/src/main/java/com/example/doctor/Enternet/EnternetActivity.kt new file mode 100644 index 0000000..9697c31 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Enternet/EnternetActivity.kt @@ -0,0 +1,26 @@ +package com.example.doctor.Enternet + +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import com.example.doctor.R +import com.example.doctor.databinding.ActivityEnternetBinding +import com.example.doctor.databinding.FragmentEnternetBinding + +// Активити для отображения экрана отсутствия интернет-соединения +class EnternetActivity : AppCompatActivity() { + private lateinit var binding: ActivityEnternetBinding // Объект привязки для доступа к элементам UI + // Функция жизненного цикла Activity: создание Activity и инициализация UI + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityEnternetBinding.inflate(layoutInflater) // Надуваем макет + setContentView(binding.root) // Устанавливаем корневой View + + // Отображаем фрагмент отсутствия интернета при создании активити + supportFragmentManager.beginTransaction() + .replace(R.id.CLEnternet, EnternetFragment.newInstance()) + .commit() // Заменяем контейнер на фрагмент + } + + + +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/Enternet/EnternetCheck.kt b/app/src/main/java/com/example/doctor/Enternet/EnternetCheck.kt new file mode 100644 index 0000000..3295610 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Enternet/EnternetCheck.kt @@ -0,0 +1,42 @@ +package com.example.doctor.Enternet + +import android.content.Context +import android.net.ConnectivityManager +import android.net.NetworkCapabilities +import android.os.Build + +// Класс для проверки наличия интернет-соединения +class EnternetCheck() { + + // Функция для проверки активного интернет-соединения + fun isOnline(context: Context): Boolean { + if (context == null) return false + val connectivityManager = + context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + val capabilities = + connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork) + if (capabilities != null) { + when { + capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> { + return true + } + + capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> { + return true + } + + capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> { + return true + } + } + } + } else { + val activeNetworkInfo = connectivityManager.activeNetworkInfo + if (activeNetworkInfo != null && activeNetworkInfo.isConnected) { + return true + } + } + return false // Интернет отсутствует + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/Enternet/EnternetFragment.kt b/app/src/main/java/com/example/doctor/Enternet/EnternetFragment.kt new file mode 100644 index 0000000..c7068e3 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Enternet/EnternetFragment.kt @@ -0,0 +1,43 @@ +package com.example.doctor.Enternet + +import android.content.Intent +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.example.doctor.MainActivity +import com.example.doctor.databinding.FragmentEnternetBinding + +// Фрагмент для отображения экрана отсутствия интернет-соединения +class EnternetFragment : Fragment() { + private lateinit var binding: FragmentEnternetBinding // Объект привязки для доступа к элементам UI макета фрагмента + + // Функция жизненного цикла Fragment: создание и возврат View (надувание макета) + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentEnternetBinding.inflate(layoutInflater,container,false) + return binding.root // Возвращаем корневой View фрагмента + } + + // Функция жизненного цикла Fragment: инициализация View и обработчиков событий после создания View + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + // Установка обработчика нажатия на кнопку "Повторить" или "Проверить интернет" + binding.btnEnternet.setOnClickListener{ + // Создание Intent для перехода к MainActivity (попытка вернуться в основное приложение) + val intetn = Intent(requireContext(), MainActivity::class.java) + activity?.finish() // Закрываем текущую активити (EnternetActivity) + startActivity(intetn) // Запускаем MainActivity + } + + } + + companion object { + // Фабричный метод для создания нового экземпляра EnternetFragment + fun newInstance() = EnternetFragment() // Создаем и возвращаем новый экземпляр фрагмента + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/Home/HomeFragment.kt b/app/src/main/java/com/example/doctor/Home/HomeFragment.kt new file mode 100644 index 0000000..d49eb59 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Home/HomeFragment.kt @@ -0,0 +1,157 @@ +package com.example.doctor.Home + +import android.content.Intent +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.activityViewModels +import com.example.doctor.Auth.AuthActivity +import com.example.doctor.CodeError.Code429Activity +import com.example.doctor.CodeError.Code500Activity +import com.example.doctor.DataModel +import com.example.doctor.DoctorViewModel +import com.example.doctor.Enternet.EnternetActivity +import com.example.doctor.Enternet.EnternetCheck +import com.example.doctor.Pref.ClearPref +import com.example.doctor.Pref.ConclusionPref +import com.example.doctor.R +import com.example.doctor.Retrofit.DoctorApi +import com.example.doctor.databinding.FragmentHomeBinding +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory + + +// Фрагмент для отображения главной страницы приложения (статистика пациента и обращений) +class HomeFragment : Fragment() { + private lateinit var binding: FragmentHomeBinding // Объект привязки для доступа к элементам UI макета фрагмента + private val dataModel: DataModel by activityViewModels()// ViewModel для обмена данными между фрагментами (например, для навигации) + + private var Token = "" // Переменная для хранения токена аутентификации + val prefDoctorClear= ClearPref() // Вспомогательный класс для очистки SharedPreferences + private lateinit var doctorApi: DoctorApi // Экземпляр Retrofit API для сетевых запросов + val prefDoctorConclusion = ConclusionPref() // Вспомогательный класс для получения данных из SharedPreferences + private val modelDoctor: DoctorViewModel by activityViewModels() // ViewModel для хранения и управления данными приложения + + // Класс проверки наличия интернета + val enternetCheck = EnternetCheck() + + // Функция жизненного цикла, создаёт и возвращает view фрагмента (надувание макета) + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentHomeBinding.inflate(layoutInflater,container, false) + return binding.root // Возвращаем корневой View фрагмента + } + + // Функция жизненного цикла, вызывается после создания view. Устанавливает обработчики нажатий и запускает загрузку данных + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + // Обработчик нажатия на карточку пациента: переход на экран списка пациентов + binding.CVPatinet.setOnClickListener { + dataModel.fragmentMenu.value = R.id.patient // Устанавливаем значение в ViewModel для навигации + } + + // Обработчик нажатия на карточку обращений: переход на экран списка обращений + binding.CVAppeals.setOnClickListener { + dataModel.fragmentMenu.value = R.id.list_check // Устанавливаем значение в ViewModel для навигации + } + + CountPatientAndAppeals() // Загружает и отображает статистику пациентов и обращений + } + + // Получение данных для главной страницы: количества пациентов и обращений с сервера + fun CountPatientAndAppeals() = with(binding) { + // Проверка интернет-соединения + if (enternetCheck.isOnline(requireContext())) { + initRetrofit() // Инициализация Retrofit + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) // Получение токена + // Запуск корутины для выполнения сетевого запроса в фоновом потоке + CoroutineScope(Dispatchers.IO).launch { + val listHomeView = doctorApi.CountPatientAndAppeals("Bearer $Tokens") // Выполнение GET-запроса для получения статистики + + // Переключение на главный поток для обновления UI и обработки ответа + requireActivity().runOnUiThread { + + + // Фиксируем полученные данные из ответа + val List = listHomeView.body() + val Nice = listHomeView.isSuccessful + val Code = listHomeView.code() + // Обработка различных кодов ответа сервера + if(Code==429){ + // Переход на экран ошибки 429 + val intetn = Intent(requireContext(), Code429Activity::class.java) + + startActivity(intetn) + } + else if(Code==200) { + // Если ответ успешный (код 200) + //Если нету ошибок + if (Nice) { + if (List != null) { + // Обновление текстовых полей с количеством пациентов и обращений + txtCountPatient.text = List?.patient.toString() + txtCountAppealsNew.text = List?.appeals_check.toString() + txtCountAppealsAll.text = List?.appeals_all.toString() + CLLoad.visibility = View.GONE // Скрываем индикатор загрузки + } + } + } + else if (Code == 500) { + // Переход на экран ошибки 500 + val intetn = Intent(requireContext(), Code500Activity::class.java) + + startActivity(intetn) + } + else if (Code == 401) { + // Переход на экран аутентификации при ошибке 401 (неавторизованный) + val intetn = Intent(requireContext(), AuthActivity::class.java) + activity?.finish() // Закрываем текущую активити + startActivity(intetn) + } + } + + } + } else { + // Переход на экран отсутствия интернета + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() // Закрываем текущую активити + startActivity(intetn) + } + + } + + // Инициализация клиента Retrofit для выполнения сетевых запросов + private fun initRetrofit() { + val interceptor = HttpLoggingInterceptor() + interceptor.level = HttpLoggingInterceptor.Level.BODY // Уровень логирования HTTP-запросов + + val client = OkHttpClient + .Builder() + .addInterceptor(interceptor) + .build() + + val retrofit = Retrofit.Builder() + .baseUrl("https://rehabilitation.vmeda.org/api/") // Базовый URL API + .client(client) // Установка OkHttpClient + .addConverterFactory(GsonConverterFactory.create()) // Добавление конвертера JSON + .build() + + doctorApi = retrofit.create(DoctorApi::class.java) // Создание экземпляра API + + } + + companion object { + // Фабричный метод для создания экземпляра HomeFragment + fun newInstance() = HomeFragment() // Создаем и возвращаем новый экземпляр фрагмента + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/Home/HomeInfoModel.kt b/app/src/main/java/com/example/doctor/Home/HomeInfoModel.kt new file mode 100644 index 0000000..4b254f5 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Home/HomeInfoModel.kt @@ -0,0 +1,9 @@ +package com.example.doctor.Home + +// Модель данных для информации на главной странице (статистика) +data class HomeInfoModel( + val patient: Int, // Количество пациентов + val appeals_check: Int, // Количество проверенных обращений + val appeals_all: Int, // Общее количество обращений +) + diff --git a/app/src/main/java/com/example/doctor/MainActivity.kt b/app/src/main/java/com/example/doctor/MainActivity.kt new file mode 100644 index 0000000..7740255 --- /dev/null +++ b/app/src/main/java/com/example/doctor/MainActivity.kt @@ -0,0 +1,518 @@ +package com.example.doctor + +import android.annotation.SuppressLint +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import android.content.SharedPreferences +import android.graphics.BitmapFactory +import android.graphics.Color +import android.os.Build +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import android.util.Log +import android.widget.ArrayAdapter +import android.widget.RemoteViews +import android.widget.Toast +import androidx.appcompat.app.AlertDialog +import androidx.constraintlayout.widget.ConstraintSet.Constraint +import androidx.work.Constraints +import androidx.work.ExistingPeriodicWorkPolicy +import androidx.work.NetworkType +import androidx.work.OneTimeWorkRequest +import androidx.work.PeriodicWorkRequest +import androidx.work.WorkManager +import androidx.work.WorkRequest +import com.example.doctor.Auth.AuthActivity +import com.example.doctor.Auth.AuthFragment +import com.example.doctor.Auth.Model.AuthModel +import com.example.doctor.Pref.ConclusionPref +import com.example.doctor.Pref.SavePref +import com.example.doctor.Retrofit.DoctorApi +import com.example.doctor.Toast.showCustomInfoToast +import com.example.doctor.Worker.MyWorker +import com.example.doctor.databinding.ActivityMainBinding +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import org.json.JSONObject +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import java.util.concurrent.TimeUnit +import android.app.TaskStackBuilder +import android.net.ConnectivityManager +import android.net.NetworkCapabilities +import android.view.View +import androidx.activity.viewModels +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat +import androidx.core.content.ContentProviderCompat.requireContext +import androidx.test.core.app.ActivityScenario.launch +import com.example.doctor.CodeError.Code429Activity +import com.example.doctor.CodeError.Code500Activity +import com.example.doctor.Enternet.EnternetActivity +import com.example.doctor.Enternet.EnternetCheck +import com.example.doctor.Enternet.EnternetFragment +import kotlinx.coroutines.Job +import java.util.Timer +import kotlin.concurrent.fixedRateTimer + + +// Главная Activity приложения, управляет отображением основного контента и аутентификацией +class MainActivity : AppCompatActivity() { + private lateinit var binding: ActivityMainBinding // Объект привязки для доступа к элементам UI макета активити + private lateinit var doctorApi: DoctorApi // Экземпляр Retrofit API для сетевых запросов + private lateinit var timer: Timer // Таймер для периодических задач + //Токен + private var Token = "" // Переменная для хранения токена аутентификации + val prefDoctorSave = SavePref() // Вспомогательный класс для сохранения данных в SharedPreferences + val prefDoctorConclusion = ConclusionPref() // Вспомогательный класс для получения данных из SharedPreferences + var backPressedTime: Long = 0 // Время последнего нажатия кнопки "Назад" + + + // Переменные для работы с уведомлениями (закомментированы) + lateinit var notificationManager: NotificationManager + lateinit var notificationChannel: NotificationChannel + lateinit var builder: Notification.Builder + private val channelId = "i.apps.notifications" + private val description = "Test notification" + + + // Константы для каналов уведомлений (закомментированы) + val CHANNEL_ID = "channelID" + val CHANNEL_NAME = "channelName" + val NOTIF_ID = 0 + + //Класс проверки интеренета + val enternetCheck = EnternetCheck() // Класс для проверки наличия интернет-соединения + + private val modelDoctor: DoctorViewModel by viewModels()// ViewModel для хранения и управления данными приложения + + // Функция жизненного цикла Activity: создание Activity и инициализация UI + @SuppressLint("RemoteViewLayout", "MissingPermission") + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityMainBinding.inflate(layoutInflater) // Надуваем макет + setContentView(binding.root) // Устанавливаем корневой View + + MainAndAuth() // Запуск процесса аутентификации или отображения основного контента + + + } + + + + + // Получение необработанных обращений с сервера и обновление ViewModel + private fun getNewAppeals() { + // Проверка интернет-соединения + if (enternetCheck.isOnline(this)) { + initRetrofit() // Инициализация Retrofit + val Tokens = prefDoctorConclusion.conclusionToken(this) // Получение токена + // Запуск корутины для выполнения сетевого запроса в фоновом потоке + CoroutineScope(Dispatchers.IO).launch { + val listAppeals = doctorApi.GetAppealsDoctorNew("Bearer $Tokens") // Выполнение GET-запроса для получения новых обращений + // Переключение на главный поток для обновления UI + runOnUiThread { + + //Фиксируем полученные данные + val AppealsMes = listAppeals.body() + + //Если нету ошибок + if (AppealsMes != null) { + if (AppealsMes.appeals_new != null) { + modelDoctor.appealsNewList.value = AppealsMes.appeals_new // Обновление списка новых обращений в ViewModel + } + } + } + } + } else { +// finish() +// val intetn = Intent(this, EnternetActivity::class.java) +// startActivity(intetn) + } + } + + // Получение обработанных обращений с сервера и обновление ViewModel + private fun getOldAppeals() { + // Проверка интернет-соединения + if (enternetCheck.isOnline(this)) { + initRetrofit() // Инициализация Retrofit + val Tokens = prefDoctorConclusion.conclusionToken(this) // Получение токена + // Запуск корутины для выполнения сетевого запроса в фоновом потоке + CoroutineScope(Dispatchers.IO).launch { + val listAppeals = doctorApi.GetAppealsDoctorOld("Bearer $Tokens") // Выполнение GET-запроса для получения обработанных обращений + // Переключение на главный поток для обновления UI + runOnUiThread { + + //Фиксируем полученные данные + val AppealsMes = listAppeals.body() + + //Если нету ошибок + if (AppealsMes != null) { + if (AppealsMes.appeals_old != null) { + modelDoctor.appealsOldList.value = AppealsMes.appeals_old // Обновление списка обработанных обращений в ViewModel + } + } + } + } + } else { +// finish() +// val intetn = Intent(this, EnternetActivity::class.java) +// startActivity(intetn) + } + } + + // Получение списка всех пациентов для поиска с сервера и обновление ViewModel + fun GetPatientListSearch() { + // Проверка интернет-соединения + if (enternetCheck.isOnline(this)) { + initRetrofit() // Инициализация Retrofit + val Tokens = prefDoctorConclusion.conclusionToken(this) // Получение токена + // Запуск корутины для выполнения сетевого запроса в фоновом потоке + CoroutineScope(Dispatchers.IO).launch { + val listPatientList = doctorApi.GetPatientAll("Bearer $Tokens") // Выполнение GET-запроса для получения всех пациентов + + // Переключение на главный поток для обновления UI + runOnUiThread { + //Фиксируем полученные данные + val patientList = listPatientList.body() + //Если нету ошибок + if (patientList != null) { + modelDoctor.patientListSearch.value = patientList.patient // Обновление списка пациентов для поиска в ViewModel + } + } + + } + } else { +// finish() +// val intetn = Intent(this, EnternetActivity::class.java) +// startActivity(intetn) + } + } + + // Получение списка активных пациентов с сервера и обновление ViewModel + fun GetPatientListYes() { + // Проверка интернет-соединения + if (enternetCheck.isOnline(this)) { + initRetrofit() // Инициализация Retrofit + val Tokens = prefDoctorConclusion.conclusionToken(this) // Получение токена + // Запуск корутины для выполнения сетевого запроса в фоновом потоке + CoroutineScope(Dispatchers.IO).launch { + val listProduct = doctorApi.GetPatientAllActive("Bearer $Tokens") // Выполнение GET-запроса для получения активных пациентов + // Переключение на главный поток для обновления UI + runOnUiThread { + + //Фиксируем полученные данные + val patientList = listProduct.body() + + //Если нету ошибок + if (patientList != null) { + modelDoctor.PatientActiveList.value = patientList.patient // Обновление списка активных пациентов в ViewModel + } + } + + } + } else { +// finish() +// val intetn = Intent(this, EnternetActivity::class.java) +// startActivity(intetn) + } + } + + // Получение списка неактивных пациентов с сервера и обновление ViewModel + fun GetPatientListNo() { + + // Проверка интернет-соединения + if (enternetCheck.isOnline(this)) { + initRetrofit() // Инициализация Retrofit + val Tokens = prefDoctorConclusion.conclusionToken(this) // Получение токена + // Запуск корутины для выполнения сетевого запроса в фоновом потоке + CoroutineScope(Dispatchers.IO).launch { + val listProduct = doctorApi.GetPatientAllNotActive("Bearer $Tokens") // Выполнение GET-запроса для получения неактивных пациентов + // Переключение на главный поток для обновления UI + runOnUiThread { + + //Фиксируем полученные данные + val patientList = listProduct.body() + + //Если нету ошибок + if (patientList != null) { + modelDoctor.PatientNotList.value = patientList.patient // Обновление списка неактивных пациентов в ViewModel + } + } + + } + + } else { +// finish() +// val intetn = Intent(this, EnternetActivity::class.java) +// startActivity(intetn) + } + } + +// private fun createNotifChannel() { +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { +// val channel = NotificationChannel( +// CHANNEL_ID, +// CHANNEL_NAME, +// NotificationManager.IMPORTANCE_DEFAULT +// ).apply { +// lightColor = Color.BLUE +// enableLights(true) +// } +// val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager +// manager.createNotificationChannel(channel) +// } +// } + +// //Одинарный +// fun myOneTimeWork() { +// val constraints = Constraints.Builder() +// .setRequiredNetworkType(NetworkType.NOT_REQUIRED)//Тут мы указыаем что сработает при наличие интернета +// .setRequiresCharging(true) +// .build() +// +// //Ограничение +// val myWorkRequest: WorkRequest = +// OneTimeWorkRequest.Builder(MyWorker::class.java)//OneTimeWorkRequest - разовая +// .setConstraints(constraints) +// .build() +// +// WorkManager.getInstance(this@MainActivity).enqueue(myWorkRequest)//Отправка запроса +// } + +// //Повторяющийся +// fun myPriodicTimeWork() { +// val constraints = Constraints.Builder() +// .setRequiredNetworkType(NetworkType.NOT_REQUIRED)//Тут мы указыаем что сработает при наличие интернета +// .setRequiresCharging(true) +// .build() +// +// //Ограничение +// val myWorkRequest = PeriodicWorkRequest.Builder( +// MyWorker::class.java, +// 15, +// TimeUnit.MINUTES +// )//PeriodicWorkRequest - переодически, 15 - время интервала(минимальное 15 минут) +// .setConstraints(constraints) +// .addTag("my_id")//Уникальный индификатор для уведомления, для того чтобы можно было кпримеру обратиться и остановать данный менеджер +// .build() +// +// WorkManager.getInstance(this@MainActivity).enqueueUniquePeriodicWork( +// "my_id", +// ExistingPeriodicWorkPolicy.KEEP, +// myWorkRequest +// )//Отправка запроса +// } + + + // Функция для определения, отображать экран аутентификации или основной контент + fun MainAndAuth() { + Token = prefDoctorConclusion.conclusionToken(this@MainActivity) // Получение токена + //CheckTokenAuth(Token) + if (Token == "") { // Если токен пустой, переходим к аутентификации + Auth() + } else { // Иначе, проверяем токен + CheckTokenAuth(Token) + } + } + + // Функция для отображения экрана аутентификации + fun Auth() { + binding.CLLoad.visibility = View.GONE // Скрываем индикатор загрузки + // Замена текущего фрагмента на фрагмент аутентификации + supportFragmentManager.beginTransaction() + .replace(R.id.CLMain, AuthFragment.newInstance()) + .commit() // Применяем изменения + } + + // Функция для отображения основного контента приложения + fun Main() { + binding.CLLoad.visibility = View.GONE // Скрываем индикатор загрузки + fragment_inicializ() // Инициализация главного фрагмента с нижним меню + +// fixedRateTimer("timer", false, 0, 10000) { +// this@MainActivity.runOnUiThread { +// getNewAppeals() +// getOldAppeals() +// GetPatientListSearch() +// GetPatientListYes() +// GetPatientListNo() +// } +// } + } + + // (Закомментированная функция для инициализации уведомлений и WorkManager) + @SuppressLint("MissingPermission") + private fun main() = with(binding) { + +// btn11.setOnClickListener { +// myOneTimeWork() +// } +// btn22.setOnClickListener { +// myPriodicTimeWork() +// } +// createNotifChannel() +// notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager +// val intent = Intent(this@MainActivity, MainActivity::class.java) +// val pendingIntent = TaskStackBuilder.create(this@MainActivity).run { +// addNextIntentWithParentStack(intent) +// getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT) +// } +// +// val notif = NotificationCompat.Builder(this@MainActivity, CHANNEL_ID) +// .setContentTitle("Sample Title") +// .setContentText("This is sample body notif") +// .setSmallIcon(R.drawable.door) +// .setPriority(NotificationCompat.PRIORITY_HIGH) +// .setContentIntent(pendingIntent) +// .build() +// +// +// val notifManger = NotificationManagerCompat.from(this@MainActivity) +// button3.setOnClickListener { +// +// notifManger.notify(NOTIF_ID, notif) +// } +// +// + + } + + + // Функция для проверки валидности токена аутентификации на сервере + private fun CheckTokenAuth(token: String) { + // Проверка интернет-соединения + if (enternetCheck.isOnline(this)) { + initRetrofit() // Инициализация Retrofit + // Запуск корутины для выполнения сетевого запроса в фоновом потоке + CoroutineScope(Dispatchers.IO).launch { + val response = doctorApi.CheckToken("Bearer $token") // Выполнение GET-запроса для проверки токена + // Переключение на главный поток для обновления UI и обработки ответа + runOnUiThread { + + + //Фиксируем полученные данные + val List = response.body() + val Nice = response.isSuccessful + val Code = response.code() + // Обработка различных кодов ответа сервера + if(Code==429){ + // Переход на экран ошибки 429 + val intetn = Intent(this@MainActivity, Code429Activity::class.java) + + startActivity(intetn) + } + else if(Code==200) { + //Если нету ошибок + if (Nice) { + if (Code.toString() == "200") { // Если код 200, токен валиден, переходим на основной экран + Main() + } else { // Иначе, токен невалиден, переходим к аутентификации + Auth() + } + } + } + else if (Code == 500) { + // Переход на экран ошибки 500 + val intetn = Intent(this@MainActivity, Code500Activity::class.java) + + startActivity(intetn) + } + else if (Code == 401) { + // Переход на экран аутентификации при ошибке 401 (неавторизованный) + val intetn = Intent(this@MainActivity, AuthActivity::class.java) + finish() // Закрываем текущую активити + startActivity(intetn) + } + } + } + } else { + // Переход на экран отсутствия интернета + val intetn = Intent(this, EnternetActivity::class.java) + finish() // Закрываем текущую активити + startActivity(intetn) + } + + } + + + // Инициализация клиента Retrofit для выполнения сетевых запросов + private fun initRetrofit() { + + val interceptor = HttpLoggingInterceptor() + interceptor.level = HttpLoggingInterceptor.Level.BODY // Уровень логирования HTTP-запросов + + val client = OkHttpClient + .Builder() + .addInterceptor(interceptor) + .build() + + val retrofit = Retrofit.Builder() + .baseUrl("https://rehabilitation.vmeda.org/api/") // Базовый URL API + .client(client) // Установка OkHttpClient + .addConverterFactory(GsonConverterFactory.create()) // Добавление конвертера JSON + .build() + + doctorApi = retrofit.create(DoctorApi::class.java) // Создание экземпляра API + + } + + // Инициализация главного фрагмента (MainFragment) с нижним меню + private fun fragment_inicializ() { + //Вывод фрагмента на активити при первоначальной загрузке + supportFragmentManager.beginTransaction() + .replace(R.id.CLMain, MainFragment.newInstance()) + .commit()//Заменяем наш экран на фрагмент (используем наш экран как основу)//R.id.placeHolder - куда всталяем //MainFragment.newInstance() - это то что мы вставляем + + } +// override fun onBackPressed() { +// if (backPressedTime + 3000 > System.currentTimeMillis()) { +// super.onBackPressed() +// finish() +// } else { +// AlertDialog.Builder(this@MainActivity) +// .setTitle("Выйти") +// .setMessage("Вы точно хотите выйти из приложения?") +// .setPositiveButton("Да") { dialog, whichButton -> +// super.onBackPressed() +// } +// .setNegativeButton("Нет") { dialog, whichButton -> +// +// } +// .show() +// } +// backPressedTime = System.currentTimeMillis() +// } + +// override fun onResume() { +// super.onResume() +// checkForUpdates(true) +// } +// +// +// override fun onStop() { +// super.onStop() +// timer.cancel() +// timer.purge() +// } +// +// private fun checkForUpdates(daemonIsTrue: Boolean) { +// Token = prefDoctorConclusion.conclusionToken(this@MainActivity) +// timer = fixedRateTimer("default", daemonIsTrue, 0, 5000) { +// this@MainActivity?.runOnUiThread { +// +// CheckTokenAuth(Token) +// } +// } +// +//} + + +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/MainFragment.kt b/app/src/main/java/com/example/doctor/MainFragment.kt new file mode 100644 index 0000000..898051a --- /dev/null +++ b/app/src/main/java/com/example/doctor/MainFragment.kt @@ -0,0 +1,211 @@ +package com.example.doctor + + +import android.annotation.SuppressLint +import android.icu.text.SimpleDateFormat +import android.icu.util.Calendar +import android.os.Bundle +import android.os.SystemClock +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels +import androidx.lifecycle.Observer +//import androidx.navigation.fragment.findNavController +import com.example.doctor.Appeals.AppealsFragment +import com.example.doctor.Home.HomeFragment +import com.example.doctor.Patients.PatientsListFragment +import com.example.doctor.Pref.ConclusionPref +import com.example.doctor.Pref.SavePref +import com.example.doctor.Setting.SettingFragment +import com.example.doctor.databinding.FragmentMainBinding +import java.util.Date + + +class MainFragment : Fragment() { + private lateinit var binding: FragmentMainBinding + private val dataModel: DataModel by activityViewModels()//Для передачи данных + val prefDoctorSave = SavePref() + val prefDoctorConclusion = ConclusionPref() + + private var Token = "" + var sdf: SimpleDateFormat = SimpleDateFormat("EEEE") + var d: Date = Date() + var dayOfTheWeek: String = sdf.format(d) + + // Функция жизненного цикла, создаёт и возвращает view фрагмента + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentMainBinding.inflate(layoutInflater, container, false) + return binding.root + } + + // Функция жизненного цикла, вызывается после создания view. Инициализирует наблюдателей и запускает начальные функции + @SuppressLint("ResourceType") + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + //MainAndAuth() + dataModel.fragmentMenu.observe(viewLifecycleOwner, Observer { + binding.buttonNavigation.selectedItemId = it + }) + + addDate() // Добавляет текущую дату на экран + + fragment_inicializ() // Инициализирует первый фрагмент + + + } + + // Добавляет текущую дату и день недели в текстовое поле на главном экране + private fun addDate() { + val calendar: Calendar = Calendar.getInstance() + val sdf: SimpleDateFormat = SimpleDateFormat("EEEE") + val d: Date = Date() + val DayOfTheWeek: String = sdf.format(d) + val Day = calendar.get(Calendar.DATE) + val Month = calendar.get(Calendar.MONTH).plus(1) + var day = Day.toString() + var month = "" + var date_of_the_week = "" + + when (Month) { + 1 -> month = "Январь" + 2 -> month = "Февраль" + 3 -> month = "Март" + 4 -> month = "Апрель" + 5 -> month = "Май" + 6 -> month = "Июнь" + 7 -> month = "Июль" + 8 -> month = "Август" + 9 -> month = "Сентябрь" + 10 -> month = "Октябрь" + 11 -> month = "Ноябрь" + 12 -> month = "Декабрь" + else -> { // обратите внимание на блок + month = "" + } + } + + when (dayOfTheWeek) { + "Monday" -> date_of_the_week = "Пн" + "Tuesday" -> date_of_the_week = "Вт" + "Wednesday" -> date_of_the_week = "Ср" + "Thursday" -> date_of_the_week = "Чт" + "Friday" -> date_of_the_week = "Пт" + "Saturday" -> date_of_the_week = "Сб" + "Sunday" -> date_of_the_week = "Вс" + "Понедельник" -> date_of_the_week = "Пн" + "Вторник" -> date_of_the_week = "Вт" + "Стреда" -> date_of_the_week = "Ср" + "Четверг" -> date_of_the_week = "Чт" + "Пятница" -> date_of_the_week = "Пт" + "Суббота" -> date_of_the_week = "Сб" + "Воскресенье" -> date_of_the_week = "Вс" + else -> { + date_of_the_week = "" + } + } + + System.out.println(calendar.get(Calendar.DATE)) + binding.txtDate.setText("${day}" + " " + "${month}") + } + +// fun MainAndAuth(){ +// +// //Token = prefDoctorConclusion.conclusionToken() +// if(Token == ""){ +// Auth() +// } +// else{ +// Main() +// } +// } + +// fun Auth(){ +// val intetn = Intent(requireContext(), AuthActivity::class.java) +// startActivity(intetn) +// activity?.finish() +// } +// fun Main(){ +// fragment_inicializ() +// } + + + // Инициализация фрагментов и навигации по нижнему меню + fun fragment_inicializ() { + + Token = prefDoctorConclusion.conclusionToken(requireContext()) + //Вывод фрагмента на активити при первоначальной загрузке + activity?.supportFragmentManager?.beginTransaction() + ?.replace(R.id.CLMainFragment, HomeFragment.newInstance()) + ?.commit()//Заменяем наш экран на фрагмент (используем наш экран как основу)//R.id.placeHolder - куда всталяем //MainFragment.newInstance() - это то что мы вставляем + + + //Эран который будет выбран по умолчанию(кнопка которая будет прожата по умолчанию) + binding.buttonNavigation.selectedItemId = + R.id.home//По умолчанию и так первая, но на всякий случай выберу еще програмным путем первую ячейку + + //Нажатие на bottom navigation + binding.buttonNavigation.setOnItemSelectedListener { + + when (it.itemId) {//it.itemId - это id нажатого элемента + R.id.home -> { + if (!isClickRecently()) { + activity?.supportFragmentManager?.beginTransaction() + ?.replace(R.id.CLMainFragment, HomeFragment.newInstance()) + ?.commit()//Заменяем наш экран на фрагмент (используем наш экран как основу)//R.id.placeHolder - куда всталяем //MainFragment.newInstance() - это то что мы вставляем + } + } + + R.id.patient -> { + if (!isClickRecently()) { + activity?.supportFragmentManager?.beginTransaction() + ?.replace(R.id.CLMainFragment, PatientsListFragment.newInstance()) + ?.commit()//Заменяем наш экран на фрагмент (используем наш экран как основу)//R.id.placeHolder - куда всталяем //MainFragment.newInstance() - это то что мы вставляем + } + } + + R.id.list_check -> {//Если вы не авторизованный пользваотель то вам не будет доступен доступ к данном фрагменту + if (!isClickRecently()) { + activity?.supportFragmentManager?.beginTransaction() + ?.replace(R.id.CLMainFragment, AppealsFragment.newInstance()) + ?.commit()//Заменяем наш экран на фрагмент (используем наш экран как основу)//R.id.placeHolder - куда всталяем //MainFragment.newInstance() - это то что мы вставляем + } + } + + R.id.setting -> {//Если вы не авторизованный пользваотель то вам не будет доступен доступ к данном фрагменту + if (!isClickRecently()) { + activity?.supportFragmentManager?.beginTransaction() + ?.replace(R.id.CLMainFragment, SettingFragment.newInstance()) + ?.commit()//Заменяем наш экран на фрагмент (используем наш экран как основу)//R.id.placeHolder - куда всталяем //MainFragment.newInstance() - это то что мы вставляем + } + } + + } + true + } + } + + // Проверяет, было ли недавнее нажатие, чтобы избежать двойных кликов + var mLastClickTime = 0L + fun isClickRecently(): Boolean { + if (SystemClock.elapsedRealtime() - mLastClickTime < 500) { + return true + } + mLastClickTime = SystemClock.elapsedRealtime() + return false + } + + + + + + companion object { + // Фабричный метод для создания экземпляра MainFragment + fun newInstance() = MainFragment() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/Patients/Adapter/PatientListAdapter.kt b/app/src/main/java/com/example/doctor/Patients/Adapter/PatientListAdapter.kt new file mode 100644 index 0000000..49b7f75 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Patients/Adapter/PatientListAdapter.kt @@ -0,0 +1,85 @@ +package com.example.doctor.Patients.Adapter + +import android.annotation.SuppressLint +import android.os.Handler +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import com.example.doctor.Patients.Model.PatientAllModel +import com.example.doctor.Patients.Model.PatientModel +import com.example.doctor.R +import com.example.doctor.databinding.ItemCardPatientBinding + +// Адаптер для отображения списка пациентов в RecyclerView +class PatientListAdapter(val listener_patient: Listener) : + ListAdapter( + Comparator() + ) { + + // ViewHolder для элемента списка пациентов + class Holder(view: View, val listener_patient: Listener) : + RecyclerView.ViewHolder(view) { + // Класс, который хранит ссылки на элементы и обрабатывает нажатия + val binding = + ItemCardPatientBinding.bind(view) + + var itemTemp: PatientAllModel? = + null // Глобальная переменная для текущего пациента, чтобы можно было передать данные при нажатии + + // Инициализация обработчика нажатия на карточку пациента + init { + itemView.setOnClickListener { + itemTemp?.let { it1 -> listener_patient.onClickPatient(it1) } + } + } + + // Привязка данных к элементу списка + @SuppressLint("SuspiciousIndentation", "SetTextI18n") + fun bind(item: PatientAllModel) = with(binding) { + itemTemp = item + txtNumberCurds.text = item.number.toString() + "." + txtLoginPatient.text = item.login + } + } + + // Создание ViewHolder для элемента списка + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.item_card_patient, parent, false) + return Holder(view, listener_patient) + } + + // Привязка данных к ViewHolder + override fun onBindViewHolder(holder: Holder, position: Int) { + val view = holder.bind(getItem(position)) + } + + // Comparator - сравнивает старый и новый списки, чтобы обновлять только изменённые элементы + class Comparator : DiffUtil.ItemCallback() { + override fun areItemsTheSame( + oldItem: PatientAllModel, + newItem: PatientAllModel + ): Boolean { + return oldItem.id == newItem.id + } + + override fun areContentsTheSame( + oldItem: PatientAllModel, + newItem: PatientAllModel + ): Boolean { + return oldItem == newItem + } + } + + // Интерфейс для обработки нажатия на карточку пациента + interface Listener { + fun onClickPatient(item: PatientAllModel) + } +} + + + + diff --git a/app/src/main/java/com/example/doctor/Patients/Model/CreatePatientModel.kt b/app/src/main/java/com/example/doctor/Patients/Model/CreatePatientModel.kt new file mode 100644 index 0000000..a47f4df --- /dev/null +++ b/app/src/main/java/com/example/doctor/Patients/Model/CreatePatientModel.kt @@ -0,0 +1,7 @@ +package com.example.doctor.Patients.Model + +data class CreatePatientModel( + //val login:String, + val login:String, + val password:String, +) diff --git a/app/src/main/java/com/example/doctor/Patients/Model/MessageModel.kt b/app/src/main/java/com/example/doctor/Patients/Model/MessageModel.kt new file mode 100644 index 0000000..47b4221 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Patients/Model/MessageModel.kt @@ -0,0 +1,6 @@ +package com.example.doctor.Patients.Model + +data class MessageModel( + val message: String, +) + diff --git a/app/src/main/java/com/example/doctor/Patients/Model/PatientAllListModel.kt b/app/src/main/java/com/example/doctor/Patients/Model/PatientAllListModel.kt new file mode 100644 index 0000000..8f9ae8c --- /dev/null +++ b/app/src/main/java/com/example/doctor/Patients/Model/PatientAllListModel.kt @@ -0,0 +1,6 @@ +package com.example.doctor.Patients.Model + +data class PatientAllListModel( + val patient: List +) + diff --git a/app/src/main/java/com/example/doctor/Patients/Model/PatientAllModel.kt b/app/src/main/java/com/example/doctor/Patients/Model/PatientAllModel.kt new file mode 100644 index 0000000..823d5cb --- /dev/null +++ b/app/src/main/java/com/example/doctor/Patients/Model/PatientAllModel.kt @@ -0,0 +1,22 @@ +package com.example.doctor.Patients.Model + +import java.util.Date + +data class PatientAllModel( + val number: Int? = null, + val id: Int? = null, + val login: String? = null, + val email: String? = null, + val loginDoctor: String? = null, + val emailDoctor: String? = null, + val pause: String? = null, + val block: String? = null, + val report7day: String? = null, + val report15day: String? = null, + val id_sport_patient: Int? = null, + val total_days_sports: Int? = null, + val now_days_sports: Int? = null, + val created_at: String? = null, + val updated_at: String? = null, +) + diff --git a/app/src/main/java/com/example/doctor/Patients/Model/PatientIdModel.kt b/app/src/main/java/com/example/doctor/Patients/Model/PatientIdModel.kt new file mode 100644 index 0000000..f3d77a6 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Patients/Model/PatientIdModel.kt @@ -0,0 +1,9 @@ +package com.example.doctor.Patients.Model + +import java.util.Date + +data class PatientIdModel( + val id: Int + +) + diff --git a/app/src/main/java/com/example/doctor/Patients/Model/PatientModel.kt b/app/src/main/java/com/example/doctor/Patients/Model/PatientModel.kt new file mode 100644 index 0000000..fed20f8 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Patients/Model/PatientModel.kt @@ -0,0 +1,8 @@ +package com.example.doctor.Patients.Model + +data class PatientModel( + val id: Int, + val login: String, + //val email:String, +) + diff --git a/app/src/main/java/com/example/doctor/Patients/Model/PauseModel.kt b/app/src/main/java/com/example/doctor/Patients/Model/PauseModel.kt new file mode 100644 index 0000000..06d4bc2 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Patients/Model/PauseModel.kt @@ -0,0 +1,6 @@ +package com.example.doctor.Patients.Model + +data class PauseModel( + val message: String, +) + diff --git a/app/src/main/java/com/example/doctor/Patients/PatientActivity.kt b/app/src/main/java/com/example/doctor/Patients/PatientActivity.kt new file mode 100644 index 0000000..e86ac5f --- /dev/null +++ b/app/src/main/java/com/example/doctor/Patients/PatientActivity.kt @@ -0,0 +1,421 @@ +package com.example.doctor.Patients + +import android.content.Context +import android.content.Intent +import android.net.ConnectivityManager +import android.net.NetworkCapabilities +import android.os.Build +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import android.util.Log +import android.view.View +import androidx.activity.viewModels +import androidx.core.content.ContentProviderCompat.requireContext +import com.example.doctor.Auth.AuthActivity +import com.example.doctor.CodeError.Code429Activity +import com.example.doctor.CodeError.Code500Activity +import com.example.doctor.DoctorViewModel +import com.example.doctor.Enternet.EnternetActivity +import com.example.doctor.Enternet.EnternetCheck +import com.example.doctor.Patients.Reports.PatientFragment +import com.example.doctor.Pref.ClearPref +import com.example.doctor.Pref.ConclusionPref +import com.example.doctor.Pref.SavePref +import com.example.doctor.R +import com.example.doctor.Retrofit.DoctorApi +import com.example.doctor.databinding.ActivityPatientBinding +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import java.util.Timer +import kotlin.concurrent.fixedRateTimer + +class PatientActivity : AppCompatActivity() { + private lateinit var binding: ActivityPatientBinding + private lateinit var doctorApi: DoctorApi + private lateinit var timer: Timer + + + val prefDoctorConclusion = ConclusionPref() + val prefDoctorClear = ClearPref() + val prefDoctorSave = SavePref() + private val modelDoctor: DoctorViewModel by viewModels()//Инициализировали класс + var idPatient = 0 + + //Класс проверки интеренета + val enternetCheck = EnternetCheck() + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityPatientBinding.inflate(layoutInflater) + setContentView(binding.root) + supportFragmentManager.beginTransaction().replace(R.id.CLPatientActivity, PatientFragment.newInstance()).commit()//Заменяем наш экран на фрагмент (используем наш экран как основу)//R.id.placeHolder - куда всталяем //MainFragment.newInstance() - это то что мы вставляем + idPatient = prefDoctorConclusion.conclusionIdPatient(this) + + + } + + + //Получения списка анкет ДО и ПОСЛЕ для пациента + fun QBAPatientList(id:Int){ + if (enternetCheck.isOnline(this@PatientActivity)) { + initRetrofit() + val Tokens = prefDoctorConclusion.conclusionToken(this@PatientActivity) + CoroutineScope(Dispatchers.IO).launch { + val list = doctorApi.GetPatientBAQiestionar("Bearer $Tokens",id) + runOnUiThread { + + //Фиксируем полученные данные + val List = list.body() + val Nice = list.isSuccessful + val Code = list.code() + if(Code==429){ + val intetn = Intent(this@PatientActivity, Code429Activity::class.java) + + startActivity(intetn) + } + else if(Code==200) { + //Если нету ошибок + if (Nice) { + if (List != null) { + if(List?.questionnaire !=null){ + modelDoctor.qbaList.value = List.questionnaire + } + } + } + + //2 + GetPatientID(idPatient) + + } + else if (Code == 500) { + val intetn = Intent(this@PatientActivity, Code500Activity::class.java) + + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(this@PatientActivity, AuthActivity::class.java) + finish() + startActivity(intetn) + } + + } + + } + } else { + finish() + val intetn = Intent(this, EnternetActivity::class.java) + startActivity(intetn) + } + + } + + private fun GetPatientID(idPatient: Int) { + if (enternetCheck.isOnline(this)) { + initRetrofit() + val Tokens = prefDoctorConclusion.conclusionToken(this) + CoroutineScope(Dispatchers.IO).launch { + val listPatient = doctorApi.GetPatientID("Bearer $Tokens", idPatient) + runOnUiThread { + + //Фиксируем полученные данные + val List = listPatient.body() + val Nice = listPatient.isSuccessful + val Code = listPatient.code() + if(Code==429){ + val intetn = Intent(this@PatientActivity, Code429Activity::class.java) + + startActivity(intetn) + } + else if(Code==200) { + //Если нету ошибок + if (Nice) { + if (List != null) { + modelDoctor.patientCurrent.value = List + + } + } + + //3 + GetCoursesAllSport(idPatient) + + } + else if (Code == 500) { + val intetn = Intent(this@PatientActivity,Code500Activity::class.java) + + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(this@PatientActivity, AuthActivity::class.java) + finish() + startActivity(intetn) + } + } + + } + } else { + val intetn = Intent(this, EnternetActivity::class.java) + + finish() + startActivity(intetn) + } + + } + + //Вывод всех курсов + fun GetCoursesAllSport(idPatient: Int){ + if (enternetCheck.isOnline(this)) { + initRetrofit() + val Tokens = prefDoctorConclusion.conclusionToken(this) + CoroutineScope(Dispatchers.IO).launch { + val listProduct = doctorApi.GetCoursAllPatient("Bearer $Tokens",idPatient) + runOnUiThread { + + + //Фиксируем полученные данные + val List = listProduct.body() + val Nice = listProduct.isSuccessful + val Code = listProduct.code() + if(Code==429){ + val intetn = Intent(this@PatientActivity, Code429Activity::class.java) + + startActivity(intetn) + } + else if(Code==200) { + //Если нету ошибок + if (Nice) { + if (List != null) { + modelDoctor.SportCoursList.value = List.courses + + } + } + + //4 + GetCoursesYouSport(idPatient) + + } + else if (Code == 500) { + val intetn = Intent(this@PatientActivity, Code500Activity::class.java) + + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(this@PatientActivity, AuthActivity::class.java) + finish() + startActivity(intetn) + } + } + } + } else { + val intetn = Intent(this@PatientActivity, EnternetActivity::class.java) + + finish() + startActivity(intetn) + } + + } + + + //Получения списка курсов созданных доктором + fun GetCoursesYouSport(idPatient:Int) { + if (enternetCheck.isOnline(this)) { + initRetrofit() + val Tokens = prefDoctorConclusion.conclusionToken(this) + CoroutineScope(Dispatchers.IO).launch { + val listCoursesDoctor = doctorApi.GetCoursesDoctorPatient("Bearer $Tokens",idPatient) + + runOnUiThread { + + + //Фиксируем полученные данные + val List = listCoursesDoctor.body() + val Nice = listCoursesDoctor.isSuccessful + val Code = listCoursesDoctor.code() + if(Code==429){ + val intetn = Intent(this@PatientActivity, Code429Activity::class.java) + + startActivity(intetn) + } + else if(Code==200) { + //Если нету ошибок + if (Nice) { + if (List != null) { + modelDoctor.SportCoursDoctorList.value = List.courses_doctor + + } + } + + //5 + GetCoursesSportYes(idPatient) + + } + else if (Code == 500) { + val intetn = Intent(this@PatientActivity, Code500Activity::class.java) + + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(this@PatientActivity, AuthActivity::class.java) + finish() + startActivity(intetn) + } + + } + + } + } else { + val intetn = Intent(this, EnternetActivity::class.java) + + finish() + startActivity(intetn) + } + + } + + //Получения списка пациентов + fun GetCoursesSportYes(id_patient:Int) { + if (enternetCheck.isOnline(this)) { + initRetrofit() + val Tokens = prefDoctorConclusion.conclusionToken(this) + CoroutineScope(Dispatchers.IO).launch { + val sportEdit = doctorApi.GetCoursesSportYes("Bearer $Tokens", id_patient) + runOnUiThread { + + + //Фиксируем полученные данные + val List = sportEdit.body() + val Nice = sportEdit.isSuccessful + val Code = sportEdit.code() + if(Code==429){ + val intetn = Intent(this@PatientActivity, Code429Activity::class.java) + + startActivity(intetn) + } + else if(Code==200) { + //Если нету ошибок + if (Nice) { + if (List != null) { + modelDoctor.EditSportListYes.value = List.set_of_sports_exercises_yes + + } + } + //6 + GetCoursesSportNo(idPatient) + } + else if (Code == 500) { + val intetn = Intent(this@PatientActivity, Code500Activity::class.java) + + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(this@PatientActivity, AuthActivity::class.java) + finish() + startActivity(intetn) + } + } + + } + } else { + val intetn = Intent(this, EnternetActivity::class.java) + + finish() + startActivity(intetn) + } + + } + //Получения списка пациентов + fun GetCoursesSportNo(id_patient:Int) { + if (enternetCheck.isOnline(this)) { + initRetrofit() + val Tokens = prefDoctorConclusion.conclusionToken(this) + CoroutineScope(Dispatchers.IO).launch { + val sportEdit = doctorApi.GetCoursesSportNo("Bearer $Tokens",id_patient) + runOnUiThread { + + //Фиксируем полученные данные + val List = sportEdit.body() + val Nice = sportEdit.isSuccessful + val Code = sportEdit.code() + if(Code==429){ + val intetn = Intent(this@PatientActivity, Code429Activity::class.java) + + startActivity(intetn) + } + else if(Code==200) { + //Если нету ошибок + if (Nice) { + if (List != null) { + modelDoctor.EditSportListNo.value = List.set_of_sports_exercises_no + + } + } + } + else if (Code == 500) { + val intetn = Intent(this@PatientActivity, Code500Activity::class.java) + + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(this@PatientActivity, AuthActivity::class.java) + finish() + startActivity(intetn) + } + } + + } + } else { + val intetn = Intent(this, EnternetActivity::class.java) + finish() + + startActivity(intetn) + } + + } + + //Инициализируем Retrofit + private fun initRetrofit() { + val interceptor = HttpLoggingInterceptor() + interceptor.level = HttpLoggingInterceptor.Level.BODY + + val client = OkHttpClient + .Builder() + .addInterceptor(interceptor) + .build() + + val retrofit = Retrofit.Builder() + .baseUrl("https://rehabilitation.vmeda.org/api/") + .client(client) + .addConverterFactory(GsonConverterFactory.create()) + .build() + + doctorApi = retrofit.create(DoctorApi::class.java) + + } + + override fun onResume() { + super.onResume() + checkForUpdates(true) + } + + override fun onStop() { + super.onStop() + timer.cancel() + timer.purge() + } + + private fun checkForUpdates(daemonIsTrue: Boolean) { + + timer = fixedRateTimer("default", daemonIsTrue, 0, 10000) { + this@PatientActivity?.runOnUiThread { + QBAPatientList(idPatient) + + } + } + + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/Patients/PatientsListFragment.kt b/app/src/main/java/com/example/doctor/Patients/PatientsListFragment.kt new file mode 100644 index 0000000..abb3d3c --- /dev/null +++ b/app/src/main/java/com/example/doctor/Patients/PatientsListFragment.kt @@ -0,0 +1,263 @@ +package com.example.doctor.Patients + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ArrayAdapter +import androidx.fragment.app.activityViewModels +import com.example.doctor.DoctorViewModel +import com.example.doctor.Patients.Model.PatientAllModel +import com.example.doctor.Patients.Reports.PatientFragment +import com.example.doctor.Pref.ConclusionPref +import android.R +import android.content.Context +import android.content.Intent +import android.net.ConnectivityManager +import android.net.NetworkCapabilities +import android.os.Build +import android.os.Handler +import android.util.Log +import androidx.fragment.app.FragmentActivity +import androidx.lifecycle.Observer +//import androidx.navigation.fragment.findNavController + +import com.example.doctor.Adapter.VpAdapterPatientList +import com.example.doctor.Auth.AuthActivity +import com.example.doctor.CodeError.Code429Activity +import com.example.doctor.CodeError.Code500Activity +import com.example.doctor.DataModel +import com.example.doctor.Enternet.EnternetActivity +import com.example.doctor.Enternet.EnternetCheck +import com.example.doctor.MainActivity +import com.example.doctor.Patients.TabLayoutPatient.ActiveCoursesPatientFragment +import com.example.doctor.Patients.TabLayoutPatient.NotActiveCoursesPatientFragment +import com.example.doctor.Pref.ClearPref +import com.example.doctor.Pref.SavePref +import com.example.doctor.Retrofit.DoctorApi +import com.example.doctor.databinding.FragmentPatientsListBinding +import com.example.user.BottomSheetMenu.NoteBottomSheetMenu +import com.google.android.material.tabs.TabLayoutMediator +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import java.util.Timer +import kotlin.concurrent.fixedRateTimer + + +class PatientsListFragment : Fragment(){ + private lateinit var binding: FragmentPatientsListBinding + private val modelDoctor: DoctorViewModel by activityViewModels() + private val dataModel: DataModel by activityViewModels()//Для передачи данных + private lateinit var doctorApi: DoctorApi + private lateinit var timer: Timer + + + val prefDoctorConclusion = ConclusionPref() + val prefDoctorClear = ClearPref() + val prefDoctorSave = SavePref() + var viewListPAtient = true + + var patientListSearch:List?=null + + //Класс проверки интеренета + val enternetCheck = EnternetCheck() + + private val tList = listOf( + "Активные", + "Без курса", + ) + //Список с фрагментами для переключения + private val flist = listOf( + ActiveCoursesPatientFragment.newInstance(), + NotActiveCoursesPatientFragment.newInstance(), + ) + // Функция жизненного цикла, создаёт и возвращает view фрагмента + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentPatientsListBinding.inflate(layoutInflater, container, false) + return binding.root + } + + // Функция жизненного цикла, вызывается после создания view. Инициализирует интерфейс и обработчики + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + //Чтобы срабатывала команда 1 раз + if(viewListPAtient){ + GetPatientList() // Загружает список пациентов + init() // Инициализация ViewPager и TabLayout + + //Нажатие на кнопку для добавления пациента + binding.btnAddPatient.setOnClickListener { + val noteBottomSheetMenu = NoteBottomSheetMenu.newInstance() + noteBottomSheetMenu.show( + requireActivity().supportFragmentManager, + "Note Bottom Sheet Fragment" + ) + } + } + // Наблюдение за изменением списка пациентов для поиска + modelDoctor.patientListSearch.observe(viewLifecycleOwner){ + if(patientListSearch!=it){ + patientListSearch = it + addListSerchPatient(it) + } + } + } + + // Инициализация ViewPager и TabLayout для переключения между вкладками пациентов + private fun init() = with(binding) { + val adapter = VpAdapterPatientList(activity as FragmentActivity, flist as List) + vpPatient.adapter = adapter + //Переключения (связываем таблаяут(переключатель) с viewpager, чтобы переключать фрагменты) + TabLayoutMediator(tabLayoutPatient, vpPatient) { tab, pos -> + tab.text = + tList[pos]//tab - нажатая кнопка, pos - позиция кнопки, tList[pos] - передаем название по полученной позиции + }.attach()// attach() - чтобы все переключалось, а не вывадило постоянно один экран + //Изменения цвета в зависомости на каком из tabLayout вы находитесь + binding.tabLayoutPatient.setTabTextColors(getResources().getColor(com.example.doctor.R.color.black), + getResources().getColor(com.example.doctor.R.color.white)); + } + +// private fun viewPatient() { +// val viewPatient = prefDoctorConclusion.conclusionViewPatient(requireContext()) +// val idPatient = prefDoctorConclusion.conclusionIdPatient(requireContext()) +// Log.i("viewPatient1",viewPatient.toString()) +// Log.i("idPatient1",viewPatient.toString()) +// if(viewPatient ==1 && idPatient !=0){ +// GetPatientID(idPatient) +// prefDoctorClear.clearIdPatient(requireContext()) +// prefDoctorClear.clearViewPatient(requireContext()) +// Log.i("viewPatient2",viewPatient.toString()) +// Log.i("idPatient2",viewPatient.toString()) +// } +// } + + // Получение списка всех пациентов с сервера и обновление ViewModel + fun GetPatientList() { + if (enternetCheck.isOnline(requireContext())) { + initRetrofit() + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) + CoroutineScope(Dispatchers.IO).launch { + val listPatientList = doctorApi.GetPatientAll("Bearer $Tokens") + requireActivity().runOnUiThread { + //Фиксируем полученные данные + val List = listPatientList.body() + val Nice = listPatientList.isSuccessful + val Code = listPatientList.code() + if(Code==429){ + val intetn = Intent(requireContext(), Code429Activity::class.java) + startActivity(intetn) + } + else if(Code==200) { + //Если нету ошибок + if (Nice) { + if (List != null) { + modelDoctor.patientListSearch.value = List?.patient + } + } + } + else if (Code == 500) { + val intetn = Intent(requireContext(), Code500Activity::class.java) + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(requireContext(), AuthActivity::class.java) + activity?.finish() + startActivity(intetn) + } + } + } + } else { + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() + startActivity(intetn) + } + } + + + + + + + // Добавляет автодополнение и обработку выбора пациента в поисковой строке + private fun addListSerchPatient(patient: List) { + val listPatient = arrayListOf("") + for(i in 0 .. patient.count()-1){ + listPatient.add(patient[i].login.toString()) + } + val adapter = ArrayAdapter(requireContext(), R.layout.simple_list_item_1, listPatient) + binding.searchPatient.threshold = 1 + binding.searchPatient.setAdapter(adapter) + binding.searchPatient.setOnItemClickListener { parent, arg1, pos, id -> + val serpat = binding.searchPatient.text.toString() + //Ищем пациента по логину + for(i in 0 ..patient.count()-1) { + if(patient[i].login == serpat){ + modelDoctor.patientCurrent.value = patient[i] + prefDoctorSave.saveIdPatient(requireContext(),patient[i].id!!) + prefDoctorSave.saveViewPatient(requireContext(),1) + val intetn = Intent(requireContext(), PatientActivity::class.java) + startActivity(intetn) + } + } + } + } + + + + // Инициализация клиента Retrofit для сетевых запросов + private fun initRetrofit() { + val interceptor = HttpLoggingInterceptor() + interceptor.level = HttpLoggingInterceptor.Level.BODY + val client = OkHttpClient + .Builder() + .addInterceptor(interceptor) + .build() + val retrofit = Retrofit.Builder() + .baseUrl("https://rehabilitation.vmeda.org/api/") + .client(client) + .addConverterFactory(GsonConverterFactory.create()) + .build() + doctorApi = retrofit.create(DoctorApi::class.java) + } + + + companion object { + // Фабричный метод для создания экземпляра PatientsListFragment + fun newInstance() = PatientsListFragment() + } + + + // Функция жизненного цикла, запускает таймер для периодического обновления списка пациентов + override fun onResume() { + super.onResume() + checkForUpdates(true) + } + + // Функция жизненного цикла, останавливает таймер при остановке фрагмента + override fun onStop() { + super.onStop() + timer.cancel() + timer.purge() + } + + // Запускает таймер, который каждые 10 секунд обновляет список пациентов + private fun checkForUpdates(daemonIsTrue: Boolean) { + timer = fixedRateTimer("default", daemonIsTrue, 0, 10000) { + activity?.runOnUiThread { + GetPatientList() + } + } + } + + + +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/Patients/Reports/Courses/CoursesAllAdapter.kt b/app/src/main/java/com/example/doctor/Patients/Reports/Courses/CoursesAllAdapter.kt new file mode 100644 index 0000000..2d0183e --- /dev/null +++ b/app/src/main/java/com/example/doctor/Patients/Reports/Courses/CoursesAllAdapter.kt @@ -0,0 +1,81 @@ +package com.example.doctor.Patients.Reports.Courses + +import android.annotation.SuppressLint +import android.graphics.Color +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import com.example.doctor.R +import com.example.doctor.databinding.ItemCardCoursesDoctorBinding +import com.example.doctor.databinding.ItemSportCourseBinding + +// Адаптер для отображения всех курсов пациента в RecyclerView +class CoursesAllAdapter(val listener_courses: Listener) : ListAdapter( + Comparator() +) { + // ViewHolder для элемента списка курсов + class Holder(view: View, val listener_courses: Listener): RecyclerView.ViewHolder(view) { + // Класс, который хранит ссылки на элементы и обрабатывает нажатия + val binding = ItemCardCoursesDoctorBinding.bind(view) + + var itemTemp: SportCoursModel? = + null // Глобальная переменная для текущего курса, чтобы можно было передать данные при нажатии + + // Инициализация обработчика нажатия на карточку курса + init { + itemView.setOnClickListener { + itemTemp?.let { it1 -> listener_courses.onClickSportCourses(it1) } + } + } + + // Привязка данных к элементу списка + @SuppressLint("SuspiciousIndentation") + fun bind(item: SportCoursModel) = with(binding) { + itemTemp = item + txtNumberCurds.text = item.number.toString() + "." + txtNameCoursesDoctor.text = item.name + if (item.visibility == 100000) { + CVCours.setCardBackgroundColor(Color.parseColor("#83da83")) + Log.i("11111111111","sasdasdasdasdasadsdasda") + } else { + CVCours.setCardBackgroundColor(Color.parseColor("#b6b6b6")) + Log.i("2222222222222","sasdasdasdasdasadsdasda") + } + } + } + + // Создание ViewHolder для элемента списка + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder { + val view = LayoutInflater.from(parent.context).inflate(R.layout.item_card_courses_doctor, parent, false) + return Holder(view, listener_courses) + } + + // Привязка данных к ViewHolder + override fun onBindViewHolder(holder: Holder, position: Int) { + val view = holder.bind(getItem(position)) + } + + // Comparator - сравнивает старый и новый списки, чтобы обновлять только изменённые элементы + class Comparator : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: SportCoursModel, newItem: SportCoursModel): Boolean { + return oldItem.id == newItem.id + } + + override fun areContentsTheSame(oldItem: SportCoursModel, newItem: SportCoursModel): Boolean { + return oldItem == newItem + } + } + + // Интерфейс для обработки нажатия на карточку курса + interface Listener { + fun onClickSportCourses(item: SportCoursModel) + } +} + + + + diff --git a/app/src/main/java/com/example/doctor/Patients/Reports/Courses/CoursesYouAdapter.kt b/app/src/main/java/com/example/doctor/Patients/Reports/Courses/CoursesYouAdapter.kt new file mode 100644 index 0000000..b19de38 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Patients/Reports/Courses/CoursesYouAdapter.kt @@ -0,0 +1,91 @@ +package com.example.doctor.Patients.Reports.Courses + +import android.annotation.SuppressLint +import android.graphics.Color +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView + +import com.example.doctor.R +import com.example.doctor.Setting.Courses.Model.CoursesDoctorModel +import com.example.doctor.databinding.ItemCardCoursesDoctorBinding + +// Адаптер для отображения курсов, созданных доктором, в RecyclerView +class CoursesYouAdapter(val listener: Listener) : + ListAdapter( + Comparator() + ) { + // ViewHolder для элемента списка курсов + class Holder(view: View, val listener: Listener) : + RecyclerView.ViewHolder(view) { + // Класс, который хранит ссылки на элементы и обрабатывает нажатия + val binding = ItemCardCoursesDoctorBinding.bind(view) + + var itemTemp: SportCoursModel? = + null // Глобальная переменная для текущего курса, чтобы можно было передать данные при нажатии + + // Инициализация обработчика нажатия на карточку курса + init { + itemView.setOnClickListener { + itemTemp?.let { it1 -> listener.onClickCourses(it1) } + } + } + + // Привязка данных к элементу списка + @SuppressLint("SuspiciousIndentation", "SetTextI18n") + fun bind(item: SportCoursModel) = with(binding) { + itemTemp = item + txtNumberCurds.text = item.number.toString() + "." + txtNameCoursesDoctor.text = item.name + if (item.visibility == 100000) { + CVCours.setCardBackgroundColor(Color.parseColor("#83da83")) + Log.i("11111111111","sasdasdasdasdasadsdasda") + } else { + CVCours.setCardBackgroundColor(Color.parseColor("#b6b6b6")) + Log.i("2222222222222","sasdasdasdasdasadsdasda") + } + } + } + + // Создание ViewHolder для элемента списка + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.item_card_courses_doctor, parent, false) + return Holder(view, listener) + } + + // Привязка данных к ViewHolder + override fun onBindViewHolder(holder: Holder, position: Int) { + val view = holder.bind(getItem(position)) + } + + // Comparator - сравнивает старый и новый списки, чтобы обновлять только изменённые элементы + class Comparator : DiffUtil.ItemCallback() { + override fun areItemsTheSame( + oldItem: SportCoursModel, + newItem: SportCoursModel + ): Boolean { + return oldItem.id == newItem.id + } + + override fun areContentsTheSame( + oldItem: SportCoursModel, + newItem: SportCoursModel + ): Boolean { + return oldItem == newItem + } + } + + // Интерфейс для обработки нажатия на карточку курса + interface Listener { + fun onClickCourses(item: SportCoursModel) + } +} + + + + diff --git a/app/src/main/java/com/example/doctor/Patients/Reports/Courses/SportCoursDoctorListModel.kt b/app/src/main/java/com/example/doctor/Patients/Reports/Courses/SportCoursDoctorListModel.kt new file mode 100644 index 0000000..ab0de5d --- /dev/null +++ b/app/src/main/java/com/example/doctor/Patients/Reports/Courses/SportCoursDoctorListModel.kt @@ -0,0 +1,8 @@ +package com.example.doctor.Patients.Reports.Courses + +// Модель данных для списка спортивных курсов, созданных доктором для пациента +// Этот класс используется для представления списка объектов SportCoursModel, связанных с врачом и пациентом. +data class SportCoursDoctorListModel( + val courses_doctor: List // Список объектов SportCoursModel, представляющих спортивные курсы, созданные доктором + ) + diff --git a/app/src/main/java/com/example/doctor/Patients/Reports/Courses/SportCoursListModel.kt b/app/src/main/java/com/example/doctor/Patients/Reports/Courses/SportCoursListModel.kt new file mode 100644 index 0000000..c66dfc4 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Patients/Reports/Courses/SportCoursListModel.kt @@ -0,0 +1,8 @@ +package com.example.doctor.Patients.Reports.Courses + +// Модель данных для списка всех спортивных курсов пациента +// Этот класс используется для представления списка объектов SportCoursModel, связанных со всеми спортивными курсами пациента. +data class SportCoursListModel( + val courses: List // Список объектов SportCoursModel, представляющих спортивные курсы пациента + ) + diff --git a/app/src/main/java/com/example/doctor/Patients/Reports/Courses/SportCoursModel.kt b/app/src/main/java/com/example/doctor/Patients/Reports/Courses/SportCoursModel.kt new file mode 100644 index 0000000..07da3fb --- /dev/null +++ b/app/src/main/java/com/example/doctor/Patients/Reports/Courses/SportCoursModel.kt @@ -0,0 +1,13 @@ +package com.example.doctor.Patients.Reports.Courses + +// Модель данных для спортивного курса пациента +// Этот класс представляет структуру данных одного спортивного курса, связанного с отчетами пациента. +data class SportCoursModel( + val number: Int, // Порядковый номер спортивного курса + val id: Int, // Уникальный идентификатор спортивного курса + val name: String, // Название спортивного курса + val description: String, // Описание спортивного курса + val visibility: Int, // Статус видимости спортивного курса (например, 0 - скрыт, 1 - виден) + val created_at: String, // Дата создания спортивного курса + val updated_at: String, // Дата последнего обновления спортивного курса +) diff --git a/app/src/main/java/com/example/doctor/Patients/Reports/Courses/TabLayout/CoursesAllFragment.kt b/app/src/main/java/com/example/doctor/Patients/Reports/Courses/TabLayout/CoursesAllFragment.kt new file mode 100644 index 0000000..5b343c8 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Patients/Reports/Courses/TabLayout/CoursesAllFragment.kt @@ -0,0 +1,217 @@ +package com.example.doctor.Patients.Reports.Courses.TabLayout + +import android.content.Intent +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.fragment.app.activityViewModels +import androidx.recyclerview.widget.GridLayoutManager +import com.example.doctor.Auth.AuthActivity +import com.example.doctor.CodeError.Code429Activity +import com.example.doctor.CodeError.Code500Activity +import com.example.doctor.DoctorViewModel +import com.example.doctor.Enternet.EnternetActivity +import com.example.doctor.Enternet.EnternetCheck +import com.example.doctor.Patients.Reports.Courses.CoursesAllAdapter +import com.example.doctor.Patients.Reports.Courses.SportCoursModel +import com.example.doctor.Patients.Reports.CoursesListAdapter +import com.example.doctor.Pref.ClearPref +import com.example.doctor.Pref.ConclusionPref +import com.example.doctor.Pref.SavePref +import com.example.doctor.R +import com.example.doctor.Retrofit.DoctorApi +import com.example.doctor.Toast.showCustomInfoToast +import com.example.doctor.databinding.FragmentCoursesAllBinding +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory + +// Фрагмент для отображения ВСЕХ спортивных курсов для пациента +// Этот фрагмент используется в TabLayout на экране отчетов пациента для отображения всех доступных спортивных курсов. +class CoursesAllFragment : Fragment(),CoursesAllAdapter.Listener { + private lateinit var binding: FragmentCoursesAllBinding // Объект привязки для доступа к элементам UI макета фрагмента + private val modelDoctor: DoctorViewModel by activityViewModels() // ViewModel для хранения и управления данными приложения + lateinit var adapterCours: CoursesAllAdapter // Адаптер для списка всех курсов + private lateinit var doctorApi: DoctorApi // Экземпляр Retrofit API для сетевых запросов + val prefDoctorConclusion = ConclusionPref() // Вспомогательный класс для получения данных из SharedPreferences + val prefDoctorClear = ClearPref() // Вспомогательный класс для очистки SharedPreferences + val prefDoctorSave = SavePref() // Вспомогательный класс для сохранения данных в SharedPreferences + var sportCourses:List?=null // Список спортивных курсов + var id: Int? = null // Идентификатор пациента (получается из ViewModel) + + //Класс проверки интеренета + val enternetCheck = EnternetCheck() // Класс для проверки наличия интернет-соединения + // Функция жизненного цикла, создаёт и возвращает view фрагмента (надувание макета) + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentCoursesAllBinding.inflate(layoutInflater,container,false) + return binding.root // Возвращаем корневой View фрагмента + } + + // Функция жизненного цикла, вызывается после создания view. Инициализирует интерфейс и подписки на данные + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + initRcViewCours() // Инициализация списка курсов (RecyclerView) + // Подписка на изменение id пациента в ViewModel + modelDoctor.id_patient.observe(viewLifecycleOwner){ + id = it // Обновляем локальную переменную id пациента + GetAllCoursesSport() // Загрузка всех курсов пациента при получении id + } + // Подписка на изменение списка всех спортивных курсов в ViewModel + modelDoctor.SportCoursList.observe(viewLifecycleOwner) {//viewLifecycleOwner - следит за циклом жизни fragment + if(sportCourses != it){ + sportCourses = it // Обновляем локальный список курсов + adapterCours.submitList(it)//Напрямую переадем созданный список в adapter(ProductAdapter) // Обновляем данные в адаптере RecyclerView + binding.txtNull.visibility = View.GONE // Скрываем текст о отсутствии курсов, если список не пустой + } + } + } + + // Получение всех курсов пациента с сервера и обновление ViewModel + fun GetAllCoursesSport(){ + // Проверка интернет-соединения + if (enternetCheck.isOnline(requireContext())) { + binding.txtNull.visibility = View.VISIBLE // Показываем текст "Загрузка" или "Нет курсов" пока идет запрос + initRetrofit() // Инициализация Retrofit + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) // Получение токена авторизации + // Запуск корутины для выполнения сетевого запроса в фоновом потоке + CoroutineScope(Dispatchers.IO).launch { + val listProduct = doctorApi.GetCoursAllPatient("Bearer $Tokens",id!!) // Выполнение GET-запроса для получения всех курсов пациента + // Переключение на главный поток для обновления UI и обработки ответа + requireActivity().runOnUiThread { + //Фиксируем полученные данные + val List = listProduct.body() // Тело ответа + val Nice = listProduct.isSuccessful // Флаг успешности запроса + val Code = listProduct.code() // Код ответа HTTP + // Обработка различных кодов ответа сервера + if(Code==429){ + // Переход на экран ошибки 429 (слишком много запросов) + val intetn = Intent(requireContext(), Code429Activity::class.java) + startActivity(intetn) + } + else if(Code==200) { + //Если нету ошибок (код 200 - OK) + if (Nice) { + if (List != null) { + modelDoctor.SportCoursList.value = List.courses // Обновление списка всех курсов в ViewModel + binding.txtNull.visibility = View.GONE // Скрываем текст о отсутствии курсов + } + } + } + else if (Code == 500) { + // Переход на экран ошибки 500 (внутренняя ошибка сервера) + val intetn = Intent(requireContext(), Code500Activity::class.java) + startActivity(intetn) + } + else if (Code == 401) { + // Переход на экран аутентификации при ошибке 401 (неавторизованный) + val intetn = Intent(requireContext(), AuthActivity::class.java) + activity?.finish() // Закрываем текущую активити + startActivity(intetn) + } + } + } + } else { + // Переход на экран отсутствия интернета + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() // Закрываем текущую активити + startActivity(intetn) + } + } + + // Инициализация клиента Retrofit для выполнения сетевых запросов + private fun initRetrofit() { + val interceptor = HttpLoggingInterceptor() // Создание перехватчика для логирования HTTP-запросов + interceptor.level = HttpLoggingInterceptor.Level.BODY // Уровень логирования: тело запроса и ответа + val client = OkHttpClient + .Builder() + .addInterceptor(interceptor) // Добавление перехватчика к клиенту + .build() + val retrofit = Retrofit.Builder() + .baseUrl("https://rehabilitation.vmeda.org/api/") // Базовый URL API + .client(client) // Установка OkHttpClient + .addConverterFactory(GsonConverterFactory.create()) // Добавление конвертера JSON для автоматической десериализации ответов + .build() + doctorApi = retrofit.create(DoctorApi::class.java) // Создание экземпляра API-сервиса + } + + // Инициализация списка курсов (RecyclerView) с адаптером CoursesAllAdapter + private fun initRcViewCours() = with(binding) { + rcView.layoutManager = GridLayoutManager(requireContext(), 1)//По вертикали будет выводить по умолчанию // Установка LayoutManager для вертикального списка + adapterCours = CoursesAllAdapter(this@CoursesAllFragment) // Создание экземпляра адаптера, передавая текущий фрагмент как слушатель нажатий + rcView.adapter = adapterCours // Установка адаптера для RecyclerView + } + + companion object { + // Фабричный метод для создания экземпляра CoursesAllFragment + fun newInstance() = CoursesAllFragment() // Создает и возвращает новый экземпляр фрагмента + } + + // Обработчик нажатия на элемент спортивного курса в списке + override fun onClickSportCourses(item: SportCoursModel) { + AddSportPatient(id!!,item.id,14) // Вызов функции добавления выбранного курса пациенту (с id пациента, id курса и количеством дней 14) + // GetAllCoursesSport() // Закомментированный вызов обновления списка курсов (возможно, перенесено в AddSportPatient) + } + + // Добавление выбранного спортивного курса пациенту через API + fun AddSportPatient(id_patient:Int,id_course:Int,all_day:Int){ + // Проверка интернет-соединения + if (enternetCheck.isOnline(requireContext())) { + initRetrofit() // Инициализация Retrofit + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) // Получение токена авторизации + // Запуск корутины для выполнения сетевого запроса в фоновом потоке + CoroutineScope(Dispatchers.IO).launch { + // Выполнение POST-запроса для добавления спортивного курса пациенту + val updatePassword= doctorApi.AddSportPatient("Bearer $Tokens",id_patient,id_course,all_day) + // Переключение на главный поток для обновления UI и обработки ответа + requireActivity().runOnUiThread { + //Фиксируем полученные данные + val List = updatePassword.body() // Тело ответа + val Nice = updatePassword.isSuccessful // Флаг успешности запроса + val Code = updatePassword.code() // Код ответа HTTP + // Обработка различных кодов ответа сервера + if(Code==429){ + // Переход на экран ошибки 429 + val intetn = Intent(requireContext(), Code429Activity::class.java) + startActivity(intetn) + } + else if(Code==200) { + //Если нету ошибок (код 200 - OK) + if (Nice) { + if (List != null) { + Toast(requireContext()).showCustomInfoToast(List.message, requireActivity()) // Показываем сообщение об успешном добавлении курса + GetAllCoursesSport() // Обновляем список всех курсов после добавления + modelDoctor.BtnSportCoursDoctorCurrent.value = 2 // Обновляем состояние кнопки "Очистить спорт пациента" (вероятно, делаем ее зеленой, указывая на наличие курса) + } + } + } + else if (Code == 500) { + // Переход на экран ошибки 500 + val intetn = Intent(requireContext(), Code500Activity::class.java) + startActivity(intetn) + } + else if (Code == 401) { + // Переход на экран аутентификации при ошибке 401 + val intetn = Intent(requireContext(), AuthActivity::class.java) + activity?.finish() // Закрываем текущую активити + startActivity(intetn) + } + } + } + } else { + // Переход на экран отсутствия интернета + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() // Закрываем текущую активити + startActivity(intetn) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/Patients/Reports/Courses/TabLayout/CoursesYouFragment.kt b/app/src/main/java/com/example/doctor/Patients/Reports/Courses/TabLayout/CoursesYouFragment.kt new file mode 100644 index 0000000..9956ba2 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Patients/Reports/Courses/TabLayout/CoursesYouFragment.kt @@ -0,0 +1,193 @@ +package com.example.doctor.Patients.Reports.Courses.TabLayout + +import android.content.Intent +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.fragment.app.activityViewModels +import androidx.recyclerview.widget.GridLayoutManager +import com.example.doctor.Auth.AuthActivity +import com.example.doctor.CodeError.Code429Activity +import com.example.doctor.CodeError.Code500Activity +import com.example.doctor.DoctorViewModel +import com.example.doctor.Enternet.EnternetActivity +import com.example.doctor.Enternet.EnternetCheck +import com.example.doctor.Patients.Reports.Courses.CoursesYouAdapter +import com.example.doctor.Patients.Reports.Courses.SportCoursModel +import com.example.doctor.Pref.ClearPref +import com.example.doctor.Pref.ConclusionPref +import com.example.doctor.Pref.SavePref +import com.example.doctor.Retrofit.DoctorApi +import com.example.doctor.Setting.Courses.Model.CoursesDoctorModel +import com.example.doctor.Toast.showCustomInfoToast +import com.example.doctor.databinding.FragmentCoursesYouBinding +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory + +class CoursesYouFragment : Fragment(),CoursesYouAdapter.Listener { + private lateinit var binding:FragmentCoursesYouBinding + private val modelDoctor: DoctorViewModel by activityViewModels() + lateinit var adapterCours: CoursesYouAdapter + private lateinit var doctorApi: DoctorApi + val prefDoctorConclusion = ConclusionPref() + val prefDoctorClear = ClearPref() + val prefDoctorSave = SavePref() + var sportCoursesDoctor:List?=null + var id: Int? = null + + //Класс проверки интеренета + val enternetCheck = EnternetCheck() + // Функция жизненного цикла, создаёт и возвращает view фрагмента + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentCoursesYouBinding.inflate(layoutInflater,container,false) + return binding.root + } + + // Функция жизненного цикла, вызывается после создания view. Инициализирует интерфейс и подписки на данные + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + initRcViewCours() // Инициализация списка курсов + modelDoctor.id_patient.observe(viewLifecycleOwner){ + id = it + GetCoursesDoctor() // Загрузка курсов, созданных доктором + } + modelDoctor.SportCoursDoctorList.observe(viewLifecycleOwner){ + if(sportCoursesDoctor!=it){ + sportCoursesDoctor = it + adapterCours.submitList(it) + binding.txtNull.visibility = View.GONE + } + } + } + + // Получение списка курсов, созданных доктором, с сервера и обновление ViewModel + fun GetCoursesDoctor() { + if (enternetCheck.isOnline(requireContext())) { + binding.txtNull.visibility = View.VISIBLE + initRetrofit() + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) + CoroutineScope(Dispatchers.IO).launch { + val listCoursesDoctor = doctorApi.GetCoursesDoctorPatient("Bearer $Tokens",id!!) + requireActivity().runOnUiThread { + //Фиксируем полученные данные + val List = listCoursesDoctor.body() + val Nice = listCoursesDoctor.isSuccessful + val Code = listCoursesDoctor.code() + if(Code==429){ + val intetn = Intent(requireContext(), Code429Activity::class.java) + startActivity(intetn) + } + else if(Code==200) { + //Если нету ошибок + if (Nice) { + if (List != null) { + modelDoctor.SportCoursDoctorList.value = List.courses_doctor + binding.txtNull.visibility = View.GONE + } + } + } + else if (Code == 500) { + val intetn = Intent(requireContext(), Code500Activity::class.java) + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(requireContext(), AuthActivity::class.java) + activity?.finish() + startActivity(intetn) + } + } + } + } else { + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() + startActivity(intetn) + } + } + // Инициализация клиента Retrofit для сетевых запросов + private fun initRetrofit() { + val interceptor = HttpLoggingInterceptor() + interceptor.level = HttpLoggingInterceptor.Level.BODY + val client = OkHttpClient + .Builder() + .addInterceptor(interceptor) + .build() + val retrofit = Retrofit.Builder() + .baseUrl("https://rehabilitation.vmeda.org/api/") + .client(client) + .addConverterFactory(GsonConverterFactory.create()) + .build() + doctorApi = retrofit.create(DoctorApi::class.java) + } + // Инициализация списка курсов (RecyclerView) + private fun initRcViewCours() = with(binding) { + rcView.layoutManager = GridLayoutManager(requireContext(), 1)//По вертикали будет выводить по умолчанию + adapterCours = CoursesYouAdapter(this@CoursesYouFragment) + rcView.adapter = adapterCours + } + + companion object { + // Фабричный метод для создания экземпляра CoursesYouFragment + fun newInstance() = CoursesYouFragment() + } + + // Обработчик нажатия на курс: добавляет курс пациенту и обновляет список + override fun onClickCourses(item: SportCoursModel) { + AddSportPatient(id!!,item.id,14) + GetCoursesDoctor() + } + + // Добавление курса пациенту через API + fun AddSportPatient(id_patient:Int,id_course:Int,all_day:Int){ + if (enternetCheck.isOnline(requireContext())) { + initRetrofit() + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) + CoroutineScope(Dispatchers.IO).launch { + val updatePassword= doctorApi.AddSportPatient("Bearer $Tokens",id_patient,id_course,all_day) + requireActivity().runOnUiThread { + //Фиксируем полученные данные + val List = updatePassword.body() + val Nice = updatePassword.isSuccessful + val Code = updatePassword.code() + if(Code==429){ + val intetn = Intent(requireContext(), Code429Activity::class.java) + startActivity(intetn) + } + else if(Code==200) { + //Если нету ошибок + if (Nice) { + if (List != null) { + Toast(requireContext()).showCustomInfoToast(List.message, requireActivity()) + GetCoursesDoctor() + modelDoctor.BtnSportCoursDoctorCurrent.value = 2 + } + } + } + else if (Code == 500) { + val intetn = Intent(requireContext(), Code500Activity::class.java) + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(requireContext(), AuthActivity::class.java) + activity?.finish() + startActivity(intetn) + } + } + } + } else { + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() + startActivity(intetn) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/Patients/Reports/CoursesListAdapter.kt b/app/src/main/java/com/example/doctor/Patients/Reports/CoursesListAdapter.kt new file mode 100644 index 0000000..6694502 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Patients/Reports/CoursesListAdapter.kt @@ -0,0 +1,81 @@ +package com.example.doctor.Patients.Reports + +import android.annotation.SuppressLint +import android.graphics.Color +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import com.example.doctor.Patients.Reports.Courses.SportCoursModel +import com.example.doctor.R +import com.example.doctor.databinding.ItemSportCourseBinding + +// Адаптер для отображения списка спортивных курсов пациента в RecyclerView +class CoursesListAdapter(val listener_courses: Listener) : ListAdapter( + Comparator() +) { + // ViewHolder для элемента списка курсов + class Holder(view: View, val listener_courses: Listener): RecyclerView.ViewHolder(view) { + // Класс, который хранит ссылки на элементы и обрабатывает нажатия + val binding = ItemSportCourseBinding.bind(view) + + var itemTemp: SportCoursModel? = + null // Глобальная переменная для текущего курса, чтобы можно было передать данные при нажатии + + // Инициализация обработчика нажатия на карточку курса + init { + binding.CardViewCourses.setOnClickListener { + itemTemp?.let { it1 -> listener_courses.onClickSportCourses(it1) } + } + } + + // Привязка данных к элементу списка + @SuppressLint("SuspiciousIndentation") + fun bind(item: SportCoursModel) = with(binding) { + itemTemp = item + txtCoursesSport.text = item.name + if (item.visibility == 1) { + CVCours.setCardBackgroundColor(Color.parseColor("#83da83")) + Log.i("11111111111","sasdasdasdasdasadsdasda") + } else { + CVCours.setCardBackgroundColor(Color.parseColor("#b6b6b6")) + Log.i("2222222222222","sasdasdasdasdasadsdasda") + } + Log.i("ssadsdasda","sasdasdasdasdasadsdasda") + } + } + + // Создание ViewHolder для элемента списка + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder { + val view = LayoutInflater.from(parent.context).inflate(R.layout.item_sport_course, parent, false) + return Holder(view, listener_courses) + } + + // Привязка данных к ViewHolder + override fun onBindViewHolder(holder: Holder, position: Int) { + val view = holder.bind(getItem(position)) + } + + // Comparator - сравнивает старый и новый списки, чтобы обновлять только изменённые элементы + class Comparator : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: SportCoursModel, newItem: SportCoursModel): Boolean { + return oldItem.id == newItem.id + } + + override fun areContentsTheSame(oldItem: SportCoursModel, newItem: SportCoursModel): Boolean { + return oldItem == newItem + } + } + + // Интерфейс для обработки нажатия на карточку курса + interface Listener { + fun onClickSportCourses(item: SportCoursModel) + } +} + + + + diff --git a/app/src/main/java/com/example/doctor/Patients/Reports/Edit/EditSportFragment.kt b/app/src/main/java/com/example/doctor/Patients/Reports/Edit/EditSportFragment.kt new file mode 100644 index 0000000..0c67a78 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Patients/Reports/Edit/EditSportFragment.kt @@ -0,0 +1,76 @@ +package com.example.doctor.Patients.Reports.Edit + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.FragmentActivity +import com.example.doctor.Adapter.VpEditSportAdapter +import com.example.doctor.Patients.Reports.Edit.TabLayout.EditSportNoFragment +import com.example.doctor.Patients.Reports.Edit.TabLayout.EditSportYesFragment +import com.example.doctor.R +import com.example.doctor.databinding.FragmentEditSportBinding +import com.google.android.material.tabs.TabLayoutMediator + + +class EditSportFragment : Fragment() { + private lateinit var binding: FragmentEditSportBinding + + private val tListSport = listOf( + "Активные", + "Отключенные", + ) + + //Список с фрагментами для переключения + private val flistSport = listOf( + EditSportYesFragment.newInstance(), + EditSportNoFragment.newInstance(), + ) + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentEditSportBinding.inflate(layoutInflater,container,false) + return binding.root + } + + companion object { + + fun newInstance() = EditSportFragment() + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + init() + + } + + + //Функция подключения переключения + private fun init() = with(binding) { + val adapter = VpEditSportAdapter(activity as FragmentActivity, flistSport) + vpEditSport.adapter = adapter + + //Переключения (связываем таблаяут(переключатель) с viewpager, чтобы переключать фрагменты) + TabLayoutMediator(tabLayoutSport, vpEditSport) { tab, pos -> + tab.text = + tListSport[pos]//tab - нажатая кнопка, pos - позиция кнопки, tList[pos] - передаем название по полученной позиции + }.attach()// attach() - чтобы все переключалось, а не вывадило постоянно один экран + + //Изменения цвета в зависомости на каком из tabLayout вы находитесь + binding.tabLayoutSport.setTabTextColors(getResources().getColor(R.color.black), + getResources().getColor(R.color.white)); + } + + + +// //Инициализация списка +// private fun initRcViewDay() = with(binding) { +// rcView.layoutManager = +// GridLayoutManager(requireContext(), 1)//По вертикали будет выводить по умолчанию +// adapterPatient = PatientListAdapter(this@PatientsListFragment) +// rcView.adapter = adapterPatient +// } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/Patients/Reports/Edit/EditSportListNoModel.kt b/app/src/main/java/com/example/doctor/Patients/Reports/Edit/EditSportListNoModel.kt new file mode 100644 index 0000000..dcfd6d6 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Patients/Reports/Edit/EditSportListNoModel.kt @@ -0,0 +1,8 @@ +package com.example.doctor.Patients.Reports.Edit + +data class EditSportListNoModel( + val set_of_sports_exercises_no: List + + + ) + diff --git a/app/src/main/java/com/example/doctor/Patients/Reports/Edit/EditSportListYesModel.kt b/app/src/main/java/com/example/doctor/Patients/Reports/Edit/EditSportListYesModel.kt new file mode 100644 index 0000000..2ea1e5b --- /dev/null +++ b/app/src/main/java/com/example/doctor/Patients/Reports/Edit/EditSportListYesModel.kt @@ -0,0 +1,10 @@ +package com.example.doctor.Patients.Reports.Edit + +import com.example.doctor.Patients.Reports.QBAModel + +data class EditSportListYesModel( + val set_of_sports_exercises_yes: List + + + ) + diff --git a/app/src/main/java/com/example/doctor/Patients/Reports/Edit/EditSportModel.kt b/app/src/main/java/com/example/doctor/Patients/Reports/Edit/EditSportModel.kt new file mode 100644 index 0000000..1268464 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Patients/Reports/Edit/EditSportModel.kt @@ -0,0 +1,11 @@ +package com.example.doctor.Patients.Reports.Edit + +data class EditSportModel( + val number: Int, + val id: Int, + val name:String, + val description:String, + val url_image:String, + var expand : Boolean = false, + ) + diff --git a/app/src/main/java/com/example/doctor/Patients/Reports/Edit/EditSportNoAdapter.kt b/app/src/main/java/com/example/doctor/Patients/Reports/Edit/EditSportNoAdapter.kt new file mode 100644 index 0000000..144a99d --- /dev/null +++ b/app/src/main/java/com/example/doctor/Patients/Reports/Edit/EditSportNoAdapter.kt @@ -0,0 +1,98 @@ +package com.example.doctor.Patients.Reports.Edit + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import com.example.doctor.R +import com.example.doctor.databinding.ItemEditSportNoBinding +import com.example.doctor.databinding.ItemEditSportYesBinding + +// Адаптер для отображения неактивных упражнений пациента в RecyclerView +class EditSportNoAdapter(val listener_sport: Listener) : + ListAdapter( + Comparator() + ) { + // ViewHolder для элемента неактивного упражнения + class Holder(view: View, val listener_sport: Listener) : + RecyclerView.ViewHolder(view) { + // Класс, который хранит ссылки на элементы и обрабатывает нажатия + val binding = ItemEditSportNoBinding.bind(view) + + var itemTemp: EditSportModel? = + null // Глобальная переменная для текущего упражнения, чтобы можно было передать данные при нажатии + + // Инициализация обработчика нажатия на кнопку "Включить упражнение" + init { + binding.btnYeyNo.setOnClickListener { + itemTemp?.let { it1 -> listener_sport.onClickAppeals(it1) } + } + } + + // Привязка данных к элементу списка + @SuppressLint("SuspiciousIndentation") + fun bind(item: EditSportModel) = with(binding) { + itemTemp = item + txtNumber.text = item.number.toString()+"." + txtNameSport.text = item.name + txtDescriptionSport.text = item.description + + if (item.expand) { + binding.txtDescriptionSport.maxLines = 100 + } else { + binding.txtDescriptionSport.maxLines = 1 + } + + binding.CardViewOld.setOnClickListener { + if (item.expand == false) { + binding.txtDescriptionSport.maxLines = 100 + item.expand = true + } else { + binding.txtDescriptionSport.maxLines = 1 + item.expand = false + } + } + } + } + + // Создание ViewHolder для элемента списка + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.item_edit_sport_no, parent, false) + return Holder(view, listener_sport) + } + + // Привязка данных к ViewHolder + override fun onBindViewHolder(holder: Holder, position: Int) { + val view = holder.bind(getItem(position)) + } + + // Comparator - сравнивает старый и новый списки, чтобы обновлять только изменённые элементы + class Comparator : DiffUtil.ItemCallback() { + override fun areItemsTheSame( + oldItem: EditSportModel, + newItem: EditSportModel + ): Boolean { + return oldItem.id == newItem.id + } + + override fun areContentsTheSame( + oldItem: EditSportModel, + newItem: EditSportModel + ): Boolean { + return oldItem == newItem + } + } + + // Интерфейс для обработки нажатия на кнопку "Включить упражнение" + interface Listener { + fun onClickAppeals(item: EditSportModel) + } +} + + + + diff --git a/app/src/main/java/com/example/doctor/Patients/Reports/Edit/EditSportSearchModel.kt b/app/src/main/java/com/example/doctor/Patients/Reports/Edit/EditSportSearchModel.kt new file mode 100644 index 0000000..fb870e2 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Patients/Reports/Edit/EditSportSearchModel.kt @@ -0,0 +1,8 @@ +package com.example.doctor.Patients.Reports.Edit + +data class EditSportSearchModel( + val id_patient: Int, + val id_sports_courses_patient: Int, + + ) + diff --git a/app/src/main/java/com/example/doctor/Patients/Reports/Edit/EditSportYesAdapter.kt b/app/src/main/java/com/example/doctor/Patients/Reports/Edit/EditSportYesAdapter.kt new file mode 100644 index 0000000..24b2c1a --- /dev/null +++ b/app/src/main/java/com/example/doctor/Patients/Reports/Edit/EditSportYesAdapter.kt @@ -0,0 +1,97 @@ +package com.example.doctor.Patients.Reports.Edit + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import com.example.doctor.R +import com.example.doctor.databinding.ItemEditSportYesBinding + +// Адаптер для отображения активных упражнений пациента в RecyclerView +class EditSportYesAdapter(val listener_sport: Listener) : + ListAdapter( + Comparator() + ) { + // ViewHolder для элемента активного упражнения + class Holder(view: View, val listener_sport: Listener) : + RecyclerView.ViewHolder(view) { + // Класс, который хранит ссылки на элементы и обрабатывает нажатия + val binding = ItemEditSportYesBinding.bind(view) + + var itemTemp: EditSportModel? = + null // Глобальная переменная для текущего упражнения, чтобы можно было передать данные при нажатии + + // Инициализация обработчика нажатия на кнопку "Отключить упражнение" + init { + binding.btnYeyYes.setOnClickListener { + itemTemp?.let { it1 -> listener_sport.onClickAppeals(it1) } + } + } + + // Привязка данных к элементу списка + @SuppressLint("SuspiciousIndentation") + fun bind(item: EditSportModel) = with(binding) { + itemTemp = item + txtNumber.text = item.number.toString()+"." + txtNameSport.text = item.name + txtDescriptionSport.text = item.description + + if (item.expand) { + binding.txtDescriptionSport.maxLines = 100 + } else { + binding.txtDescriptionSport.maxLines = 1 + } + + binding.CardViewOld.setOnClickListener { + if (item.expand == false) { + binding.txtDescriptionSport.maxLines = 100 + item.expand = true + } else { + binding.txtDescriptionSport.maxLines = 1 + item.expand = false + } + } + } + } + + // Создание ViewHolder для элемента списка + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.item_edit_sport_yes, parent, false) + return Holder(view, listener_sport) + } + + // Привязка данных к ViewHolder + override fun onBindViewHolder(holder: Holder, position: Int) { + val view = holder.bind(getItem(position)) + } + + // Comparator - сравнивает старый и новый списки, чтобы обновлять только изменённые элементы + class Comparator : DiffUtil.ItemCallback() { + override fun areItemsTheSame( + oldItem: EditSportModel, + newItem: EditSportModel + ): Boolean { + return oldItem.id == newItem.id + } + + override fun areContentsTheSame( + oldItem: EditSportModel, + newItem: EditSportModel + ): Boolean { + return oldItem == newItem + } + } + + // Интерфейс для обработки нажатия на кнопку "Отключить упражнение" + interface Listener { + fun onClickAppeals(item: EditSportModel) + } +} + + + + diff --git a/app/src/main/java/com/example/doctor/Patients/Reports/Edit/TabLayout/EditSportNoFragment.kt b/app/src/main/java/com/example/doctor/Patients/Reports/Edit/TabLayout/EditSportNoFragment.kt new file mode 100644 index 0000000..9086db8 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Patients/Reports/Edit/TabLayout/EditSportNoFragment.kt @@ -0,0 +1,317 @@ +package com.example.doctor.Patients.Reports.Edit.TabLayout + +import android.content.Intent +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.fragment.app.activityViewModels +import androidx.recyclerview.widget.GridLayoutManager +import com.example.doctor.Auth.AuthActivity +import com.example.doctor.CodeError.Code429Activity +import com.example.doctor.CodeError.Code500Activity +import com.example.doctor.DoctorViewModel +import com.example.doctor.Enternet.EnternetActivity +import com.example.doctor.Enternet.EnternetCheck +import com.example.doctor.Patients.Reports.Edit.EditSportYesAdapter +import com.example.doctor.Patients.Reports.Edit.EditSportModel +import com.example.doctor.Patients.Reports.Edit.EditSportNoAdapter +import com.example.doctor.Pref.ConclusionPref +import com.example.doctor.Retrofit.DoctorApi +import com.example.doctor.Toast.showCustomInfoToast +import com.example.doctor.databinding.FragmentEditSportNoBinding +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory + + +class EditSportNoFragment : Fragment(),EditSportNoAdapter.Listener { + private lateinit var binding:FragmentEditSportNoBinding + lateinit var adapterNo: EditSportNoAdapter + private lateinit var doctorApi: DoctorApi + val prefDoctorConclusion = ConclusionPref() + private val model: DoctorViewModel by activityViewModels() + var id_patient = 0; + + var editNo:List?=null + + + //Класс проверки интеренета + val enternetCheck = EnternetCheck() + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentEditSportNoBinding.inflate(layoutInflater,container,false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + initRcViewDay() + model.EditSportListNo.observe(viewLifecycleOwner){ + if(it!=null){ + if(editNo!=it){ + editNo = it + adapterNo.submitList(it) + binding.txtNo.visibility = View.GONE + } + } + else{ + binding.txtNo.visibility = View.VISIBLE + } + + } + model.id_patient.observe(viewLifecycleOwner) {id-> + id_patient = id.toInt() + GetCoursesSportNo(id_patient) + } + + + btnClick() + } + + private fun btnClick()=with(binding) { + CLLoad.setOnClickListener { } + } + + + //Инициализация списка + private fun initRcViewDay() = with(binding) { + rcViewEditSportNo.layoutManager = + GridLayoutManager(requireContext(), 1)//По вертикали будет выводить по умолчанию + adapterNo = EditSportNoAdapter(this@EditSportNoFragment) + rcViewEditSportNo.adapter = adapterNo + } + + //Инициализируем Retrofit + private fun initRetrofit() { + val interceptor = HttpLoggingInterceptor() + interceptor.level = HttpLoggingInterceptor.Level.BODY + + val client = OkHttpClient + .Builder() + .addInterceptor(interceptor) + .build() + + val retrofit = Retrofit.Builder() + .baseUrl("https://rehabilitation.vmeda.org/api/") + .client(client) + .addConverterFactory(GsonConverterFactory.create()) + .build() + + doctorApi = retrofit.create(DoctorApi::class.java) + + } + + + //Получения списка пациентов + fun GetCoursesSportYes(id_patient:Int)= with(binding) { + if (enternetCheck.isOnline(requireContext())) { + initRetrofit() + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) + CoroutineScope(Dispatchers.IO).launch { + val sportEdit = doctorApi.GetCoursesSportYes("Bearer $Tokens", id_patient) + requireActivity().runOnUiThread { + + //Фиксируем полученные данные + val List = sportEdit.body() + val Nice = sportEdit.isSuccessful + val Code = sportEdit.code() + if(Code==429){ + CLLoad.visibility = View.GONE + + val intetn = Intent(requireContext(), Code429Activity::class.java) + + startActivity(intetn) + } + else if(Code==200){ + //Если нету ошибок + if(Nice){ + if (List != null) { + model.EditSportListYes.value = List.set_of_sports_exercises_yes + + } + + } + else{ + CLLoad.visibility = View.GONE + } + } + else if(Code==500){ + CLLoad.visibility = View.GONE + val intetn = Intent(requireContext(), Code500Activity::class.java) + + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(requireContext(), AuthActivity::class.java) + activity?.finish() + startActivity(intetn) + } + else{ + CLLoad.visibility = View.GONE + } + + + + } + + } + } else { + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() + startActivity(intetn) + } + + } + //Получения списка пациентов + fun GetCoursesSportNo(id_patient:Int)= with(binding) { + if (enternetCheck.isOnline(requireContext())) { + initRetrofit() + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) + CoroutineScope(Dispatchers.IO).launch { + val sportEdit = doctorApi.GetCoursesSportNo("Bearer $Tokens", id_patient) + requireActivity().runOnUiThread { + + + //Фиксируем полученные данные + val List = sportEdit.body() + val Nice = sportEdit.isSuccessful + val Code = sportEdit.code() + if(Code==429){ + CLLoad.visibility = View.GONE + + val intetn = Intent(requireContext(), Code429Activity::class.java) + + startActivity(intetn) + } + else if(Code==200){ + //Если нету ошибок + if(Nice){ + if (List != null) { + model.EditSportListNo.value = List.set_of_sports_exercises_no + binding.txtNo.visibility = View.GONE + binding.CLLoad.visibility = View.GONE + } + else{ + binding.txtNo.visibility = View.VISIBLE + binding.CLLoad.visibility = View.GONE + } + } + else{ + CLLoad.visibility = View.GONE + } + } + else if(Code==500){ + CLLoad.visibility = View.GONE + val intetn = Intent(requireContext(), Code500Activity::class.java) + + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(requireContext(), AuthActivity::class.java) + activity?.finish() + startActivity(intetn) + } + else{ + CLLoad.visibility = View.GONE + } + + } + + } + } else { + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() + startActivity(intetn) + } + + } + + + //Удаление упражнения из блока + fun UpdateBlockSportTasksNo(id:Int)= with(binding) { + if (enternetCheck.isOnline(requireContext())) { + initRetrofit() + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) + CoroutineScope(Dispatchers.IO).launch { + val NoEdit = doctorApi.UpdateBlockSportTasksNo("Bearer $Tokens", id) + requireActivity().runOnUiThread { + + //Фиксируем полученные данные + val List = NoEdit.body() + val Nice = NoEdit.isSuccessful + val Code = NoEdit.code() + if(Code==429){ + CLLoad.visibility = View.GONE + + val intetn = Intent(requireContext(), Code429Activity::class.java) + + startActivity(intetn) + } + else if(Code==200){ + //Если нету ошибок + if(Nice){ + if (List != null) { + Toast(requireContext()).showCustomInfoToast(List.message, requireActivity()) + GetCoursesSportYes(id_patient) + GetCoursesSportNo(id_patient) + + } + else{ + if (List != null) { + Toast(requireContext()).showCustomInfoToast(List.message, requireActivity()) + CLLoad.visibility = View.GONE + } + CLLoad.visibility = View.GONE + } + } + else{ + CLLoad.visibility = View.GONE + } + } + else if(Code==500){ + CLLoad.visibility = View.GONE + val intetn = Intent(requireContext(), Code500Activity::class.java) + + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(requireContext(), AuthActivity::class.java) + activity?.finish() + startActivity(intetn) + } + else{ + CLLoad.visibility = View.GONE + } + + } + } + + + } else { + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() + startActivity(intetn) + } + + } + + + companion object { + fun newInstance() = EditSportNoFragment() + } + + override fun onClickAppeals(item: EditSportModel) { + binding.CLLoad.visibility = View.VISIBLE + UpdateBlockSportTasksNo(item.id) + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/Patients/Reports/Edit/TabLayout/EditSportYesFragment.kt b/app/src/main/java/com/example/doctor/Patients/Reports/Edit/TabLayout/EditSportYesFragment.kt new file mode 100644 index 0000000..12f44bb --- /dev/null +++ b/app/src/main/java/com/example/doctor/Patients/Reports/Edit/TabLayout/EditSportYesFragment.kt @@ -0,0 +1,325 @@ +package com.example.doctor.Patients.Reports.Edit.TabLayout + +import android.content.Intent +import android.os.Bundle +import android.util.Log +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.fragment.app.activityViewModels +import androidx.recyclerview.widget.GridLayoutManager +import com.example.doctor.Auth.AuthActivity +import com.example.doctor.CodeError.Code429Activity +import com.example.doctor.CodeError.Code500Activity +import com.example.doctor.DoctorViewModel +import com.example.doctor.Enternet.EnternetActivity +import com.example.doctor.Enternet.EnternetCheck +import com.example.doctor.Patients.Reports.Edit.EditSportYesAdapter +import com.example.doctor.Patients.Reports.Edit.EditSportModel +import com.example.doctor.Patients.Reports.Edit.EditSportNoAdapter +import com.example.doctor.Pref.ConclusionPref +import com.example.doctor.Retrofit.DoctorApi +import com.example.doctor.Toast.showCustomInfoToast +import com.example.doctor.databinding.FragmentEditSportYesBinding +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory + + +class EditSportYesFragment : Fragment(), EditSportYesAdapter.Listener { + private lateinit var binding:FragmentEditSportYesBinding + lateinit var adapterYes: EditSportYesAdapter + private lateinit var doctorApi: DoctorApi + val prefDoctorConclusion = ConclusionPref() + private val model: DoctorViewModel by activityViewModels() + var id_patient = 0; + var editYes:List?=null + + //Класс проверки интеренета + val enternetCheck = EnternetCheck() + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentEditSportYesBinding.inflate(layoutInflater,container,false) + return binding.root + } + + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + initRcViewDay() + + + model.id_patient.observe(viewLifecycleOwner) {id-> + id_patient = id.toInt() + Log.i("id_patient",id_patient.toString()) + Log.i("id",id.toString()) + GetCoursesSportYes(id_patient) + } + + model.EditSportListYes.observe(viewLifecycleOwner){ + if(it!=null){ + if(editYes!=it){ + editYes=it + adapterYes.submitList(it) + binding.txtYes.visibility = View.GONE + } + } + else{ + binding.txtYes.visibility = View.GONE + } + + + } + btnClick() + } + + private fun btnClick()=with(binding) { + CLLoad.setOnClickListener { } + } + + + //Инициализация списка + private fun initRcViewDay() = with(binding) { + rcViewEditSportYes.layoutManager = + GridLayoutManager(requireContext(), 1)//По вертикали будет выводить по умолчанию + adapterYes = EditSportYesAdapter(this@EditSportYesFragment) + rcViewEditSportYes.adapter = adapterYes + } + + + + //Инициализируем Retrofit + private fun initRetrofit() { + val interceptor = HttpLoggingInterceptor() + interceptor.level = HttpLoggingInterceptor.Level.BODY + + val client = OkHttpClient + .Builder() + .addInterceptor(interceptor) + .build() + + val retrofit = Retrofit.Builder() + .baseUrl("https://rehabilitation.vmeda.org/api/") + .client(client) + .addConverterFactory(GsonConverterFactory.create()) + .build() + + doctorApi = retrofit.create(DoctorApi::class.java) + + } + + //Получения списка пациентов + fun GetCoursesSportYes(id_patient:Int)=with(binding) { + if (enternetCheck.isOnline(requireContext())) { + initRetrofit() + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) + CoroutineScope(Dispatchers.IO).launch { + val sportEdit = doctorApi.GetCoursesSportYes("Bearer $Tokens", id_patient) + requireActivity().runOnUiThread { + + //Фиксируем полученные данные + val List = sportEdit.body() + val Nice = sportEdit.isSuccessful + val Code = sportEdit.code() + if(Code==429){ + CLLoad.visibility = View.GONE + + val intetn = Intent(requireContext(), Code429Activity::class.java) + + startActivity(intetn) + } + else if(Code==200){ + //Если нету ошибок + if(Nice){ + if (List != null) { + model.EditSportListYes.value = List.set_of_sports_exercises_yes + binding.txtYes.visibility = View.GONE + binding.CLLoad.visibility = View.GONE + } + else{ + model.EditSportListYes.value = null + + binding.txtYes.visibility = View.VISIBLE + binding.rcViewEditSportYes.visibility = View.GONE + binding.CLLoad.visibility = View.GONE + } + } + else{ + CLLoad.visibility = View.GONE + } + } + else if(Code==500){ + CLLoad.visibility = View.GONE + val intetn = Intent(requireContext(), Code500Activity::class.java) + + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(requireContext(), AuthActivity::class.java) + activity?.finish() + startActivity(intetn) + } + else{ + CLLoad.visibility = View.GONE + } + + //Фиксируем полученные данные + val sportEditList = sportEdit.body() + + //Если нету ошибок + + } + + } + } else { + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() + startActivity(intetn) + } + + } + //Получения списка пациентов + fun GetCoursesSportNo(id_patient:Int)=with(binding) { + if (enternetCheck.isOnline(requireContext())) { + initRetrofit() + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) + CoroutineScope(Dispatchers.IO).launch { + val sportEdit = doctorApi.GetCoursesSportNo("Bearer $Tokens",id_patient) + requireActivity().runOnUiThread { + + + //Фиксируем полученные данные + val List = sportEdit.body() + val Nice = sportEdit.isSuccessful + val Code = sportEdit.code() + if(Code==429){ + CLLoad.visibility = View.GONE + + val intetn = Intent(requireContext(), Code429Activity::class.java) + + startActivity(intetn) + } + else if(Code==200){ + //Если нету ошибок + if(Nice){ + if (List != null) { + model.EditSportListNo.value = List.set_of_sports_exercises_no + } + CLLoad.visibility = View.GONE + } + else{ + CLLoad.visibility = View.GONE + } + } + else if(Code==500){ + CLLoad.visibility = View.GONE + val intetn = Intent(requireContext(), Code500Activity::class.java) + + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(requireContext(), AuthActivity::class.java) + activity?.finish() + startActivity(intetn) + } + else{ + CLLoad.visibility = View.GONE + } + + } + + } + } else { + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() + startActivity(intetn) + } + + } + + + //Отключение упражнения + fun UpdateBlockSportTasksYes(id_pateint:Int,id_sports_tasks:Int)=with(binding) { + if (enternetCheck.isOnline(requireContext())) { + initRetrofit() + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) + CoroutineScope(Dispatchers.IO).launch { + val sportEdit = doctorApi.UpdateBlockSportTasksYes("Bearer $Tokens", id_pateint,id_sports_tasks) + requireActivity().runOnUiThread { + + //Фиксируем полученные данные + val List = sportEdit.body() + val Nice = sportEdit.isSuccessful + val Code = sportEdit.code() + if(Code==429){ + CLLoad.visibility = View.GONE + val intetn = Intent(requireContext(), Code429Activity::class.java) + + startActivity(intetn) + } + else if(Code==200){ + //Если нету ошибок + if(Nice){ + if (List != null) { + Toast(requireContext()).showCustomInfoToast(List.message, requireActivity()) + GetCoursesSportYes(id_patient) + GetCoursesSportNo(id_patient) + + } + else{ + if (List != null) { + Toast(requireContext()).showCustomInfoToast(List.message, requireActivity()) + CLLoad.visibility = View.GONE + } + CLLoad.visibility = View.GONE + } + } + else{ + CLLoad.visibility = View.GONE + } + } + else if(Code==500){ + CLLoad.visibility = View.GONE + val intetn = Intent(requireContext(), Code500Activity::class.java) + + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(requireContext(), AuthActivity::class.java) + activity?.finish() + startActivity(intetn) + } + else{ + CLLoad.visibility = View.GONE + } + + } + } + + + } else { + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() + startActivity(intetn) + } + } + + + companion object { + fun newInstance() = EditSportYesFragment() + } + + override fun onClickAppeals(item: EditSportModel) { + binding.CLLoad.visibility = View.VISIBLE + UpdateBlockSportTasksYes(id_patient, item.id) + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/Patients/Reports/Edit/UpdateSportTaskNoModel.kt b/app/src/main/java/com/example/doctor/Patients/Reports/Edit/UpdateSportTaskNoModel.kt new file mode 100644 index 0000000..40206c0 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Patients/Reports/Edit/UpdateSportTaskNoModel.kt @@ -0,0 +1,6 @@ +package com.example.doctor.Patients.Reports.Edit + +data class UpdateSportTaskNoModel( + val id: Int, +) + diff --git a/app/src/main/java/com/example/doctor/Patients/Reports/Edit/UpdateSportTaskYesModel.kt b/app/src/main/java/com/example/doctor/Patients/Reports/Edit/UpdateSportTaskYesModel.kt new file mode 100644 index 0000000..4750ab6 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Patients/Reports/Edit/UpdateSportTaskYesModel.kt @@ -0,0 +1,7 @@ +package com.example.doctor.Patients.Reports.Edit + +data class UpdateSportTaskYesModel( + val id_sport_patient: Int, + val id_sports_tasks: Int, + ) + diff --git a/app/src/main/java/com/example/doctor/Patients/Reports/PatientFragment.kt b/app/src/main/java/com/example/doctor/Patients/Reports/PatientFragment.kt new file mode 100644 index 0000000..f330175 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Patients/Reports/PatientFragment.kt @@ -0,0 +1,998 @@ +package com.example.doctor.Patients.Reports + +import android.graphics.Color +import android.os.Bundle +import android.transition.TransitionInflater +import android.util.Log +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.Menu +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.appcompat.widget.PopupMenu +import androidx.fragment.app.activityViewModels +import androidx.recyclerview.widget.GridLayoutManager +import com.example.doctor.DoctorViewModel +import com.example.doctor.Patients.PatientsListFragment +import com.example.doctor.Patients.Reports.Edit.EditSportFragment +import com.example.doctor.Pref.ConclusionPref +import com.example.doctor.R as R_D +import android.R +import android.content.Intent +import androidx.fragment.app.FragmentActivity +import com.example.doctor.Adapter.VpAdapter +import com.example.doctor.Auth.AuthActivity +import com.example.doctor.CodeError.Code429Activity +import com.example.doctor.CodeError.Code500Activity +import com.example.doctor.Enternet.EnternetActivity +import com.example.doctor.Enternet.EnternetCheck +import com.example.doctor.Patients.Model.PatientAllModel +import com.example.doctor.Patients.Reports.Courses.TabLayout.CoursesAllFragment +import com.example.doctor.Patients.Reports.Courses.TabLayout.CoursesYouFragment +import com.example.doctor.Pref.ClearPref +import com.example.doctor.Pref.SavePref +import com.example.doctor.Retrofit.DoctorApi +import com.example.doctor.Setting.Courses.EditCourses.TabLayoutEditCourses.AllSportCoursesDoctorFragment +import com.example.doctor.Setting.Courses.EditCourses.TabLayoutEditCourses.YourSportCoursesDoctorFragment +import com.example.doctor.Toast.showCustomInfoToast +import com.example.doctor.Toast.showCustomNiceToast +import com.example.doctor.databinding.FragmentPatientsBinding +import com.example.doctor.databinding.ItemQuestionnaireAfterBinding +import com.google.android.material.tabs.TabLayoutMediator +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import java.time.LocalDate + + +// Фрагмент для отображения детальной информации о пациенте, отчетов и курсов +class PatientFragment : Fragment() { + private lateinit var binding: FragmentPatientsBinding // Объект привязки для доступа к элементам UI макета фрагмента + private val modelDoctor: DoctorViewModel by activityViewModels() // ViewModel для хранения и управления данными приложения + lateinit var adapter: QBBAdapter // Адаптер для списка анкет ДО + lateinit var adapterCours: CoursesListAdapter // Адаптер для списка спортивных курсов + private lateinit var doctorApi: DoctorApi // Экземпляр Retrofit API для сетевых запросов + val prefDoctorConclusion = ConclusionPref() // Вспомогательный класс для получения данных из SharedPreferences + val prefDoctorClear = ClearPref() // Вспомогательный класс для очистки SharedPreferences + val prefDoctorSave = SavePref() // Вспомогательный класс для сохранения данных в SharedPreferences + var id: Int? = null // Идентификатор пациента + var block = "" // Статус блокировки пациента + var pause = "" // Статус паузы для пациента + var id_sport = "" // Идентификатор спортивного курса пациента + + var btnSC = 0; // Переменная для управления состоянием кнопки (не используется в видимом коде) + + var idPatient = 0; // Идентификатор текущего пациента + + //Класс проверки интеренета + val enternetCheck = EnternetCheck() // Класс для проверки наличия интернет-соединения + + // Для 10 сек обновления (переменные для хранения данных, вероятно, для периодического обновления) + var qba:List?=null // Список анкет ДО и ПОСЛЕ + var patientAll:PatientAllModel?=null // Информация о пациенте + + // Список заголовков для вкладок курсов + private val tList = listOf( + "Все", // Заголовок вкладки "Все курсы" + "Ваши", // Заголовок вкладки "Ваши курсы" + ) + + // Список фрагментов для переключения во ViewPager2 + private val flist = listOf( + CoursesAllFragment.newInstance(), // Фрагмент для отображения всех курсов + CoursesYouFragment.newInstance(), // Фрагмент для отображения курсов, созданных доктором + ) + + // Функция жизненного цикла, создаёт и возвращает view фрагмента (надувание макета) + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentPatientsBinding.inflate(layoutInflater, container, false) + return binding.root // Возвращаем корневой View фрагмента + } + + // Функция жизненного цикла, вызывается после создания view. Инициализирует интерфейс, обработчики и подписки на данные + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + initRcViewDay() // Инициализация списка анкет (RecyclerView с адаптером QBBAdapter) + init() // Инициализация вкладок курсов (ViewPager2 и TabLayout) + modelDoctor.qbaList.observe(viewLifecycleOwner) {//viewLifecycleOwner - следит за циклом жизни fragment + if(qba != it){ + qba = it + adapter.submitList(it)//Напрямую переадем созданный список в adapter(ProductAdapter) + // binding.txtNullQBA.visibility = View.GONE // Скрытие текста о отсутствии анкет, если список не пустой + } + } + + idPatient = prefDoctorConclusion.conclusionIdPatient(requireContext()) + + modelDoctor.BtnSportCoursDoctorCurrent.observe(viewLifecycleOwner){ + if(it == 1){ // Если значение 1, кнопка красная (очистить) + binding.btnClearSportPatient.setCardBackgroundColor(Color.parseColor("#D86767")) + } + else if(it == 2){ // Если значение 2, кнопка зеленая (нечего очищать) + binding.btnClearSportPatient.setCardBackgroundColor(Color.parseColor("#A9D867")) + } + else{ // По умолчанию кнопка красная + binding.btnClearSportPatient.setCardBackgroundColor(Color.parseColor("#D86767")) + } + } + + val inflater = TransitionInflater.from(requireContext()) + enterTransition = inflater.inflateTransition(R_D.transition.slide_right) + + binding.btnExit.setOnClickListener { + exitTransition = inflater.inflateTransition(R_D.transition.slide_right) + activity?.finish() // Закрытие родительской активити (PatientActivity) + } + + popMenu() // Инициализация popup-меню для выбора типа анкеты + viewPatient() // Проверка и загрузка данных пациента при необходимости + + // Получаем id пациента из ViewModel (вероятно, дублирование с получением из preferences) + modelDoctor.patientId.observe(viewLifecycleOwner){ + Log.i("adsa","as111111111111dsadasd") // Логирование id пациента + } + + // Подписка на изменение данных пациента в ViewModel + modelDoctor.patientCurrent.observe(viewLifecycleOwner) { + if(patientAll !=it){ // Если данные пациента изменились + patientAll = it // Обновляем переменную с данными пациента + binding.txtLogin.text = it.login // Обновляем логин пациента в UI + id = it.id // Обновляем id пациента + modelDoctor.id_patient.value = id // Обновляем id пациента в ViewModel + block = it.block.toString() // Получаем статус блокировки + pause = it.pause.toString() // Получаем статус паузы + id_sport = it.id_sport_patient.toString() // Получаем id спортивного курса + + // Проверка даты блокировки: если дата прошла или статус null, отображаем как не заблокированный + if(block == "null"){ + block(block) + } + else{ + if(LocalDate.parse(block) + tab.text = + tList[pos]//tab - нажатая кнопка, pos - позиция кнопки, tList[pos] - передаем название по полученной позиции + }.attach()// attach() - чтобы все переключалось, а не вывадило постоянно один экран + //Изменения цвета в зависомости на каком из tabLayout вы находитесь + binding.tabLayoutCourses.setTabTextColors( + getResources().getColor(com.example.doctor.R.color.black), // Цвет текста невыбранной вкладки + getResources().getColor(com.example.doctor.R.color.white) // Цвет текста выбранной вкладки + ); + } + + // Проверяет необходимость загрузки данных пациента по id из SharedPreferences при переходе и очищает их + private fun viewPatient() { + val viewPatient = prefDoctorConclusion.conclusionViewPatient(requireContext()) // Получаем флаг необходимости загрузки + val idPatient = 23412 // Хардкодный id пациента (возможно, заглушка) + Log.i("viewPatient1",viewPatient.toString()) // Логирование флага + Log.i("idPatient1",viewPatient.toString()) // Логирование id (ошибка, должно быть idPatient) + if(viewPatient ==1 && idPatient !=0 && idPatient !=null){ + GetPatientID(idPatient) // Загружаем данные пациента по id + prefDoctorClear.clearIdPatient(requireContext()) // Очищаем сохраненный id пациента + prefDoctorClear.clearViewPatient(requireContext()) // Очищаем флаг необходимости загрузки + Log.i("viewPatient2",viewPatient.toString()) // Логирование флага после очистки + Log.i("idPatient2",viewPatient.toString()) // Логирование id после очистки (ошибка) + } + } + + // Инициализация клиента Retrofit для выполнения сетевых запросов + private fun initRetrofit() { + val interceptor = HttpLoggingInterceptor() + interceptor.level = HttpLoggingInterceptor.Level.BODY // Уровень логирования HTTP-запросов + val client = OkHttpClient + .Builder() + .addInterceptor(interceptor) + .build() + val retrofit = Retrofit.Builder() + .baseUrl("https://rehabilitation.vmeda.org/api/") // Базовый URL API + .client(client) // Установка OkHttpClient + .addConverterFactory(GsonConverterFactory.create()) // Добавление конвертера JSON + .build() + doctorApi = retrofit.create(DoctorApi::class.java) // Создание экземпляра API + } + + // Получение данных пациента по его id с сервера + private fun GetPatientID(idPatient: Int) { + // Проверка интернет-соединения + if (enternetCheck.isOnline(requireContext())) { + initRetrofit() // Инициализация Retrofit + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) // Получение токена + // Запуск корутины для выполнения сетевого запроса в фоновом потоке + CoroutineScope(Dispatchers.IO).launch { + val listPatient = doctorApi.GetPatientID("Bearer $Tokens", idPatient) // Выполнение GET-запроса для получения данных пациента + // Переключение на главный поток для обновления UI + requireActivity().runOnUiThread { + + + //Фиксируем полученные данные + val List = listPatient.body() + val Nice = listPatient.isSuccessful + val Code = listPatient.code() + if(Code==429){ + val intetn = Intent(requireContext(), Code429Activity::class.java) + + startActivity(intetn) + } + else if(Code==200) { + //Если нету ошибок + if (Nice) { + if (List != null) { + modelDoctor.patientCurrent.value = List + } + } + } + else if (Code == 500) { + val intetn = Intent(requireContext(), Code500Activity::class.java) + + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(requireContext(), AuthActivity::class.java) + activity?.finish() + startActivity(intetn) + } + } + + } + } else { + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() + startActivity(intetn) + } + + } + + // Инициализация popup-меню для выбора типа анкеты (ДО или ПОСЛЕ) + private fun popMenu() { + // Создание меню + val popMenu = PopupMenu( + requireContext(), // Контекст + binding.btnQBA // View, к которому привязано меню + ) + + // Добавление пунктов меню + popMenu.menu.add(Menu.NONE, 0, 0, "Анкета ДО") + popMenu.menu.add(Menu.NONE, 1, 1, "Анкета ПОСЛЕ") + + // Обработчик нажатия на пункты меню + popMenu.setOnMenuItemClickListener { + val id = it.itemId // Получаем id выбранного пункта + // Ситуации при нажатие на один из пунктов + if (id == 0) { // Если выбран пункт "Анкета ДО" + binding.CVQB.visibility = View.VISIBLE // Отображаем карточку с анкетой ДО + } else if (id == 1) { // Если выбран пункт "Анкета ПОСЛЕ" + binding.CVQA.visibility = View.VISIBLE // Отображаем карточку с анкетой ПОСЛЕ + } else { // В случае ошибки + Toast.makeText(requireContext(), "Ошибка", Toast.LENGTH_SHORT).show() // Показываем сообщение об ошибке + } + + false // Возвращаем false, чтобы меню не закрывалось после выбора (при необходимости) + } + // Обработчик нажатия на кнопку открытия меню + binding.btnQBA.setOnClickListener { + // Активируем меню + popMenu.show() // Отображаем popup-меню + } + } + + + + // Функция для очистки спортивного курса пациента через API + fun ClearSportPaient(){ + // Проверка интернет-соединения + if (enternetCheck.isOnline(requireContext())) { + initRetrofit() // Инициализация Retrofit + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) // Получение токена + // Запуск корутины для выполнения сетевого запроса в фоновом потоке + CoroutineScope(Dispatchers.IO).launch { + val listcsp = doctorApi.ClearPatientSport("Bearer $Tokens",id!!) // Выполнение POST-запроса на очистку курса + // Переключение на главный поток для обновления UI и обработки ответа + requireActivity().runOnUiThread { + + + + //Фиксируем полученные данные + val List = listcsp.body() + val Nice = listcsp.isSuccessful + val Code = listcsp.code() + if(Code==429){ + val intetn = Intent(requireContext(), Code429Activity::class.java) + + startActivity(intetn) + } + else if(Code==200) { + //Если нету ошибок + if (Nice) { + if (List != null) { + GetAllCoursesSport() // Обновляем список всех курсов + GetCoursesDoctor() // Обновляем список курсов доктора + modelDoctor.BtnSportCoursDoctorCurrent.value = 1 // Устанавливаем состояние кнопки "Очистить" + + Toast(requireContext()).showCustomInfoToast(List.message, requireActivity()) // Показываем сообщение об успешной очистке + } + else{ + Toast(requireContext()).showCustomInfoToast("${List?.message}", requireActivity()) // Показываем сообщение об ошибке + } + } + } + else if (Code == 500) { + val intetn = Intent(requireContext(), Code500Activity::class.java) + + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(requireContext(), AuthActivity::class.java) + activity?.finish() + startActivity(intetn) + } + } + } + } else { + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() + startActivity(intetn) + } + + } + + // Получение списка курсов, созданных доктором, для данного пациента с сервера + fun GetCoursesDoctor() { + // Проверка интернет-соединения + if (enternetCheck.isOnline(requireContext())) { + initRetrofit() // Инициализация Retrofit + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) // Получение токена + // Запуск корутины для выполнения сетевого запроса в фоновом потоке + CoroutineScope(Dispatchers.IO).launch { + val listCoursesDoctor = doctorApi.GetCoursesDoctorPatient("Bearer $Tokens",id!!) // Выполнение GET-запроса для получения курсов доктора пациента + + // Переключение на главный поток для обновления UI и обработки ответа + requireActivity().runOnUiThread { + + + //Фиксируем полученные данные + val List = listCoursesDoctor.body() + val Nice = listCoursesDoctor.isSuccessful + val Code = listCoursesDoctor.code() + if(Code==429){ + val intetn = Intent(requireContext(), Code429Activity::class.java) + + startActivity(intetn) + } + else if(Code==200) { + //Если нету ошибок + if (Nice) { + if (List != null) { + modelDoctor.SportCoursDoctorList.value = List.courses_doctor // Обновление списка курсов доктора в ViewModel + } + } + } + else if (Code == 500) { + val intetn = Intent(requireContext(), Code500Activity::class.java) + + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(requireContext(), AuthActivity::class.java) + activity?.finish() + startActivity(intetn) + } + + } + + } + } else { + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() + startActivity(intetn) + } + + } + + // Получение списка всех спортивных курсов для данного пациента с сервера + fun GetAllCoursesSport(){ + // Проверка интернет-соединения + if (enternetCheck.isOnline(requireContext())) { + initRetrofit() // Инициализация Retrofit + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) // Получение токена + // Запуск корутины для выполнения сетевого запроса в фоновом потоке + CoroutineScope(Dispatchers.IO).launch { + val listProduct = doctorApi.GetCoursAllPatient("Bearer $Tokens",id!!) // Выполнение GET-запроса для получения всех курсов пациента + // Переключение на главный поток для обновления UI и обработки ответа + requireActivity().runOnUiThread { + + //Фиксируем полученные данные + val List = listProduct.body() + val Nice = listProduct.isSuccessful + val Code = listProduct.code() + if(Code==429){ + val intetn = Intent(requireContext(), Code429Activity::class.java) + + startActivity(intetn) + } + else if(Code==200) { + //Если нету ошибок + if (Nice) { + if (List != null) { + modelDoctor.SportCoursList.value = List.courses // Обновление списка всех курсов в ViewModel + + } + } + } + else if (Code == 500) { + val intetn = Intent(requireContext(), Code500Activity::class.java) + + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(requireContext(), AuthActivity::class.java) + activity?.finish() + startActivity(intetn) + } + } + } + } else { + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() + startActivity(intetn) + } + + } + + // Обновляет состояние кнопки "Очистить спорт пациента" в зависимости от наличия активного спортивного курса + private fun btnClearSportPatient(id_sport:String) { + if (id_sport == "null") { // Если id спортивного курса null, кнопка красная + modelDoctor.BtnSportCoursDoctorCurrent.value = 1 + } else { // Иначе, кнопка зеленая + modelDoctor.BtnSportCoursDoctorCurrent.value = 2 + } + // Обработчик нажатия на кнопку "Очистить спорт пациента" + binding.btnClearSportPatient.setOnClickListener { + + ClearSportPaient() // Вызываем функцию очистки спортивного курса + } + } + + // Получение списка анкет ДО и ПОСЛЕ для пациента с сервера + fun QBAPatientList()=with(binding){ + // Проверка интернет-соединения + if (enternetCheck.isOnline(requireContext())) { + initRetrofit() // Инициализация Retrofit + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) // Получение токена + // Запуск корутины для выполнения сетевого запроса в фоновом потоке + CoroutineScope(Dispatchers.IO).launch { + val listProduct = doctorApi.GetPatientBAQiestionar("Bearer $Tokens",id!!) // Выполнение GET-запроса для получения анкет + // Переключение на главный поток для обновления UI и обработки ответа + requireActivity().runOnUiThread { + + + //Фиксируем полученные данные + val List = listProduct.body() + val Nice = listProduct.isSuccessful + val Code = listProduct.code() + if(Code==429){ + val intetn = Intent(requireContext(), Code429Activity::class.java) + + startActivity(intetn) + } + else if(Code==200) { + //Если нету ошибок + if (Nice) { + if (List != null) { + if(List.questionnaire !=null){ + txtNullQBA.visibility = View.GONE + modelDoctor.qbaList.value = List.questionnaire + } + } + } + } + else if (Code == 500) { + val intetn = Intent(requireContext(), Code500Activity::class.java) + + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(requireContext(), AuthActivity::class.java) + activity?.finish() + startActivity(intetn) + } + } + + } + } else { + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() + startActivity(intetn) + } + + } + + + + // Обработчик нажатия на кнопку "Пауза" + private fun pause(pause:String) { + if (pause == "null") { // Если статус паузы null, карточка зеленая (не на паузе) + binding.CVPause.setCardBackgroundColor(Color.parseColor("#A9D867")) + } else { // Иначе, карточка красная (на паузе) + binding.CVPause.setCardBackgroundColor(Color.parseColor("#D86767")) + } + // Установка обработчика нажатия на кнопку паузы + binding.btnPause.setOnClickListener { + UpdatePausePatient() // Вызываем функцию обновления статуса паузы + } + } + + // Обработчик нажатия на кнопку "Блок" + private fun block(block:String) { + if (block == "null") { // Если статус блокировки null, карточка зеленая (не заблокирован) + binding.CVBlock.setCardBackgroundColor(Color.parseColor("#A9D867")) + } else { // Иначе, карточка красная (заблокирован) + binding.CVBlock.setCardBackgroundColor(Color.parseColor("#D86767")) + } + // Установка обработчика нажатия на кнопку блокировки + binding.btnBlock.setOnClickListener { + UpdateBlockPatient() // Вызываем функцию обновления статуса блокировки + } + } + + // Функция для блокировки/разблокировки аккаунта пациента через API + fun UpdateBlockPatient(){ + // Проверка интернет-соединения + if (enternetCheck.isOnline(requireContext())) { + initRetrofit() // Инициализация Retrofit + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) // Получение токена + // Запуск корутины для выполнения сетевого запроса в фоновом потоке + CoroutineScope(Dispatchers.IO).launch { + val listProduct = doctorApi.UpdateBlockAccountPatient("Bearer $Tokens", id!!) // Выполнение POST-запроса на обновление статуса блокировки + // Переключение на главный поток для обновления UI и обработки ответа + requireActivity().runOnUiThread { + + + //Фиксируем полученные данные + val List = listProduct.body() + val Nice = listProduct.isSuccessful + val Code = listProduct.code() + if(Code==429){ + val intetn = Intent(requireContext(), Code429Activity::class.java) + + startActivity(intetn) + } + else if(Code==200) { + //Если нету ошибок + if (Nice) { + if (List != null) { + if (block == "null") { // Если был не заблокирован, делаем заблокированным + block = "true" + binding.CVBlock.setCardBackgroundColor(Color.parseColor("#D86767")) // Меняем цвет карточки на красный + Toast(requireContext()).showCustomInfoToast("Блок установлен", requireActivity()) // Показываем сообщение + } else { // Если был заблокирован, снимаем блокировку + block = "null" + binding.CVBlock.setCardBackgroundColor(Color.parseColor("#A9D867")) // Меняем цвет карточки на зеленый + Toast(requireContext()).showCustomInfoToast("Блок убран", requireActivity()) // Показываем сообщение + } + } + } + } + else if (Code == 500) { + val intetn = Intent(requireContext(), Code500Activity::class.java) + + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(requireContext(), AuthActivity::class.java) + activity?.finish() // Закрываем текущую активити + startActivity(intetn) + } + } + + } + } else { + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() // Закрываем текущую активити + startActivity(intetn) + } + + } + + // Функция для установки/снятия паузы для пациента через API + fun UpdatePausePatient(){ + // Проверка интернет-соединения + if (enternetCheck.isOnline(requireContext())) { + initRetrofit() // Инициализация Retrofit + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) // Получение токена + // Запуск корутины для выполнения сетевого запроса в фоновом потоке + CoroutineScope(Dispatchers.IO).launch { + val listProduct = doctorApi.UpdatePauseAccountPatient("Bearer $Tokens", id!!) // Выполнение POST-запроса на обновление статуса паузы + // Переключение на главный поток для обновления UI и обработки ответа + requireActivity().runOnUiThread { + + + //Фиксируем полученные данные + val List = listProduct.body() + val Nice = listProduct.isSuccessful + val Code = listProduct.code() + if(Code==429){ + val intetn = Intent(requireContext(), Code429Activity::class.java) + + startActivity(intetn) + } + else if(Code==200) { + //Если нету ошибок + if (Nice) { + if (List != null) { + if (pause == "null") { // Если не на паузе, ставим на паузу + pause = "true" + binding.CVPause.setCardBackgroundColor(Color.parseColor("#D86767")) // Меняем цвет карточки на красный + Toast(requireContext()).showCustomInfoToast("Пауза установлен", requireActivity()) // Показываем сообщение + } else { // Если на паузе, снимаем паузу + pause = "null" + binding.CVPause.setCardBackgroundColor(Color.parseColor("#A9D867")) // Меняем цвет карточки на зеленый + Toast(requireContext()).showCustomInfoToast("Пауза убран", requireActivity()) // Показываем сообщение + } + } + } + } + else if (Code == 500) { + val intetn = Intent(requireContext(), Code500Activity::class.java) + + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(requireContext(), AuthActivity::class.java) + activity?.finish() // Закрываем текущую активити + startActivity(intetn) + } + } + } + } else { + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() // Закрываем текущую активити + startActivity(intetn) + } + + } + + + + // Функция для обновления логина пациента через API + fun UpdatePatientLogin(id:Int,login:String){ + // Проверка интернет-соединения + if (enternetCheck.isOnline(requireContext())) { + initRetrofit() // Инициализация Retrofit + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) // Получение токена + // Запуск корутины для выполнения сетевого запроса в фоновом потоке + CoroutineScope(Dispatchers.IO).launch { + val updateLogin = doctorApi.UpdatePatientLogin("Bearer $Tokens",id,login) // Выполнение POST-запроса на обновление логина + // Переключение на главный поток для обновления UI и обработки ответа + requireActivity().runOnUiThread { + + + + //Фиксируем полученные данные + val List = updateLogin.body() + val Nice = updateLogin.isSuccessful + val Code = updateLogin.code() + if(Code==429){ + val intetn = Intent(requireContext(), Code429Activity::class.java) + + startActivity(intetn) + } + else if(Code==200) { + //Если нету ошибок + if (Nice) { + if (List != null) { + Toast(requireContext()).showCustomInfoToast(List.message, requireActivity()) // Показываем сообщение об успешном обновлении + } + else{ + if (List != null) { // Показываем сообщение об ошибке, если есть + Toast(requireContext()).showCustomInfoToast(List.message, requireActivity()) + } + } + } + } + else if (Code == 500) { + val intetn = Intent(requireContext(), Code500Activity::class.java) + + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(requireContext(), AuthActivity::class.java) + activity?.finish() // Закрываем текущую активити + startActivity(intetn) + } + } + } + } else { + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() // Закрываем текущую активити + startActivity(intetn) + } + + } + + // Функция для обновления пароля пациента через API + fun UpdatePatientPassword(id:Int,password:String){ + // Проверка интернет-соединения + if (enternetCheck.isOnline(requireContext())) { + initRetrofit() // Инициализация Retrofit + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) // Получение токена + // Запуск корутины для выполнения сетевого запроса в фоновом потоке + CoroutineScope(Dispatchers.IO).launch { + val updatePassword= doctorApi.UpdatePatientPassword("Bearer $Tokens",id,password) // Выполнение POST-запроса на обновление пароля + // Переключение на главный поток для обновления UI и обработки ответа + requireActivity().runOnUiThread { +//Фиксируем полученные данные + val List = updatePassword.body() + val Nice = updatePassword.isSuccessful + val Code = updatePassword.code() + if(Code==429){ + val intetn = Intent(requireContext(), Code429Activity::class.java) + + startActivity(intetn) + } + else if(Code==200) { + //Если нету ошибок + if (Nice) { + if (List != null) { + Toast(requireContext()).showCustomInfoToast(List.message, requireActivity()) // Показываем сообщение об успешном обновлении + } + else{ + if (List != null) { + Toast(requireContext()).showCustomInfoToast(List.message, requireActivity()) + } + } + } + } + else if (Code == 500) { + val intetn = Intent(requireContext(), Code500Activity::class.java) + + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(requireContext(), AuthActivity::class.java) + activity?.finish() // Закрываем текущую активити + startActivity(intetn) + } + + } + } + } else { + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() // Закрываем текущую активити + startActivity(intetn) + } + + } + + + + + + + + + + + private fun sport() { + + } + + // Инициализация обработчиков нажатий для карточек отчета, спорта и редактирования данных/спорта + private fun report() = with(binding) { + + + + // Обработчик нажатия на кнопку выхода: возвращает на экран списка пациентов + btnExitPatientFragment.setOnClickListener { + activity?.supportFragmentManager?.beginTransaction() + ?.replace(com.example.doctor.R.id.CLMainListPatient, PatientsListFragment.newInstance()) // Заменяем текущий фрагмент на PatientsListFragment + ?.commit() // Применяем изменения + //this@PatientFragment.onDestroy() // Закомментированный вызов onDestroy + } + + // Обработчик нажатия на кнопку "Отчет": отображает карточку отчетов и загружает анкеты + btnReport.setOnClickListener { + CVReport.visibility = View.VISIBLE // Отображаем карточку отчетов + QBAPatientList() // Загружаем анкеты ДО и ПОСЛЕ + } + + // Обработчик нажатия на кнопку "Спорт": отображает карточку спортивных курсов + btnSport.setOnClickListener { + binding.CVSport.visibility = View.VISIBLE // Отображаем карточку спортивных курсов + } + + // Обработчик нажатия на кнопку закрытия карточки отчетов + btnCloseReport.setOnClickListener { + CVReport.visibility = View.GONE // Скрываем карточку отчетов + initRcViewDay() // Повторная инициализация списка анкет (возможно, для сброса) + } + + // Обработчик нажатия на кнопку закрытия карточки редактирования спорта + btnClouseEditSport.setOnClickListener { + CVEditSport.visibility = View.GONE // Скрываем карточку редактирования спорта + initRcViewDay() // Повторная инициализация списка анкет (возможно, для сброса) + } + // Обработчик нажатия на саму карточку редактирования спорта для закрытия (вероятно, фон) + CVEditSport.setOnClickListener { + CVEditSport.visibility = View.GONE // Скрываем карточку редактирования спорта + initRcViewDay() // Повторная инициализация списка анкет (возможно, для сброса) + } + + // Обработчик нажатия на саму карточку редактирования данных пациента для закрытия (вероятно, фон) + CVEditPatient.setOnClickListener{ + CVEditPatient.visibility = View.GONE // Скрываем карточку редактирования данных пациента + initRcViewDay() // Повторная инициализация списка анкет (возможно, для сброса) + } + // Обработчик нажатия на кнопку закрытия карточки редактирования данных пациента + btnClouseEditPatient.setOnClickListener{ + CVEditPatient.visibility = View.GONE // Скрываем карточку редактирования данных пациента + initRcViewDay() // Повторная инициализация списка анкет (возможно, для сброса) + } + + + // Обработчик нажатия на кнопку закрытия карточки спортивных курсов + btnCloseReport2.setOnClickListener { + CVSport.visibility = View.GONE // Скрываем карточку спортивных курсов + } + + + // Обработчик нажатия на кнопку закрытия карточки "Анкета ДО" + btnCloseBefore.setOnClickListener { + CVQB.visibility = View.GONE // Скрываем карточку "Анкета ДО" + } + // Обработчик нажатия на кнопку закрытия карточки "Анкета ПОСЛЕ" + btnCloseAfter.setOnClickListener { + CVQA.visibility = View.GONE // Скрываем карточку "Анкета ПОСЛЕ" + } + + + // Обработчик нажатия на саму карточку отчетов для закрытия (вероятно, фон) + CVReport.setOnClickListener { + CVReport.visibility = View.GONE // Скрываем карточку отчетов + initRcViewDay() // Повторная инициализация списка анкет (возможно, для сброса) + } + // Обработчик нажатия на саму карточку спортивных курсов для закрытия (вероятно, фон) + CVSport.setOnClickListener { + CVSport.visibility = View.GONE // Скрываем карточку спортивных курсов + } + + // Обработчик нажатия на карточку списка отчетов (пустой) + CVReportList.setOnClickListener { + } + + // Обработчик нажатия на кнопку "Редактировать данные пациента": отображает карточку редактирования + btnEditPatient.setOnClickListener { + CVEditPatient.visibility = View.VISIBLE // Отображаем карточку редактирования данных пациента + } + + // Переход на страницу редактирования спортивных занятий (фрагмент EditSportFragment) + binding.btnEditSport.setOnClickListener{ + binding.CVEditSport.visibility = View.VISIBLE // Отображаем карточку редактирования спортивных занятий + // Вывод фрагмента редактирования спорта в контейнер CLEditSport + activity?.supportFragmentManager?.beginTransaction() + ?.replace(com.example.doctor.R.id.CLEditSport, EditSportFragment.newInstance()) // Заменяем текущий фрагмент на EditSportFragment + ?.commit() // Применяем изменения + } + + + // Обработчик нажатия на кнопку для обновления логина пациента + btnUpdateLogin.setOnClickListener{ + val login = edLogin.text.toString() // Получаем введенный логин + if(login.count() != 0){ // Проверяем, что поле логина не пустое + if(login.count() > 3){ // Проверяем минимальную длину логина + UpdatePatientLogin(id!!,login) // Вызываем функцию обновления логина + } + else{ + Toast(requireContext()).showCustomInfoToast("Логин слишком короткий", requireActivity()) // Показываем сообщение об ошибке (короткий логин) + } + } + else{ + Toast(requireContext()).showCustomInfoToast("Поле пустое", requireActivity()) // Показываем сообщение об ошибке (поле пустое) + } + } + + // Обработчик нажатия на кнопку для обновления пароля пациента + btnUpdatePassword.setOnClickListener{ + val password = edPassword.text.toString() // Получаем введенный пароль + if(password.count() != 0){ // Проверяем, что поле пароля не пустое + if(password.count() > 10){ // Проверяем минимальную длину пароля + UpdatePatientPassword(id!!,password) // Вызываем функцию обновления пароля + } + else{ + Toast(requireContext()).showCustomInfoToast("Пароль слишком короткий", requireActivity()) // Показываем сообщение об ошибке (короткий пароль) + } + } + else{ + Toast(requireContext()).showCustomInfoToast("Поле пустое", requireActivity()) // Показываем сообщение об ошибке (поле пустое) + } + } + } + + // Инициализация списка анкет ДО (RecyclerView с адаптером QBBAdapter) + private fun initRcViewDay() = with(binding) { + RCView.layoutManager = GridLayoutManager(requireContext(), 1)//По вертикали будет выводить по умолчанию // Устанавливаем GridLayoutManager с 1 столбцом (вертикальный список) + adapter = QBBAdapter() // Создаем экземпляр адаптера QBBAdapter + RCView.adapter = adapter // Устанавливаем адаптер для RecyclerView + } + + + // Функция для скрытия карточки отчета и очистки логина (вызывается при закрытии или уничтожении фрагмента) + fun closeViewCard() { + binding.txtLogin.text = "" // Очищаем текст логина + binding.CVReport.visibility = View.GONE // Скрываем карточку отчетов + initRcViewDay() // Повторная инициализация списка анкет (возможно, для сброса) + } + + + // Функция жизненного цикла фрагмента: вызывается перед уничтожением фрагмента + override fun onDestroy() { + super.onDestroy() // Вызываем метод родительского класса + closeViewCard() // Вызываем функцию скрытия карточки и очистки данных + } + + companion object { + // Фабричный метод для создания экземпляра PatientFragment + fun newInstance() = PatientFragment() // Создаем и возвращаем новый экземпляр фрагмента + } + + + + + + + + +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/Patients/Reports/QAAdapter.kt b/app/src/main/java/com/example/doctor/Patients/Reports/QAAdapter.kt new file mode 100644 index 0000000..7f383af --- /dev/null +++ b/app/src/main/java/com/example/doctor/Patients/Reports/QAAdapter.kt @@ -0,0 +1,66 @@ +package com.example.doctor.Patients.Reports + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import com.example.doctor.R +import com.example.doctor.databinding.ItemQuestionnaireAfterBinding + +// Адаптер для отображения анкет "После" (QA) в RecyclerView +class QAAdapter() : ListAdapter(Comparator()) { + + // ViewHolder для элемента анкеты "После" + class Holder(view: View) : + RecyclerView.ViewHolder(view) { + + val binding = ItemQuestionnaireAfterBinding.bind(view) // Объект привязки для доступа к элементам UI элемента списка + + // Привязка данных к элементу анкеты (здесь можно реализовать отображение ответов) + @SuppressLint("SuspiciousIndentation") // Подавление предупреждения о подозрительном отступе + fun bind(item: QAModel) = with(binding) { + // Здесь можно реализовать логику отображения ответов на вопросы анкеты + // Пример: textViewQuestion.text = item.question + // Пример: textViewAnswer.text = item.answer + } + } + + // Создание ViewHolder для элемента списка. Вызывается RecyclerView при необходимости создания нового ViewHolder. + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder { + val view = LayoutInflater.from(parent.context) // Получаем LayoutInflater из контекста родительского ViewGroup + .inflate(R.layout.item_questionnaire_after, parent, false) // "Надуваем" макет элемента списка + return Holder(view) // Возвращаем новый экземпляр ViewHolder + } + + // Привязка данных к ViewHolder. Вызывается RecyclerView для отображения данных в определенной позиции. + override fun onBindViewHolder(holder: Holder, position: Int) { + val view = holder.bind(getItem(position)) // Получаем объект данных для текущей позиции и привязываем его к ViewHolder + } + + // Comparator - сравнивает старый и новый списки, чтобы обновлять только изменённые элементы. Используется DiffUtil для эффективного обновления списка. + class Comparator : DiffUtil.ItemCallback() { + // Проверяет, представляют ли два объекта один и тот же элемент (сравнивает уникальные идентификаторы) + override fun areItemsTheSame( + oldItem: QAModel, + newItem: QAModel + ): Boolean { + return oldItem.id == newItem.id // Сравниваем по id анкеты + } + + // Проверяет, совпадает ли содержимое двух элементов (после того, как areItemsTheSame вернул true) + override fun areContentsTheSame( + oldItem: QAModel, + newItem: QAModel + ): Boolean { + return oldItem == newItem // Сравниваем объекты целиком (если data class, сравнивает все свойства) + } + } + +} + + + + diff --git a/app/src/main/java/com/example/doctor/Patients/Reports/QAModel.kt b/app/src/main/java/com/example/doctor/Patients/Reports/QAModel.kt new file mode 100644 index 0000000..09295be --- /dev/null +++ b/app/src/main/java/com/example/doctor/Patients/Reports/QAModel.kt @@ -0,0 +1,21 @@ +package com.example.doctor.Patients.Reports + +// Модель данных для анкеты "После" (Questionnaire After - QA) +// Этот класс представляет структуру данных для хранения ответов пациента на вопросы анкеты после прохождения курса/лечения. +data class QAModel( + val id: Int, // Уникальный идентификатор анкеты + val status: String, // Статус анкеты (например, "заполнена", "не заполнена") + val one: Int, // Ответ на первый вопрос анкеты (например, оценка по шкале) + val two: Int, // Ответ на второй вопрос анкеты + val three: Int, // Ответ на третий вопрос анкеты + val four: Int, // Ответ на четвертый вопрос анкеты + val five: Int, // Ответ на пятый вопрос анкеты + val six: Int, // Ответ на шестой вопрос анкеты + val seven: Int, // Ответ на седьмой вопрос анкеты + val eight: Int, // Ответ на восьмой вопрос анкеты + val nine: Int, // Ответ на девятый вопрос анкеты + val ten: Int, // Ответ на десятый вопрос анкеты + val eleven: Int, // Ответ на одиннадцатый вопрос анкеты + val twelve: Int, // Ответ на двенадцатый вопрос анкеты + ) + diff --git a/app/src/main/java/com/example/doctor/Patients/Reports/QBAListModel.kt b/app/src/main/java/com/example/doctor/Patients/Reports/QBAListModel.kt new file mode 100644 index 0000000..dba227f --- /dev/null +++ b/app/src/main/java/com/example/doctor/Patients/Reports/QBAListModel.kt @@ -0,0 +1,8 @@ +package com.example.doctor.Patients.Reports + +// Модель данных для списка анкет ДО и ПОСЛЕ (QBA - Questionnaire Before and After) +// Этот класс используется для представления списка объектов QBAModel, вероятно, получаемых из API. +data class QBAListModel( + val questionnaire: List // Список объектов QBAModel, содержащих данные анкет +) + diff --git a/app/src/main/java/com/example/doctor/Patients/Reports/QBAModel.kt b/app/src/main/java/com/example/doctor/Patients/Reports/QBAModel.kt new file mode 100644 index 0000000..d5ce51d --- /dev/null +++ b/app/src/main/java/com/example/doctor/Patients/Reports/QBAModel.kt @@ -0,0 +1,37 @@ +package com.example.doctor.Patients.Reports + +// Модель данных для одной комбинированной анкеты ДО и ПОСЛЕ (QBA - Questionnaire Before and After Model) +// Этот класс объединяет данные из анкет ДО и ПОСЛЕ для конкретной записи/даты. +data class QBAModel( + val id: Int, // Уникальный идентификатор комбинированной записи анкет + + val date: String, // Дата заполнения анкет + + val idb: Int, // Идентификатор анкеты ДО + //val statusb: Int, // Закомментированное поле: статус анкеты ДО (возможно, не используется или статус определяется по наличию idb) + val oneb: Int, // Ответ на первый вопрос анкеты ДО + val twob: Int, // Ответ на второй вопрос анкеты ДО + val threeb: Int, // Ответ на третий вопрос анкеты ДО + val fourb: Int, // Ответ на четвертый вопрос анкеты ДО + val fiveb: Int, // Ответ на пятый вопрос анкеты ДО + val sixb: Int, // Ответ на шестой вопрос анкеты ДО + val sevenb: Int, // Ответ на седьмой вопрос анкеты ДО + val eightb: Int, // Ответ на восьмой вопрос анкеты ДО + val nineb: Int, // Ответ на девятый вопрос анкеты ДО + + val statusa: Int, // Статус анкеты ПОСЛЕ (например, заполнена/не заполнена) + val ida: Int, // Идентификатор анкеты ПОСЛЕ + val onea: Int, // Ответ на первый вопрос анкеты ПОСЛЕ + val twoa: Int, // Ответ на второй вопрос анкеты ПОСЛЕ + val threea: Int, // Ответ на третий вопрос анкеты ПОСЛЕ + val foura: Int, // Ответ на четвертый вопрос анкеты ПОСЛЕ + val fivea: Int, // Ответ на пятый вопрос анкеты ПОСЛЕ + val sixa: Int, // Ответ на шестой вопрос анкеты ПОСЛЕ + val sevena: Int, // Ответ на седьмой вопрос анкеты ПОСЛЕ + val eighta: Int, // Ответ на восьмой вопрос анкеты ПОСЛЕ + val ninea: Int, // Ответ на девятый вопрос анкеты ПОСЛЕ + val tena: Int, // Ответ на десятый вопрос анкеты ПОСЛЕ + val elevena: Int, // Ответ на одиннадцатый вопрос анкеты ПОСЛЕ + val twelvea: Int, // Ответ на двенадцатый вопрос анкеты ПОСЛЕ + ) + diff --git a/app/src/main/java/com/example/doctor/Patients/Reports/QBAdapter.kt b/app/src/main/java/com/example/doctor/Patients/Reports/QBAdapter.kt new file mode 100644 index 0000000..c2d34ec --- /dev/null +++ b/app/src/main/java/com/example/doctor/Patients/Reports/QBAdapter.kt @@ -0,0 +1,62 @@ +package com.example.doctor.Patients.Reports + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import com.example.doctor.R +import com.example.doctor.databinding.ItemQuestionnaireBeforeBinding + +// Адаптер для отображения анкет "До" (QB - Questionnaire Before) в RecyclerView +// Этот адаптер используется для привязки данных из списка QBModel к элементам списка в пользовательском интерфейсе. +class QBAdapter() : ListAdapter(Comparator()) { + // ViewHolder для элемента анкеты "До". Предоставляет доступ к View элемента списка. + class Holder(view: View) : + RecyclerView.ViewHolder(view) { + + // Объект привязки для доступа к элементам UI макета item_questionnaire_before.xml + val binding = ItemQuestionnaireBeforeBinding.bind(view) + + // Привязка данных из объекта QBModel к элементам UI ViewHolder + @SuppressLint("SuspiciousIndentation") // Подавление предупреждения о подозрительном отступе + fun bind(item: QBModel) = with(binding) { + // Здесь можно реализовать логику отображения ответов на вопросы анкеты "До". + // Например, присваивание текста TextView из свойств item. + // Пример: textViewQuestion1.text = item.questionOne + // Пример: textViewAnswer1.text = item.answerOne + } + } + + // Создание ViewHolder для элемента списка. Вызывается RecyclerView, когда нужно создать новый ViewHolder. + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder { + // Создание View из макета item_questionnaire_before.xml + val view = LayoutInflater.from(parent.context).inflate(R.layout.item_questionnaire_before, parent, false) + return Holder(view) // Возвращаем созданный ViewHolder + } + + // Привязка данных к ViewHolder. Вызывается RecyclerView для отображения данных в указанной позиции. + override fun onBindViewHolder(holder: Holder, position: Int) { + // Получение объекта QBModel по позиции и привязка его к ViewHolder + holder.bind(getItem(position)) + } + + // Comparator для сравнения элементов списка. Используется DiffUtil для определения различий между списками и эффективного обновления. + class Comparator : DiffUtil.ItemCallback() { + // Проверяет, представляют ли два элемента один и тот же объект (сравнивает уникальные идентификаторы). + override fun areItemsTheSame(oldItem: QBModel, newItem: QBModel): Boolean { + return oldItem.id == newItem.id // Сравниваем по id анкеты + } + + // Проверяет, совпадает ли содержимое двух элементов (вызывается, если areItemsTheSame вернул true). + override fun areContentsTheSame(oldItem: QBModel, newItem: QBModel): Boolean { + return oldItem == newItem // Сравниваем объекты целиком (для data class сравнивает все свойства) + } + } +} + + + + diff --git a/app/src/main/java/com/example/doctor/Patients/Reports/QBBAdapter.kt b/app/src/main/java/com/example/doctor/Patients/Reports/QBBAdapter.kt new file mode 100644 index 0000000..dc99dbc --- /dev/null +++ b/app/src/main/java/com/example/doctor/Patients/Reports/QBBAdapter.kt @@ -0,0 +1,293 @@ +package com.example.doctor.Patients.Reports + +import android.annotation.SuppressLint +import android.graphics.Color +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import com.example.doctor.R +import com.example.doctor.databinding.ItemQbaBinding + +// Адаптер для отображения результатов анкетирования (QBA) в RecyclerView +class QBBAdapter() : ListAdapter(Comparator()) { + // ViewHolder для элемента результата анкетирования + class Holder(view: View) : + RecyclerView.ViewHolder(view) { + + val binding = ItemQbaBinding.bind(view)//view - здесь храянтся элементы и мы их bind заполним в ListItemBinding//ListItemBinding - это клласс даннйо разметки(карточки) которую мы будем заполнять, а нужна дання переменная(binding), чтобы мжно было при заполнение обращатся к элементам карточки + + // Привязка данных к элементу результата анкетирования, отображение статусов и цветов + @SuppressLint("SuspiciousIndentation") + fun bind(item: QBAModel) = with(binding) { + // Здесь реализована логика отображения статусов, баллов и цветов для каждой анкеты + // (см. подробную реализацию в теле функции) +// if (item.one == 1) { +// txtB1.text = "Да" +// } else { +// txtB1.text = "Нет" +// } + + txtDate.text = item.date + if(item.oneb >= 6){ + txtStatusB.text = "Плохо" + CVB.setCardBackgroundColor(Color.parseColor("#DA8383")) + } + + else if(item.fourb == 1 || item.fiveb == 1 ||item.sixb == 1 ||item.sevenb == 1 ||item.nineb == 1){ + txtStatusB.text = "Средне" + CVB.setCardBackgroundColor(Color.parseColor("#DABA83")) + } + else{ + txtStatusB.text = "Хорошо" + CVB.setCardBackgroundColor(Color.parseColor("#ABDA83")) + } + + + txtB1.text = item.oneb.toString() + if(item.oneb==0){ + CLQB1.setBackgroundColor(Color.parseColor("#C686DF8F")) + } + else if(item.oneb in 1..5){ + CLQB1.setBackgroundColor(Color.parseColor("#C6DFD386")) + } + else{ + CLQB1.setBackgroundColor(Color.parseColor("#C6DF8686")) + } + + //Красный - "#C6DF8686" + //Зеленый - "#C686DF8F" + //Желтый - "#C6DFD386" + if (item.twob == 1) { + txtB2.text = "Да" + CLQB2.setBackgroundColor(Color.parseColor("#C6DF8686")) + } else { + txtB2.text = "Нет" + CLQB2.setBackgroundColor(Color.parseColor("#C686DF8F")) + } + + if (item.threeb == 1) { + txtB3.text = "Да" + CLQB3.setBackgroundColor(Color.parseColor("#C6DF8686")) + } else { + txtB3.text = "Нет" + CLQB3.setBackgroundColor(Color.parseColor("#C686DF8F")) + } + + if (item.fourb == 1) { + txtB4.text = "Да" + CLQB4.setBackgroundColor(Color.parseColor("#C6DF8686")) + } else { + txtB4.text = "Нет" + CLQB4.setBackgroundColor(Color.parseColor("#C686DF8F")) + } + + if (item.fiveb == 1) { + txtB5.text = "Да" + CLQB5.setBackgroundColor(Color.parseColor("#C6DF8686")) + } else { + txtB5.text = "Нет" + CLQB5.setBackgroundColor(Color.parseColor("#C686DF8F")) + } + + if (item.sixb == 1) { + txtB6.text = "Да" + CLQB6.setBackgroundColor(Color.parseColor("#C6DF8686")) + } else { + txtB6.text = "Нет" + CLQB6.setBackgroundColor(Color.parseColor("#C686DF8F")) + } + + if (item.sevenb == 1) { + txtB7.text = "Да" + CLQB7.setBackgroundColor(Color.parseColor("#C6DF8686")) + } else { + txtB7.text = "Нет" + CLQB7.setBackgroundColor(Color.parseColor("#C686DF8F")) + } + + if (item.eightb == 1) { + txtB8.text = "Да" + CLQB8.setBackgroundColor(Color.parseColor("#C686DF8F")) + } else { + txtB8.text = "Нет" + CLQB8.setBackgroundColor(Color.parseColor("#C6DF8686")) + } + + if (item.nineb == 1) { + txtB9.text = "Да" + CLQB9.setBackgroundColor(Color.parseColor("#C6DF8686")) + } else { + txtB9.text = "Нет" + CLQB9.setBackgroundColor(Color.parseColor("#C686DF8F")) + } + + //Если анкета после была заполнена + if(item.statusa == 1){ + if(item.ninea == 1){ + txtStatusA.text = "Плохо" + CVA.setCardBackgroundColor(Color.parseColor("#DA8383")) + } + else if(item.onea >= 6 || item.threea == 1 ||item.sixa == 1 ||item.eighta == 1 ||item.elevena == 1){ + txtStatusA.text = "Средне" + CVA.setCardBackgroundColor(Color.parseColor("#DABA83")) + } + else{ + txtStatusA.text= "Хорошо" + CVA.setCardBackgroundColor(Color.parseColor("#ABDA83")) + } + + txtA1.text = item.onea.toString() + if(item.onea==0){ + CLQA1.setBackgroundColor(Color.parseColor("#C686DF8F")) + } + else if(item.onea in 1..5){ + CLQA1.setBackgroundColor(Color.parseColor("#C6DFD386")) + } + else{ + CLQA1.setBackgroundColor(Color.parseColor("#C6DF8686")) + } + + if (item.twoa == 1) { + txtA2.text = "Да" + CLQA2.setBackgroundColor(Color.parseColor("#C6DF8686")) + } else { + txtA2.text = "Нет" + CLQA2.setBackgroundColor(Color.parseColor("#C686DF8F")) + } + + if (item.threea == 1) { + txtA3.text = "Да" + CLQA3.setBackgroundColor(Color.parseColor("#C6DF8686")) + } else { + txtA3.text = "Нет" + CLQA3.setBackgroundColor(Color.parseColor("#C686DF8F")) + } + + if (item.foura == 1) { + txtA4.text = "Да" + CLQA4.setBackgroundColor(Color.parseColor("#C6DF8686")) + } else { + txtA4.text = "Нет" + CLQA4.setBackgroundColor(Color.parseColor("#C686DF8F")) + } + + if (item.fivea == 1) { + txtA5.text = "Да" + CLQA5.setBackgroundColor(Color.parseColor("#C6DF8686")) + } else { + txtA5.text = "Нет" + CLQA5.setBackgroundColor(Color.parseColor("#C686DF8F")) + } + + if (item.sixa == 1) { + txtA6.text = "Да" + CLQA6.setBackgroundColor(Color.parseColor("#C6DF8686")) + + } else { + txtA6.text = "Нет" + CLQA6.setBackgroundColor(Color.parseColor("#C686DF8F")) + } + + if (item.sevena == 1) { + txtA7.text = "Да" + CLQA7.setBackgroundColor(Color.parseColor("#C6DF8686")) + } else { + txtA7.text = "Нет" + CLQA7.setBackgroundColor(Color.parseColor("#C686DF8F")) + } + + if (item.eighta == 1) { + txtA8.text = "Да" + CLQA8.setBackgroundColor(Color.parseColor("#C6DF8686")) + } else { + txtA8.text = "Нет" + CLQA8.setBackgroundColor(Color.parseColor("#C686DF8F")) + } + + if (item.ninea == 1) { + txtA9.text = "Да" + CLQA9.setBackgroundColor(Color.parseColor("#C6DF8686")) + + } else { + txtA9.text = "Нет" + CLQA9.setBackgroundColor(Color.parseColor("#C686DF8F")) + } + + if (item.tena == 1) { + txtA10.text = "Да" + CLQA10.setBackgroundColor(Color.parseColor("#C6DF8686")) + } else { + txtA10.text = "Нет" + CLQA10.setBackgroundColor(Color.parseColor("#C686DF8F")) + } + + if (item.elevena == 1) { + txtA11.text = "Да" + CLQA11.setBackgroundColor(Color.parseColor("#C6DF8686")) + } else { + txtA11.text = "Нет" + CLQA11.setBackgroundColor(Color.parseColor("#C686DF8F")) + } + + if (item.twelvea == 1) { + txtA12.text = "Да" + CLQA12.setBackgroundColor(Color.parseColor("#C686DF8F")) + } else { + txtA12.text = "Нет" + CLQA12.setBackgroundColor(Color.parseColor("#C6DF8686")) + } + } + else{ + //Если анкета ПОСЛЕ небыла заполнена + txtStatusA.text = "ПУСТО" + txtStatusA.setTextColor(Color.parseColor("#FFFFFF")) + CVA.setCardBackgroundColor(Color.parseColor("#000000")) + CVColorAfter.setCardBackgroundColor(Color.parseColor("#FF8E8E8E")) + + txtA1.text = "" + txtA2.text = "" + txtA3.text = "" + txtA4.text = "" + txtA5.text = "" + txtA6.text = "" + txtA7.text = "" + txtA8.text = "" + txtA9.text = "" + txtA10.text = "" + txtA11.text = "" + txtA12.text = "" + } + + + } + } + + // Создание ViewHolder для элемента списка + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder { + val view = LayoutInflater.from(parent.context).inflate(R.layout.item_qba, parent, false) + return Holder(view) + } + + // Привязка данных к ViewHolder + override fun onBindViewHolder(holder: Holder, position: Int) { + val view = holder.bind(getItem(position)) + } + + // Comparator - сравнивает старый и новый списки, чтобы обновлять только изменённые элементы + class Comparator : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: QBAModel, newItem: QBAModel): Boolean { + return oldItem.id == newItem.id + } + + override fun areContentsTheSame(oldItem: QBAModel, newItem: QBAModel): Boolean { + return oldItem == newItem + } + } +} + + + + diff --git a/app/src/main/java/com/example/doctor/Patients/Reports/QBModel.kt b/app/src/main/java/com/example/doctor/Patients/Reports/QBModel.kt new file mode 100644 index 0000000..224924c --- /dev/null +++ b/app/src/main/java/com/example/doctor/Patients/Reports/QBModel.kt @@ -0,0 +1,16 @@ +package com.example.doctor.Patients.Reports + +data class QBModel( + val id: Int, + val status: String, + val one: Int, + val two: Int, + val three: Int, + val four: Int, + val five: Int, + val six: Int, + val seven: Int, + val eight: Int, + val nine: Int, + ) + diff --git a/app/src/main/java/com/example/doctor/Patients/Reports/Requests/ClearSportPatientModel.kt b/app/src/main/java/com/example/doctor/Patients/Reports/Requests/ClearSportPatientModel.kt new file mode 100644 index 0000000..535047b --- /dev/null +++ b/app/src/main/java/com/example/doctor/Patients/Reports/Requests/ClearSportPatientModel.kt @@ -0,0 +1,6 @@ +package com.example.doctor.Patients.Reports.Requests + +data class ClearSportPatientModel( + val message: String, + ) + diff --git a/app/src/main/java/com/example/doctor/Patients/TabLayoutPatient/ActiveCoursesPatientFragment.kt b/app/src/main/java/com/example/doctor/Patients/TabLayoutPatient/ActiveCoursesPatientFragment.kt new file mode 100644 index 0000000..436742f --- /dev/null +++ b/app/src/main/java/com/example/doctor/Patients/TabLayoutPatient/ActiveCoursesPatientFragment.kt @@ -0,0 +1,173 @@ +package com.example.doctor.Patients.TabLayoutPatient + +import android.content.Intent +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ArrayAdapter +import androidx.fragment.app.activityViewModels +import androidx.recyclerview.widget.GridLayoutManager +import com.example.doctor.Auth.AuthActivity +import com.example.doctor.CodeError.Code429Activity +import com.example.doctor.CodeError.Code500Activity +import com.example.doctor.DataModel +import com.example.doctor.DoctorViewModel +import com.example.doctor.Enternet.EnternetActivity +import com.example.doctor.Enternet.EnternetCheck +import com.example.doctor.Patients.Adapter.PatientListAdapter +import com.example.doctor.Patients.Model.PatientAllModel +import com.example.doctor.Patients.Model.PatientIdModel +import com.example.doctor.Patients.PatientActivity +import com.example.doctor.Patients.Reports.PatientFragment +import com.example.doctor.Pref.ClearPref +import com.example.doctor.Pref.ConclusionPref +import com.example.doctor.Pref.SavePref +import com.example.doctor.R +import com.example.doctor.Retrofit.DoctorApi +import com.example.doctor.databinding.FragmentActiveCoursesPatientBinding +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory + + +class ActiveCoursesPatientFragment : Fragment(),PatientListAdapter.Listener { + private lateinit var binding:FragmentActiveCoursesPatientBinding + private val modelDoctor: DoctorViewModel by activityViewModels() + private val dataModel: DataModel by activityViewModels()//Для передачи данных + lateinit var adapterPatient: PatientListAdapter + private lateinit var doctorApi: DoctorApi + val prefDoctorConclusion = ConclusionPref() + val prefDoctorClear = ClearPref() + val prefDoctorSave = SavePref() + + //Класс проверки интеренета + val enternetCheck = EnternetCheck() + + var patientYesList:List?=null + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentActiveCoursesPatientBinding.inflate(layoutInflater,container,false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + initRcViewDay() + GetPatientList() + + modelDoctor.PatientActiveList.observe(viewLifecycleOwner){ + if(patientYesList!=it){ + patientYesList=it + adapterPatient.submitList(it) + } + } + } + + //Инициализация списка + private fun initRcViewDay() = with(binding) { + rcView.layoutManager = + GridLayoutManager(requireContext(), 1)//По вертикали будет выводить по умолчанию + adapterPatient = PatientListAdapter(this@ActiveCoursesPatientFragment) + rcView.adapter = adapterPatient + } + + //Инициализируем Retrofit + private fun initRetrofit() { + val interceptor = HttpLoggingInterceptor() + interceptor.level = HttpLoggingInterceptor.Level.BODY + + val client = OkHttpClient + .Builder() + .addInterceptor(interceptor) + .build() + + val retrofit = Retrofit.Builder() + .baseUrl("https://rehabilitation.vmeda.org/api/") + .client(client) + .addConverterFactory(GsonConverterFactory.create()) + .build() + + doctorApi = retrofit.create(DoctorApi::class.java) + + } + //Получения списка пациентов + fun GetPatientList() { + if (enternetCheck.isOnline(requireContext())) { + initRetrofit() + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) + CoroutineScope(Dispatchers.IO).launch { + val listProduct = doctorApi.GetPatientAllActive("Bearer $Tokens") + requireActivity().runOnUiThread { + + //Фиксируем полученные данные + val List = listProduct.body() + val Nice = listProduct.isSuccessful + val Code = listProduct.code() + if(Code==429){ + val intetn = Intent(requireContext(), Code429Activity::class.java) + + startActivity(intetn) + } + else if(Code==200) { + //Если нету ошибок + if (Nice) { + if (List != null) { + modelDoctor.PatientActiveList.value = List.patient + + } + } + } + else if (Code == 500) { + val intetn = Intent(requireContext(), Code500Activity::class.java) + + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(requireContext(), AuthActivity::class.java) + activity?.finish() + startActivity(intetn) + } + } + + } + } else { + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() + startActivity(intetn) + } + + } + + + companion object { + fun newInstance() = ActiveCoursesPatientFragment() + } + + override fun onClickPatient(item: PatientAllModel) { + + //Вывод фрагмента на активити при первоначальной загрузке +// activity?.supportFragmentManager?.beginTransaction() +// ?.replace(com.example.doctor.R.id.CLMainPatient, PatientFragment.newInstance()) +// ?.commit() +// prefDoctorSave.saveIdPatient(requireContext(),item.id!!) +// prefDoctorSave.saveViewPatient(requireContext(),1) + //modelDoctor.patientId.value = PatientIdModel(item.id!!) + + prefDoctorSave.saveIdPatient(requireContext(),item.id!!) + prefDoctorSave.saveViewPatient(requireContext(),1) + val intetn = Intent(requireContext(), PatientActivity::class.java) + startActivity(intetn) + + + // dataModel.patientList.value = 1 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/Patients/TabLayoutPatient/NotActiveCoursesPatientFragment.kt b/app/src/main/java/com/example/doctor/Patients/TabLayoutPatient/NotActiveCoursesPatientFragment.kt new file mode 100644 index 0000000..53fb183 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Patients/TabLayoutPatient/NotActiveCoursesPatientFragment.kt @@ -0,0 +1,173 @@ +package com.example.doctor.Patients.TabLayoutPatient + +import android.content.Intent +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.activityViewModels +import androidx.recyclerview.widget.GridLayoutManager +import com.example.doctor.Auth.AuthActivity +import com.example.doctor.CodeError.Code429Activity +import com.example.doctor.CodeError.Code500Activity +import com.example.doctor.DataModel +import com.example.doctor.DoctorViewModel +import com.example.doctor.Enternet.EnternetActivity +import com.example.doctor.Enternet.EnternetCheck +import com.example.doctor.Patients.Adapter.PatientListAdapter +import com.example.doctor.Patients.Model.PatientAllModel +import com.example.doctor.Patients.Model.PatientIdModel +import com.example.doctor.Patients.PatientActivity +import com.example.doctor.Patients.Reports.PatientFragment +import com.example.doctor.Pref.ClearPref +import com.example.doctor.Pref.ConclusionPref +import com.example.doctor.Pref.SavePref +import com.example.doctor.R +import com.example.doctor.Retrofit.DoctorApi +import com.example.doctor.databinding.FragmentNotActiveCoursesPatientBinding +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory + + +class NotActiveCoursesPatientFragment : Fragment(),PatientListAdapter.Listener { + private lateinit var binding:FragmentNotActiveCoursesPatientBinding + private val model: DoctorViewModel by activityViewModels() + private val dataModel: DataModel by activityViewModels()//Для передачи данных + lateinit var adapterPatient: PatientListAdapter + private lateinit var doctorApi: DoctorApi + val prefDoctorConclusion = ConclusionPref() + val prefDoctorClear = ClearPref() + val prefDoctorSave = SavePref() + + //Класс проверки интеренета + val enternetCheck = EnternetCheck() + + //Список не активных пациентов + var patientNoList: List? = null + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentNotActiveCoursesPatientBinding.inflate(layoutInflater,container,false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + initRcViewDay() + GetPatientList() + + model.PatientNotList.observe(viewLifecycleOwner){ + if(patientNoList != it){ + patientNoList = it + adapterPatient.submitList(it) + } + } + } + + //Инициализация списка + private fun initRcViewDay() = with(binding) { + rcView.layoutManager = + GridLayoutManager(requireContext(), 1)//По вертикали будет выводить по умолчанию + adapterPatient = PatientListAdapter(this@NotActiveCoursesPatientFragment) + rcView.adapter = adapterPatient + } + + + //Инициализируем Retrofit + private fun initRetrofit() { + val interceptor = HttpLoggingInterceptor() + interceptor.level = HttpLoggingInterceptor.Level.BODY + + val client = OkHttpClient + .Builder() + .addInterceptor(interceptor) + .build() + + val retrofit = Retrofit.Builder() + .baseUrl("https://rehabilitation.vmeda.org/api/") + .client(client) + .addConverterFactory(GsonConverterFactory.create()) + .build() + + doctorApi = retrofit.create(DoctorApi::class.java) + + } + + //Получения списка пациентов + fun GetPatientList() { + if (enternetCheck.isOnline(requireContext())) { + initRetrofit() + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) + CoroutineScope(Dispatchers.IO).launch { + val listProduct = doctorApi.GetPatientAllNotActive("Bearer $Tokens") + requireActivity().runOnUiThread { + + //Фиксируем полученные данные + val List = listProduct.body() + val Nice = listProduct.isSuccessful + val Code = listProduct.code() + if(Code==429){ + val intetn = Intent(requireContext(), Code429Activity::class.java) + + startActivity(intetn) + } + else if(Code==200) { + //Если нету ошибок + if (Nice) { + if (List != null) { + model.PatientNotList.value = List.patient + + } + } + } + else if (Code == 500) { + val intetn = Intent(requireContext(), Code500Activity::class.java) + + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(requireContext(), AuthActivity::class.java) + activity?.finish() + startActivity(intetn) + } + } + + } + } else { + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() + startActivity(intetn) + } + + } + override fun onClickPatient(item: PatientAllModel) { + + //Вывод фрагмента на активити при первоначальной загрузке +// activity?.supportFragmentManager?.beginTransaction() +// ?.replace(com.example.doctor.R.id.CLMainPatient, PatientFragment.newInstance()) +// ?.commit() + prefDoctorSave.saveIdPatient(requireContext(),item.id!!) + prefDoctorSave.saveViewPatient(requireContext(),1) + //model.patientId.value = PatientIdModel(item.id!!) + + val intetn = Intent(requireContext(), PatientActivity::class.java) + startActivity(intetn) + + + // dataModel.patientList.value = 1 + } + + + + companion object { + + fun newInstance() = NotActiveCoursesPatientFragment() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/Pref/ClearPref.kt b/app/src/main/java/com/example/doctor/Pref/ClearPref.kt new file mode 100644 index 0000000..16c9095 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Pref/ClearPref.kt @@ -0,0 +1,42 @@ +package com.example.doctor.Pref + +import android.content.Context +import android.content.SharedPreferences + +class ClearPref() { + + fun clearToken(context: Context) { + val prefDoctor: SharedPreferences = context.getSharedPreferences("DOCTORtoken", Context.MODE_PRIVATE) + val edit = prefDoctor.edit() + edit?.clear() + edit?.apply() + } + + fun clearViewPatient(context: Context) { + val prefDoctor: SharedPreferences = context.getSharedPreferences("DOCTORviewPatient", Context.MODE_PRIVATE) + val edit = prefDoctor.edit() + edit?.clear() + edit?.apply() + } + + fun clearIdPatient(context: Context) { + val prefDoctor: SharedPreferences = context.getSharedPreferences("DOCTORIdPatient", Context.MODE_PRIVATE) + val edit = prefDoctor.edit() + edit?.clear() + edit?.apply() + } + + fun clearIdCourses(context: Context) { + val prefDoctor: SharedPreferences = context.getSharedPreferences("DOCTORIdCourses", Context.MODE_PRIVATE) + val edit = prefDoctor.edit() + edit?.clear() + edit?.apply() + } + + fun clearIdActivitis(context: Context) { + val prefDoctor: SharedPreferences = context.getSharedPreferences("DOCTORIdActivitis", Context.MODE_PRIVATE) + val edit = prefDoctor.edit() + edit?.clear() + edit?.apply() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/Pref/ConclusionPref.kt b/app/src/main/java/com/example/doctor/Pref/ConclusionPref.kt new file mode 100644 index 0000000..1a31981 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Pref/ConclusionPref.kt @@ -0,0 +1,37 @@ +package com.example.doctor.Pref + +import android.content.Context +import android.content.SharedPreferences + +class ConclusionPref() { + + fun conclusionToken(context: Context):String { + val prefDoctor: SharedPreferences = context.getSharedPreferences("DOCTORtoken", Context.MODE_PRIVATE) + val token = prefDoctor.getString("token", "") + return token.toString() + } + + fun conclusionViewPatient(context: Context):Int { + val prefDoctor: SharedPreferences = context.getSharedPreferences("DOCTORviewPatient", Context.MODE_PRIVATE) + val viewPatient = prefDoctor.getInt("viewPatient", 0) + return viewPatient.toInt() + } + + fun conclusionIdPatient(context: Context):Int { + val prefDoctor: SharedPreferences = context.getSharedPreferences("DOCTORidPatient", Context.MODE_PRIVATE) + val idPatient = prefDoctor.getInt("idPatient", 0) + return idPatient.toInt() + } + + fun conclusionIdCourses(context: Context):Int { + val prefDoctor: SharedPreferences = context.getSharedPreferences("DOCTORidCourses", Context.MODE_PRIVATE) + val idPatient = prefDoctor.getInt("idCourses", 0) + return idPatient.toInt() + } + + fun conclusionIdActivitis(context: Context):Int { + val prefDoctor: SharedPreferences = context.getSharedPreferences("DOCTORIdActivitis", Context.MODE_PRIVATE) + val idPatient = prefDoctor.getInt("idActivitis", 0) + return idPatient.toInt() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/Pref/SavePref.kt b/app/src/main/java/com/example/doctor/Pref/SavePref.kt new file mode 100644 index 0000000..689d6f4 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Pref/SavePref.kt @@ -0,0 +1,39 @@ +package com.example.doctor.Pref + +import android.annotation.SuppressLint +import android.content.Context +import android.content.SharedPreferences + +class SavePref (){ + + + @SuppressLint("CommitPrefEdits") + fun saveToken(context: Context, token: String) { + val prefDoctor: SharedPreferences = context.getSharedPreferences("DOCTORtoken", Context.MODE_PRIVATE) + prefDoctor.edit().putString("token", token).apply() + } + + + @SuppressLint("CommitPrefEdits") + fun saveViewPatient(context: Context, view_patient: Int) { + val prefDoctor: SharedPreferences = context.getSharedPreferences("DOCTORviewPatient", Context.MODE_PRIVATE) + prefDoctor.edit().putInt("viewPatient", view_patient).apply() + } + + @SuppressLint("CommitPrefEdits") + fun saveIdPatient(context: Context, view_patient: Int) { + val prefDoctor: SharedPreferences = context.getSharedPreferences("DOCTORidPatient", Context.MODE_PRIVATE) + prefDoctor.edit().putInt("idPatient", view_patient).apply() + } + + @SuppressLint("CommitPrefEdits") + fun saveIdCourses(context: Context, view_patient: Int) { + val prefDoctor: SharedPreferences = context.getSharedPreferences("DOCTORidCourses", Context.MODE_PRIVATE) + prefDoctor.edit().putInt("idCourses", view_patient).apply() + } + @SuppressLint("CommitPrefEdits") + fun saveIdActivitis(context: Context, view_patient: Int) { + val prefDoctor: SharedPreferences = context.getSharedPreferences("DOCTORIdActivitis", Context.MODE_PRIVATE) + prefDoctor.edit().putInt("idActivitis", view_patient).apply() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/Retrofit/DoctorApi.kt b/app/src/main/java/com/example/doctor/Retrofit/DoctorApi.kt new file mode 100644 index 0000000..e924ba7 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Retrofit/DoctorApi.kt @@ -0,0 +1,234 @@ +package com.example.doctor.Retrofit + +import com.example.doctor.Appeals.TabLayout.Model.AppealsNewListModel +import com.example.doctor.Appeals.TabLayout.Model.AppealsOldListModel +import com.example.doctor.Auth.Model.AuthModel +import com.example.doctor.Auth.Model.CheckTokenModel +import com.example.doctor.Auth.Model.UserModel +import com.example.doctor.Home.HomeInfoModel +import com.example.doctor.Patients.Model.MessageModel +import com.example.doctor.Patients.Model.CreatePatientModel +import com.example.doctor.Patients.Model.PatientAllListModel +import com.example.doctor.Patients.Model.PatientAllModel +import com.example.doctor.Patients.Model.PatientModel +import com.example.doctor.Patients.Model.PauseModel +import com.example.doctor.Patients.Reports.Courses.SportCoursDoctorListModel +import com.example.doctor.Patients.Reports.Courses.SportCoursListModel +import com.example.doctor.Patients.Reports.Edit.EditSportListNoModel +import com.example.doctor.Patients.Reports.Edit.EditSportListYesModel +import com.example.doctor.Patients.Reports.QBAListModel +import com.example.doctor.Patients.Reports.Requests.ClearSportPatientModel +import com.example.doctor.Setting.Courses.EditCourses.TabLayoutEditCourses.Model.EditCoursesDoctorListAllModel +import com.example.doctor.Setting.Courses.EditCourses.TabLayoutEditCourses.Model.EditCoursesDoctorListYourModel +import com.example.doctor.Setting.Courses.Model.CoursesDoctorListModel +import com.example.doctor.Sport.AddSportPatientModel +import retrofit2.Response +import retrofit2.http.Body +import retrofit2.http.DELETE +import retrofit2.http.GET +import retrofit2.http.Header +import retrofit2.http.Headers +import retrofit2.http.POST +import retrofit2.http.PUT +import retrofit2.http.Query + +interface DoctorApi { + + //Проверка токена + @Headers("Content-Type: application/json") + @GET("CheckTokenDoctor") + suspend fun CheckToken(@Header("Authorization") token:String): Response + + //Выход из аккаунта + @Headers("Content-Type: application/json") + @POST("LoginDoctor") + suspend fun LoginDoctor(@Body authModel: AuthModel): Response + + //Выход из аккаунта + @Headers("Content-Type: application/json") + @POST("LogoutDoctor") + suspend fun LogoutDoctor(@Header("Authorization") token:String):Response + + + //Вывод всех пациентов + @Headers("Content-Type: application/json") + @GET("GetPatientAll") + suspend fun GetPatientAll(@Header("Authorization") token:String):Response + + //Вывод пациентов конкретного врача с активным курсом + @Headers("Content-Type: application/json") + @GET("GetPatientAllActive") + suspend fun GetPatientAllActive(@Header("Authorization") token:String):Response + + //Вывод пациентов конкретного врача с не активным курсом + @Headers("Content-Type: application/json") + @GET("GetPatientAllNotActive") + suspend fun GetPatientAllNotActive(@Header("Authorization") token:String):Response + + //Вывод всех пациентов + @Headers("Content-Type: application/json") + @GET("GetPatientID") + suspend fun GetPatientID(@Header("Authorization") token:String,@Query("id") id:Int):Response + + //Поиск пациентов + @Headers("Content-Type: application/json") + @GET("GetPatientSearch") + suspend fun GetPatientSearch(@Header("Authorization") token:String,@Query("login") login:String):PatientModel + + //Вывод всех курсов + @Headers("Content-Type: application/json") + @GET("GetCoursAll") + suspend fun GetCoursAll(@Header("Authorization") token:String):Response + //Вывод всех курсов + @Headers("Content-Type: application/json") + @GET("GetCoursAllPatient") + suspend fun GetCoursAllPatient(@Header("Authorization") token:String,@Query("id") id:Int):Response + + //Вывод курсов созданных врачем + @Headers("Content-Type: application/json") + @GET("GetCoursesDoctorPatient") + suspend fun GetCoursesDoctorPatient(@Header("Authorization") token:String,@Query("id") id:Int):Response + + + //Вывод анкеты пациентов которые сейчас действуют + @Headers("Content-Type: application/json") + @GET("GetPatientBAQiestionar") + suspend fun GetPatientBAQiestionar(@Header("Authorization") token:String,@Query("id") id:Int):Response + + //Создание аккаунта + @Headers("Content-Type: application/json") + @POST("CreateAccountPatient") + suspend fun CreateAccountPatient(@Header("Authorization") token:String,@Body createPatientModel: CreatePatientModel):Response + + //Обновление данных пользователя + @Headers("Content-Type: application/json") + @PUT("UpdateAccountPatient") + suspend fun UpdateAccountPatient(@Header("Authorization") token:String,@Query("id") id:String, @Query("login") login:String,@Query("password") password:String) + + //Добавление блокировки + @Headers("Content-Type: application/json") + @PUT("UpdateBlockAccountPatient") + suspend fun UpdateBlockAccountPatient(@Header("Authorization") token:String,@Query("id") id:Int):Response + + //Добавление паузы + @Headers("Content-Type: application/json") + @PUT("UpdatePauseAccountPatient") + suspend fun UpdatePauseAccountPatient(@Header("Authorization") token:String,@Query("id") id:Int):Response + + //Получения курса пациента в зависимости от того сколько дней пройдено + @Headers("Content-Type: application/json") + @GET("GetCours") + suspend fun GetCours(@Header("Authorization") token:String,@Query("name") name:String,@Query("day") day:String) + + //Добавление курса пациенту + @Headers("Content-Type: application/json") + @POST("AddSportPatient") + suspend fun AddSportPatient(@Header("Authorization") token:String,@Body addSportPatientModel: AddSportPatientModel) + + //Очистка курса у пациента + @Headers("Content-Type: application/json") + @PUT("ClearPatientSport") + suspend fun ClearPatientSport(@Header("Authorization") token:String,@Query("id_patient") id_patient:Int):Response + + + //Вывод всех курсов + @Headers("Content-Type: application/json") + @GET("GetCoursesSport") + suspend fun GetCoursesSport(@Header("Authorization") token:String,@Query("id_patient") id_patient:Int,@Query("id_sports_courses_patient") id_sports_courses_patient:Int):Response + + //Вывод всех включенных упражненйи + @Headers("Content-Type: application/json") + @GET("GetCoursesSportYes") + suspend fun GetCoursesSportYes(@Header("Authorization") token:String,@Query("id_patient") id_patient:Int):Response + + //Вывод всех отключенных упражнений + @Headers("Content-Type: application/json") + @GET("GetCoursesSportNo") + suspend fun GetCoursesSportNo(@Header("Authorization") token:String,@Query("id_patient") id_patient:Int):Response + + //Отключенных упражнений + @Headers("Content-Type: application/json") + @PUT("UpdateBlockSportTasksYes") + suspend fun UpdateBlockSportTasksYes(@Header("Authorization") token:String,@Query("id_patient") id_patient:Int,@Query("id_sports_tasks") id_sports_tasks:Int):Response + //Отключенных упражнений + @Headers("Content-Type: application/json") + @PUT("UpdateBlockSportTasksNo") + suspend fun UpdateBlockSportTasksNo(@Header("Authorization") token:String,@Query("id") id:Int):Response + + + //Отключенных упражнений + @Headers("Content-Type: application/json") + @PUT("UpdatePatientLogin") + suspend fun UpdatePatientLogin(@Header("Authorization") token:String,@Query("id") id:Int,@Query("login") login:String):Response + //Отключенных упражнений + @Headers("Content-Type: application/json") + @PUT("UpdatePatientPassword") + suspend fun UpdatePatientPassword(@Header("Authorization") token:String,@Query("id") id:Int,@Query("password") password:String):Response + + //Добавление курса пациенту + @Headers("Content-Type: application/json") + @POST("AddSportPatient") + suspend fun AddSportPatient(@Header("Authorization") token:String,@Query("id_patient") id:Int,@Query("id_course") id_course:Int,@Query("all_day") all_day:Int):Response + + + + + //Вывод необработанных сообщений + @Headers("Content-Type: application/json") + @GET("GetAppealsDoctorNew") + suspend fun GetAppealsDoctorNew(@Header("Authorization") token:String) :Response + + //Вывод необработанных сообщений + @Headers("Content-Type: application/json") + @GET("GetAppealsDoctorOld") + suspend fun GetAppealsDoctorOld(@Header("Authorization") token:String) :Response + + + //Подтверждение необработанных сообщений + @Headers("Content-Type: application/json") + @PUT("UpdateMessageDoctor") + suspend fun UpdateMessageDoctor(@Header("Authorization") token:String,@Query("id") id:Int,@Query("id_patient") id_patient:Int) :Response + + //Отправка сообщения пациенту + @Headers("Content-Type: application/json") + @POST("AddMessageDoctor") + suspend fun AddMessageDoctor(@Header("Authorization") token:String,@Query("login") login:String,@Query("text") text:String) :Response + + //Создание курса врачем + @Headers("Content-Type: application/json") + @POST("AddCoursesName") + suspend fun AddCoursesName(@Header("Authorization") token:String,@Query("name") name:String,@Query("description") description:String) :Response + + //Вывод курсов созданных врачем + @Headers("Content-Type: application/json") + @GET("GetCoursesDoctor") + suspend fun GetCoursesDoctor(@Header("Authorization") token:String) :Response + + //Вывод упражнений которые не входят в курс + @Headers("Content-Type: application/json") + @GET("GetEditCourseDoctorAll") + suspend fun GetEditCourseDoctorAll(@Header("Authorization") token:String,@Query("id_doctor_courses") id_doctor_courses:Int) :Response + + + //Вывод упражнений которые входят в курс + @Headers("Content-Type: application/json") + @GET("GetEditCourseDoctorYour") + suspend fun GetEditCourseDoctorYour(@Header("Authorization") token:String,@Query("id_doctor_courses") id_doctor_courses:Int) :Response + + + //Добавление упражнений в курс + @Headers("Content-Type: application/json") + @POST("AddCoursesCreatingDoctor") + suspend fun AddCoursesCreatingDoctor(@Header("Authorization") token:String,@Query("id_sports_tasks") id_sports_tasks:Int,@Query("id_sets_of_sports_activities") id_sets_of_sports_activities:Int) :Response + + //Удаление упражнений из курса + @Headers("Content-Type: application/json") + @DELETE("ClearCoursesCreatingDoctor") + suspend fun ClearCoursesCreatingDoctor(@Header("Authorization") token:String,@Query("id_sets_of_sports_exercises") id_sets_of_sports_exercises:Int) :Response + + //Вывод данных для первой страницы врача + @Headers("Content-Type: application/json") + @GET("CountPatientAndAppeals") + suspend fun CountPatientAndAppeals(@Header("Authorization") token:String) :Response + +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/Setting/Courses/Adapter/CoursesDoctorAdapter.kt b/app/src/main/java/com/example/doctor/Setting/Courses/Adapter/CoursesDoctorAdapter.kt new file mode 100644 index 0000000..df1f594 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Setting/Courses/Adapter/CoursesDoctorAdapter.kt @@ -0,0 +1,90 @@ +package com.example.doctor.Setting.Courses.Adapter + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView + +import com.example.doctor.R +import com.example.doctor.Setting.Courses.Model.CoursesDoctorModel +import com.example.doctor.databinding.ItemCardCoursesDoctorBinding + + +class CoursesDoctorAdapter(val listener: Listener) : + ListAdapter( + Comparator() + ) {//Productitem - по этой форме будем заполнять.//ProductAdapter.holder - это создаваемый holder который хранит логику как нужно заполнять карточку + + + //В holder создаетс код с помошью которого мы будем заполнять и сохронять разметку + class Holder(view: View, val listener: Listener) : + RecyclerView.ViewHolder(view) {//Класс который будет хранить сссылки на элементы, и отвечает за один элемент за 1 раз, то есть сначсало нулевой элемент заполнит, потом первый, потом второй и т.д. + //Для передачи данных + + val binding = ItemCardCoursesDoctorBinding.bind(view)//view - здесь храянтся элементы и мы их bind заполним в ListItemBinding//ListItemBinding - это клласс даннйо разметки(карточки) которую мы будем заполнять, а нужна дання переменная(binding), чтобы мжно было при заполнение обращатся к элементам карточки + + var itemTemp: CoursesDoctorModel? = + null//Глобальная переменная для нашего item, чтобы можно было передать данные для нажатия + + //init - дает возможность внутри адаптера обращаться к элементам экрана + init { + itemView.setOnClickListener {//Нажатие на ячейку//itemView - это весь элемент карточки из списка + //itemView.setEnabled(false) + itemTemp?.let { it1 -> listener.onClickCourses(it1) } + + } + } + + @SuppressLint("SuspiciousIndentation", "SetTextI18n") + fun bind(item: CoursesDoctorModel) = with(binding) {//Productitem - перпедаем данные + itemTemp = item + txtNumberCurds.text = item.number.toString() + "." + txtNameCoursesDoctor.text = item.name + } + + + } + + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.item_card_courses_doctor, parent, false)//Создаем(надуваем) list_item + return Holder(view, listener)//Через Holder возврощаем view + } + + override fun onBindViewHolder(holder: Holder, position: Int) { + val view = holder.bind(getItem(position))//Заполняем по позиции карточку + } + + + //Comparator - сравнивает старый список и новый и если что-то изменилось, то работает с конкретным изменением списке, а не весь список переписывает + class Comparator : DiffUtil.ItemCallback() { + override fun areItemsTheSame( + oldItem: CoursesDoctorModel, + newItem: CoursesDoctorModel + ): Boolean {//Тут лучше всего сравнивать по id//oldItem - элементы старого списка, newItem - элементы нового списка//Возврощает Boolean, тоесть есть изменения или нет + return oldItem.id == newItem.id//Сравниваем полностью весь список новы и старый, по очередно по одной карточке и по элементно, то есть нулевой элемент, первый, второй и т.д.. Но лучше сравнивать по id списки, а не просто весь список, так как это эфективнее, так как id уникальный(oldItem.id == newItem.id) + } + + override fun areContentsTheSame( + oldItem: CoursesDoctorModel, + newItem: CoursesDoctorModel + ): Boolean {//Утут нужно сравнивать весь спсок старых элементов и новых + return oldItem == newItem//Сравниваем полностью весь список новы и старый + } + } + + //Интерфейс нажатия на кнопку удалить товар из корзины + interface Listener { + fun onClickCourses(item: CoursesDoctorModel) + } + + +} + + + + diff --git a/app/src/main/java/com/example/doctor/Setting/Courses/CoursesActivity.kt b/app/src/main/java/com/example/doctor/Setting/Courses/CoursesActivity.kt new file mode 100644 index 0000000..2012e23 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Setting/Courses/CoursesActivity.kt @@ -0,0 +1,22 @@ +package com.example.doctor.Setting.Courses + +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import com.example.doctor.Patients.Reports.PatientFragment +import com.example.doctor.R +import com.example.doctor.Setting.Courses.EditCourses.CreateCoursesFragment +import com.example.doctor.databinding.ActivityCoursesBinding + +class CoursesActivity : AppCompatActivity() { + private lateinit var binding: ActivityCoursesBinding + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityCoursesBinding.inflate(layoutInflater) + setContentView(binding.root) + + binding.apply { + supportFragmentManager.beginTransaction().replace(R.id.CLCoursesActivity, CreateCoursesFragment.newInstance()).commit()//Заменяем наш экран на фрагмент (используем наш экран как основу)//R.id.placeHolder - куда всталяем //MainFragment.newInstance() - это то что мы вставляем + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/Setting/Courses/EditCourses/CreateCoursesFragment.kt b/app/src/main/java/com/example/doctor/Setting/Courses/EditCourses/CreateCoursesFragment.kt new file mode 100644 index 0000000..4c29306 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Setting/Courses/EditCourses/CreateCoursesFragment.kt @@ -0,0 +1,407 @@ +package com.example.doctor.Setting.Courses.EditCourses + +import android.content.Context +import android.content.Intent +import android.net.ConnectivityManager +import android.net.NetworkCapabilities +import android.os.Build +import android.os.Bundle +import android.util.Log +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.fragment.app.FragmentActivity +import androidx.fragment.app.activityViewModels +import androidx.recyclerview.widget.GridLayoutManager +import com.example.doctor.Adapter.VpAdapter +import com.example.doctor.Auth.AuthActivity +import com.example.doctor.CodeError.Code429Activity +import com.example.doctor.CodeError.Code500Activity +import com.example.doctor.DoctorViewModel +import com.example.doctor.Enternet.EnternetActivity +import com.example.doctor.Enternet.EnternetFragment +import com.example.doctor.Pref.ClearPref +import com.example.doctor.Pref.ConclusionPref +import com.example.doctor.Pref.SavePref +import com.example.doctor.R +import com.example.doctor.Retrofit.DoctorApi +import com.example.doctor.Setting.Courses.Adapter.CoursesDoctorAdapter +import com.example.doctor.Setting.Courses.EditCourses.TabLayoutEditCourses.AllSportCoursesDoctorFragment +import com.example.doctor.Setting.Courses.EditCourses.TabLayoutEditCourses.YourSportCoursesDoctorFragment +import com.example.doctor.Setting.Courses.Model.CoursesDoctorModel +import com.example.doctor.Toast.showCustomInfoToast +import com.example.doctor.databinding.FragmentCreateCoursesBinding +import com.google.android.material.tabs.TabLayoutMediator +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory + + +class CreateCoursesFragment : Fragment(),CoursesDoctorAdapter.Listener { + private lateinit var binding: FragmentCreateCoursesBinding + private val model: DoctorViewModel by activityViewModels() + private lateinit var doctorApi: DoctorApi + lateinit var adapter: CoursesDoctorAdapter + val prefDoctorConclusion = ConclusionPref() + val prefDoctorClear = ClearPref() + val prefDoctorSave = SavePref() + + + + + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentCreateCoursesBinding.inflate(layoutInflater,container,false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.apply { + initRcView() + + GetCoursesDoctor() + button() + + CLCreate.visibility = View.GONE + + btnCreateCourses.setOnClickListener{ + CLCreate.visibility = View.VISIBLE + } + + //Закрытие экоана создания названия + CLCreate.setOnClickListener{ + CLCreate.visibility = View.GONE + } + btnExitCreat.setOnClickListener{ + CLCreate.visibility = View.GONE + } + +// CLEditCourses.setOnClickListener { +// CLEditCourses.visibility = View.GONE +// } + + + btnExit.setOnClickListener{ + activity?.finish() + } + + + + + model.CoursesDoctorList.observe(viewLifecycleOwner) { + adapter.submitList(it) + } + + + btnCreateCoursrsDoctor.setOnClickListener{ + CreateCoursesDoctor() + } + + + + } + } + + + + private fun CreateCoursesDoctor() { + val name = binding.edCoursesDoctorName.text.toString() + val description = binding.edCoursesDoctorDescription.text.toString() + if(name!="" && description !=""){ + AddCoursesName(name,description) + } + else{ + Toast(requireContext()).showCustomInfoToast("Не все поля заполнены", requireActivity()) + } + + } + + private fun button() = with(binding){ + CVCardCreateCourses.setOnClickListener { + + } +// CVEditCourses.setOnClickListener { +// +// } + } + + //Получения списка пациентов + fun AddCoursesName(name:String,description:String) { + if (isOnline(requireContext())) { + visibleLoadYes() + initRetrofit() + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) + CoroutineScope(Dispatchers.IO).launch { + val listCreateCoursesDoctor = doctorApi.AddCoursesName("Bearer $Tokens",name,description) + + requireActivity().runOnUiThread { + + + + //Фиксируем полученные данные + val List = listCreateCoursesDoctor.body() + val Nice = listCreateCoursesDoctor.isSuccessful + val Code = listCreateCoursesDoctor.code() + if(Code==429){ + val intetn = Intent(requireContext(), Code429Activity::class.java) + + startActivity(intetn) + } + else if(Code==200) { + //Если нету ошибок + if (Nice) { + if (List != null) { + Toast(requireContext()).showCustomInfoToast(List.message, requireActivity()) + GetCoursesDoctor() + } + } + } + else if (Code == 500) { + val intetn = Intent(requireContext(), Code500Activity::class.java) + + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(requireContext(), AuthActivity::class.java) + activity?.finish() + startActivity(intetn) + } + visibleLoadNo() + } + + } + } else { + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() + startActivity(intetn) + } + + } + //Получения списка курсов созданных доктором + fun GetCoursesDoctor() { + if (isOnline(requireContext())) { + visibleLoadYes() + initRetrofit() + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) + CoroutineScope(Dispatchers.IO).launch { + val listCoursesDoctor = doctorApi.GetCoursesDoctor("Bearer $Tokens") + + requireActivity().runOnUiThread { + + + + //Фиксируем полученные данные + val List = listCoursesDoctor.body() + val Nice = listCoursesDoctor.isSuccessful + val Code = listCoursesDoctor.code() + if(Code==429){ + val intetn = Intent(requireContext(), Code429Activity::class.java) + + startActivity(intetn) + } + else if(Code==200) { + //Если нету ошибок + if (Nice) { + if (List != null) { + model.CoursesDoctorList.value = List.sport_courses_doctor + binding.txtNull.visibility = View.GONE + } + } + } + else if (Code == 500) { + val intetn = Intent(requireContext(), Code500Activity::class.java) + + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(requireContext(), AuthActivity::class.java) + activity?.finish() + startActivity(intetn) + } + + + visibleLoadNo() + } + + } + } else { + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() + startActivity(intetn) + } + + } + + //Инициализация списка + private fun initRcView() = with(binding) { + rcView.layoutManager = GridLayoutManager(requireContext(), 1)//По вертикали будет выводить по умолчанию + adapter = CoursesDoctorAdapter(this@CreateCoursesFragment) + rcView.adapter = adapter + } + + //Инициализируем Retrofit + private fun initRetrofit() { + val interceptor = HttpLoggingInterceptor() + interceptor.level = HttpLoggingInterceptor.Level.BODY + + val client = OkHttpClient + .Builder() + .addInterceptor(interceptor) + .build() + + val retrofit = Retrofit.Builder() + .baseUrl("https://rehabilitation.vmeda.org/api/") + .client(client) + .addConverterFactory(GsonConverterFactory.create()) + .build() + + doctorApi = retrofit.create(DoctorApi::class.java) + + } + + + fun visible1()=with(binding){ + constraintLayout2.visibility = View.VISIBLE + CLCreate.visibility = View.GONE + // CLEditCourses.visibility = View.GONE + CLLoad.visibility = View.GONE + } + fun visible2()=with(binding){ + constraintLayout2.visibility = View.GONE + CLCreate.visibility = View.VISIBLE + //CLEditCourses.visibility = View.GONE + CLLoad.visibility = View.GONE + } + + fun visible3()=with(binding){ + constraintLayout2.visibility = View.GONE + CLCreate.visibility = View.GONE + // CLEditCourses.visibility = View.VISIBLE + CLLoad.visibility = View.GONE + } + + fun visibleLoadYes()=with(binding){ + CLLoad.visibility = View.VISIBLE + } + fun visibleLoadNo()=with(binding){ + CLLoad.visibility = View.GONE + } + companion object { + + fun newInstance() = CreateCoursesFragment() + } +// //Получения списка курсов созданных доктором +// fun GetEditCourseDoctorAll(id:Int) { +// if (isOnline(requireContext())) { +// initRetrofit() +// val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) +// CoroutineScope(Dispatchers.IO).launch { +// val listCoursesDoctor = doctorApi.GetEditCourseDoctorAll("Bearer $Tokens",id) +// +// requireActivity().runOnUiThread { +// +// //Фиксируем полученные данные +// val CoursesDoctorList = listCoursesDoctor.body() +// +// //Если нету ошибок +// if (CoursesDoctorList != null) { +// model.EditCoursesDoctorAllList.value = CoursesDoctorList.set_of_sports_exercises_all +// binding.txtNull.visibility = View.GONE +// } +// else{ +// binding.txtNull.visibility = View.VISIBLE +// } +// +// } +// +// } +// } else { +// val intetn = Intent(requireContext(), EnternetActivity::class.java) +// activity?.finish() +// startActivity(intetn) +// } +// +// } +// //Получения списка курсов созданных доктором +// fun GetEditCourseDoctorYour(id:Int) { +// if (isOnline(requireContext())) { +// initRetrofit() +// val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) +// CoroutineScope(Dispatchers.IO).launch { +// val listCoursesDoctor = doctorApi.GetEditCourseDoctorYour("Bearer $Tokens",id) +// +// requireActivity().runOnUiThread { +// +// //Фиксируем полученные данные +// val CoursesDoctorList = listCoursesDoctor.body() +// +// //Если нету ошибок +// if (CoursesDoctorList != null) { +// model.EditCoursesDoctorYourList.value = CoursesDoctorList.set_of_sports_exercises_your +// } +// } +// } +// } else { +// val intetn = Intent(requireContext(), EnternetActivity::class.java) +// activity?.finish() +// startActivity(intetn) +// } +// +// } + + + + //Проверка интернета + fun isOnline(context: Context): Boolean { + if (context == null) return false + val connectivityManager = + context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + val capabilities = + connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork) + if (capabilities != null) { + when { + capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> { + return true + } + + capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> { + return true + } + + capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> { + return true + } + } + } + } else { + val activeNetworkInfo = connectivityManager.activeNetworkInfo + if (activeNetworkInfo != null && activeNetworkInfo.isConnected) { + return true + } + } + return false + } + override fun onClickCourses(item: CoursesDoctorModel){ +// // CLEditCourses.visibility = View.VISIBLE +// prefDoctorSave.saveIdCourses(requireContext(),item.id) +// prefDoctorSave.saveIdActivitis(requireContext(),item.id_activity) +// // model.CoursesDoctorCA.value= CoursesDoctorCA(item.id,item.id_activity) +// GetEditCourseDoctorAll(item.id) +// GetEditCourseDoctorYour(item.id) + + model.CoursesCustomDoctor.value = item + activity?.supportFragmentManager?.beginTransaction()?.replace(R.id.CLCoursesActivity, EditCoursesFragment.newInstance())?.commit()//Заменяем наш экран на фрагмент (используем наш экран как основу)//R.id.placeHolder - куда всталяем //MainFragment.newInstance() - это то что мы вставляем + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/Setting/Courses/EditCourses/EditCoursesFragment.kt b/app/src/main/java/com/example/doctor/Setting/Courses/EditCourses/EditCoursesFragment.kt new file mode 100644 index 0000000..18ac01d --- /dev/null +++ b/app/src/main/java/com/example/doctor/Setting/Courses/EditCourses/EditCoursesFragment.kt @@ -0,0 +1,242 @@ +package com.example.doctor.Setting.Courses.EditCourses + +import android.content.Context +import android.content.Intent +import android.net.ConnectivityManager +import android.net.NetworkCapabilities +import android.os.Build +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.FragmentActivity +import androidx.fragment.app.activityViewModels +import com.example.doctor.Adapter.VpAdapter +import com.example.doctor.Auth.AuthActivity +import com.example.doctor.CodeError.Code429Activity +import com.example.doctor.CodeError.Code500Activity +import com.example.doctor.DoctorViewModel +import com.example.doctor.Enternet.EnternetActivity +import com.example.doctor.Pref.ClearPref +import com.example.doctor.Pref.ConclusionPref +import com.example.doctor.Pref.SavePref +import com.example.doctor.R +import com.example.doctor.Retrofit.DoctorApi +import com.example.doctor.Setting.Courses.Adapter.CoursesDoctorAdapter +import com.example.doctor.Setting.Courses.EditCourses.TabLayoutEditCourses.AllSportCoursesDoctorFragment +import com.example.doctor.Setting.Courses.EditCourses.TabLayoutEditCourses.YourSportCoursesDoctorFragment +import com.example.doctor.databinding.FragmentEditCoursesBinding +import com.google.android.material.tabs.TabLayoutMediator +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory + + +class EditCoursesFragment : Fragment() { + private lateinit var binding:FragmentEditCoursesBinding + private val model: DoctorViewModel by activityViewModels() + private lateinit var doctorApi: DoctorApi + lateinit var adapter: CoursesDoctorAdapter + val prefDoctorConclusion = ConclusionPref() + val prefDoctorClear = ClearPref() + val prefDoctorSave = SavePref() + + private val tList = listOf( + "добавленные", + "Все", + ) + + //Список с фрагментами для переключения + private val flist = listOf( + YourSportCoursesDoctorFragment.newInstance(), + AllSportCoursesDoctorFragment.newInstance(), + + ) + + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentEditCoursesBinding.inflate(layoutInflater,container,false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.apply { + init() + + binding.btnBack.setOnClickListener{ + activity?.supportFragmentManager?.beginTransaction()?.replace(R.id.CLCoursesActivity, CreateCoursesFragment.newInstance())?.commit()//Заменяем наш экран на фрагмент (используем наш экран как основу)//R.id.placeHolder - куда всталяем //MainFragment.newInstance() - это то что мы вставляем + + } + + model.CoursesCustomDoctor.observe(viewLifecycleOwner){ + txtNameCourses.text = it.name + GetEditCourseDoctorYour(it.id) + GetEditCourseDoctorAll(it.id) + } + } + } + + //Получения списка упражнений созданных врачем + fun GetEditCourseDoctorYour(id:Int) { + if (isOnline(requireContext())) { + initRetrofit() + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) + CoroutineScope(Dispatchers.IO).launch { + val listCoursesDoctor = doctorApi.GetEditCourseDoctorYour("Bearer $Tokens",id) + + requireActivity().runOnUiThread { + + + //Фиксируем полученные данные + val List = listCoursesDoctor.body() + val Nice = listCoursesDoctor.isSuccessful + val Code = listCoursesDoctor.code() + if(Code==429){ + val intetn = Intent(requireContext(), Code429Activity::class.java) + + startActivity(intetn) + } + else if(Code==200) { + //Если нету ошибок + if (Nice) { + if (List != null) { + model.EditCoursesDoctorYourList.value = List.set_of_sports_exercises_your + + } + } + } + else if (Code == 500) { + val intetn = Intent(requireContext(), Code500Activity::class.java) + + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(requireContext(), AuthActivity::class.java) + activity?.finish() + startActivity(intetn) + } + } + } + } else { + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() + startActivity(intetn) + } + + } + //Получения списка курсов созданных доктором + fun GetEditCourseDoctorAll(id:Int) { + if (isOnline(requireContext())) { + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) + CoroutineScope(Dispatchers.IO).launch { + val listCoursesDoctor = doctorApi.GetEditCourseDoctorAll("Bearer $Tokens",id) + + requireActivity().runOnUiThread { + + //Фиксируем полученные данные + val CoursesDoctorList = listCoursesDoctor.body() + + //Если нету ошибок + if (CoursesDoctorList != null) { + model.EditCoursesDoctorAllList.value = CoursesDoctorList.set_of_sports_exercises_all + } + + + } + + } + } else { + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() + startActivity(intetn) + } + + } + + + //Функция подключения переключения + private fun init() = with(binding) { + val adapter = VpAdapter(activity as FragmentActivity, flist) + vpCourses.adapter = adapter + + //Переключения (связываем таблаяут(переключатель) с viewpager, чтобы переключать фрагменты) + TabLayoutMediator(tabLayoutCourses, vpCourses) { tab, pos -> + tab.text = + tList[pos]//tab - нажатая кнопка, pos - позиция кнопки, tList[pos] - передаем название по полученной позиции + }.attach()// attach() - чтобы все переключалось, а не вывадило постоянно один экран + + //Изменения цвета в зависомости на каком из tabLayout вы находитесь + binding.tabLayoutCourses.setTabTextColors( + getResources().getColor(R.color.black), + getResources().getColor(R.color.white) + ); + + + } + + //Инициализируем Retrofit + private fun initRetrofit() { + val interceptor = HttpLoggingInterceptor() + interceptor.level = HttpLoggingInterceptor.Level.BODY + + val client = OkHttpClient + .Builder() + .addInterceptor(interceptor) + .build() + + val retrofit = Retrofit.Builder() + .baseUrl("https://rehabilitation.vmeda.org/api/") + .client(client) + .addConverterFactory(GsonConverterFactory.create()) + .build() + + doctorApi = retrofit.create(DoctorApi::class.java) + + } + + //Проверка интернета + fun isOnline(context: Context): Boolean { + if (context == null) return false + val connectivityManager = + context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + val capabilities = + connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork) + if (capabilities != null) { + when { + capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> { + return true + } + + capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> { + return true + } + + capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> { + return true + } + } + } + } else { + val activeNetworkInfo = connectivityManager.activeNetworkInfo + if (activeNetworkInfo != null && activeNetworkInfo.isConnected) { + return true + } + } + return false + } + + + companion object { + fun newInstance() = EditCoursesFragment() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/Setting/Courses/EditCourses/TabLayoutEditCourses/Adapter/EditCoursesDoctorAllAdapter.kt b/app/src/main/java/com/example/doctor/Setting/Courses/EditCourses/TabLayoutEditCourses/Adapter/EditCoursesDoctorAllAdapter.kt new file mode 100644 index 0000000..2b9b13a --- /dev/null +++ b/app/src/main/java/com/example/doctor/Setting/Courses/EditCourses/TabLayoutEditCourses/Adapter/EditCoursesDoctorAllAdapter.kt @@ -0,0 +1,107 @@ +package com.example.doctor.Setting.Courses.EditCourses.TabLayoutEditCourses.Adapter + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import com.example.doctor.R +import com.example.doctor.Setting.Courses.EditCourses.TabLayoutEditCourses.Model.EditCoursesDoctorModel +import com.example.doctor.databinding.ItemEditSportYesBinding + + +class EditCoursesDoctorAllAdapter(val listener_sport: Listener) : + ListAdapter( + Comparator() + ) {//Productitem - по этой форме будем заполнять.//ProductAdapter.holder - это создаваемый holder который хранит логику как нужно заполнять карточку + + + //В holder создаетс код с помошью которого мы будем заполнять и сохронять разметку + class Holder(view: View, val listener_sport: Listener) : + RecyclerView.ViewHolder(view) {//Класс который будет хранить сссылки на элементы, и отвечает за один элемент за 1 раз, то есть сначсало нулевой элемент заполнит, потом первый, потом второй и т.д. + //Для передачи данных + + val binding = ItemEditSportYesBinding.bind(view)//view - здесь храянтся элементы и мы их bind заполним в ListItemBinding//ListItemBinding - это клласс даннйо разметки(карточки) которую мы будем заполнять, а нужна дання переменная(binding), чтобы мжно было при заполнение обращатся к элементам карточки + + var itemTemp: EditCoursesDoctorModel? = + null//Глобальная переменная для нашего item, чтобы можно было передать данные для нажатия + + //init - дает возможность внутри адаптера обращаться к элементам экрана + init { + binding.btnYeyYes.setOnClickListener {//Нажатие на ячейку//itemView - это весь элемент карточки из списка + itemTemp?.let { it1 -> listener_sport.onClickCoursesAll(it1) } + } + } + + @SuppressLint("SuspiciousIndentation") + fun bind(item: EditCoursesDoctorModel) = with(binding) {//Productitem - перпедаем данные + itemTemp = item + txtNumber.text = item.number.toString()+"." + txtNameSport.text = item.name + txtDescriptionSport.text = item.description + + if (item.expand) { + binding.txtDescriptionSport.maxLines = 100 + + } else { + binding.txtDescriptionSport.maxLines = 1 + + } + + binding.CardViewOld.setOnClickListener { + if (item.expand == false) { + binding.txtDescriptionSport.maxLines = 100 + item.expand = true + } else { + binding.txtDescriptionSport.maxLines = 1 + item.expand = false + } + } + + } + + + } + + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.item_edit_sport_yes, parent, false)//Создаем(надуваем) list_item + return Holder(view, listener_sport)//Через Holder возврощаем view + } + + override fun onBindViewHolder(holder: Holder, position: Int) { + val view = holder.bind(getItem(position))//Заполняем по позиции карточку + } + + + //Comparator - сравнивает старый список и новый и если что-то изменилось, то работает с конкретным изменением списке, а не весь список переписывает + class Comparator : DiffUtil.ItemCallback() { + override fun areItemsTheSame( + oldItem: EditCoursesDoctorModel, + newItem: EditCoursesDoctorModel + ): Boolean {//Тут лучше всего сравнивать по id//oldItem - элементы старого списка, newItem - элементы нового списка//Возврощает Boolean, тоесть есть изменения или нет + return oldItem.id == newItem.id//Сравниваем полностью весь список новы и старый, по очередно по одной карточке и по элементно, то есть нулевой элемент, первый, второй и т.д.. Но лучше сравнивать по id списки, а не просто весь список, так как это эфективнее, так как id уникальный(oldItem.id == newItem.id) + } + + override fun areContentsTheSame( + oldItem: EditCoursesDoctorModel, + newItem: EditCoursesDoctorModel + ): Boolean {//Утут нужно сравнивать весь спсок старых элементов и новых + return oldItem == newItem//Сравниваем полностью весь список новы и старый + } + } + + //Интерфейс нажатия на кнопку удалить товар из корзины + interface Listener { + fun onClickCoursesAll(item: EditCoursesDoctorModel) + } + + +} + + + + diff --git a/app/src/main/java/com/example/doctor/Setting/Courses/EditCourses/TabLayoutEditCourses/Adapter/EditCoursesDoctorYourAdapter.kt b/app/src/main/java/com/example/doctor/Setting/Courses/EditCourses/TabLayoutEditCourses/Adapter/EditCoursesDoctorYourAdapter.kt new file mode 100644 index 0000000..54769d2 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Setting/Courses/EditCourses/TabLayoutEditCourses/Adapter/EditCoursesDoctorYourAdapter.kt @@ -0,0 +1,107 @@ +package com.example.doctor.Setting.Courses.EditCourses.TabLayoutEditCourses.Adapter + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import com.example.doctor.R +import com.example.doctor.Setting.Courses.EditCourses.TabLayoutEditCourses.Model.EditCoursesDoctorModel +import com.example.doctor.databinding.ItemEditSportNoBinding + + +class EditCoursesDoctorYourAdapter(val listener_sport: Listener) : + ListAdapter( + Comparator() + ) {//Productitem - по этой форме будем заполнять.//ProductAdapter.holder - это создаваемый holder который хранит логику как нужно заполнять карточку + + + //В holder создаетс код с помошью которого мы будем заполнять и сохронять разметку + class Holder(view: View, val listener_sport: Listener) : + RecyclerView.ViewHolder(view) {//Класс который будет хранить сссылки на элементы, и отвечает за один элемент за 1 раз, то есть сначсало нулевой элемент заполнит, потом первый, потом второй и т.д. + //Для передачи данных + + val binding = ItemEditSportNoBinding.bind(view)//view - здесь храянтся элементы и мы их bind заполним в ListItemBinding//ListItemBinding - это клласс даннйо разметки(карточки) которую мы будем заполнять, а нужна дання переменная(binding), чтобы мжно было при заполнение обращатся к элементам карточки + + var itemTemp: EditCoursesDoctorModel? = + null//Глобальная переменная для нашего item, чтобы можно было передать данные для нажатия + + //init - дает возможность внутри адаптера обращаться к элементам экрана + init { + binding.btnYeyNo.setOnClickListener {//Нажатие на ячейку//itemView - это весь элемент карточки из списка + itemTemp?.let { it1 -> listener_sport.onClickCoursesYour(it1) } + } + } + + @SuppressLint("SuspiciousIndentation") + fun bind(item: EditCoursesDoctorModel) = with(binding) {//Productitem - перпедаем данные + itemTemp = item + txtNumber.text = item.number.toString()+"." + txtNameSport.text = item.name + txtDescriptionSport.text = item.description + + if (item.expand) { + binding.txtDescriptionSport.maxLines = 100 + + } else { + binding.txtDescriptionSport.maxLines = 1 + + } + + binding.CardViewOld.setOnClickListener { + if (item.expand == false) { + binding.txtDescriptionSport.maxLines = 100 + item.expand = true + } else { + binding.txtDescriptionSport.maxLines = 1 + item.expand = false + } + } + + } + + + } + + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.item_edit_sport_no, parent, false)//Создаем(надуваем) list_item + return Holder(view, listener_sport)//Через Holder возврощаем view + } + + override fun onBindViewHolder(holder: Holder, position: Int) { + val view = holder.bind(getItem(position))//Заполняем по позиции карточку + } + + + //Comparator - сравнивает старый список и новый и если что-то изменилось, то работает с конкретным изменением списке, а не весь список переписывает + class Comparator : DiffUtil.ItemCallback() { + override fun areItemsTheSame( + oldItem: EditCoursesDoctorModel, + newItem: EditCoursesDoctorModel + ): Boolean {//Тут лучше всего сравнивать по id//oldItem - элементы старого списка, newItem - элементы нового списка//Возврощает Boolean, тоесть есть изменения или нет + return oldItem.id == newItem.id//Сравниваем полностью весь список новы и старый, по очередно по одной карточке и по элементно, то есть нулевой элемент, первый, второй и т.д.. Но лучше сравнивать по id списки, а не просто весь список, так как это эфективнее, так как id уникальный(oldItem.id == newItem.id) + } + + override fun areContentsTheSame( + oldItem: EditCoursesDoctorModel, + newItem: EditCoursesDoctorModel + ): Boolean {//Утут нужно сравнивать весь спсок старых элементов и новых + return oldItem == newItem//Сравниваем полностью весь список новы и старый + } + } + + //Интерфейс нажатия на кнопку удалить товар из корзины + interface Listener { + fun onClickCoursesYour(item: EditCoursesDoctorModel) + } + + +} + + + + diff --git a/app/src/main/java/com/example/doctor/Setting/Courses/EditCourses/TabLayoutEditCourses/AllSportCoursesDoctorFragment.kt b/app/src/main/java/com/example/doctor/Setting/Courses/EditCourses/TabLayoutEditCourses/AllSportCoursesDoctorFragment.kt new file mode 100644 index 0000000..3f56817 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Setting/Courses/EditCourses/TabLayoutEditCourses/AllSportCoursesDoctorFragment.kt @@ -0,0 +1,292 @@ +package com.example.doctor.Setting.Courses.EditCourses.TabLayoutEditCourses + +import android.content.Context +import android.content.Intent +import android.net.ConnectivityManager +import android.net.NetworkCapabilities +import android.os.Build +import android.os.Bundle +import android.util.Log +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.fragment.app.activityViewModels +import androidx.recyclerview.widget.GridLayoutManager +import com.example.doctor.Auth.AuthActivity +import com.example.doctor.CodeError.Code429Activity +import com.example.doctor.CodeError.Code500Activity +import com.example.doctor.DoctorViewModel +import com.example.doctor.Enternet.EnternetActivity +import com.example.doctor.Enternet.EnternetFragment +import com.example.doctor.Pref.ClearPref +import com.example.doctor.Pref.ConclusionPref +import com.example.doctor.Pref.SavePref +import com.example.doctor.R +import com.example.doctor.Retrofit.DoctorApi +import com.example.doctor.Setting.Courses.EditCourses.TabLayoutEditCourses.Adapter.EditCoursesDoctorAllAdapter +import com.example.doctor.Setting.Courses.EditCourses.TabLayoutEditCourses.Model.EditCoursesDoctorModel +import com.example.doctor.Toast.showCustomInfoToast +import com.example.doctor.databinding.FragmentAllSportCoursesDoctorBinding +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory + +class AllSportCoursesDoctorFragment : Fragment(), EditCoursesDoctorAllAdapter.Listener{ + private lateinit var binding:FragmentAllSportCoursesDoctorBinding + lateinit var adapterAll: EditCoursesDoctorAllAdapter + private lateinit var doctorApi: DoctorApi + private val model: DoctorViewModel by activityViewModels() + val prefDoctorConclusion = ConclusionPref() + val prefDoctorClear = ClearPref() + val prefDoctorSave = SavePref() + var id_patient = 0; + var id_courses = 0; + var id_activitis = 0; + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentAllSportCoursesDoctorBinding.inflate(layoutInflater,container,false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + initRcView() +// id_courses = prefDoctorConclusion.conclusionIdCourses(requireContext()) +// id_activitis = prefDoctorConclusion.conclusionIdActivitis(requireContext()) + + model.CoursesCustomDoctor.observe(viewLifecycleOwner){ + id_courses = it.id + id_activitis = it.id_activity + GetEditCourseDoctorAll(id_courses) + } + + + model.EditCoursesDoctorAllList.observe(viewLifecycleOwner){ + adapterAll.submitList(it) + } +// model.CoursesDoctorCA.observe(viewLifecycleOwner){ +// requireActivity().runOnUiThread { +// id_courses = it.id_courses +// id_activitis = it.id_activitis +// } +// } + Log.i("2sadas","2sadasd") + } + + override fun onResume() { + super.onResume() + Log.i("2sadas","22sadasd") + } + + //Получения списка курсов созданных доктором + fun GetEditCourseDoctorAll(id:Int) { + if (isOnline(requireContext())) { + initRetrofit() + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) + CoroutineScope(Dispatchers.IO).launch { + val listCoursesDoctor = doctorApi.GetEditCourseDoctorAll("Bearer $Tokens",id) + + requireActivity().runOnUiThread { + + + //Фиксируем полученные данные + val List = listCoursesDoctor.body() + val Nice = listCoursesDoctor.isSuccessful + val Code = listCoursesDoctor.code() + if(Code==429){ + val intetn = Intent(requireContext(), Code429Activity::class.java) + + startActivity(intetn) + } + else if(Code==200) { + //Если нету ошибок + if (Nice) { + //Если нету ошибок + if (List != null) { + model.EditCoursesDoctorAllList.value = List.set_of_sports_exercises_all + binding.txtNull.visibility = View.GONE + } + else{ + binding.txtNull.visibility = View.VISIBLE + } + } + } + else if (Code == 500) { + val intetn = Intent(requireContext(), Code500Activity::class.java) + + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(requireContext(), AuthActivity::class.java) + activity?.finish() + startActivity(intetn) + } + + } + + } + } else { + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() + startActivity(intetn) + } + + } + //Получения списка курсов созданных доктором + fun GetEditCourseDoctorYour(id:Int) { + if (isOnline(requireContext())) { + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) + CoroutineScope(Dispatchers.IO).launch { + val listCoursesDoctor = doctorApi.GetEditCourseDoctorYour("Bearer $Tokens",id) + + requireActivity().runOnUiThread { + + //Фиксируем полученные данные + val CoursesDoctorList = listCoursesDoctor.body() + + //Если нету ошибок + if (CoursesDoctorList != null) { + model.EditCoursesDoctorYourList.value = CoursesDoctorList.set_of_sports_exercises_your + } + + } + + } + } else { + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() + startActivity(intetn) + } + + } + + //Добавление упражнения в курс + fun AddCoursesCreatingDoctor(id:Int,id_activitis:Int) { + if (isOnline(requireContext())) { + initRetrofit() + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) + CoroutineScope(Dispatchers.IO).launch { + val listCoursesDoctorAllAdd = doctorApi.AddCoursesCreatingDoctor("Bearer $Tokens",id,id_activitis) + + requireActivity().runOnUiThread { + + + //Фиксируем полученные данные + val List = listCoursesDoctorAllAdd.body() + val Nice = listCoursesDoctorAllAdd.isSuccessful + val Code = listCoursesDoctorAllAdd.code() + if(Code==429){ + val intetn = Intent(requireContext(), Code429Activity::class.java) + + startActivity(intetn) + } + else if(Code==200) { + //Если нету ошибок + if (Nice) { + if (List != null) { + Toast(requireContext()).showCustomInfoToast(List.message, requireActivity()) + GetEditCourseDoctorAll(id_courses) + GetEditCourseDoctorYour(id_courses) + } + } + } + else if (Code == 500) { + val intetn = Intent(requireContext(), Code500Activity::class.java) + + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(requireContext(), AuthActivity::class.java) + activity?.finish() + startActivity(intetn) + } + + } + + } + } else { + activity?.supportFragmentManager?.beginTransaction() + ?.replace(R.id.CLMain, EnternetFragment.newInstance()) + ?.commit() + } + } + + //Инициализируем Retrofit + private fun initRetrofit() { + val interceptor = HttpLoggingInterceptor() + interceptor.level = HttpLoggingInterceptor.Level.BODY + + val client = OkHttpClient + .Builder() + .addInterceptor(interceptor) + .build() + + val retrofit = Retrofit.Builder() + .baseUrl("https://rehabilitation.vmeda.org/api/") + .client(client) + .addConverterFactory(GsonConverterFactory.create()) + .build() + + doctorApi = retrofit.create(DoctorApi::class.java) + + } + + //Проверка интернета + fun isOnline(context: Context): Boolean { + if (context == null) return false + val connectivityManager = + context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + val capabilities = + connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork) + if (capabilities != null) { + when { + capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> { + return true + } + + capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> { + return true + } + + capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> { + return true + } + } + } + } else { + val activeNetworkInfo = connectivityManager.activeNetworkInfo + if (activeNetworkInfo != null && activeNetworkInfo.isConnected) { + return true + } + } + return false + } + + + + + //Инициализация списка + private fun initRcView() = with(binding) { + rcView.layoutManager = GridLayoutManager(requireContext(), 1)//По вертикали будет выводить по умолчанию + adapterAll = EditCoursesDoctorAllAdapter(this@AllSportCoursesDoctorFragment) + rcView.adapter = adapterAll + } + + companion object { + fun newInstance() = AllSportCoursesDoctorFragment() + } + + + override fun onClickCoursesAll(item: EditCoursesDoctorModel) { + AddCoursesCreatingDoctor(item.id,id_activitis) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/Setting/Courses/EditCourses/TabLayoutEditCourses/Model/CoursesDoctorCA.kt b/app/src/main/java/com/example/doctor/Setting/Courses/EditCourses/TabLayoutEditCourses/Model/CoursesDoctorCA.kt new file mode 100644 index 0000000..570ecda --- /dev/null +++ b/app/src/main/java/com/example/doctor/Setting/Courses/EditCourses/TabLayoutEditCourses/Model/CoursesDoctorCA.kt @@ -0,0 +1,8 @@ +package com.example.doctor.Setting.Courses.EditCourses.TabLayoutEditCourses.Model + +data class CoursesDoctorCA( + val id_courses: Int, + val id_activitis: Int, + + ) + diff --git a/app/src/main/java/com/example/doctor/Setting/Courses/EditCourses/TabLayoutEditCourses/Model/EditCoursesDoctorListAllModel.kt b/app/src/main/java/com/example/doctor/Setting/Courses/EditCourses/TabLayoutEditCourses/Model/EditCoursesDoctorListAllModel.kt new file mode 100644 index 0000000..6a206c8 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Setting/Courses/EditCourses/TabLayoutEditCourses/Model/EditCoursesDoctorListAllModel.kt @@ -0,0 +1,8 @@ +package com.example.doctor.Setting.Courses.EditCourses.TabLayoutEditCourses.Model + +data class EditCoursesDoctorListAllModel( + val set_of_sports_exercises_all: List + + + ) + diff --git a/app/src/main/java/com/example/doctor/Setting/Courses/EditCourses/TabLayoutEditCourses/Model/EditCoursesDoctorListYourModel.kt b/app/src/main/java/com/example/doctor/Setting/Courses/EditCourses/TabLayoutEditCourses/Model/EditCoursesDoctorListYourModel.kt new file mode 100644 index 0000000..2ea8533 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Setting/Courses/EditCourses/TabLayoutEditCourses/Model/EditCoursesDoctorListYourModel.kt @@ -0,0 +1,8 @@ +package com.example.doctor.Setting.Courses.EditCourses.TabLayoutEditCourses.Model + +data class EditCoursesDoctorListYourModel( + val set_of_sports_exercises_your: List + + + ) + diff --git a/app/src/main/java/com/example/doctor/Setting/Courses/EditCourses/TabLayoutEditCourses/Model/EditCoursesDoctorModel.kt b/app/src/main/java/com/example/doctor/Setting/Courses/EditCourses/TabLayoutEditCourses/Model/EditCoursesDoctorModel.kt new file mode 100644 index 0000000..6d0a519 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Setting/Courses/EditCourses/TabLayoutEditCourses/Model/EditCoursesDoctorModel.kt @@ -0,0 +1,12 @@ +package com.example.doctor.Setting.Courses.EditCourses.TabLayoutEditCourses.Model + +data class EditCoursesDoctorModel( + val number: Int, + val id: Int, + val id_exercises: Int, + val name:String, + val description:String, + val url_image:String, + var expand : Boolean = false, + ) + diff --git a/app/src/main/java/com/example/doctor/Setting/Courses/EditCourses/TabLayoutEditCourses/YourSportCoursesDoctorFragment.kt b/app/src/main/java/com/example/doctor/Setting/Courses/EditCourses/TabLayoutEditCourses/YourSportCoursesDoctorFragment.kt new file mode 100644 index 0000000..37f8e3f --- /dev/null +++ b/app/src/main/java/com/example/doctor/Setting/Courses/EditCourses/TabLayoutEditCourses/YourSportCoursesDoctorFragment.kt @@ -0,0 +1,290 @@ +package com.example.doctor.Setting.Courses.EditCourses.TabLayoutEditCourses + +import android.content.Context +import android.content.Intent +import android.net.ConnectivityManager +import android.net.NetworkCapabilities +import android.os.Build +import android.os.Bundle +import android.util.Log +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.fragment.app.activityViewModels +import androidx.recyclerview.widget.GridLayoutManager +import com.example.doctor.Auth.AuthActivity +import com.example.doctor.CodeError.Code429Activity +import com.example.doctor.CodeError.Code500Activity +import com.example.doctor.DoctorViewModel +import com.example.doctor.Enternet.EnternetActivity +import com.example.doctor.Enternet.EnternetFragment +import com.example.doctor.Pref.ConclusionPref +import com.example.doctor.R +import com.example.doctor.Retrofit.DoctorApi +import com.example.doctor.Setting.Courses.EditCourses.TabLayoutEditCourses.Adapter.EditCoursesDoctorYourAdapter +import com.example.doctor.Setting.Courses.EditCourses.TabLayoutEditCourses.Model.EditCoursesDoctorModel +import com.example.doctor.Toast.showCustomInfoToast +import com.example.doctor.databinding.FragmentYourSportCoursesDoctorBinding +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory + +class YourSportCoursesDoctorFragment : Fragment(), EditCoursesDoctorYourAdapter.Listener { + private lateinit var binding: FragmentYourSportCoursesDoctorBinding + lateinit var adapterYour: EditCoursesDoctorYourAdapter + private lateinit var doctorApi: DoctorApi + val prefDoctorConclusion = ConclusionPref() + private val model: DoctorViewModel by activityViewModels() + var id_patient = 0; + var id_courses = 0; + var id_activitis = 0; + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentYourSportCoursesDoctorBinding.inflate(layoutInflater,container,false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + initRcView() + + model.CoursesCustomDoctor.observe(viewLifecycleOwner){ + id_courses = it.id + id_activitis = it.id_activity + GetEditCourseDoctorYour(id_courses) + } +// id_courses = prefDoctorConclusion.conclusionIdCourses(requireContext()) +// id_activitis = prefDoctorConclusion.conclusionIdActivitis(requireContext()) + + + model.EditCoursesDoctorYourList.observe(viewLifecycleOwner){ + adapterYour.submitList(it) + } +// model.CoursesDoctorCA.observe(viewLifecycleOwner){ +// id_courses = it.id_courses +// id_activitis = it.id_activitis +// } + Log.i("1sadas","1sadasd") + } + + override fun onResume() { + super.onResume() + Log.i("1sadas","11sadasd") + + } + //Получения списка курсов созданных доктором + fun GetEditCourseDoctorYour(id:Int) { + if (isOnline(requireContext())) { + initRetrofit() + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) + CoroutineScope(Dispatchers.IO).launch { + val listCoursesDoctor = doctorApi.GetEditCourseDoctorYour("Bearer $Tokens",id) + + requireActivity().runOnUiThread { + + + //Фиксируем полученные данные + val List = listCoursesDoctor.body() + val Nice = listCoursesDoctor.isSuccessful + val Code = listCoursesDoctor.code() + if(Code==429){ + val intetn = Intent(requireContext(), Code429Activity::class.java) + + startActivity(intetn) + } + else if(Code==200) { + //Если нету ошибок + if (Nice) { + //Если нету ошибок + if (List != null) { + model.EditCoursesDoctorYourList.value = List.set_of_sports_exercises_your + binding.txtNull.visibility = View.GONE + } + else{ + binding.txtNull.visibility = View.VISIBLE + } + } + } + else if (Code == 500) { + val intetn = Intent(requireContext(), Code500Activity::class.java) + + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(requireContext(), AuthActivity::class.java) + activity?.finish() + startActivity(intetn) + } + + } + + } + } else { + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() + startActivity(intetn) + } + + } + + //Получения списка курсов созданных доктором + fun GetEditCourseDoctorAll(id:Int) { + if (isOnline(requireContext())) { + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) + CoroutineScope(Dispatchers.IO).launch { + val listCoursesDoctor = doctorApi.GetEditCourseDoctorAll("Bearer $Tokens",id) + + requireActivity().runOnUiThread { + + //Фиксируем полученные данные + val CoursesDoctorList = listCoursesDoctor.body() + + //Если нету ошибок + if (CoursesDoctorList != null) { + model.EditCoursesDoctorAllList.value = CoursesDoctorList.set_of_sports_exercises_all + } + + + } + + } + } else { + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() + startActivity(intetn) + } + + } + + //Получения списка курсов созданных доктором + fun ClearCoursesCreatingDoctor(id:Int) { + + if (isOnline(requireContext())) { + initRetrofit() + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) + CoroutineScope(Dispatchers.IO).launch { + val listCoursesDoctorYourClear = doctorApi.ClearCoursesCreatingDoctor("Bearer $Tokens",id) + + requireActivity().runOnUiThread { + + + //Фиксируем полученные данные + val List = listCoursesDoctorYourClear.body() + val Nice = listCoursesDoctorYourClear.isSuccessful + val Code = listCoursesDoctorYourClear.code() + if(Code==429){ + val intetn = Intent(requireContext(), Code429Activity::class.java) + + startActivity(intetn) + } + else if(Code==200) { + //Если нету ошибок + if (Nice) { + if (List != null) { + Toast(requireContext()).showCustomInfoToast(List.message, requireActivity()) + GetEditCourseDoctorYour(id_courses) + GetEditCourseDoctorAll(id_courses) + } + } + } + else if (Code == 500) { + val intetn = Intent(requireContext(), Code500Activity::class.java) + + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(requireContext(), AuthActivity::class.java) + activity?.finish() + startActivity(intetn) + } + + } + + } + } else { + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() + startActivity(intetn) + } + + + } + + //Проверка интернета + fun isOnline(context: Context): Boolean { + if (context == null) return false + val connectivityManager = + context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + val capabilities = + connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork) + if (capabilities != null) { + when { + capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> { + return true + } + + capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> { + return true + } + + capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> { + return true + } + } + } + } else { + val activeNetworkInfo = connectivityManager.activeNetworkInfo + if (activeNetworkInfo != null && activeNetworkInfo.isConnected) { + return true + } + } + return false + } + + + //Инициализируем Retrofit + private fun initRetrofit() { + val interceptor = HttpLoggingInterceptor() + interceptor.level = HttpLoggingInterceptor.Level.BODY + + val client = OkHttpClient + .Builder() + .addInterceptor(interceptor) + .build() + + val retrofit = Retrofit.Builder() + .baseUrl("https://rehabilitation.vmeda.org/api/") + .client(client) + .addConverterFactory(GsonConverterFactory.create()) + .build() + + doctorApi = retrofit.create(DoctorApi::class.java) + + } + + //Инициализация списка + private fun initRcView() = with(binding) { + rcView.layoutManager = GridLayoutManager(requireContext(), 1)//По вертикали будет выводить по умолчанию + adapterYour = EditCoursesDoctorYourAdapter(this@YourSportCoursesDoctorFragment) + rcView.adapter = adapterYour + } + companion object { + + fun newInstance() = YourSportCoursesDoctorFragment() + } + + override fun onClickCoursesYour(item: EditCoursesDoctorModel) { + Log.i("sadsadsadasd",item.toString()) + + ClearCoursesCreatingDoctor(item.id_exercises) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/Setting/Courses/Model/CoursesDoctorListModel.kt b/app/src/main/java/com/example/doctor/Setting/Courses/Model/CoursesDoctorListModel.kt new file mode 100644 index 0000000..22ef494 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Setting/Courses/Model/CoursesDoctorListModel.kt @@ -0,0 +1,6 @@ +package com.example.doctor.Setting.Courses.Model + +data class CoursesDoctorListModel( + val sport_courses_doctor: List + ) + diff --git a/app/src/main/java/com/example/doctor/Setting/Courses/Model/CoursesDoctorModel.kt b/app/src/main/java/com/example/doctor/Setting/Courses/Model/CoursesDoctorModel.kt new file mode 100644 index 0000000..3898a26 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Setting/Courses/Model/CoursesDoctorModel.kt @@ -0,0 +1,14 @@ +package com.example.doctor.Setting.Courses.Model + +data class CoursesDoctorModel( + val number: Int, + val id: Int, + val name: String, + val description: String, + val visibility: Int, + val user_id: Int, + val id_activity:Int, + val created_at: String, + val updated_at: String, + ) + diff --git a/app/src/main/java/com/example/doctor/Setting/SettingFragment.kt b/app/src/main/java/com/example/doctor/Setting/SettingFragment.kt new file mode 100644 index 0000000..97251f3 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Setting/SettingFragment.kt @@ -0,0 +1,175 @@ +package com.example.doctor.Setting + +import android.content.Context +import android.content.Intent +import android.net.ConnectivityManager +import android.net.NetworkCapabilities +import android.os.Build +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.appcompat.app.AlertDialog +import com.example.doctor.Auth.AuthActivity +import com.example.doctor.Auth.AuthFragment +import com.example.doctor.CodeError.Code429Activity +import com.example.doctor.CodeError.Code500Activity +import com.example.doctor.Enternet.EnternetActivity +import com.example.doctor.Enternet.EnternetCheck +import com.example.doctor.Enternet.EnternetFragment +import com.example.doctor.MainActivity +import com.example.doctor.Pref.ClearPref +import com.example.doctor.Pref.ConclusionPref +import com.example.doctor.R +import com.example.doctor.Retrofit.DoctorApi +import com.example.doctor.Setting.Courses.CoursesActivity +import com.example.doctor.Toast.showCustomInfoToast +import com.example.doctor.databinding.FragmentSettingBinding +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory + +class SettingFragment : Fragment() { + private lateinit var binding:FragmentSettingBinding + + private var Token = "" + val prefDoctorClear= ClearPref() + private lateinit var doctorApi: DoctorApi + val prefDoctorConclusion = ConclusionPref() + + //Класс проверки интеренета + val enternetCheck = EnternetCheck() + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentSettingBinding.inflate(layoutInflater,container,false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + + button() + } + + private fun button() = with(binding) { + btnLogout.setOnClickListener{ + + createAlеrtDialogExitAuth() + } + + btnYourData.setOnClickListener{ + activity?.supportFragmentManager?.beginTransaction() + ?.replace(R.id.CLMainFragment, YourDataFragment.newInstance()) + ?.commit() + } + + binding.btnAddCourses.setOnClickListener{ + val intetn = Intent(requireContext(), CoursesActivity::class.java) + startActivity(intetn) + } + + + + } + + + //Диалоговое окно + private fun createAlеrtDialogExitAuth() { + val builder = AlertDialog.Builder(requireContext()) + builder.setTitle("Выход") + builder.setMessage("Вы уверены что хотите выйти из аккаунта") + builder.setNegativeButton("Назад") { dialogInterface, i -> + + } + builder.setPositiveButton("Подтвердить") { dialogInterface, i -> + + Logout() + + } + builder.show() + } + + //Получения списка пациентов + fun Logout() { + if (enternetCheck.isOnline(requireContext())) { + initRetrofit() + val Tokens = prefDoctorConclusion.conclusionToken(requireContext()) + CoroutineScope(Dispatchers.IO).launch { + val listProduct = doctorApi.LogoutDoctor("Bearer $Tokens") + + activity?.runOnUiThread { + + + //Фиксируем полученные данные + val List = listProduct.body() + val Nice = listProduct.isSuccessful + val Code = listProduct.code() + if(Code==429){ + val intetn = Intent(requireContext(), Code429Activity::class.java) + startActivity(intetn) + } + else if(Code==200) { + //Если нету ошибок + if (Nice) { + if (List != null) { + Toast(requireContext()).showCustomInfoToast(List?.message.toString(), requireActivity()) + prefDoctorClear.clearToken(requireContext()) + activity?.supportFragmentManager?.beginTransaction() + ?.replace(R.id.CLMain, AuthFragment.newInstance()) + ?.commit() + } + } + } + else if (Code == 500) { + val intetn = Intent(requireContext(), Code500Activity::class.java) + startActivity(intetn) + } + else if (Code == 401) { + val intetn = Intent(requireContext(), AuthActivity::class.java) + activity?.finish() + startActivity(intetn) + } + } + } + } else { + val intetn = Intent(requireContext(), EnternetActivity::class.java) + activity?.finish() + startActivity(intetn) + } + } + + //Инициализируем Retrofit + private fun initRetrofit() { + val interceptor = HttpLoggingInterceptor() + interceptor.level = HttpLoggingInterceptor.Level.BODY + + val client = OkHttpClient + .Builder() + .addInterceptor(interceptor) + .build() + + val retrofit = Retrofit.Builder() + .baseUrl("https://rehabilitation.vmeda.org/api/") + .client(client) + .addConverterFactory(GsonConverterFactory.create()) + .build() + + doctorApi = retrofit.create(DoctorApi::class.java) + + } + + + + companion object { + fun newInstance() = SettingFragment() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/Setting/YourDataFragment.kt b/app/src/main/java/com/example/doctor/Setting/YourDataFragment.kt new file mode 100644 index 0000000..8651804 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Setting/YourDataFragment.kt @@ -0,0 +1,44 @@ +package com.example.doctor.Setting + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.example.doctor.R +import com.example.doctor.databinding.FragmentYourDataBinding + + +class YourDataFragment : Fragment() { + private lateinit var binding:FragmentYourDataBinding + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentYourDataBinding.inflate(layoutInflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + button() + } + + private fun button()= with(binding) { + btnExitYorData.setOnClickListener{ + activity?.supportFragmentManager?.beginTransaction() + ?.replace(R.id.CLMainFragment, SettingFragment.newInstance()) + ?.commit() + } + + } + + companion object { + fun newInstance() = YourDataFragment() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/Sport/AddSportPatientModel.kt b/app/src/main/java/com/example/doctor/Sport/AddSportPatientModel.kt new file mode 100644 index 0000000..02190aa --- /dev/null +++ b/app/src/main/java/com/example/doctor/Sport/AddSportPatientModel.kt @@ -0,0 +1,8 @@ +package com.example.doctor.Sport + +data class AddSportPatientModel( + val id_patient: Int, + val id_course: Int, + val all_day: Int, +) + diff --git a/app/src/main/java/com/example/doctor/Toast/WrapToast.kt b/app/src/main/java/com/example/doctor/Toast/WrapToast.kt new file mode 100644 index 0000000..e77ae1d --- /dev/null +++ b/app/src/main/java/com/example/doctor/Toast/WrapToast.kt @@ -0,0 +1,103 @@ +package com.example.doctor.Toast + +import android.app.Activity +import android.view.Gravity +import android.widget.TextView +import android.widget.Toast +import com.example.doctor.R + + +fun Toast.showCustomToast(message: String, activity: Activity) +{ + + val layout = activity.layoutInflater.inflate ( + R.layout.custom_toast_layout, + activity.findViewById(R.id.toast_container) + ) + + + // set the text of the TextView of the message + val textView = layout.findViewById(R.id.toast_text) + //Передача текста который мы введем + textView.text = message + + //Если добавить кнопку то можно сделать некую функцию, к примеру при добавление товара в корзину, можно сверху выводить перейти в корзину и по нажатию мы переходим в корзину + /* textView.setOnClickListener{ + setToolbarTitle.perehods() + Log.d("dsfsd","111111111111111111111111111") + }*/ + // use the application extension function + this.apply { + //setGravity(Gravity.BOTTOM, 0, 40) + duration = Toast.LENGTH_SHORT + view = layout.rootView + show() + } + + +} fun Toast.showCustomDangerToast(message: String, activity: Activity) +{ + val layout = activity.layoutInflater.inflate ( + R.layout.custom_toast_layout_danger, + activity.findViewById(R.id.toast_container) + ) + + // set the text of the TextView of the message + val textView = layout.findViewById(R.id.toast_text) + //Передача текста который мы введем + textView.text = message + + //Если добавить кнопку то можно сделать некую функцию, к примеру при добавление товара в корзину, можно сверху выводить перейти в корзину и по нажатию мы переходим в корзину + + // use the application extension function + this.apply { + setGravity(Gravity.BOTTOM, 0, 40) + duration = Toast.LENGTH_SHORT + view = layout.rootView + show() + } +} + +fun Toast.showCustomInfoToast(message: String, activity: Activity) { + val layout = activity.layoutInflater.inflate( + R.layout.custom_toast_layout_info, + activity.findViewById(R.id.toast_container) + ) + + // set the text of the TextView of the message + val textView = layout.findViewById(R.id.toast_text) + //Передача текста который мы введем + textView.text = message + + //Если добавить кнопку то можно сделать некую функцию, к примеру при добавление товара в корзину, можно сверху выводить перейти в корзину и по нажатию мы переходим в корзину + + // use the application extension function + this.apply { + setGravity(Gravity.BOTTOM, 0, 40) + duration = Toast.LENGTH_SHORT + view = layout.rootView + show() + } +} + +fun Toast.showCustomNiceToast(message: String, activity: Activity) { + val layout = activity.layoutInflater.inflate( + R.layout.custom_toast_layout_nice, + activity.findViewById(R.id.toast_container) + ) + + // set the text of the TextView of the message + val textView = layout.findViewById(R.id.toast_text) + //Передача текста который мы введем + textView.text = message + + //Если добавить кнопку то можно сделать некую функцию, к примеру при добавление товара в корзину, можно сверху выводить перейти в корзину и по нажатию мы переходим в корзину + + // use the application extension function + this.apply { + setGravity(Gravity.BOTTOM, 0, 40) + duration = Toast.LENGTH_SHORT + view = layout.rootView + show() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/doctor/Worker/MyWorker.kt b/app/src/main/java/com/example/doctor/Worker/MyWorker.kt new file mode 100644 index 0000000..f3975b1 --- /dev/null +++ b/app/src/main/java/com/example/doctor/Worker/MyWorker.kt @@ -0,0 +1,100 @@ +package com.example.doctor.Worker + +import android.annotation.SuppressLint +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import android.os.Build +import android.util.Log +import androidx.core.app.NotificationManagerCompat +import androidx.work.Worker +import androidx.work.WorkerParameters +import com.example.doctor.MainActivity +import com.example.doctor.R +import java.nio.file.attribute.AclEntry.Builder + +class MyWorker(context: Context, workerParameters: WorkerParameters): Worker(context,workerParameters) { + companion object{ + const val CHANNEL_ID="channel_id" + const val NOTIFICATION=1 + } + override fun doWork(): Result { + Log.d("doWork","doWork - error") + showNotification() + return Result.success()//Возврощаем результат + } + + @SuppressLint("UnspecifiedImmutableFlag", "MissingPermission", "ObsoleteSdkInt") + private fun showNotification(){ + val intent = Intent(applicationContext,MainActivity::class.java).apply{ + flags=Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + } + + val pendingIntetn= PendingIntent.getActivity(applicationContext,0,intent,0) + + val notification = Notification.Builder(applicationContext, CHANNEL_ID) + .setSmallIcon(R.drawable.door) + .setContentTitle("new task") + .setContentText("Сообщение 1")//Сообщение + .setPriority(Notification.PRIORITY_MAX)//Приоритет + .setAutoCancel(true)//Время отмены + .setContentIntent(pendingIntetn)//Ожидание завершения + + + if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){ + val channelName = "channel name" + val channelDescription = "channel Description" + val channelImportance=NotificationManager.IMPORTANCE_HIGH + + //Канал + val channel = NotificationChannel(CHANNEL_ID,channelName,channelImportance).apply { + description = channelDescription + } + + //Менеджер уведомлений + val notificationManager = applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + notificationManager.createNotificationChannel(channel)//Создаем канал с помошью менеджера уведомлений + + with(NotificationManagerCompat.from(applicationContext)){ + notify(NOTIFICATION,notification.build()) + } + } + } +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/anim/fade_in.xml b/app/src/main/res/anim/fade_in.xml new file mode 100644 index 0000000..64a46de --- /dev/null +++ b/app/src/main/res/anim/fade_in.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/app/src/main/res/anim/fade_out.xml b/app/src/main/res/anim/fade_out.xml new file mode 100644 index 0000000..afab199 --- /dev/null +++ b/app/src/main/res/anim/fade_out.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/app/src/main/res/anim/slide_in.xml b/app/src/main/res/anim/slide_in.xml new file mode 100644 index 0000000..8a4a29c --- /dev/null +++ b/app/src/main/res/anim/slide_in.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/app/src/main/res/anim/slide_out.xml b/app/src/main/res/anim/slide_out.xml new file mode 100644 index 0000000..220ef3b --- /dev/null +++ b/app/src/main/res/anim/slide_out.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/app/src/main/res/color/bnv_tab_item_foreground.xml b/app/src/main/res/color/bnv_tab_item_foreground.xml new file mode 100644 index 0000000..4c5d3f0 --- /dev/null +++ b/app/src/main/res/color/bnv_tab_item_foreground.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/add1.png b/app/src/main/res/drawable/add1.png new file mode 100644 index 0000000..515d45e Binary files /dev/null and b/app/src/main/res/drawable/add1.png differ diff --git a/app/src/main/res/drawable/add_patient_1.png b/app/src/main/res/drawable/add_patient_1.png new file mode 100644 index 0000000..b9429e1 Binary files /dev/null and b/app/src/main/res/drawable/add_patient_1.png differ diff --git a/app/src/main/res/drawable/add_patient_2.png b/app/src/main/res/drawable/add_patient_2.png new file mode 100644 index 0000000..81b3fd0 Binary files /dev/null and b/app/src/main/res/drawable/add_patient_2.png differ diff --git a/app/src/main/res/drawable/angle_right.png b/app/src/main/res/drawable/angle_right.png new file mode 100644 index 0000000..611eda6 Binary files /dev/null and b/app/src/main/res/drawable/angle_right.png differ diff --git a/app/src/main/res/drawable/appeals.png b/app/src/main/res/drawable/appeals.png new file mode 100644 index 0000000..dac2ecc Binary files /dev/null and b/app/src/main/res/drawable/appeals.png differ diff --git a/app/src/main/res/drawable/arrow_24.xml b/app/src/main/res/drawable/arrow_24.xml new file mode 100644 index 0000000..8452791 --- /dev/null +++ b/app/src/main/res/drawable/arrow_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/arrow_right.png b/app/src/main/res/drawable/arrow_right.png new file mode 100644 index 0000000..1f6eb56 Binary files /dev/null and b/app/src/main/res/drawable/arrow_right.png differ diff --git a/app/src/main/res/drawable/check.png b/app/src/main/res/drawable/check.png new file mode 100644 index 0000000..ede32da Binary files /dev/null and b/app/src/main/res/drawable/check.png differ diff --git a/app/src/main/res/drawable/close.png b/app/src/main/res/drawable/close.png new file mode 100644 index 0000000..ff3cb9c Binary files /dev/null and b/app/src/main/res/drawable/close.png differ diff --git a/app/src/main/res/drawable/close_24.xml b/app/src/main/res/drawable/close_24.xml new file mode 100644 index 0000000..844b6b6 --- /dev/null +++ b/app/src/main/res/drawable/close_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/door.png b/app/src/main/res/drawable/door.png new file mode 100644 index 0000000..efa0134 Binary files /dev/null and b/app/src/main/res/drawable/door.png differ diff --git a/app/src/main/res/drawable/door2.png b/app/src/main/res/drawable/door2.png new file mode 100644 index 0000000..6dfac0a Binary files /dev/null and b/app/src/main/res/drawable/door2.png differ diff --git a/app/src/main/res/drawable/eye_no.png b/app/src/main/res/drawable/eye_no.png new file mode 100644 index 0000000..62dc80a Binary files /dev/null and b/app/src/main/res/drawable/eye_no.png differ diff --git a/app/src/main/res/drawable/eye_yes.png b/app/src/main/res/drawable/eye_yes.png new file mode 100644 index 0000000..59c30d9 Binary files /dev/null and b/app/src/main/res/drawable/eye_yes.png differ diff --git a/app/src/main/res/drawable/folder.png b/app/src/main/res/drawable/folder.png new file mode 100644 index 0000000..a7f15a2 Binary files /dev/null and b/app/src/main/res/drawable/folder.png differ diff --git a/app/src/main/res/drawable/gradientv1.xml b/app/src/main/res/drawable/gradientv1.xml new file mode 100644 index 0000000..2b00d72 --- /dev/null +++ b/app/src/main/res/drawable/gradientv1.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/home.png b/app/src/main/res/drawable/home.png new file mode 100644 index 0000000..8eea8b2 Binary files /dev/null and b/app/src/main/res/drawable/home.png differ diff --git a/app/src/main/res/drawable/home_24.xml b/app/src/main/res/drawable/home_24.xml new file mode 100644 index 0000000..5a870f5 --- /dev/null +++ b/app/src/main/res/drawable/home_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/kyb.png b/app/src/main/res/drawable/kyb.png new file mode 100644 index 0000000..02dcecd Binary files /dev/null and b/app/src/main/res/drawable/kyb.png differ diff --git a/app/src/main/res/drawable/loading.gif b/app/src/main/res/drawable/loading.gif new file mode 100644 index 0000000..8e4be53 Binary files /dev/null and b/app/src/main/res/drawable/loading.gif differ diff --git a/app/src/main/res/drawable/logovm.png b/app/src/main/res/drawable/logovm.png new file mode 100644 index 0000000..13ed986 Binary files /dev/null and b/app/src/main/res/drawable/logovm.png differ diff --git a/app/src/main/res/drawable/logovmediv2.png b/app/src/main/res/drawable/logovmediv2.png new file mode 100644 index 0000000..57f2d36 Binary files /dev/null and b/app/src/main/res/drawable/logovmediv2.png differ diff --git a/app/src/main/res/drawable/nice.png b/app/src/main/res/drawable/nice.png new file mode 100644 index 0000000..ebe772d Binary files /dev/null and b/app/src/main/res/drawable/nice.png differ diff --git a/app/src/main/res/drawable/patient.png b/app/src/main/res/drawable/patient.png new file mode 100644 index 0000000..d225a6d Binary files /dev/null and b/app/src/main/res/drawable/patient.png differ diff --git a/app/src/main/res/drawable/search.png b/app/src/main/res/drawable/search.png new file mode 100644 index 0000000..48b8e6d Binary files /dev/null and b/app/src/main/res/drawable/search.png differ diff --git a/app/src/main/res/drawable/setting.png b/app/src/main/res/drawable/setting.png new file mode 100644 index 0000000..7a85563 Binary files /dev/null and b/app/src/main/res/drawable/setting.png differ diff --git a/app/src/main/res/drawable/tab_indicator.xml b/app/src/main/res/drawable/tab_indicator.xml new file mode 100644 index 0000000..64dcce6 --- /dev/null +++ b/app/src/main/res/drawable/tab_indicator.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/font/akaya_telivigala.xml b/app/src/main/res/font/akaya_telivigala.xml new file mode 100644 index 0000000..acd5639 --- /dev/null +++ b/app/src/main/res/font/akaya_telivigala.xml @@ -0,0 +1,7 @@ + + + diff --git a/app/src/main/res/font/hero.ttf b/app/src/main/res/font/hero.ttf new file mode 100644 index 0000000..53cf604 Binary files /dev/null and b/app/src/main/res/font/hero.ttf differ diff --git a/app/src/main/res/font/hero_medium.ttf b/app/src/main/res/font/hero_medium.ttf new file mode 100644 index 0000000..985d768 Binary files /dev/null and b/app/src/main/res/font/hero_medium.ttf differ diff --git a/app/src/main/res/font/montserrat_alternates.ttf b/app/src/main/res/font/montserrat_alternates.ttf new file mode 100644 index 0000000..1930e0e Binary files /dev/null and b/app/src/main/res/font/montserrat_alternates.ttf differ diff --git a/app/src/main/res/font/montserrat_alternates_medium.ttf b/app/src/main/res/font/montserrat_alternates_medium.ttf new file mode 100644 index 0000000..b5d0af3 Binary files /dev/null and b/app/src/main/res/font/montserrat_alternates_medium.ttf differ diff --git a/app/src/main/res/layout/activity_appeals.xml b/app/src/main/res/layout/activity_appeals.xml new file mode 100644 index 0000000..2563287 --- /dev/null +++ b/app/src/main/res/layout/activity_appeals.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_auth.xml b/app/src/main/res/layout/activity_auth.xml new file mode 100644 index 0000000..6f9d1fd --- /dev/null +++ b/app/src/main/res/layout/activity_auth.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_code429.xml b/app/src/main/res/layout/activity_code429.xml new file mode 100644 index 0000000..297b844 --- /dev/null +++ b/app/src/main/res/layout/activity_code429.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_code500.xml b/app/src/main/res/layout/activity_code500.xml new file mode 100644 index 0000000..0778283 --- /dev/null +++ b/app/src/main/res/layout/activity_code500.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_courses.xml b/app/src/main/res/layout/activity_courses.xml new file mode 100644 index 0000000..9cffebc --- /dev/null +++ b/app/src/main/res/layout/activity_courses.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_enternet.xml b/app/src/main/res/layout/activity_enternet.xml new file mode 100644 index 0000000..d2b6e93 --- /dev/null +++ b/app/src/main/res/layout/activity_enternet.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..aa6ed3d --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +