Merge branch 'master' of gitlab.navinfo.com:vivo/navinfovivo
Conflicts: app/build.gradle app/src/main/java/com/navinfo/volvo/ui/MainActivity.kt app/src/main/java/com/navinfo/volvo/ui/fragments/message/ObtainMessageFragment.kt app/src/main/java/com/navinfo/volvo/ui/fragments/message/ObtainMessageViewModel.kt app/src/main/res/layout/activity_main.xml app/src/main/res/layout/fragment_dashboard.xml app/src/main/res/layout/fragment_obtain_message.xml app/src/main/res/navigation/mobile_navigation.xml app/src/main/res/values-night/themes.xml app/src/main/res/values/themes.xml
This commit is contained in:
commit
9492580b84
@ -121,6 +121,24 @@ dependencies {
|
||||
// androidTestImplementation "com.google.dagger:hilt-android-testing:2.41"
|
||||
// kaptAndroidTest "com.google.dagger:hilt-android-compiler:2.41"
|
||||
|
||||
// 显示错误提示 https://github.com/nhaarman/supertooltips
|
||||
implementation 'com.nhaarman.supertooltips:library:3.0.0'
|
||||
|
||||
// 权限请求框架:https://github.com/getActivity/XXPermissions
|
||||
implementation 'com.github.getActivity:XXPermissions:16.5'
|
||||
|
||||
// 相机库 https://natario1.github.io/CameraView/about/getting-started
|
||||
implementation("com.otaliastudios:cameraview:2.7.2")
|
||||
// 图片压缩算法 https://github.com/Curzibn/Luban
|
||||
implementation 'top.zibin:Luban:1.1.8'
|
||||
// Android工具类库 https://github.com/gycold/EasyAndroid
|
||||
implementation 'io.github.gycold:easyandroid:2.0.7'
|
||||
// 日志工具 https://github.com/elvishew/xLog/blob/master/README_ZH.md
|
||||
implementation 'com.elvishew:xlog:1.10.1'
|
||||
//加载图片的依赖包
|
||||
implementation ("com.github.bumptech.glide:glide:4.11.0") {
|
||||
exclude group: "com.android.support"
|
||||
}
|
||||
}
|
||||
|
||||
kapt {
|
||||
|
||||
@ -1,6 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- 拍照 -->
|
||||
<uses-permission
|
||||
android:name="android.permission.CAMERA"
|
||||
android:required="false" /> <!-- 网络请求 -->
|
||||
<uses-permission
|
||||
android:name="android.permission.INTERNET"
|
||||
android:required="false" /> <!-- 录音 -->
|
||||
<uses-permission
|
||||
android:name="android.permission.RECORD_AUDIO"
|
||||
android:required="false" /> <!-- 读写文件 -->
|
||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
|
||||
<application
|
||||
android:name=".MyApplication"
|
||||
@ -9,11 +22,21 @@
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.NavinfoVolvo"
|
||||
tools:targetApi="31">
|
||||
android:usesCleartextTraffic="true">
|
||||
<activity
|
||||
android:name=".ui.message.MessageActivity"
|
||||
android:exported="false"
|
||||
android:label="@string/title_activity_second"
|
||||
android:theme="@style/Theme.NavinfoVolvo.NoActionBar">
|
||||
<meta-data
|
||||
android:name="android.app.lib_name"
|
||||
android:value="" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name="com.navinfo.volvo.ui.MainActivity"
|
||||
android:exported="true"
|
||||
@ -23,11 +46,12 @@
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.app.lib_name"
|
||||
android:value="" />
|
||||
</activity>
|
||||
|
||||
<meta-data
|
||||
android:name="android.app.lib_name"
|
||||
android:value="" />
|
||||
<meta-data android:name="ScopedStorage" android:value="true" />
|
||||
</application>
|
||||
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
|
||||
</manifest>
|
||||
@ -0,0 +1,66 @@
|
||||
package com.navinfo.volvo
|
||||
|
||||
import android.media.MediaRecorder
|
||||
import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import com.easytools.tools.DateUtils
|
||||
import com.elvishew.xlog.XLog
|
||||
import com.navinfo.volvo.utils.SystemConstant
|
||||
import java.util.*
|
||||
|
||||
class RecorderLifecycleObserver: DefaultLifecycleObserver {
|
||||
private var mediaRecorder: MediaRecorder? = null
|
||||
private lateinit var recorderAudioPath: String
|
||||
|
||||
fun initAndStartRecorder() {
|
||||
recorderAudioPath = "${SystemConstant.SoundFolder}/${DateUtils.date2Str(Date(), DateUtils.FORMAT_YMDHMS)}.m4a"
|
||||
mediaRecorder = MediaRecorder().apply {
|
||||
setAudioSource(MediaRecorder.AudioSource.MIC)
|
||||
setOutputFormat(MediaRecorder.OutputFormat.DEFAULT)
|
||||
setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
|
||||
// 开始录音
|
||||
setOutputFile(recorderAudioPath)
|
||||
try {
|
||||
prepare()
|
||||
} catch (e: Exception) {
|
||||
XLog.e("prepare() failed")
|
||||
}
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
fun stopAndReleaseRecorder(): String {
|
||||
mediaRecorder?.stop()
|
||||
mediaRecorder?.release()
|
||||
mediaRecorder = null
|
||||
return recorderAudioPath
|
||||
}
|
||||
|
||||
// override fun onCreate(owner: LifecycleOwner) {
|
||||
// super.onCreate(owner)
|
||||
//
|
||||
// }
|
||||
//
|
||||
// override fun onStart(owner: LifecycleOwner) {
|
||||
// super.onStart(owner)
|
||||
// }
|
||||
//
|
||||
// override fun onResume(owner: LifecycleOwner) {
|
||||
// super.onResume(owner)
|
||||
// }
|
||||
|
||||
override fun onPause(owner: LifecycleOwner) {
|
||||
super.onPause(owner)
|
||||
mediaRecorder?.pause()
|
||||
}
|
||||
|
||||
override fun onStop(owner: LifecycleOwner) {
|
||||
super.onStop(owner)
|
||||
mediaRecorder?.stop()
|
||||
}
|
||||
|
||||
override fun onDestroy(owner: LifecycleOwner) {
|
||||
super.onDestroy(owner)
|
||||
mediaRecorder?.release()
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package com.navinfo.volvo.http
|
||||
|
||||
class DefaultResponse<T> {
|
||||
var code: Int = 0
|
||||
var message: String = ""
|
||||
var data: T? = null
|
||||
}
|
||||
30
app/src/main/java/com/navinfo/volvo/http/NavinfoVolvoCall.kt
Normal file
30
app/src/main/java/com/navinfo/volvo/http/NavinfoVolvoCall.kt
Normal file
@ -0,0 +1,30 @@
|
||||
package com.navinfo.volvo.http
|
||||
|
||||
import com.navinfo.volvo.db.dao.entity.Attachment
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
import retrofit2.create
|
||||
import java.io.File
|
||||
|
||||
class NavinfoVolvoCall {
|
||||
companion object {
|
||||
private val service by lazy {
|
||||
Retrofit.Builder().baseUrl("http://ec2-52-81-73-5.cn-north-1.compute.amazonaws.com.cn:8088/")
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.build()
|
||||
.create(NavinfoVolvoService::class.java)
|
||||
}
|
||||
|
||||
private var instance: NavinfoVolvoCall? = null
|
||||
get() {
|
||||
if (field == null) {
|
||||
field = NavinfoVolvoCall()
|
||||
}
|
||||
return field
|
||||
}
|
||||
|
||||
fun getApi(): NavinfoVolvoService {
|
||||
return service
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
package com.navinfo.volvo.http
|
||||
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.RequestBody
|
||||
import retrofit2.Call
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.Multipart
|
||||
import retrofit2.http.POST
|
||||
import retrofit2.http.Part
|
||||
import java.io.File
|
||||
|
||||
interface NavinfoVolvoService {
|
||||
@POST("/navi/cardDelivery/insertCardByApp")
|
||||
fun insertCardByApp(@Body insertData: MutableMap<String, String>)
|
||||
@POST("/navi/cardDelivery/updateCardByApp")
|
||||
fun updateCardByApp(@Body updateData: MutableMap<String, String>)
|
||||
@POST("/navi/cardDelivery/queryCardListByApp")
|
||||
fun queryCardListByApp(@Body queryData: MutableMap<String, String>)
|
||||
@POST("/navi/cardDelivery/deleteCardByApp")
|
||||
fun deleteCardByApp(@Body deleteData: MutableMap<String, String>)
|
||||
@POST("/img/upload")
|
||||
@Multipart
|
||||
suspend fun uploadAttachment(@Part attachmentFile: MultipartBody.Part):DefaultResponse<MutableMap<String, String>>
|
||||
@POST("/img/download")
|
||||
fun downLoadAttachment(@Body downloadData: MutableMap<String, String>):Call<DefaultResponse<MutableMap<String, String>>>
|
||||
}
|
||||
@ -1,13 +1,18 @@
|
||||
package com.navinfo.volvo.ui
|
||||
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.content.DialogInterface
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.navigation.ui.AppBarConfiguration
|
||||
import androidx.navigation.ui.setupActionBarWithNavController
|
||||
import androidx.navigation.ui.setupWithNavController
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.navinfo.volvo.R
|
||||
import com.navinfo.volvo.databinding.ActivityMainBinding
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
@ -23,6 +28,38 @@ class MainActivity : AppCompatActivity() {
|
||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
setupNavigation()
|
||||
|
||||
XXPermissions.with(this)
|
||||
// 申请单个权限
|
||||
.permission(Permission.WRITE_EXTERNAL_STORAGE)
|
||||
.permission(Permission.READ_EXTERNAL_STORAGE)
|
||||
// 设置权限请求拦截器(局部设置)
|
||||
//.interceptor(new PermissionInterceptor())
|
||||
// 设置不触发错误检测机制(局部设置)
|
||||
//.unchecked()
|
||||
.request(object : OnPermissionCallback {
|
||||
|
||||
override fun onGranted(permissions: MutableList<String>, all: Boolean) {
|
||||
if (!all) {
|
||||
Toast.makeText(this@MainActivity, "获取部分权限成功,但部分权限未正常授予", Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
// 在SD卡创建项目目录
|
||||
createRootFolder()
|
||||
}
|
||||
|
||||
override fun onDenied(permissions: MutableList<String>, never: Boolean) {
|
||||
if (never) {
|
||||
Toast.makeText(this@MainActivity, "永久拒绝授权,请手动授权文件读写权限", Toast.LENGTH_SHORT).show()
|
||||
// 如果是被永久拒绝就跳转到应用权限系统设置页面
|
||||
XXPermissions.startPermissionActivity(this@MainActivity, permissions)
|
||||
} else {
|
||||
onSDCardDenied()
|
||||
showRationaleForSDCard(permissions)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
private fun setupNavigation() {
|
||||
@ -57,4 +94,73 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
override fun onSupportNavigateUp() =
|
||||
findNavController(R.id.nav_host_fragment_activity_main).navigateUp()
|
||||
|
||||
// @NeedsPermission(Manifest.permission.MANAGE_EXTERNAL_STORAGE)
|
||||
fun createRootFolder() {
|
||||
// 在SD卡创建项目目录
|
||||
val sdCardPath = getExternalFilesDir(null)
|
||||
// SystemConstant.ROOT_PATH = "${sdCardPath}/${SystemConstant.FolderName}"
|
||||
SystemConstant.ROOT_PATH = sdCardPath!!.absolutePath
|
||||
SystemConstant.LogFolder = "${sdCardPath!!.absolutePath}/log"
|
||||
FileUtils.createOrExistsDir(SystemConstant.LogFolder)
|
||||
SystemConstant.CameraFolder = "${sdCardPath!!.absolutePath}/camera"
|
||||
FileUtils.createOrExistsDir(SystemConstant.CameraFolder)
|
||||
SystemConstant.SoundFolder = "${sdCardPath!!.absolutePath}/sound"
|
||||
FileUtils.createOrExistsDir(SystemConstant.SoundFolder)
|
||||
xLogInit(SystemConstant.LogFolder)
|
||||
}
|
||||
|
||||
fun xLogInit(logFolder: String) {
|
||||
val config = LogConfiguration.Builder()
|
||||
.logLevel(
|
||||
if (BuildConfig.DEBUG)
|
||||
LogLevel.ALL // 指定日志级别,低于该级别的日志将不会被打印,默认为 LogLevel.ALL
|
||||
else LogLevel.NONE
|
||||
)
|
||||
.tag("Volvo") // 指定 TAG,默认为 "X-LOG"
|
||||
.enableThreadInfo() // 允许打印线程信息,默认禁止
|
||||
.enableStackTrace(2) // 允许打印深度为 2 的调用栈信息,默认禁止
|
||||
.enableBorder() // 允许打印日志边框,默认禁止
|
||||
.addInterceptor(
|
||||
BlacklistTagsFilterInterceptor( // 添加黑名单 TAG 过滤器
|
||||
"blacklist1", "blacklist2", "blacklist3"
|
||||
)
|
||||
)
|
||||
.build()
|
||||
|
||||
val androidPrinter: Printer = AndroidPrinter(true) // 通过 android.util.Log 打印日志的打印器
|
||||
|
||||
val consolePrinter: Printer = ConsolePrinter() // 通过 System.out 打印日志到控制台的打印器
|
||||
|
||||
val filePrinter: Printer = FilePrinter.Builder("${SystemConstant.ROOT_PATH}/Logs") // 指定保存日志文件的路径
|
||||
.fileNameGenerator(DateFileNameGenerator()) // 指定日志文件名生成器,默认为 ChangelessFileNameGenerator("log")
|
||||
.backupStrategy(NeverBackupStrategy()) // 指定日志文件备份策略,默认为 FileSizeBackupStrategy(1024 * 1024)
|
||||
.build()
|
||||
|
||||
XLog.init( // 初始化 XLog
|
||||
config, // 指定日志配置,如果不指定,会默认使用 new LogConfiguration.Builder().build()
|
||||
androidPrinter, // 添加任意多的打印器。如果没有添加任何打印器,会默认使用 AndroidPrinter(Android)/ConsolePrinter(java)
|
||||
consolePrinter,
|
||||
filePrinter
|
||||
)
|
||||
}
|
||||
|
||||
// @OnShowRationale(Manifest.permission.MANAGE_EXTERNAL_STORAGE)
|
||||
fun showRationaleForSDCard(permissions: MutableList<String>) {
|
||||
// showRationaleDialog(R.string.permission_camera_rationale, request)
|
||||
// Toast.makeText(context, "当前操作需要您授权相机权限!", Toast.LENGTH_SHORT).show()
|
||||
MaterialAlertDialogBuilder(this)
|
||||
.setTitle("提示")
|
||||
.setMessage("当前操作需要您授权读写SD卡权限!")
|
||||
.setPositiveButton("确定", DialogInterface.OnClickListener { dialogInterface, i ->
|
||||
dialogInterface.dismiss()
|
||||
XXPermissions.startPermissionActivity(this@MainActivity, permissions)
|
||||
})
|
||||
.show()
|
||||
}
|
||||
|
||||
// @OnPermissionDenied(Manifest.permission.MANAGE_EXTERNAL_STORAGE)
|
||||
fun onSDCardDenied() {
|
||||
Toast.makeText(this, "当前操作需要您授权读写SD卡权限!", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
112
app/src/main/java/com/navinfo/volvo/ui/camera/CameraFragment.kt
Normal file
112
app/src/main/java/com/navinfo/volvo/ui/camera/CameraFragment.kt
Normal file
@ -0,0 +1,112 @@
|
||||
package com.navinfo.volvo.ui.camera
|
||||
|
||||
import android.os.Bundle
|
||||
import android.text.TextUtils
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.get
|
||||
import androidx.navigation.Navigation
|
||||
import com.easytools.tools.DateUtils
|
||||
import com.easytools.tools.FileUtils
|
||||
import com.elvishew.xlog.XLog
|
||||
import com.navinfo.volvo.databinding.FragmentCameraBinding
|
||||
import com.navinfo.volvo.ui.message.ObtainMessageViewModel
|
||||
import com.navinfo.volvo.utils.SystemConstant
|
||||
import com.otaliastudios.cameraview.CameraListener
|
||||
import com.otaliastudios.cameraview.CameraView
|
||||
import com.otaliastudios.cameraview.FileCallback
|
||||
import com.otaliastudios.cameraview.PictureResult
|
||||
import top.zibin.luban.Luban
|
||||
import top.zibin.luban.OnCompressListener
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
|
||||
class CameraFragment : Fragment() {
|
||||
|
||||
private var _binding: FragmentCameraBinding? = null
|
||||
|
||||
// This property is only valid between onCreateView and
|
||||
// onDestroyView.
|
||||
private val binding get() = _binding!!
|
||||
// private val exportFolderPath by lazy {
|
||||
// "${SystemConstant.ROOT_PATH}/exportPic/"
|
||||
// }
|
||||
|
||||
private val cameraLifeCycleObserver: CameraLifeCycleObserver by lazy {
|
||||
CameraLifeCycleObserver()
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
// lifecycle.addObserver(cameraLifeCycleObserver)
|
||||
|
||||
val cameraViewModel =
|
||||
ViewModelProvider(this).get(CameraViewModel::class.java)
|
||||
|
||||
_binding = FragmentCameraBinding.inflate(inflater, container, false)
|
||||
val root: View = binding.root
|
||||
|
||||
val cameraView: CameraView = binding.camera
|
||||
cameraView.setLifecycleOwner(this)
|
||||
cameraView.addCameraListener(object:CameraListener() { // 添加拍照回调
|
||||
override fun onPictureTaken(result: PictureResult) {
|
||||
super.onPictureTaken(result)
|
||||
// FileUtils.createOrExistsDir(cameraFolderPath)
|
||||
val resultFile = File("${SystemConstant.CameraFolder}/${DateUtils.date2Str(Date(), DateUtils.FORMAT_YMDHMS)}.jpg")
|
||||
result.toFile(resultFile, object: FileCallback {
|
||||
override fun onFileReady(resultFile: File?) {
|
||||
// 压缩图片文件
|
||||
Luban.with(context)
|
||||
.load<Any>(mutableListOf(resultFile) as List<Any>?)
|
||||
.ignoreBy(200)
|
||||
.setTargetDir("${SystemConstant.CameraFolder}")
|
||||
.filter { path ->
|
||||
!(TextUtils.isEmpty(path) || path.lowercase(Locale.getDefault())
|
||||
.endsWith(".gif"))
|
||||
}
|
||||
.setCompressListener(object : OnCompressListener {
|
||||
override fun onStart() {
|
||||
XLog.d("开始压缩图片")
|
||||
}
|
||||
|
||||
override fun onSuccess(file: File?) {
|
||||
XLog.d("压缩图片成功:${file?.absolutePath}")
|
||||
// 删除源文件
|
||||
if (!resultFile!!.absolutePath.equals(file!!.absolutePath)) {
|
||||
resultFile!!.delete()
|
||||
}
|
||||
// 跳转回原Fragment,展示拍摄的照片
|
||||
ViewModelProvider(requireActivity()).get(ObtainMessageViewModel::class.java).updateMessagePic(file!!.absolutePath)
|
||||
// 跳转回原界面
|
||||
Navigation.findNavController(root).popBackStack()
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
XLog.d("压缩图片失败:${e.message}")
|
||||
}
|
||||
}).launch()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
// 点击拍照
|
||||
binding.imgStartCamera.setOnClickListener {
|
||||
cameraView.takePicture()
|
||||
}
|
||||
|
||||
return root
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
// lifecycle.removeObserver(cameraLifeCycleObserver)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
package com.navinfo.volvo.ui.camera
|
||||
|
||||
import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
|
||||
class CameraLifeCycleObserver: DefaultLifecycleObserver {
|
||||
|
||||
override fun onCreate(owner: LifecycleOwner) {
|
||||
super.onCreate(owner)
|
||||
}
|
||||
|
||||
override fun onStart(owner: LifecycleOwner) {
|
||||
super.onStart(owner)
|
||||
}
|
||||
|
||||
override fun onResume(owner: LifecycleOwner) {
|
||||
super.onResume(owner)
|
||||
}
|
||||
|
||||
override fun onPause(owner: LifecycleOwner) {
|
||||
super.onPause(owner)
|
||||
}
|
||||
|
||||
override fun onStop(owner: LifecycleOwner) {
|
||||
super.onStop(owner)
|
||||
}
|
||||
|
||||
override fun onDestroy(owner: LifecycleOwner) {
|
||||
super.onDestroy(owner)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package com.navinfo.volvo.ui.camera
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
|
||||
class CameraViewModel : ViewModel() {
|
||||
|
||||
private val _text = MutableLiveData<String>().apply {
|
||||
value = "This is dashboard Fragment"
|
||||
}
|
||||
val text: LiveData<String> = _text
|
||||
}
|
||||
@ -1,21 +1,57 @@
|
||||
package com.navinfo.volvo.ui.fragments.message
|
||||
|
||||
import android.content.DialogInterface
|
||||
import android.os.Bundle
|
||||
import android.text.TextUtils
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.View.*
|
||||
import android.view.ViewGroup
|
||||
import android.widget.AdapterView
|
||||
import android.widget.AdapterView.OnItemSelectedListener
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.Toast
|
||||
import androidx.core.widget.addTextChangedListener
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.navigation.Navigation
|
||||
import com.bumptech.glide.Glide
|
||||
import com.easytools.tools.DateUtils
|
||||
import com.easytools.tools.ToastUtils
|
||||
import com.elvishew.xlog.XLog
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.gredicer.datetimepicker.DateTimePickerFragment
|
||||
import com.hjq.permissions.OnPermissionCallback
|
||||
import com.hjq.permissions.Permission
|
||||
import com.hjq.permissions.XXPermissions
|
||||
import com.navinfo.volvo.R
|
||||
import com.navinfo.volvo.RecorderLifecycleObserver
|
||||
import com.navinfo.volvo.databinding.FragmentObtainMessageBinding
|
||||
import com.navinfo.volvo.db.dao.entity.AttachmentType
|
||||
import com.navinfo.volvo.db.dao.entity.Message
|
||||
import com.navinfo.volvo.ui.markRequiredInRed
|
||||
import com.navinfo.volvo.utils.EasyMediaFile
|
||||
import com.navinfo.volvo.utils.SystemConstant
|
||||
import com.nhaarman.supertooltips.ToolTip
|
||||
import top.zibin.luban.Luban
|
||||
import top.zibin.luban.OnCompressListener
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
|
||||
//@RuntimePermissions
|
||||
class ObtainMessageFragment: Fragment() {
|
||||
private var _binding: FragmentObtainMessageBinding? = null
|
||||
private val obtainMessageViewModel by lazy {
|
||||
ViewModelProvider(requireActivity()).get(ObtainMessageViewModel::class.java)
|
||||
}
|
||||
private val photoHelper by lazy {
|
||||
EasyMediaFile().setCrop(true)
|
||||
}
|
||||
private val recorderLifecycleObserver by lazy {
|
||||
RecorderLifecycleObserver()
|
||||
}
|
||||
|
||||
// This property is only valid between onCreateView and
|
||||
// onDestroyView.
|
||||
@ -29,29 +65,91 @@ class ObtainMessageFragment: Fragment() {
|
||||
_binding = FragmentObtainMessageBinding.inflate(inflater, container, false)
|
||||
val root: View = binding.root
|
||||
|
||||
obtainMessageViewModel.getMessageLiveData()?.observe(
|
||||
obtainMessageViewModel.setCurrentMessage(Message())
|
||||
|
||||
obtainMessageViewModel?.getMessageLiveData()?.observe(
|
||||
viewLifecycleOwner, Observer {
|
||||
// 初始化界面显示内容
|
||||
if(it.title!=null)
|
||||
binding.tvMessageTitle.setText(it.title)
|
||||
if (it.sendDate!=null) {
|
||||
if(it.title?.isNotEmpty() == true)
|
||||
binding.tvMessageTitle?.setText(it.title)
|
||||
if (it.sendDate?.isNotEmpty() == true) {
|
||||
binding.btnSendTime.text = it.sendDate
|
||||
}
|
||||
var hasPhoto = false
|
||||
var hasAudio = false
|
||||
if (it.attachment.isNotEmpty()) {
|
||||
// 展示照片文件或录音文件
|
||||
for (attachment in it.attachment) {
|
||||
if (attachment.attachmentType == AttachmentType.PIC) {
|
||||
Glide.with(context!!)
|
||||
.asBitmap().fitCenter()
|
||||
.load(attachment.pathUrl)
|
||||
.into(binding.imgMessageAttachment)
|
||||
// 显示名称
|
||||
binding.tvPhotoName.text = attachment.pathUrl.replace("\\", "/").substringAfterLast("/")
|
||||
hasPhoto = true
|
||||
|
||||
// 如果当前attachment文件是本地文件,开始尝试网络上传
|
||||
if (!attachment.pathUrl.startsWith("http")) {
|
||||
obtainMessageViewModel.uploadAttachment(File(attachment.pathUrl))
|
||||
}
|
||||
}
|
||||
if (attachment.attachmentType == AttachmentType.AUDIO) {
|
||||
binding.tvAudioName.text = attachment.pathUrl.replace("\\", "/").substringAfterLast("/")
|
||||
hasAudio = true
|
||||
}
|
||||
}
|
||||
}
|
||||
binding.layerPhotoResult.visibility = if (hasPhoto) VISIBLE else GONE
|
||||
binding.layerGetPhoto.visibility = if (hasPhoto) GONE else VISIBLE
|
||||
binding.layerAudioResult.visibility = if (hasAudio) VISIBLE else GONE
|
||||
binding.layerGetAudio.visibility = if (hasAudio) GONE else VISIBLE
|
||||
}
|
||||
)
|
||||
lifecycle.addObserver(recorderLifecycleObserver)
|
||||
initView()
|
||||
return root
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
fun initView() {
|
||||
// 设置问候信息提示的红色星号
|
||||
binding.tiLayoutTitle.markRequiredInRed()
|
||||
binding.tvMessageTitle.addTextChangedListener {
|
||||
obtainMessageViewModel.updateMessageTitle(it.toString())
|
||||
}
|
||||
|
||||
binding.imgPhotoDelete.setOnClickListener {
|
||||
obtainMessageViewModel.updateMessagePic(null)
|
||||
}
|
||||
|
||||
binding.imgAudioDelete.setOnClickListener {
|
||||
obtainMessageViewModel.updateMessageAudio(null)
|
||||
}
|
||||
|
||||
val sendToArray = mutableListOf<String>("绑定车辆1(LYVXFEFEXNL754427)")
|
||||
binding.edtSendTo.adapter = ArrayAdapter<String>(context!!,
|
||||
android.R.layout.simple_dropdown_item_1line, android.R.id.text1, sendToArray)
|
||||
binding.edtSendTo.onItemSelectedListener = object: OnItemSelectedListener {
|
||||
override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
|
||||
obtainMessageViewModel.getMessageLiveData().value?.toId = sendToArray[p2]
|
||||
}
|
||||
|
||||
override fun onNothingSelected(p0: AdapterView<*>?) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 设置点击按钮选择发送时间
|
||||
binding.btnSendTime.setOnClickListener {
|
||||
val dialog = DateTimePickerFragment.newInstance().mode(0)
|
||||
dialog.listener = object : DateTimePickerFragment.OnClickListener {
|
||||
override fun onClickListener(selectTime: String) {
|
||||
obtainMessageViewModel.updateMessageSendTime(selectTime)
|
||||
val sendDate = DateUtils.str2Date(selectTime, "yyyy-MM-dd HH:mm")
|
||||
if (sendDate <= Date()) {
|
||||
obtainMessageViewModel.updateMessageSendTime("现在")
|
||||
} else {
|
||||
obtainMessageViewModel.updateMessageSendTime(selectTime)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -59,8 +157,197 @@ class ObtainMessageFragment: Fragment() {
|
||||
}
|
||||
|
||||
// 点击按钮选择拍照
|
||||
binding.edtSendTo.setOnClickListener {
|
||||
binding.btnStartCamera.setOnClickListener {
|
||||
// 启动相机
|
||||
XXPermissions.with(this)
|
||||
// 申请单个权限
|
||||
.permission(Permission.CAMERA)
|
||||
.request(object : OnPermissionCallback {
|
||||
|
||||
override fun onGranted(permissions: MutableList<String>, all: Boolean) {
|
||||
if (!all) {
|
||||
Toast.makeText(activity, "获取部分权限成功,但部分权限未正常授予", Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
// 开始启动拍照界面
|
||||
photoHelper.setCrop(true).takePhoto(activity!!)
|
||||
}
|
||||
|
||||
override fun onDenied(permissions: MutableList<String>, never: Boolean) {
|
||||
if (never) {
|
||||
Toast.makeText(activity, "永久拒绝授权,请手动授权拍照权限", Toast.LENGTH_SHORT).show()
|
||||
// 如果是被永久拒绝就跳转到应用权限系统设置页面
|
||||
XXPermissions.startPermissionActivity(context!!, permissions)
|
||||
} else {
|
||||
onCameraDenied()
|
||||
showRationaleForCamera(permissions)
|
||||
}
|
||||
}
|
||||
})
|
||||
// startCamera(it)
|
||||
}
|
||||
|
||||
binding.btnStartPhoto.setOnClickListener {
|
||||
photoHelper.setCrop(true).selectPhoto(activity!!)
|
||||
}
|
||||
|
||||
// 用户选择录音文件
|
||||
binding.btnSelectSound.setOnClickListener {
|
||||
photoHelper.setCrop(false).selectAudio(activity!!)
|
||||
}
|
||||
|
||||
// 开始录音
|
||||
binding.btnStartRecord.setOnClickListener {
|
||||
// 申请权限
|
||||
XXPermissions.with(this)
|
||||
// 申请单个权限
|
||||
.permission(Permission.RECORD_AUDIO)
|
||||
.request(object : OnPermissionCallback {
|
||||
override fun onGranted(permissions: MutableList<String>, all: Boolean) {
|
||||
if (!all) {
|
||||
Toast.makeText(activity, "获取部分权限成功,但部分权限未正常授予", Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
|
||||
if (it.isSelected) {
|
||||
it.isSelected = false
|
||||
val recorderAudioPath = recorderLifecycleObserver.stopAndReleaseRecorder()
|
||||
if (File(recorderAudioPath).exists()) {
|
||||
obtainMessageViewModel.updateMessageAudio(recorderAudioPath)
|
||||
}
|
||||
} else{
|
||||
it.isSelected = true
|
||||
|
||||
recorderLifecycleObserver.initAndStartRecorder()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDenied(permissions: MutableList<String>, never: Boolean) {
|
||||
if (never) {
|
||||
Toast.makeText(activity, "永久拒绝授权,请手动授权拍照权限", Toast.LENGTH_SHORT).show()
|
||||
// 如果是被永久拒绝就跳转到应用权限系统设置页面
|
||||
XXPermissions.startPermissionActivity(context!!, permissions)
|
||||
} else {
|
||||
onCameraDenied()
|
||||
showRationaleForCamera(permissions)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 获取照片文件和音频文件
|
||||
photoHelper.setCallback {
|
||||
if (it.exists()) {
|
||||
val fileName = it.name.lowercase()
|
||||
if (fileName.endsWith(".jpg")||fileName.endsWith(".jpeg")||fileName.endsWith(".png")) {
|
||||
// 获取选中的图片,自动压缩图片质量
|
||||
// 压缩图片文件
|
||||
Luban.with(context)
|
||||
.load<Any>(mutableListOf(it) as List<Any>?)
|
||||
.ignoreBy(200)
|
||||
.setTargetDir("${SystemConstant.CameraFolder}")
|
||||
.filter { path ->
|
||||
!(TextUtils.isEmpty(path) || path.lowercase(Locale.getDefault())
|
||||
.endsWith(".gif"))
|
||||
}
|
||||
.setCompressListener(object : OnCompressListener {
|
||||
override fun onStart() {
|
||||
XLog.d("开始压缩图片/${it.absolutePath}")
|
||||
}
|
||||
|
||||
override fun onSuccess(file: File?) {
|
||||
XLog.d("压缩图片成功:${file?.absolutePath}")
|
||||
// 删除源文件
|
||||
if (!it.absolutePath.equals(file?.absolutePath)) {
|
||||
it?.delete()
|
||||
}
|
||||
// 跳转回原Fragment,展示拍摄的照片
|
||||
ViewModelProvider(requireActivity()).get(ObtainMessageViewModel::class.java).updateMessagePic(file!!.absolutePath)
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
XLog.d("压缩图片失败:${e.message}")
|
||||
}
|
||||
}).launch()
|
||||
} else if (fileName.endsWith(".mp3")||fileName.endsWith(".wav")||fileName.endsWith(".amr")||fileName.endsWith(".m4a")) {
|
||||
ToastUtils.showToast(it.absolutePath)
|
||||
obtainMessageViewModel.updateMessageAudio(it.absolutePath)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
photoHelper.setError {
|
||||
ToastUtils.showToast(it.message)
|
||||
}
|
||||
|
||||
binding.btnObtainMessageBack.setOnClickListener {
|
||||
Navigation.findNavController(it).popBackStack()
|
||||
}
|
||||
|
||||
binding.btnObtainMessageConfirm.setOnClickListener {
|
||||
// 检查当前输入数据
|
||||
val messageData = obtainMessageViewModel.getMessageLiveData().value
|
||||
if (messageData?.title?.isEmpty() == true) {
|
||||
val toolTipRelativeLayout =
|
||||
binding.ttTitle
|
||||
val toolTip = ToolTip()
|
||||
.withText("请输入问候信息")
|
||||
.withColor(com.navinfo.volvo.R.color.purple_200)
|
||||
.withShadow()
|
||||
.withAnimationType(ToolTip.AnimationType.FROM_MASTER_VIEW)
|
||||
toolTipRelativeLayout.showToolTipForView(toolTip, binding.tiLayoutTitle)
|
||||
}
|
||||
var hasPic = false
|
||||
var hasAudio = false
|
||||
for (attachment in messageData?.attachment!!) {
|
||||
if (attachment.attachmentType == AttachmentType.PIC) {
|
||||
hasPic = true
|
||||
}
|
||||
if (attachment.attachmentType == AttachmentType.AUDIO) {
|
||||
hasAudio = true
|
||||
}
|
||||
}
|
||||
if (!hasPic) {
|
||||
val toolTipRelativeLayout =
|
||||
binding.ttPic
|
||||
val toolTip = ToolTip()
|
||||
.withText("需要提供照片文件")
|
||||
.withColor(com.navinfo.volvo.R.color.purple_200)
|
||||
.withShadow()
|
||||
.withAnimationType(ToolTip.AnimationType.FROM_MASTER_VIEW)
|
||||
toolTipRelativeLayout.showToolTipForView(toolTip, binding.tvUploadPic)
|
||||
}
|
||||
if (!hasAudio) {
|
||||
val toolTipRelativeLayout =
|
||||
binding.ttAudio
|
||||
val toolTip = ToolTip()
|
||||
.withText("需要提供音频文件")
|
||||
.withColor(com.navinfo.volvo.R.color.purple_200)
|
||||
.withShadow()
|
||||
.withAnimationType(ToolTip.AnimationType.FROM_MASTER_VIEW)
|
||||
toolTipRelativeLayout.showToolTipForView(toolTip, binding.tvUploadPic)
|
||||
}
|
||||
|
||||
if (messageData?.fromId?.isEmpty()==true) {
|
||||
val toolTipRelativeLayout =
|
||||
binding.ttSendFrom
|
||||
val toolTip = ToolTip()
|
||||
.withText("请输入您的名称")
|
||||
.withColor(com.navinfo.volvo.R.color.purple_200)
|
||||
.withShadow()
|
||||
.withAnimationType(ToolTip.AnimationType.FROM_MASTER_VIEW)
|
||||
toolTipRelativeLayout.showToolTipForView(toolTip, binding.edtSendFrom)
|
||||
}
|
||||
if (messageData?.toId?.isEmpty()==true) {
|
||||
val toolTipRelativeLayout =
|
||||
binding.ttSendTo
|
||||
val toolTip = ToolTip()
|
||||
.withText("请选择要发送的车辆")
|
||||
.withColor(com.navinfo.volvo.R.color.purple_200)
|
||||
.withShadow()
|
||||
.withAnimationType(ToolTip.AnimationType.FROM_MASTER_VIEW)
|
||||
toolTipRelativeLayout.showToolTipForView(toolTip, binding.edtSendTo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,4 +355,45 @@ class ObtainMessageFragment: Fragment() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
|
||||
fun startCamera(it: View) {
|
||||
Navigation.findNavController(binding.root).navigate(com.navinfo.volvo.R.id.nav_2_camera)
|
||||
}
|
||||
|
||||
fun showRationaleForCamera(permissions: MutableList<String>) {
|
||||
// showRationaleDialog(R.string.permission_camera_rationale, request)
|
||||
// Toast.makeText(context, "当前操作需要您授权相机权限!", Toast.LENGTH_SHORT).show()
|
||||
MaterialAlertDialogBuilder(context!!)
|
||||
.setTitle("提示")
|
||||
.setMessage("当前操作需要您授权拍摄权限!")
|
||||
.setPositiveButton("确定", DialogInterface.OnClickListener { dialogInterface, i ->
|
||||
dialogInterface.dismiss()
|
||||
XXPermissions.startPermissionActivity(activity!!, permissions)
|
||||
})
|
||||
.show()
|
||||
}
|
||||
|
||||
// @OnPermissionDenied(Manifest.permission.MANAGE_EXTERNAL_STORAGE)
|
||||
fun onCameraDenied() {
|
||||
ToastUtils.showToast("当前操作需要您授权拍摄权限!")
|
||||
}
|
||||
|
||||
fun showRationaleForRecorder(permissions: MutableList<String>) {
|
||||
MaterialAlertDialogBuilder(context!!)
|
||||
.setTitle("提示")
|
||||
.setMessage("当前操作需要您授权录音权限!")
|
||||
.setPositiveButton("确定", DialogInterface.OnClickListener { dialogInterface, i ->
|
||||
dialogInterface.dismiss()
|
||||
XXPermissions.startPermissionActivity(activity!!, permissions)
|
||||
})
|
||||
.show()
|
||||
}
|
||||
fun onRecorderDenied() {
|
||||
ToastUtils.showToast("当前操作需要您授权录音权限!")
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
lifecycle.removeObserver(recorderLifecycleObserver)
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,19 @@
|
||||
package com.navinfo.volvo.ui.fragments.message
|
||||
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.navinfo.volvo.model.Message
|
||||
import com.navinfo.volvo.model.AttachmentType
|
||||
import androidx.lifecycle.*
|
||||
import com.easytools.tools.ToastUtils
|
||||
import com.elvishew.xlog.XLog
|
||||
import com.navinfo.volvo.db.dao.entity.Attachment
|
||||
import com.navinfo.volvo.db.dao.entity.AttachmentType
|
||||
import com.navinfo.volvo.db.dao.entity.Message
|
||||
import com.navinfo.volvo.http.NavinfoVolvoCall
|
||||
import kotlinx.coroutines.launch
|
||||
import okhttp3.MediaType
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.RequestBody
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
|
||||
class ObtainMessageViewModel: ViewModel() {
|
||||
private val msgLiveData: MutableLiveData<Message> by lazy {
|
||||
@ -25,22 +35,41 @@ class ObtainMessageViewModel: ViewModel() {
|
||||
}
|
||||
|
||||
// 更新消息附件中的照片文件
|
||||
fun updateMessagePic(picUrl: String) {
|
||||
fun updateMessagePic(picUrl: String?) {
|
||||
var hasPic = false
|
||||
|
||||
for (attachment in this.msgLiveData.value!!.attachment) {
|
||||
if (attachment.attachmentType == AttachmentType.PIC) {
|
||||
attachment.pathUrl = picUrl
|
||||
if (picUrl==null||picUrl.isEmpty()) {
|
||||
this.msgLiveData.value!!.attachment.remove(attachment)
|
||||
} else {
|
||||
attachment.pathUrl = picUrl
|
||||
}
|
||||
hasPic = true
|
||||
}
|
||||
}
|
||||
if (!hasPic&&picUrl!=null) {
|
||||
this.msgLiveData.value!!.attachment.add(Attachment(UUID.randomUUID().toString(), picUrl, AttachmentType.PIC))
|
||||
}
|
||||
this.msgLiveData.postValue(this.msgLiveData.value)
|
||||
}
|
||||
|
||||
// 更新消息附件中的录音文件
|
||||
fun updateMessageAudio(audioUrl: String) {
|
||||
fun updateMessageAudio(audioUrl: String?) {
|
||||
var hasAudio = false
|
||||
for (attachment in this.msgLiveData.value!!.attachment) {
|
||||
if (attachment.attachmentType == AttachmentType.AUDIO) {
|
||||
attachment.pathUrl = audioUrl
|
||||
if (audioUrl==null||audioUrl.isEmpty()) {
|
||||
this.msgLiveData.value!!.attachment.remove(attachment)
|
||||
} else {
|
||||
attachment.pathUrl = audioUrl
|
||||
}
|
||||
hasAudio = true
|
||||
}
|
||||
}
|
||||
if (!hasAudio&&audioUrl!=null) {
|
||||
this.msgLiveData.value!!.attachment.add(Attachment(UUID.randomUUID().toString(), audioUrl, AttachmentType.AUDIO))
|
||||
}
|
||||
this.msgLiveData.postValue(this.msgLiveData.value)
|
||||
}
|
||||
|
||||
@ -61,4 +90,25 @@ class ObtainMessageViewModel: ViewModel() {
|
||||
this.msgLiveData.value?.sendDate = sendTime
|
||||
this.msgLiveData.postValue(this.msgLiveData.value)
|
||||
}
|
||||
|
||||
fun uploadAttachment(attachmentFile: File) {
|
||||
// 启用协程调用网络请求
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
val requestFile: RequestBody =
|
||||
RequestBody.create(MediaType.parse("multipart/form-data"), attachmentFile)
|
||||
val body = MultipartBody.Part.createFormData("picture", attachmentFile.getName(), requestFile)
|
||||
val result = NavinfoVolvoCall.getApi().uploadAttachment(body)
|
||||
XLog.d(result.code)
|
||||
if (result.code == 200) { // 请求成功
|
||||
// 获取上传后的结果
|
||||
} else {
|
||||
ToastUtils.showToast(result.message)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
ToastUtils.showToast(e.message)
|
||||
XLog.d(e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
package com.navinfo.volvo.ui.message
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.navigation.Navigation
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.navigation.ui.AppBarConfiguration
|
||||
import androidx.navigation.ui.navigateUp
|
||||
import androidx.navigation.ui.setupActionBarWithNavController
|
||||
import com.navinfo.volvo.R
|
||||
import com.navinfo.volvo.databinding.ActivityMessageBinding
|
||||
|
||||
class MessageActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var appBarConfiguration: AppBarConfiguration
|
||||
private lateinit var binding: ActivityMessageBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
binding = ActivityMessageBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
setSupportActionBar(binding.toolbar)
|
||||
binding.toolbar.setOnClickListener {
|
||||
Navigation.findNavController(it).popBackStack()
|
||||
}
|
||||
|
||||
val navController = findNavController(R.id.nav_host_fragment_message)
|
||||
appBarConfiguration = AppBarConfiguration(navController.graph)
|
||||
setupActionBarWithNavController(navController, appBarConfiguration)
|
||||
}
|
||||
|
||||
override fun onSupportNavigateUp(): Boolean {
|
||||
val navController = findNavController(R.id.nav_host_fragment_message)
|
||||
return navController.navigateUp(appBarConfiguration)
|
||||
|| super.onSupportNavigateUp()
|
||||
}
|
||||
}
|
||||
648
app/src/main/java/com/navinfo/volvo/utils/EasyMediaFile.kt
Normal file
648
app/src/main/java/com/navinfo/volvo/utils/EasyMediaFile.kt
Normal file
@ -0,0 +1,648 @@
|
||||
package com.navinfo.volvo.utils
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Fragment
|
||||
import android.content.*
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import android.os.*
|
||||
import android.os.Environment.*
|
||||
import android.provider.MediaStore
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
|
||||
/**
|
||||
* 创建日期:2018/8/21 0021on 下午 4:40
|
||||
* 描述:多媒体选择工具类
|
||||
* @author:Vincent
|
||||
*/
|
||||
class EasyMediaFile {
|
||||
/**
|
||||
* 设置图片选择结果回调
|
||||
*/
|
||||
private var callback: ((file: File) -> Unit)? = null
|
||||
private var isCrop: Boolean = false
|
||||
private var error: ((error: Exception) -> Unit)? = null
|
||||
|
||||
/**
|
||||
* 视频录制/音频录制/拍照/剪切后图片的存放位置(参考file_provider_paths.xml中的路径)
|
||||
*/
|
||||
private var mFilePath: File? = null
|
||||
|
||||
private val mainHandler = Handler(Looper.getMainLooper())
|
||||
|
||||
fun setError(error: ((error: Exception) -> Unit)?): EasyMediaFile {
|
||||
this.error = error
|
||||
return this
|
||||
}
|
||||
|
||||
fun setCallback(callback: ((file: File) -> Unit)): EasyMediaFile {
|
||||
this.callback = callback
|
||||
return this
|
||||
}
|
||||
|
||||
fun setCrop(isCrop: Boolean): EasyMediaFile {
|
||||
this.isCrop = isCrop
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改图片的存储路径(默认的图片存储路径是SD卡上 Android/data/应用包名/时间戳.jpg)
|
||||
*
|
||||
* @param imgPath 图片的存储路径(包括文件名和后缀)
|
||||
*/
|
||||
fun setFilePath(imgPath: String?): EasyMediaFile {
|
||||
if (imgPath.isNullOrEmpty()) {
|
||||
this.mFilePath = null
|
||||
} else {
|
||||
this.mFilePath = File(imgPath)
|
||||
this.mFilePath?.parentFile?.mkdirs()
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 选择文件
|
||||
* 支持图片、音频、视频
|
||||
*/
|
||||
// fun selectFile(activity: Activity) {
|
||||
// isCrop = false
|
||||
// val intent = Intent(Intent.ACTION_PICK, null).apply {
|
||||
// setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "*/*")
|
||||
// setDataAndType(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, "*/*")
|
||||
// setDataAndType(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, "*/*")
|
||||
// }
|
||||
// if (Looper.myLooper() == Looper.getMainLooper()) {
|
||||
// selectFileInternal(intent, activity, -1)
|
||||
// } else {
|
||||
// mainHandler.post { selectFileInternal(intent, activity, -1) }
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* 选择视频
|
||||
*/
|
||||
fun selectVideo(activity: Activity) {
|
||||
isCrop = false
|
||||
val intent = Intent(Intent.ACTION_PICK, null).apply {
|
||||
setDataAndType(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, "video/*")
|
||||
}
|
||||
if (Looper.myLooper() == Looper.getMainLooper()) {
|
||||
selectFileInternal(intent, activity, 2)
|
||||
} else {
|
||||
mainHandler.post { selectFileInternal(intent, activity, 2) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择音频
|
||||
*/
|
||||
fun selectAudio(activity: Activity) {
|
||||
isCrop = false
|
||||
val intent = Intent(Intent.ACTION_PICK, null).apply {
|
||||
setDataAndType(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, "audio/*")
|
||||
}
|
||||
if (Looper.myLooper() == Looper.getMainLooper()) {
|
||||
selectFileInternal(intent, activity, 1)
|
||||
} else {
|
||||
mainHandler.post { selectFileInternal(intent, activity, 1) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择图片
|
||||
*/
|
||||
fun selectPhoto(activity: Activity) {
|
||||
val intent = Intent(Intent.ACTION_PICK, null).apply {
|
||||
setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*")
|
||||
}
|
||||
if (Looper.myLooper() == Looper.getMainLooper()) {
|
||||
selectFileInternal(intent, activity, 0)
|
||||
} else {
|
||||
mainHandler.post { selectFileInternal(intent, activity, 0) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 选择文件
|
||||
*/
|
||||
private fun selectFileInternal(intent: Intent, activity: Activity, type: Int) {
|
||||
val resolveInfoList = activity.packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY)
|
||||
if (resolveInfoList.isEmpty()) {
|
||||
error?.invoke(IllegalStateException("No Activity found to handle Intent "))
|
||||
} else {
|
||||
PhotoFragment.findOrCreate(activity).start(intent, PhotoFragment.REQ_SELECT_FILE) { requestCode: Int, data: Intent? ->
|
||||
if (requestCode != PhotoFragment.REQ_SELECT_FILE) {
|
||||
return@start
|
||||
}
|
||||
data ?: return@start
|
||||
data.data ?: return@start
|
||||
try {
|
||||
val inputFile = if (type != -1) {
|
||||
uriToFile(activity, data.data!!, type)
|
||||
} else {
|
||||
if (data.data!!.path!!.contains(".")) {
|
||||
File(data.data!!.path!!)
|
||||
} else {
|
||||
when {
|
||||
data.data!!.path!!.contains("images") -> {
|
||||
uriToFile(activity, data.data!!, 0)
|
||||
}
|
||||
data.data!!.path!!.contains("video") -> {
|
||||
uriToFile(activity, data.data!!, 2)
|
||||
}
|
||||
else -> {
|
||||
uriToFile(activity, data.data!!, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isCrop) {//裁剪
|
||||
zoomPhoto(inputFile, mFilePath
|
||||
?: File(generateFilePath(activity)), activity)
|
||||
} else {//不裁剪
|
||||
callback?.invoke(inputFile)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
error?.invoke(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun uriToFile(activity: Activity, uri: Uri): File {
|
||||
|
||||
// 首先使用系统提供的CursorLoader进行file获取
|
||||
val context = activity.application
|
||||
val projection = arrayOf(MediaStore.Images.Media.DATA)
|
||||
var path: String
|
||||
try {
|
||||
CursorLoader(context, uri, projection, null, null, null)
|
||||
.loadInBackground().apply {
|
||||
getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
|
||||
val index = getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
|
||||
moveToFirst()
|
||||
path = getString(index)
|
||||
close()
|
||||
|
||||
}
|
||||
return File(path)
|
||||
} catch (e: Exception) {
|
||||
// 当没获取到。再使用别的方式进行获取
|
||||
val scheme = uri.scheme
|
||||
path = uri.path ?: throw RuntimeException("Could not find path in this uri:[$uri]")
|
||||
when (scheme) {
|
||||
"file" -> {
|
||||
val cr = context.contentResolver
|
||||
val buff = StringBuffer()
|
||||
buff.append("(").append(MediaStore.Images.ImageColumns.DATA).append("=").append("'$path'").append(")")
|
||||
cr.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, arrayOf(MediaStore.Images.ImageColumns._ID,
|
||||
MediaStore.Images.ImageColumns.DATA), buff.toString(), null, null).apply {
|
||||
this ?: throw RuntimeException("cursor is null")
|
||||
var dataIdx: Int
|
||||
while (!this.isAfterLast) {
|
||||
dataIdx = this.getColumnIndex(MediaStore.Images.ImageColumns.DATA)
|
||||
path = this.getString(dataIdx)
|
||||
this.moveToNext()
|
||||
}
|
||||
close()
|
||||
}
|
||||
|
||||
return File(path)
|
||||
}
|
||||
"content" -> {
|
||||
|
||||
context.contentResolver.query(uri, arrayOf(MediaStore.Images.Media.DATA), null, null, null).apply {
|
||||
this ?: throw RuntimeException("cursor is null")
|
||||
if (this.moveToFirst()) {
|
||||
val columnIndex = this.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
|
||||
path = this.getString(columnIndex)
|
||||
}
|
||||
close()
|
||||
}
|
||||
return File(path)
|
||||
|
||||
}
|
||||
else -> {
|
||||
throw IllegalArgumentException("Could not find file by this uri:$uri")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 拍照获取
|
||||
*/
|
||||
fun takePhoto(activity: Activity) {
|
||||
val imgFile = if (isCrop) {
|
||||
File(generateFilePath(activity))
|
||||
} else {
|
||||
mFilePath ?: File(generateFilePath(activity))
|
||||
}
|
||||
|
||||
val imgUri = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
|
||||
Uri.fromFile(imgFile)
|
||||
} else {
|
||||
//兼容android7.0 使用共享文件的形式
|
||||
val contentValues = ContentValues(1)
|
||||
contentValues.put(MediaStore.Images.Media.DATA, imgFile.absolutePath)
|
||||
activity.application.contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
|
||||
}
|
||||
|
||||
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
|
||||
intent.putExtra(MediaStore.EXTRA_OUTPUT, imgUri)
|
||||
if (Looper.myLooper() == Looper.getMainLooper()) {
|
||||
takeFileInternal(imgFile, intent, activity)
|
||||
} else {
|
||||
mainHandler.post { takeFileInternal(imgFile, intent, activity) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 音频录制
|
||||
*/
|
||||
fun takeAudio(activity: Activity) {
|
||||
isCrop = false
|
||||
val imgFile = mFilePath ?: File(generateFilePath(activity, 1))
|
||||
val imgUri = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
|
||||
Uri.fromFile(imgFile)
|
||||
} else {
|
||||
//兼容android7.0 使用共享文件的形式
|
||||
val contentValues = ContentValues(1)
|
||||
contentValues.put(MediaStore.Audio.Media.DATA, imgFile.absolutePath)
|
||||
activity.application.contentResolver.insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, contentValues)
|
||||
}
|
||||
|
||||
val intent = Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION)
|
||||
intent.putExtra(MediaStore.EXTRA_OUTPUT, imgUri)
|
||||
if (Looper.myLooper() == Looper.getMainLooper()) {
|
||||
takeFileInternal(imgFile, intent, activity, 1)
|
||||
} else {
|
||||
mainHandler.post { takeFileInternal(imgFile, intent, activity, 1) }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 视频录制
|
||||
*/
|
||||
fun takeVideo(activity: Activity) {
|
||||
isCrop = false
|
||||
val imgFile = mFilePath ?: File(generateFilePath(activity, 2))
|
||||
val imgUri = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
|
||||
Uri.fromFile(imgFile)
|
||||
} else {
|
||||
//兼容android7.0 使用共享文件的形式
|
||||
val contentValues = ContentValues(1)
|
||||
contentValues.put(MediaStore.Video.Media.DATA, imgFile.absolutePath)
|
||||
activity.application.contentResolver.insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, contentValues)
|
||||
}
|
||||
|
||||
val intent = Intent(MediaStore.ACTION_VIDEO_CAPTURE).apply {
|
||||
putExtra(MediaStore.EXTRA_OUTPUT, imgUri)
|
||||
// 默认录制时间10秒 部分手机该设置无效
|
||||
// putExtra(MediaStore.EXTRA_DURATION_LIMIT, 10000)
|
||||
}
|
||||
if (Looper.myLooper() == Looper.getMainLooper()) {
|
||||
takeFileInternal(imgFile, intent, activity, 2)
|
||||
} else {
|
||||
mainHandler.post { takeFileInternal(imgFile, intent, activity, 2) }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 拍照或选择
|
||||
*/
|
||||
fun getImage(activity: Activity) {
|
||||
|
||||
val imgFile = if (isCrop) {
|
||||
File(generateFilePath(activity))
|
||||
} else {
|
||||
mFilePath ?: File(generateFilePath(activity))
|
||||
}
|
||||
|
||||
val imgUri = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
|
||||
Uri.fromFile(imgFile)
|
||||
} else {
|
||||
//兼容android7.0 使用共享文件的形式
|
||||
val contentValues = ContentValues(1)
|
||||
contentValues.put(MediaStore.Images.Media.DATA, imgFile.absolutePath)
|
||||
activity.application.contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
|
||||
}
|
||||
|
||||
|
||||
val cameraIntents = ArrayList<Intent>()
|
||||
val captureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
|
||||
val packageManager = activity.packageManager
|
||||
val camList = packageManager.queryIntentActivities(captureIntent, 0)
|
||||
for (res in camList) {
|
||||
val packageName = res.activityInfo.packageName
|
||||
val intent = Intent(captureIntent)
|
||||
intent.component = ComponentName(res.activityInfo.packageName, res.activityInfo.name)
|
||||
intent.setPackage(packageName)
|
||||
intent.putExtra(MediaStore.EXTRA_OUTPUT, imgUri)
|
||||
cameraIntents.add(intent)
|
||||
}
|
||||
val intent = Intent.createChooser(createPickMore(), "请选择").also {
|
||||
it.putExtra(Intent.EXTRA_INITIAL_INTENTS, cameraIntents.toTypedArray())
|
||||
|
||||
}
|
||||
|
||||
if (Looper.myLooper() == Looper.getMainLooper()) {
|
||||
takeFileInternal(imgFile, intent, activity)
|
||||
} else {
|
||||
mainHandler.post { takeFileInternal(imgFile, intent, activity) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 音频录制或选择
|
||||
*/
|
||||
fun getAudio(activity: Activity) {
|
||||
isCrop = false
|
||||
val imgFile = mFilePath ?: File(generateFilePath(activity))
|
||||
|
||||
val imgUri = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
|
||||
Uri.fromFile(imgFile)
|
||||
} else {
|
||||
//兼容android7.0 使用共享文件的形式
|
||||
val contentValues = ContentValues(1)
|
||||
contentValues.put(MediaStore.Audio.Media.DATA, imgFile.absolutePath)
|
||||
activity.application.contentResolver.insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, contentValues)
|
||||
}
|
||||
val cameraIntents = ArrayList<Intent>()
|
||||
val captureIntent = Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION)
|
||||
val packageManager = activity.packageManager
|
||||
val camList = packageManager.queryIntentActivities(captureIntent, 0)
|
||||
for (res in camList) {
|
||||
val packageName = res.activityInfo.packageName
|
||||
val intent = Intent(captureIntent)
|
||||
intent.component = ComponentName(res.activityInfo.packageName, res.activityInfo.name)
|
||||
intent.setPackage(packageName)
|
||||
intent.putExtra(MediaStore.EXTRA_OUTPUT, imgUri)
|
||||
cameraIntents.add(intent)
|
||||
}
|
||||
val intent = Intent.createChooser(createPickMore("audio/*"), "请选择").also {
|
||||
it.putExtra(Intent.EXTRA_INITIAL_INTENTS, cameraIntents.toTypedArray())
|
||||
|
||||
}
|
||||
|
||||
if (Looper.myLooper() == Looper.getMainLooper()) {
|
||||
takeFileInternal(imgFile, intent, activity, 1)
|
||||
} else {
|
||||
mainHandler.post { takeFileInternal(imgFile, intent, activity, 1) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 视频拍摄或选择
|
||||
*/
|
||||
fun getVideo(activity: Activity) {
|
||||
isCrop = false
|
||||
val imgFile = mFilePath ?: File(generateFilePath(activity, 2))
|
||||
|
||||
val imgUri = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
|
||||
Uri.fromFile(imgFile)
|
||||
} else {
|
||||
//兼容android7.0 使用共享文件的形式
|
||||
val contentValues = ContentValues(1)
|
||||
contentValues.put(MediaStore.Video.Media.DATA, imgFile.absolutePath)
|
||||
activity.application.contentResolver.insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, contentValues)
|
||||
}
|
||||
val cameraIntents = ArrayList<Intent>()
|
||||
val captureIntent = Intent(MediaStore.ACTION_VIDEO_CAPTURE)
|
||||
// 某些手机此设置是不生效的,需要自行封装解决
|
||||
// captureIntent.putExtra(MediaStore.EXTRA_DURATION_LIMIT, 10000)
|
||||
val packageManager = activity.packageManager
|
||||
val camList = packageManager.queryIntentActivities(captureIntent, 0)
|
||||
for (res in camList) {
|
||||
val packageName = res.activityInfo.packageName
|
||||
val intent = Intent(captureIntent)
|
||||
intent.component = ComponentName(res.activityInfo.packageName, res.activityInfo.name)
|
||||
intent.setPackage(packageName)
|
||||
intent.putExtra(MediaStore.EXTRA_OUTPUT, imgUri)
|
||||
cameraIntents.add(intent)
|
||||
}
|
||||
val intent = Intent.createChooser(createPickMore("video/*"), "请选择").also {
|
||||
it.putExtra(Intent.EXTRA_INITIAL_INTENTS, cameraIntents.toTypedArray())
|
||||
|
||||
}
|
||||
|
||||
if (Looper.myLooper() == Looper.getMainLooper()) {
|
||||
takeFileInternal(imgFile, intent, activity, 2)
|
||||
} else {
|
||||
mainHandler.post { takeFileInternal(imgFile, intent, activity, 2) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 向系统发出指令
|
||||
*/
|
||||
private fun takeFileInternal(takePhotoPath: File, intent: Intent, activity: Activity, type: Int = 0) {
|
||||
val fragment = PhotoFragment.findOrCreate(activity)
|
||||
fragment.start(intent, PhotoFragment.REQ_TAKE_FILE) { requestCode: Int, data: Intent? ->
|
||||
if (requestCode == PhotoFragment.REQ_TAKE_FILE) {
|
||||
if (data?.data != null) {
|
||||
mFilePath = when (type) {
|
||||
0 -> {
|
||||
uriToFile(activity, data.data!!)
|
||||
}
|
||||
else -> uriToFile(activity, data.data!!, type)
|
||||
}
|
||||
|
||||
if (isCrop) {
|
||||
zoomPhoto(takePhotoPath, mFilePath
|
||||
?: File(generateFilePath(activity)), activity)
|
||||
} else {
|
||||
callback?.invoke(mFilePath!!)
|
||||
mFilePath = null
|
||||
}
|
||||
return@start
|
||||
}
|
||||
if (isCrop) {
|
||||
zoomPhoto(takePhotoPath, mFilePath
|
||||
?: File(generateFilePath(activity)), activity)
|
||||
} else {
|
||||
callback?.invoke(takePhotoPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun uriToFile(activity: Activity, data: Uri, type: Int): File {
|
||||
val cursor = activity.managedQuery(data, arrayOf(if (type == 1) MediaStore.Audio.Media.DATA else MediaStore.Video.Media.DATA), null,
|
||||
null, null)
|
||||
val path = if (cursor == null) {
|
||||
data.path
|
||||
} else {
|
||||
val index = cursor.getColumnIndexOrThrow(if (type == 1) MediaStore.Audio.Media.DATA else MediaStore.Video.Media.DATA)
|
||||
cursor.moveToFirst()
|
||||
cursor.getString(index)
|
||||
}
|
||||
// 手动关掉报错如下
|
||||
// Caused by: android.database.StaleDataException: Attempted to access a cursor after it has been closed.
|
||||
// cursor.close()
|
||||
return File(path!!)
|
||||
}
|
||||
|
||||
/***
|
||||
* 图片裁剪
|
||||
*/
|
||||
private fun zoomPhoto(inputFile: File?, outputFile: File, activity: Activity) {
|
||||
try {
|
||||
val intent = Intent("com.android.camera.action.CROP")
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
intent.setDataAndType(getImageContentUri(activity, inputFile), "image/*")
|
||||
} else {
|
||||
intent.setDataAndType(Uri.fromFile(inputFile), "image/*")
|
||||
}
|
||||
intent.putExtra("crop", "true")
|
||||
|
||||
// 是否返回uri
|
||||
intent.putExtra("return-data", false)
|
||||
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString())
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
val imgFile = File("${Environment.getExternalStoragePublicDirectory(DIRECTORY_PICTURES)}/${outputFile.name}")
|
||||
// 通过 MediaStore API 插入file 为了拿到系统裁剪要保存到的uri(因为App没有权限不能访问公共存储空间,需要通过 MediaStore API来操作)
|
||||
val values = ContentValues()
|
||||
values.put(MediaStore.Images.Media.DATA, imgFile.getAbsolutePath());
|
||||
values.put(MediaStore.Images.Media.DISPLAY_NAME, outputFile.name);
|
||||
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
|
||||
val uri = activity.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
|
||||
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(outputFile))
|
||||
zoomPhotoInternal(outputFile, intent, activity)
|
||||
}else {
|
||||
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(outputFile))
|
||||
zoomPhotoInternal(outputFile, intent, activity)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
error?.invoke(e)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun zoomPhotoInternal(outputFile: File, intent: Intent, activity: Activity) {
|
||||
PhotoFragment.findOrCreate(activity).start(intent, PhotoFragment.REQ_ZOOM_PHOTO) { requestCode: Int, data: Intent? ->
|
||||
if (requestCode == PhotoFragment.REQ_ZOOM_PHOTO) {
|
||||
data ?: return@start
|
||||
callback?.invoke(outputFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**构建文件多选Intent*/
|
||||
private fun createPickMore(fileType: String = "image/*"): Intent {
|
||||
val pictureChooseIntent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI).apply {
|
||||
type = fileType
|
||||
}
|
||||
pictureChooseIntent.putExtra(Intent.EXTRA_LOCAL_ONLY, true)
|
||||
/**临时授权app访问URI代表的文件所有权*/
|
||||
pictureChooseIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
return pictureChooseIntent
|
||||
}
|
||||
/**
|
||||
* 产生图片的路径,带文件夹和文件名,文件名为当前毫秒数
|
||||
*/
|
||||
private fun generateFilePath(activity: Activity, fileType: Int = 0): String {
|
||||
val file = when (fileType) {
|
||||
// 音频路径
|
||||
1 -> "${SystemConstant.SoundFolder}" + File.separator + System.currentTimeMillis().toString() + ".m4a"
|
||||
// 视频路径
|
||||
2 -> "${SystemConstant.CameraFolder}" + File.separator + System.currentTimeMillis().toString() + ".mp4"
|
||||
// 图片路径
|
||||
else -> "${SystemConstant.CameraFolder}" + File.separator + System.currentTimeMillis().toString() + ".jpg"
|
||||
}
|
||||
File(file).parentFile.mkdirs()
|
||||
return file
|
||||
}
|
||||
/**
|
||||
* 获取SD下的应用目录
|
||||
*/
|
||||
private fun getExternalStoragePath(activity: Activity): String {
|
||||
val sb = "${activity.getExternalFilesDir(null)}/tmp"
|
||||
return sb
|
||||
}
|
||||
/**
|
||||
* 安卓7.0裁剪根据文件路径获取uri
|
||||
*/
|
||||
private fun getImageContentUri(context: Context, imageFile: File?): Uri? {
|
||||
val filePath = imageFile?.absolutePath
|
||||
val cursor = context.contentResolver.query(
|
||||
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
|
||||
arrayOf(MediaStore.Images.Media._ID),
|
||||
MediaStore.Images.Media.DATA + "=? ",
|
||||
arrayOf(filePath), null)
|
||||
cursor.use { _ ->
|
||||
return if (cursor != null && cursor.moveToFirst()) {
|
||||
val id = cursor.getInt(cursor
|
||||
.getColumnIndex(MediaStore.MediaColumns._ID))
|
||||
val baseUri = Uri.parse("content://media/external/images/media")
|
||||
Uri.withAppendedPath(baseUri, "" + id)
|
||||
} else {
|
||||
imageFile?.let {
|
||||
if (it.exists()) {
|
||||
val values = ContentValues()
|
||||
values.put(MediaStore.Images.Media.DATA, filePath)
|
||||
context.contentResolver.insert(
|
||||
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 用于获取图片的Fragment
|
||||
*/
|
||||
class PhotoFragment : Fragment() {
|
||||
/**
|
||||
* Fragment处理照片后返回接口
|
||||
*/
|
||||
private var callback: ((requestCode: Int, intent: Intent?) -> Unit)? = null
|
||||
/**
|
||||
* 开启系统相册
|
||||
* 裁剪图片、打开相册选择单张图片、拍照
|
||||
*/
|
||||
fun start(intent: Intent, requestCode: Int, callback: ((requestCode: Int, intent: Intent?) -> Unit)) {
|
||||
this.callback = callback
|
||||
startActivityForResult(intent, requestCode)
|
||||
}
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
callback?.invoke(requestCode, data)
|
||||
}
|
||||
}
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
retainInstance = true
|
||||
}
|
||||
companion object {
|
||||
const val REQ_TAKE_FILE = 10001
|
||||
const val REQ_SELECT_FILE = 10002
|
||||
const val REQ_ZOOM_PHOTO = 10003
|
||||
private const val TAG = "EasyPhoto:PhotoFragment"
|
||||
@JvmStatic
|
||||
fun findOrCreate(activity: Activity): PhotoFragment {
|
||||
var fragment: PhotoFragment? = activity.fragmentManager.findFragmentByTag(TAG) as PhotoFragment?
|
||||
if (fragment == null) {
|
||||
fragment = PhotoFragment()
|
||||
activity.fragmentManager.beginTransaction()
|
||||
.add(fragment, TAG)
|
||||
.commitAllowingStateLoss()
|
||||
activity.fragmentManager.executePendingTransactions()
|
||||
}
|
||||
return fragment
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
10
app/src/main/java/com/navinfo/volvo/utils/SystemConstant.kt
Normal file
10
app/src/main/java/com/navinfo/volvo/utils/SystemConstant.kt
Normal file
@ -0,0 +1,10 @@
|
||||
package com.navinfo.volvo.utils
|
||||
|
||||
class SystemConstant {
|
||||
companion object {
|
||||
lateinit var ROOT_PATH: String
|
||||
lateinit var CameraFolder: String
|
||||
lateinit var SoundFolder: String
|
||||
lateinit var LogFolder: String
|
||||
}
|
||||
}
|
||||
6
app/src/main/res/drawable/ic_baseline_camera_alt_24.xml
Normal file
6
app/src/main/res/drawable/ic_baseline_camera_alt_24.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<vector android:height="64dp" android:tint="#000000"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="64dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M12,12m-3.2,0a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0"/>
|
||||
<path android:fillColor="@android:color/white" android:pathData="M9,2L7.17,4L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2h-3.17L15,2L9,2zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5z"/>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/ic_baseline_delete_24.xml
Normal file
5
app/src/main/res/drawable/ic_baseline_delete_24.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#000000"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/ic_baseline_volume_down_24.xml
Normal file
5
app/src/main/res/drawable/ic_baseline_volume_down_24.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<vector android:autoMirrored="true" android:height="24dp"
|
||||
android:tint="#000000" android:viewportHeight="24"
|
||||
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M18.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM5,9v6h4l5,5V4L9,9H5z"/>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/ic_baseline_volume_mute_24.xml
Normal file
5
app/src/main/res/drawable/ic_baseline_volume_mute_24.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<vector android:autoMirrored="true" android:height="24dp"
|
||||
android:tint="#000000" android:viewportHeight="24"
|
||||
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M7,9v6h4l5,5V4l-5,5H7z"/>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/ic_baseline_volume_up_24.xml
Normal file
5
app/src/main/res/drawable/ic_baseline_volume_up_24.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<vector android:autoMirrored="true" android:height="24dp"
|
||||
android:tint="#000000" android:viewportHeight="24"
|
||||
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M3,9v6h4l5,5L12,4L7,9L3,9zM16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77s-2.99,-7.86 -7,-8.77z"/>
|
||||
</vector>
|
||||
35
app/src/main/res/layout/activity_message.xml
Normal file
35
app/src/main/res/layout/activity_message.xml
Normal file
@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".ui.message.MessageActivity">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/Theme.NavinfoVolvo.AppBarOverlay">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
app:navigationIcon="@drawable/ic_back_file_picker"
|
||||
app:popupTheme="@style/Theme.NavinfoVolvo.PopupOverlay" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<include layout="@layout/content_message" />
|
||||
|
||||
<!-- <com.google.android.material.floatingactionbutton.FloatingActionButton-->
|
||||
<!-- android:id="@+id/fab"-->
|
||||
<!-- android:layout_width="wrap_content"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- android:layout_gravity="bottom|end"-->
|
||||
<!-- android:layout_marginEnd="@dimen/fab_margin"-->
|
||||
<!-- android:layout_marginBottom="16dp"-->
|
||||
<!-- app:srcCompat="@android:drawable/ic_dialog_email" />-->
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
@ -9,7 +9,7 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:src="@mipmap/ic_launcher"
|
||||
android:src="@mipmap/volvo_logo_small"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
19
app/src/main/res/layout/content_message.xml
Normal file
19
app/src/main/res/layout/content_message.xml
Normal file
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
|
||||
<fragment
|
||||
android:id="@+id/nav_host_fragment_message"
|
||||
android:name="androidx.navigation.fragment.NavHostFragment"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:defaultNavHost="true"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:navGraph="@navigation/nav_graph" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
21
app/src/main/res/layout/fragment_camera.xml
Normal file
21
app/src/main/res/layout/fragment_camera.xml
Normal file
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<com.otaliastudios.cameraview.CameraView
|
||||
android:id="@+id/camera"
|
||||
android:keepScreenOn="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/img_start_camera"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="10dp"
|
||||
app:shapeAppearance="@style/CircleStyle"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:src="@drawable/ic_baseline_camera_alt_24"></com.google.android.material.imageview.ShapeableImageView>
|
||||
</RelativeLayout>
|
||||
@ -1,11 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="com.navinfo.volvo.ui.fragments.dashboard.DashboardFragment">
|
||||
tools:context="com.navinfo.volvo.ui.dashboard.CameraFragment">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_dashboard"
|
||||
|
||||
28
app/src/main/res/layout/fragment_first.xml
Normal file
28
app/src/main/res/layout/fragment_first.xml
Normal file
@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".FirstFragment">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textview_first"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/hello_first_fragment"
|
||||
app:layout_constraintBottom_toTopOf="@id/button_first"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_first"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/next"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/textview_first" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@ -1,20 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="@dimen/activity_default_padding"
|
||||
tools:context=".ui.fragments.message.ObtainMessageFragment">
|
||||
|
||||
tools:context=".ui.message.ObtainMessageFragment">
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:overScrollMode="never"
|
||||
android:scrollbars="none"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:overScrollMode="never"
|
||||
android:scrollbars="none">
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
@ -25,10 +24,10 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/default_widget_padding"
|
||||
android:hint="问候信息"
|
||||
app:counterEnabled="true"
|
||||
app:counterMaxLength="10"
|
||||
app:errorEnabled="true"
|
||||
android:hint="请输入问候信息"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
@ -41,13 +40,19 @@
|
||||
tools:ignore="SpeakableTextPresentCheck,TouchTargetSizeCheck" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.nhaarman.supertooltips.ToolTipRelativeLayout
|
||||
android:id="@+id/tt_title"
|
||||
app:layout_constraintTop_toBottomOf="@id/ti_layout_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/label_message_subtitle"
|
||||
style="@style/TextAppearance.AppCompat.Subhead"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="问候信息"
|
||||
app:layout_constraintTop_toBottomOf="@id/ti_layout_title"></com.google.android.material.textview.MaterialTextView>
|
||||
android:text="问候附件"
|
||||
app:layout_constraintTop_toBottomOf="@id/tt_title"></com.google.android.material.textview.MaterialTextView>
|
||||
|
||||
<com.google.android.material.divider.MaterialDivider
|
||||
android:id="@+id/div_message"
|
||||
@ -78,31 +83,74 @@
|
||||
android:textColor="@color/red"></TextView>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_upload_pic"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="上传图片:"></TextView>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
style="@style/Widget.Material3.Button.ElevatedButton"
|
||||
<LinearLayout
|
||||
android:id="@+id/layer_get_photo"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/default_widget_padding"
|
||||
android:text="点击拍照"
|
||||
app:icon="@drawable/ic_baseline_camera_24"></com.google.android.material.button.MaterialButton>
|
||||
|
||||
<Space
|
||||
android:layout_width="@dimen/default_widget_padding"
|
||||
android:layout_height="wrap_content"></Space>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
style="@style/Widget.Material3.Button.ElevatedButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_start_camera"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/Widget.Material3.Button.ElevatedButton"
|
||||
app:icon="@drawable/ic_baseline_camera_24"
|
||||
android:text="点击拍照"
|
||||
android:padding="@dimen/default_widget_padding"></com.google.android.material.button.MaterialButton>
|
||||
<Space
|
||||
android:layout_width="@dimen/default_widget_padding"
|
||||
android:layout_height="wrap_content"></Space>
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_start_photo"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/Widget.Material3.Button.ElevatedButton"
|
||||
app:icon="@drawable/ic_baseline_image_search_24"
|
||||
android:text="相册选择"
|
||||
android:padding="@dimen/default_widget_padding"></com.google.android.material.button.MaterialButton>
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:id="@+id/layer_photo_result"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/default_widget_padding"
|
||||
android:text="相册选择"
|
||||
app:icon="@drawable/ic_baseline_image_search_24"></com.google.android.material.button.MaterialButton>
|
||||
android:gravity="center"
|
||||
android:visibility="gone"
|
||||
android:orientation="horizontal">
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/tv_photo_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text=""></com.google.android.material.textview.MaterialTextView>
|
||||
<Space
|
||||
android:layout_width="@dimen/default_widget_padding"
|
||||
android:layout_height="wrap_content"></Space>
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/img_photo_delete"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:src="@drawable/ic_baseline_delete_24"></com.google.android.material.imageview.ShapeableImageView>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/img_message_attachment"
|
||||
android:layout_width="160dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginLeft="@dimen/activity_horizontal_margin"
|
||||
android:scaleType="fitCenter">
|
||||
</androidx.appcompat.widget.AppCompatImageView>
|
||||
|
||||
<com.nhaarman.supertooltips.ToolTipRelativeLayout
|
||||
android:id="@+id/tt_pic"
|
||||
app:layout_constraintTop_toBottomOf="@id/ti_layout_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<LinearLayout
|
||||
style="@style/default_line"
|
||||
android:layout_width="match_parent"
|
||||
@ -120,27 +168,69 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="上传音频:"></TextView>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
style="@style/Widget.Material3.Button.ElevatedButton"
|
||||
<LinearLayout
|
||||
android:id="@+id/layer_get_audio"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/default_widget_padding"
|
||||
android:text="长按录音"
|
||||
app:icon="@drawable/ic_baseline_fiber_manual_record_24"></com.google.android.material.button.MaterialButton>
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal">
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_start_record"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/Widget.Material3.Button.ElevatedButton"
|
||||
app:icon="@drawable/ic_baseline_fiber_manual_record_24"
|
||||
android:text="录制音频"
|
||||
android:padding="@dimen/default_widget_padding"></com.google.android.material.button.MaterialButton>
|
||||
<Space
|
||||
android:layout_width="@dimen/default_widget_padding"
|
||||
android:layout_height="wrap_content"></Space>
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_select_sound"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/Widget.Material3.Button.ElevatedButton"
|
||||
app:icon="@drawable/ic_baseline_audio_file_24"
|
||||
android:text="音频选择"
|
||||
android:padding="@dimen/default_widget_padding"></com.google.android.material.button.MaterialButton>
|
||||
</LinearLayout>
|
||||
|
||||
<Space
|
||||
android:layout_width="@dimen/default_widget_padding"
|
||||
android:layout_height="wrap_content"></Space>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
style="@style/Widget.Material3.Button.ElevatedButton"
|
||||
android:layout_width="wrap_content"
|
||||
<LinearLayout
|
||||
android:id="@+id/layer_audio_result"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/default_widget_padding"
|
||||
android:text="音频选择"
|
||||
app:icon="@drawable/ic_baseline_audio_file_24"></com.google.android.material.button.MaterialButton>
|
||||
android:gravity="center"
|
||||
android:visibility="gone"
|
||||
android:orientation="horizontal">
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/tv_audio_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text=""></com.google.android.material.textview.MaterialTextView>
|
||||
<Space
|
||||
android:layout_width="@dimen/default_widget_padding"
|
||||
android:layout_height="wrap_content"></Space>
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/img_audio_delete"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:src="@drawable/ic_baseline_delete_24"></com.google.android.material.imageview.ShapeableImageView>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/img_sound_play"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_baseline_volume_up_24"
|
||||
android:visibility="gone"
|
||||
android:layout_gravity="center"></com.google.android.material.imageview.ShapeableImageView>
|
||||
<com.nhaarman.supertooltips.ToolTipRelativeLayout
|
||||
android:id="@+id/tt_audio"
|
||||
app:layout_constraintTop_toBottomOf="@id/ti_layout_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
@ -162,12 +252,8 @@
|
||||
android:id="@+id/layer_send_info"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:animateLayoutChanges="true"
|
||||
android:background="@drawable/shape_radius5_white"
|
||||
android:divider="@drawable/shape_divider_linear"
|
||||
android:orientation="vertical"
|
||||
android:padding="@dimen/default_widget_padding"
|
||||
android:showDividers="middle"
|
||||
app:layout_constraintTop_toBottomOf="@id/div_send_info">
|
||||
|
||||
<LinearLayout
|
||||
@ -192,21 +278,44 @@
|
||||
android:id="@+id/edt_send_from"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/selector_bg_4_round_corner"
|
||||
tools:ignore="TouchTargetSizeCheck,SpeakableTextPresentCheck"></androidx.appcompat.widget.AppCompatEditText>
|
||||
android:background="@drawable/selector_bg_4_round_corner"></androidx.appcompat.widget.AppCompatEditText>
|
||||
</LinearLayout>
|
||||
|
||||
<com.nhaarman.supertooltips.ToolTipRelativeLayout
|
||||
android:id="@+id/tt_send_from"
|
||||
app:layout_constraintTop_toBottomOf="@id/ti_layout_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/edt_send_to"
|
||||
<LinearLayout
|
||||
style="@style/default_line"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableStart="@drawable/ic_baseline_star_8"
|
||||
android:drawableEnd="@drawable/ic_baseline_navigate_next_24"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:text="发给谁:" />
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="*"
|
||||
android:textColor="@color/red"></TextView>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="发给谁:"></TextView>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatSpinner
|
||||
android:id="@+id/edt_send_to"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"></androidx.appcompat.widget.AppCompatSpinner>
|
||||
</LinearLayout>
|
||||
|
||||
<com.nhaarman.supertooltips.ToolTipRelativeLayout
|
||||
android:id="@+id/tt_send_to"
|
||||
app:layout_constraintTop_toBottomOf="@id/ti_layout_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<LinearLayout
|
||||
style="@style/default_line"
|
||||
@ -228,19 +337,22 @@
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_send_time"
|
||||
style="@style/Widget.Material3.Button.ElevatedButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/default_widget_padding"
|
||||
style="@style/Widget.Material3.Button.ElevatedButton"
|
||||
app:icon="@drawable/ic_baseline_access_time_24"
|
||||
android:text="现在"
|
||||
app:icon="@drawable/ic_baseline_access_time_24"></com.google.android.material.button.MaterialButton>
|
||||
android:padding="@dimen/default_widget_padding"></com.google.android.material.button.MaterialButton>
|
||||
</LinearLayout>
|
||||
|
||||
<com.nhaarman.supertooltips.ToolTipRelativeLayout
|
||||
android:id="@+id/tt_send_time"
|
||||
app:layout_constraintTop_toBottomOf="@id/ti_layout_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</ScrollView>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@ -248,6 +360,7 @@
|
||||
app:layout_constraintBottom_toBottomOf="parent">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_obtain_message_back"
|
||||
style="@style/Widget.Material3.Button.ElevatedButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
@ -259,6 +372,7 @@
|
||||
android:layout_height="wrap_content"></Space>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_obtain_message_confirm"
|
||||
style="@style/Widget.Material3.Button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
27
app/src/main/res/layout/fragment_second.xml
Normal file
27
app/src/main/res/layout/fragment_second.xml
Normal file
@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".SecondFragment">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textview_second"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toTopOf="@id/button_second"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_second"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/previous"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/textview_second" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
BIN
app/src/main/res/mipmap-xxhdpi/volvo_logo_small.png
Normal file
BIN
app/src/main/res/mipmap-xxhdpi/volvo_logo_small.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.7 KiB |
23
app/src/main/res/navigation/nav_graph.xml
Normal file
23
app/src/main/res/navigation/nav_graph.xml
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/nav_graph"
|
||||
app:startDestination="@id/navigation_obtain_message">
|
||||
|
||||
<fragment
|
||||
android:id="@+id/navigation_obtain_message"
|
||||
android:name="com.navinfo.volvo.ui.message.ObtainMessageFragment"
|
||||
android:label="问候编辑"
|
||||
tools:layout="@layout/fragment_obtain_message" >
|
||||
<action
|
||||
android:id="@+id/nav_2_camera"
|
||||
app:destination="@id/navigation_camera"></action>
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/navigation_camera"
|
||||
android:name="com.navinfo.volvo.ui.camera.CameraFragment"
|
||||
android:label="拍照"
|
||||
tools:layout="@layout/fragment_camera" />
|
||||
</navigation>
|
||||
3
app/src/main/res/values-land/dimens.xml
Normal file
3
app/src/main/res/values-land/dimens.xml
Normal file
@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<dimen name="fab_margin">48dp</dimen>
|
||||
</resources>
|
||||
@ -1,6 +1,6 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- Base application theme. -->
|
||||
<style name="Theme.NavinfoVolvo" parent="Theme.Material3.DynamicColors.DayNight">
|
||||
<style name="Theme.NavinfoVolvo" parent="Theme.Material3.Dark">
|
||||
<!-- Primary brand color. -->
|
||||
<item name="colorPrimary">@color/purple_200</item>
|
||||
<item name="colorPrimaryVariant">@color/purple_700</item>
|
||||
|
||||
3
app/src/main/res/values-w1240dp/dimens.xml
Normal file
3
app/src/main/res/values-w1240dp/dimens.xml
Normal file
@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<dimen name="fab_margin">200dp</dimen>
|
||||
</resources>
|
||||
3
app/src/main/res/values-w600dp/dimens.xml
Normal file
3
app/src/main/res/values-w600dp/dimens.xml
Normal file
@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<dimen name="fab_margin">48dp</dimen>
|
||||
</resources>
|
||||
@ -5,4 +5,5 @@
|
||||
<dimen name="activity_default_padding">12dp</dimen>
|
||||
<dimen name="default_font_size">18sp</dimen>
|
||||
<dimen name="default_widget_padding">6dp</dimen>
|
||||
<dimen name="fab_margin">16dp</dimen>
|
||||
</resources>
|
||||
@ -6,4 +6,13 @@
|
||||
<string name="delete">Del</string>
|
||||
<string name="share">Share</string>
|
||||
<string name="my">My</string>
|
||||
<string name="title_activity_second">SecondActivity</string>
|
||||
<!-- Strings used for fragments for navigation -->
|
||||
<string name="first_fragment_label">First Fragment</string>
|
||||
<string name="second_fragment_label">Second Fragment</string>
|
||||
<string name="next">Next</string>
|
||||
<string name="previous">Previous</string>
|
||||
|
||||
<string name="hello_first_fragment">Hello first fragment</string>
|
||||
<string name="hello_second_fragment">Hello second fragment. Arg: %1$s</string>
|
||||
</resources>
|
||||
@ -3,4 +3,10 @@
|
||||
<style name="default_line">
|
||||
<item name="android:padding">@dimen/default_widget_padding</item>
|
||||
</style>
|
||||
|
||||
<!--ShapeableImageView 圆 -->
|
||||
<style name="CircleStyle">
|
||||
<item name="cornerFamily">rounded</item>
|
||||
<item name="cornerSize">50%</item>
|
||||
</style>
|
||||
</resources>
|
||||
@ -1,6 +1,6 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- Base application theme. -->
|
||||
<style name="Theme.NavinfoVolvo" parent="Theme.Material3.DynamicColors.Light">
|
||||
<style name="Theme.NavinfoVolvo" parent="Theme.Material3.Light">
|
||||
<!-- Primary brand color. -->
|
||||
<item name="colorPrimary">@color/purple_500</item>
|
||||
<item name="colorPrimaryVariant">@color/purple_700</item>
|
||||
@ -13,4 +13,13 @@
|
||||
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
|
||||
<!-- Customize your theme here. -->
|
||||
</style>
|
||||
|
||||
<style name="Theme.NavinfoVolvo.NoActionBar">
|
||||
<item name="windowActionBar">false</item>
|
||||
<item name="windowNoTitle">true</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.NavinfoVolvo.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
|
||||
|
||||
<style name="Theme.NavinfoVolvo.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
|
||||
</resources>
|
||||
Loading…
x
Reference in New Issue
Block a user