MY API KEY(和风)

670ca929136a456992608cd2e794df24

Mainactivity

超级好的Mainactivity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package com.example.diary_3.activity

import androidx.core.widget.addTextChangedListener
import android.app.Activity
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.diary_3.adapter.DiaryAdapter
import com.example.diary_3.database.Diary
import com.example.diary_3.database.DiaryDatabase
import com.example.diary_3.databinding.ActivityMainBinding
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var diaryDatabase: DiaryDatabase
private lateinit var diaryAdapter: DiaryAdapter

private val addDiaryLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
when (result.resultCode) {
Activity.RESULT_OK -> loadAllDiaries()
Activity.RESULT_CANCELED -> {}
else -> Log.d("MainActivity", "未知的返回结果: ${result.resultCode}")
}
}

private val diarySavedReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (intent?.action == "SAVED") {
loadAllDiaries()
}
}
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)

diaryDatabase = DiaryDatabase.getInstance(this)
binding.recyclerView.layoutManager = LinearLayoutManager(this)
diaryAdapter = DiaryAdapter(this, emptyList())
binding.recyclerView.adapter = diaryAdapter

val filter = IntentFilter("SAVED")
registerReceiver(diarySavedReceiver, filter)

loadAllDiaries()

// 监听搜索框
binding.searchBox.addTextChangedListener { text ->
searchDiaries(text.toString())
}

// 启动添加日记页面
binding.addDiaryButton.setOnClickListener {
val intent = Intent(this, AddDiaryActivity::class.java)
addDiaryLauncher.launch(intent)
}

// 绑定删除按钮
binding.deleteDiaryButton.setOnClickListener {
deleteDiary()
}
}

// 删除日记的方法
private fun deleteDiary() {
val diaries = diaryAdapter.getCurrentDiaries()
if (diaries.isEmpty()) {
Toast.makeText(this, "当前没有日记可删除", Toast.LENGTH_SHORT).show()
return
}

val diaryTitles = diaries.map { it.title }.toTypedArray()
var selectedIndex = 0

AlertDialog.Builder(this)
.setTitle("选择要删除的日记")
.setSingleChoiceItems(diaryTitles, 0) { _, which ->
selectedIndex = which
}
.setPositiveButton("删除") { _, _ ->
val diaryToDelete = diaries[selectedIndex]
lifecycleScope.launch(Dispatchers.IO) {
diaryDatabase.diaryDao().deleteDiary(diaryToDelete)
withContext(Dispatchers.Main) {
loadAllDiaries()
}
}
}
.setNegativeButton("取消", null)
.show()
}

// 加载所有日记
private fun loadAllDiaries() {
diaryDatabase.diaryDao().getDiaries().observe(this, Observer { diaries ->
diaryAdapter.updateData(diaries) // 更新数据
})
}

// 搜索日记
private fun searchDiaries(query: String) {
diaryDatabase.diaryDao().searchDiaries("%$query%")
.observe(this, Observer { diaries ->
diaryAdapter.updateData(diaries)
})
}

override fun onDestroy() {
super.onDestroy()
unregisterReceiver(diarySavedReceiver)
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
package com.example.diary_3.activity
import androidx.core.widget.addTextChangedListener
import android.app.Activity
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Build
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.util.Log
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.room.InvalidationTracker
import com.example.diary_3.adapter.DiaryAdapter
import com.example.diary_3.database.Diary
import com.example.diary_3.database.DiaryDatabase
import com.example.diary_3.databinding.ActivityMainBinding
import java.util.concurrent.Executors
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import androidx.lifecycle.Observer


class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var diaryDatabase: DiaryDatabase
private lateinit var diaryAdapter: DiaryAdapter


private val addDiaryLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
when (result.resultCode) {
Activity.RESULT_OK -> {
// 如果有返回结果,刷新数据
loadAllDiaries()
}
Activity.RESULT_CANCELED -> {


}
else -> {
// 处理其他可能的返回情况
Log.d("MainActivity", "未知的返回结果: ${result.resultCode}")
}
}
}
private val diarySavedReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (intent?.action == "SAVED") {
loadAllDiaries()
}
}
}//gl gd

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
diaryDatabase = DiaryDatabase.getInstance(this)
binding.recyclerView.layoutManager = LinearLayoutManager(this)
diaryAdapter = DiaryAdapter(this, emptyList())
binding.recyclerView.adapter = diaryAdapter
val filter = IntentFilter("SAVED")
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU) {
registerReceiver(diarySavedReceiver, filter, Context.RECEIVER_NOT_EXPORTED)
} else {
registerReceiver(diarySavedReceiver, filter, RECEIVER_NOT_EXPORTED)
}//ys5 gd
loadAllDiaries()
binding.searchBox.addTextChangedListener { text ->
searchDiaries(text.toString())
}//ys3 gd
// 使用 ActivityResultLauncher 启动 AddDiaryActivity
binding.addDiaryButton.setOnClickListener {
val intent = Intent(this, AddDiaryActivity::class.java)
addDiaryLauncher.launch(intent)
}//ys4 gd

}



private fun loadAllDiaries() {
lifecycleScope.launch {
val diaries = withContext(Dispatchers.IO) {
diaryDatabase.diaryDao().getAllDiaries()
}
diaryAdapter.updateData(diaries) // 更新数据
}
}//ys8 gd


private fun searchDiaries(query: String) {
diaryDatabase.diaryDao().searchDiaries("%$query%")
.observe(this, object : Observer<List<Diary>> {
override fun onChanged(diaries: List<Diary>) {
diaryAdapter = DiaryAdapter(this@MainActivity, diaries)
binding.recyclerView.adapter = diaryAdapter
}
})
}//ys9 gd




override fun onDestroy() {
super.onDestroy()
unregisterReceiver(diarySavedReceiver)
}
}

AddDiaryActivity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
package com.example.diary_3.activity
import androidx.appcompat.app.AlertDialog
import android.view.View
import android.widget.EditText
import com.example.diary_3.utils.DateUtils
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Bundle
import android.os.Looper
import android.provider.MediaStore
import android.util.Log
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
import com.example.diary_3.R
import com.example.diary_3.database.Diary
import com.example.diary_3.database.DiaryDatabase
import com.example.diary_3.databinding.ActivityAddDiaryBinding
import com.example.diary_3.model.CityResponse
import com.example.diary_3.model.WeatherResponse
import com.example.diary_3.network.RetrofitClient
import com.example.diary_3.network.WeatherApi
import com.example.diary_3.utils.LocationUtils
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import java.util.Calendar
import java.util.concurrent.Executors

class AddDiaryActivity : AppCompatActivity() {
private val selectedLocalImageUris = mutableListOf<Uri>()
private val networkImageUrls = mutableListOf<String>()
private val API_KEY = "670ca929136a456992608cd2e794df24"
private lateinit var binding: ActivityAddDiaryBinding
private var selectedDate: Calendar = Calendar.getInstance()
private lateinit var diaryDatabase: DiaryDatabase
private lateinit var locationUtils: LocationUtils
private val PERMISSION_REQUEST_CODE = 1
private var selectedLocalImageUri: Uri? = null
private var networkImageUrl: String? = null

private fun hasPermission(permission: String): Boolean {
return ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityAddDiaryBinding.inflate(layoutInflater)
setContentView(binding.root)

diaryDatabase = DiaryDatabase.getInstance(this)
locationUtils = LocationUtils(this)

binding.selectImageButton.setOnClickListener {
pickLocalImage()
}

binding.addNetworkImageButton.setOnClickListener {
// 创建一个输入框
val input = EditText(this)
input.hint = "请输入有效的图片链接"

// 构建对话框
AlertDialog.Builder(this)
.setTitle("输入图片URL")
.setView(input)
.setPositiveButton("确定") { dialog, which ->
val url = input.text.toString().trim()
if (url.isNotBlank()) {
networkImageUrl = url
Glide.with(this)
.load(url)
.placeholder(R.drawable.loading_placeholder) // 加载过程中的占位符图片
.apply(RequestOptions.circleCropTransform())
.error(R.drawable.no) // 加载失败时显示的图片
.into(binding.selectedImageView)
binding.selectedImageView.visibility = View.VISIBLE
} else {
Toast.makeText(this, "请输入有效的图片链接", Toast.LENGTH_SHORT).show()
binding.selectedImageView.visibility = View.GONE
}
}
.setNegativeButton("取消") { dialog, which ->
dialog.cancel()
}
.show()
}

binding.saveDiaryButton.setOnClickListener {
saveDiary()
}



// 新增按钮:点击刷新地点和天气
binding.refreshWeatherLocationButton.setOnClickListener {
getLocation()
}

val permissionsToRequest = mutableListOf<String>()

if (!hasPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)) {
permissionsToRequest.add(android.Manifest.permission.ACCESS_FINE_LOCATION)
}

if (!hasPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE)) {
permissionsToRequest.add(android.Manifest.permission.READ_EXTERNAL_STORAGE)
}

if (permissionsToRequest.isNotEmpty()) {
ActivityCompat.requestPermissions(
this,
permissionsToRequest.toTypedArray(),
PERMISSION_REQUEST_CODE
)
} else {
getLocation()
}
}

private fun pickLocalImage() {
val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
startActivityForResult(intent, 1)
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)

if (requestCode == 1 && resultCode == RESULT_OK && data != null) {
selectedLocalImageUri = data.data
Glide.with(this)
.load(selectedLocalImageUri)
.into(binding.selectedImageView)
binding.selectedImageView.visibility = android.view.View.VISIBLE
}
}

private fun getLocation() {
locationUtils.getLocation { location ->
if (location != null) {
val latitude = location.latitude
val longitude = location.longitude
Log.d("AddDiaryActivity", "获取到的经纬度: 纬度 $latitude, 经度 $longitude")
// 格式化经纬度字符串,注意这里 longitude 和 latitude 的顺序可以根据接口要求调整
val loc = String.format("%.2f,%.2f", location.longitude, location.latitude)
Executors.newSingleThreadExecutor().execute {
runOnUiThread {
binding.locationTextView.text = loc
getCityId(loc)
}
}
} else {
runOnUiThread {
Toast.makeText(this, "无法获取当前位置", Toast.LENGTH_SHORT).show()
}
}
}
}

private fun showToastOnUiThread(message: String) {
if (Looper.myLooper() == Looper.getMainLooper()) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
} else {
runOnUiThread {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}
}
}

private fun getCityId(cityName: String) {
val weatherApi = RetrofitClient.geoInstance.create(WeatherApi::class.java)
weatherApi.getCityInfo(API_KEY, cityName).enqueue(object : Callback<CityResponse> {
override fun onResponse(call: Call<CityResponse>, response: Response<CityResponse>) {
if (response.isSuccessful && response.body()?.code == "200") {
val cityLocation = response.body()?.location?.firstOrNull()
if (cityLocation != null) {
binding.locationTextView.text = cityLocation.name
getWeatherInfo(cityLocation.id)
}
} else {
Log.e("AddDiaryActivity", "获取城市 ID 失败: ${response.message()}, 响应代码: ${response.code()}, 响应体: ${response.errorBody()?.string()}")
showToastOnUiThread("获取城市 ID 失败")
}
}

override fun onFailure(call: Call<CityResponse>, t: Throwable) {
Log.e("AddDiaryActivity", "获取城市 ID 网络请求失败: ${t.message}", t)
showToastOnUiThread("获取城市 ID 网络请求失败")
}
})
}

private fun getWeatherInfo(cityId: String) {
val weatherApi = RetrofitClient.weatherInstance.create(WeatherApi::class.java)
weatherApi.getWeatherInfo(API_KEY, cityId).enqueue(object : Callback<WeatherResponse> {
override fun onResponse(call: Call<WeatherResponse>, response: Response<WeatherResponse>) {
if (response.isSuccessful && response.body()?.code == "200") {
val today = DateUtils.formatDate(Calendar.getInstance().time)
val todayWeather = response.body()?.daily?.firstOrNull { it.fxDate == today }
if (todayWeather != null) {
val weatherText = todayWeather.textDay
binding.weatherTextView.text = weatherText
}
} else {
Log.e("AddDiaryActivity", "获取天气信息失败: ${response.message()}, 响应代码: ${response.code()}, 响应体: ${response.errorBody()?.string()}")
showToastOnUiThread("获取天气信息失败")
}
}

override fun onFailure(call: Call<WeatherResponse>, t: Throwable) {
Log.e("AddDiaryActivity", "获取天气信息网络请求失败: ${t.message}", t)
showToastOnUiThread("获取天气信息网络请求失败")
}
})
}

private fun saveDiary() {
Executors.newSingleThreadExecutor().execute {
val title = binding.titleEditText.text.toString()
val content = binding.contentEditText.text.toString()
val localImagePath = selectedLocalImageUri?.toString()
val date = DateUtils.formatDate(Calendar.getInstance().time)
val weather = binding.weatherTextView.text.toString()
val location = binding.locationTextView.text.toString()

val diary = Diary(
title = title,
content = content,
localImagePath = localImagePath,
networkImageUrl = networkImageUrl,
date = date,
weather = weather,
location = location
)

diaryDatabase.diaryDao().insertDiary(diary)
runOnUiThread {
Toast.makeText(this, "日记保存成功", Toast.LENGTH_SHORT).show()
val intent = Intent("SAVED") // 或者 "com.example.app.DIARY_SAVED"
sendBroadcast(intent)
finish()
}
}
}
}

DetailUtils

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.example.diary_final.utils

import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale

object DateUtils {
private const val DEFAULT_DATE_FORMAT = "yyyy-MM-dd"

// 线程安全的 SimpleDateFormat 生成
private fun getDateFormat(pattern: String): SimpleDateFormat {
return SimpleDateFormat(pattern, Locale.getDefault())
}

// 格式化日期(默认格式 "yyyy-MM-dd")
fun formatDate(date: Date, pattern: String = DEFAULT_DATE_FORMAT): String {
return getDateFormat(pattern).format(date)
}

// 获取当前日期的格式化字符串
fun getCurrentDate(pattern: String = DEFAULT_DATE_FORMAT): String {
return formatDate(Date(), pattern)
}
}

LocationUtils

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
package com.example.diary_final.utils

import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.location.Location
import android.location.LocationListener
import android.location.LocationManager
import android.os.Bundle
import androidx.core.app.ActivityCompat

class LocationUtils(private val context: Context) {

private val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
private var locationListener: LocationListener? = null

/**
* 获取当前位置
*/
fun getLocation(onLocationReceived: (Location) -> Unit) {
// 权限检查
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED
) {
return // 没有权限,直接返回
}

// 获取上次已知位置(可能会加快首次获取速度)
val lastKnownLocation: Location? = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)
?: locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER)
if (lastKnownLocation != null) {
onLocationReceived(lastKnownLocation)
}

// 监听实时位置更新
locationListener = object : LocationListener {
override fun onLocationChanged(location: Location) {
onLocationReceived(location)
removeLocationUpdates() // 获取到位置后移除监听
}

override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {}
override fun onProviderEnabled(provider: String) {}
override fun onProviderDisabled(provider: String) {}
}

// 请求位置更新(优先使用 GPS,网络作为备用)
try {
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
5000L, // 5秒更新一次
10f, // 10米变化更新
locationListener!!
)
} catch (e: Exception) {
e.printStackTrace()
}

try {
locationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER,
5000L,
10f,
locationListener!!
)
} catch (e: Exception) {
e.printStackTrace()
}
}

/**
* 移除位置更新监听,防止内存泄漏
*/
fun removeLocationUpdates() {
locationListener?.let {
locationManager.removeUpdates(it)
locationListener = null
}
}
}