📜  构建一个 Android 应用程序来检查 COVID-19 疫苗接种的可用性(1)

📅  最后修改于: 2023-12-03 15:40:21.788000             🧑  作者: Mango

构建 Android 应用程序来检查 COVID-19 疫苗接种的可用性

介绍

随着 COVID-19 疫苗的推出,许多人都想获得疫苗接种。但是,由于疫苗供应的有限性,许多人可能会面临检查疫苗接种可用性的困难问题。这个 Android 应用程序的主要目的就是为人们提供一个快速、简单的方式来检查疫苗接种的可用性。

功能

这个 Android 应用程序将具有以下功能:

  1. 用户可以查看疫苗接种的可用性和位置。
  2. 用户可以根据所在地、日期和时间等信息来搜索可用的疫苗接种点。
  3. 用户可以查看疫苗接种点的详细信息,如地址、电话号码等。
  4. 用户可以将疫苗接种点添加到收藏夹中,方便以后查看。
技术

这个 Android 应用程序将使用以下技术:

  1. Kotlin 语言编写。
  2. Retrofit 库来进行 API 请求。
  3. Google Maps API 来展示地图和位置。
  4. Firebase 来进行身份验证和用户数据的存储。
实现

首先,我们需要创建一个新的 Android 项目,并为其命名为 "VaccineAvailability"。然后,我们需要添加所需的依赖项:

dependencies {
    // Retrofit library for HTTP requests
    implementation 'com.squareup.retrofit2:retrofit:2.xx.x'
    implementation 'com.squareup.retrofit2:converter-gson:2.xx.x'
    
    // Google Maps library
    implementation 'com.google.android.gms:play-services-maps:xx.x.x'
    
    // Firebase libraries
    implementation 'com.google.firebase:firebase-auth:xx.x.x'
    implementation 'com.google.firebase:firebase-firestore:xx.x.x'
}

接下来,我们需要添加 Google Maps API 的凭据。我们可以通过访问 Google Cloud Console 并创建一个新项目来获取凭据。

我们需要创建一个包含 "Maps SDK for Android" API 的 API 密钥。然后将其添加到我们的 Android 项目中的 AndroidManifest.xml 文件中:

<meta-data
    android:name="com.google.android.geo.API_KEY"
    android:value="YOUR_API_KEY_HERE" />

我们还需要在 AndroidManifest.xml 文件中添加以下权限:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

现在,我们可以开始编写代码了。我们需要创建一个包含以下类的包:

  • model:包含数据模型类。
  • network:包含 Retrofit 的 API 接口类。
  • view:包含与 UI 相关的类。
  • ViewModel:包含视图模型类。
  • repository:包含数据处理逻辑的类。

我们需要使用 Open Vaccine API 来获取可用的疫苗接种信息。我们需要创建一个 API 接口类,在其中定义我们需要使用的 API 请求:

interface OpenVaccineApiService {

    @GET("v1/vaccines")
    suspend fun getVaccineLocations(
        @Query("date") date: String,
        @Query("location") location: String,
        @Query("dose") dose: String
    ): List<VaccineLocationResponse>
}

接下来,我们需要创建 VaccineLocationResponse 模型类,以便解析 API 响应:

data class VaccineLocationResponse(
    val name: String,
    val address: String,
    val city: String,
    val state: String,
    val zip: String,
    val availability: List<Availability>,
    val phone: String,
    val website: String
)

data class Availability(
    val date: String,
    val available_capacity_dose1: Int,
    val available_capacity_dose2: Int
)

我们需要使用 Retrofit 库与 API 交互。我们需要创建一个包含 RetrofitOkHttp 实例的单例类:

object RetrofitInstance {
    private const val BASE_URL = "https://open-vaccine-api.herokuapp.com/"

    private val client = OkHttpClient.Builder()
        .addInterceptor(HttpLoggingInterceptor().apply {
            level = if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.NONE
        })
        .build()

    private val retrofitInstance: Retrofit by lazy {
        Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .client(client)
            .build()
    }

    val openVaccineApi: OpenVaccineApiService by lazy {
        retrofitInstance.create(OpenVaccineApiService::class.java)
    }
}

接下来,我们需要创建一个类来处理我们的数据。这个类将使用我们的 API 接口来搜索可用的疫苗接种点:

class VaccineRepository {

    suspend fun getVaccineLocations(
        date: String,
        location: String,
        dose: String
    ): List<VaccineLocation> {
        // Call API using Retrofit
        val response = RetrofitInstance.openVaccineApi.getVaccineLocations(date, location, dose)

        return response.map { it.toVaccineLocation() }
    }

    private fun VaccineLocationResponse.toVaccineLocation(): VaccineLocation {
        val availability = availability.find { it.available_capacity_dose1 > 0 || it.available_capacity_dose2 > 0 }
            ?: Availability(date = "", available_capacity_dose1 = 0, available_capacity_dose2 = 0)

        return VaccineLocation(
            name = name,
            address = address,
            city = city,
            state = state,
            zip = zip,
            availability = availability,
            phone = phone,
            website = website
        )
    }
}

现在,我们可以创建 VaccineLocationVaccineAvailability 模型类,以封装我们的数据。我们需要使用 LiveData 类型来通知 UI 层数据的变化:

data class VaccineLocation(
    val name: String,
    val address: String,
    val city: String,
    val state: String,
    val zip: String,
    val availability: Availability,
    val phone: String,
    val website: String,
    var isFavorite: Boolean = false
)

data class VaccineAvailability(
    val location: VaccineLocation?,
    val date: String?,
    val dose1Availability: Int,
    val dose2Availability: Int
)

接下来,我们需要创建一个 VaccineViewModel 的类,该类将负责处理 UI 相关的逻辑:

class VaccineViewModel : ViewModel() {

    private val repository = VaccineRepository()

    private val _vaccineLocations = MutableLiveData<List<VaccineLocation>>()
    val vaccineLocations = _vaccineLocations as LiveData<List<VaccineLocation>>

    private val _favoriteLocations = MutableLiveData<List<VaccineLocation>>()
    val favoriteLocations = _favoriteLocations as LiveData<List<VaccineLocation>>

    private val _vaccineAvailability = MutableLiveData<VaccineAvailability>()
    val vaccineAvailability = _vaccineAvailability as LiveData<VaccineAvailability>

    fun getVaccineLocations(date: String, location: String, dose: String) {
        viewModelScope.launch {
            _vaccineLocations.value = repository.getVaccineLocations(date, location, dose)
        }
    }

    fun getFavoriteLocations() {
        viewModelScope.launch {
            _favoriteLocations.value = repository.getFavoriteLocations()
        }
    }

    fun updateFavoriteLocation(location: VaccineLocation) {
        viewModelScope.launch {
            repository.updateFavoriteLocation(location)
        }
    }

    fun getVaccineAvailability(location: VaccineLocation, date: String, dose: String) {
        viewModelScope.launch {
            val availability = repository.getVaccineAvailability(location, date, dose)

            _vaccineAvailability.value = if (availability != null) {
                VaccineAvailability(
                    location = location,
                    date = availability.date,
                    dose1Availability = availability.available_capacity_dose1,
                    dose2Availability = availability.available_capacity_dose2
                )
            } else {
                null
            }
        }
    }
}

我们将使用 Firebase 来进行身份验证和存储用户数据。我们需要将 Firebase 添加到我们的 Android 项目中。我们可以通过访问 Firebase 官方网站 创建一个新的项目并添加 Firebase 到我们的 Android 应用程序中。

我们需要创建一个带有 Firebase 实例的单例类:

object FirebaseInstance {
    val auth: FirebaseAuth = Firebase.auth
    val firestore: FirebaseFirestore = Firebase.firestore
}

接下来,我们需要创建一个 FirebaseRepository 类,该类将负责处理与 Firebase 相关的逻辑:

class FirebaseRepository {

    suspend fun signIn(email: String, password: String) {
        FirebaseInstance.auth.signInWithEmailAndPassword(email, password).await()
    }

    suspend fun signOut() {
        FirebaseInstance.auth.signOut()
    }

    suspend fun isUserLoggedIn(): Boolean {
        return FirebaseInstance.auth.currentUser != null
    }

    suspend fun getUserData(): User? {
        val user = FirebaseInstance.auth.currentUser ?: return null

        val userDoc = FirebaseInstance.firestore.collection("users").document(user.uid).get().await()

        return if (userDoc.exists()) {
            userDoc.toObject(User::class.java)
        } else {
            createUser(user)
        }
    }

    private suspend fun createUser(firebaseUser: FirebaseUser): User {
        val newUser = User(firebaseUser.uid, firebaseUser.displayName ?: "", firebaseUser.email ?: "")

        FirebaseInstance.firestore.collection("users").document(firebaseUser.uid).set(newUser).await()

        return newUser
    }

    suspend fun getFavoriteLocations(): List<VaccineLocation> {
        val user = FirebaseInstance.auth.currentUser ?: return emptyList()

        val snapshot = FirebaseInstance.firestore.collection("favorites").document(user.uid)
            .collection("locations").get().await()

        return snapshot.documents.map { it.toObject(VaccineLocation::class.java)!! }
    }

    suspend fun updateFavoriteLocation(location: VaccineLocation) {
        val user = FirebaseInstance.auth.currentUser ?: return

        val locationMap = mapOf(
            "name" to location.name,
            "address" to location.address,
            "city" to location.city,
            "state" to location.state,
            "zip" to location.zip,
            "availability" to mapOf(
                "date" to location.availability.date,
                "available_capacity_dose1" to location.availability.available_capacity_dose1,
                "available_capacity_dose2" to location.availability.available_capacity_dose2
            ),
            "phone" to location.phone,
            "website" to location.website,
            "is_favorite" to location.isFavorite
        )

        FirebaseInstance.firestore.collection("favorites").document(user.uid).collection("locations")
            .document(location.name).set(locationMap).await()
    }
}

最后,我们需要在 MainActivity 类中创建以下类:

  • VaccineListAdapter:用于展示可用的疫苗接种点的 RecyclerView 适配器。
  • FavoriteListAdapter:用于展示收藏的疫苗接种点的 RecyclerView 适配器。
  • LocationPickerDialogFragment:用于选择用户位置的 DialogFragment。
  • DatePickerDialogFragment:用于选择日期的 DialogFragment。
  • TimePickerDialogFragment:用于选择时间的 DialogFragment。

我们还需要在 MainActivity 类中创建以下方法:

  • setupNavigation():用于设置底部导航栏和 ViewPager。
  • showLocationPickerDialog():用于显示选择位置的 DialogFragment。
  • showDatePickerDialog():用于显示选择日期的 DialogFragment。
  • showTimePickerDialog():用于显示选择时间的 DialogFragment。
  • searchAvailability():用于搜索可用的疫苗接种点。
  • updateFavoriteLocations():用于更新收藏的疫苗接种点。
结论

这个 Android 应用程序将为用户提供一个简单、快速的方式来搜索可用的疫苗接种点。使用 Retrofit 库和 Google Maps API,我们可以轻松地与 Open Vaccine API 交互,并展示疫苗接种点的位置。使用 Firebase 实例,我们可以轻松地进行身份验证和存储用户数据。