增加上传图片,从相册选取照片显示还有点问题
This commit is contained in:
@@ -106,6 +106,8 @@ dependencies {
|
|||||||
implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
|
implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
|
||||||
//
|
//
|
||||||
implementation 'com.squareup.okhttp3:okhttp:4.7.0'
|
implementation 'com.squareup.okhttp3:okhttp:4.7.0'
|
||||||
|
//log打印
|
||||||
|
implementation 'com.squareup.okhttp3:logging-interceptor:4.7.0'
|
||||||
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
|
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
|
||||||
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
|
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
|
||||||
// implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
|
// implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
|
||||||
@@ -137,4 +139,6 @@ dependencies {
|
|||||||
implementation 'me.jessyan:autosize:1.2.1'
|
implementation 'me.jessyan:autosize:1.2.1'
|
||||||
// 吐司框架:https://github.com/getActivity/Toaster
|
// 吐司框架:https://github.com/getActivity/Toaster
|
||||||
implementation 'com.github.getActivity:Toaster:12.6'
|
implementation 'com.github.getActivity:Toaster:12.6'
|
||||||
|
//图片裁切
|
||||||
|
implementation 'com.github.yalantis:ucrop:2.2.10'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,16 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.ttstd.elderlyassistant">
|
package="com.ttstd.elderlyassistant">
|
||||||
|
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.CALL_PHONE" />
|
<uses-permission android:name="android.permission.CALL_PHONE" />
|
||||||
<uses-permission android:name="android.permission.SEND_SMS" />
|
<uses-permission android:name="android.permission.SEND_SMS" />
|
||||||
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
|
||||||
<!-- NFC权限 -->
|
<!-- NFC权限 -->
|
||||||
<uses-permission android:name="android.permission.NFC" /> <!-- 声明只有带NFC的手机才可以下载 -->
|
<uses-permission android:name="android.permission.NFC" /> <!-- 声明只有带NFC的手机才可以下载 -->
|
||||||
|
|
||||||
<uses-feature
|
<uses-feature
|
||||||
android:name="android.hardware.nfc"
|
android:name="android.hardware.nfc"
|
||||||
android:required="true" />
|
android:required="true" />
|
||||||
@@ -16,6 +21,7 @@
|
|||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
|
android:requestLegacyExternalStorage="true"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme">
|
android:theme="@style/AppTheme">
|
||||||
@@ -78,7 +84,9 @@
|
|||||||
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
|
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<!-- 指定 Scheme 协议 -->
|
<!-- 指定 Scheme 协议 -->
|
||||||
<data android:scheme="elderlyassistant" android:host="ttstd.com" />
|
<data
|
||||||
|
android:host="ttstd.com"
|
||||||
|
android:scheme="elderlyassistant" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
@@ -101,7 +109,7 @@
|
|||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name="androidx.core.content.FileProvider"
|
android:name="androidx.core.content.FileProvider"
|
||||||
android:authorities="${applicationId}.tag.provider"
|
android:authorities="${applicationId}.FileProvider"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:grantUriPermissions="true">
|
android:grantUriPermissions="true">
|
||||||
<meta-data
|
<meta-data
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
package com.ttstd.elderlyassistant.activity.write;
|
package com.ttstd.elderlyassistant.activity.write;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.app.Activity;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.nfc.NdefMessage;
|
import android.nfc.NdefMessage;
|
||||||
import android.nfc.NdefRecord;
|
import android.nfc.NdefRecord;
|
||||||
@@ -9,34 +13,56 @@ import android.nfc.NfcAdapter;
|
|||||||
import android.nfc.Tag;
|
import android.nfc.Tag;
|
||||||
import android.nfc.tech.Ndef;
|
import android.nfc.tech.Ndef;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.provider.MediaStore;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.activity.result.ActivityResult;
|
||||||
|
import androidx.activity.result.ActivityResultCallback;
|
||||||
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.core.app.ActivityCompat;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
import androidx.core.content.FileProvider;
|
||||||
import androidx.databinding.DataBindingUtil;
|
import androidx.databinding.DataBindingUtil;
|
||||||
|
import androidx.lifecycle.Observer;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
|
||||||
|
import com.bumptech.glide.Glide;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
import com.hjq.toast.Toaster;
|
import com.hjq.toast.Toaster;
|
||||||
|
import com.ttstd.elderlyassistant.BuildConfig;
|
||||||
import com.ttstd.elderlyassistant.R;
|
import com.ttstd.elderlyassistant.R;
|
||||||
import com.ttstd.elderlyassistant.bean.ContactInfo;
|
import com.ttstd.elderlyassistant.bean.ContactInfo;
|
||||||
import com.ttstd.elderlyassistant.databinding.ActivityWriteMessageBinding;
|
import com.ttstd.elderlyassistant.databinding.ActivityWriteMessageBinding;
|
||||||
import com.ttstd.elderlyassistant.gson.GsonUtils;
|
import com.ttstd.elderlyassistant.gson.GsonUtils;
|
||||||
import com.ttstd.elderlyassistant.utils.AESUtil;
|
import com.ttstd.elderlyassistant.utils.AESUtil;
|
||||||
|
import com.ttstd.elderlyassistant.utils.BitmapUtils;
|
||||||
|
import com.ttstd.elderlyassistant.utils.FileUtils;
|
||||||
import com.ttstd.elderlyassistant.utils.HexUtils;
|
import com.ttstd.elderlyassistant.utils.HexUtils;
|
||||||
import com.ttstd.elderlyassistant.utils.WriteMessageUtils;
|
import com.ttstd.elderlyassistant.utils.WriteMessageUtils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
|
||||||
public class WriteMessageActivity extends AppCompatActivity {
|
public class WriteMessageActivity extends AppCompatActivity {
|
||||||
private static final String TAG = "WriteMessageActivity";
|
private static final String TAG = "WriteMessageActivity";
|
||||||
|
private static final int REQUEST_PERMISSION_CODE = 1234;
|
||||||
|
private static final int REQUEST_CAMERA = 1111;
|
||||||
|
private static final int REQUEST_GALLERY = 2222;
|
||||||
|
private static final int REQUEST_CROP = 3333;
|
||||||
|
|
||||||
|
|
||||||
private ActivityWriteMessageBinding mBinding;
|
private ActivityWriteMessageBinding mBinding;
|
||||||
|
private WriteMessageViewModel mViewModel;
|
||||||
|
|
||||||
private Gson mGson = new Gson();
|
private Gson mGson = new Gson();
|
||||||
|
|
||||||
@@ -45,6 +71,8 @@ public class WriteMessageActivity extends AppCompatActivity {
|
|||||||
private Tag mTag;
|
private Tag mTag;
|
||||||
private int mOperationId;
|
private int mOperationId;
|
||||||
|
|
||||||
|
private String mUploadImageUrl;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
@@ -52,6 +80,15 @@ public class WriteMessageActivity extends AppCompatActivity {
|
|||||||
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_write_message);
|
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_write_message);
|
||||||
mBinding.setLifecycleOwner(this);
|
mBinding.setLifecycleOwner(this);
|
||||||
mBinding.setClick(new BtnClick());
|
mBinding.setClick(new BtnClick());
|
||||||
|
mViewModel = new ViewModelProvider(this).get(WriteMessageViewModel.class);
|
||||||
|
mBinding.setViewModel(mViewModel);
|
||||||
|
|
||||||
|
Log.e(TAG, "onCreate: getDataDir = " + getDataDir().getAbsolutePath());
|
||||||
|
Log.e(TAG, "onCreate: getExternalFilesDir = " + getExternalFilesDir(null));
|
||||||
|
Log.e(TAG, "onCreate: getFilesDir = " + getFilesDir());
|
||||||
|
Log.e(TAG, "onCreate: getCacheDir = " + getCacheDir());
|
||||||
|
Log.e(TAG, "onCreate: getExternalCacheDir = " + getExternalCacheDir());
|
||||||
|
Log.e(TAG, "onCreate: getFilesDir = " + getFilesDir());
|
||||||
|
|
||||||
// 获取NFC适配器实例
|
// 获取NFC适配器实例
|
||||||
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
|
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
|
||||||
@@ -60,6 +97,15 @@ public class WriteMessageActivity extends AppCompatActivity {
|
|||||||
finish();
|
finish();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mViewModel.mUploadImageUrlData.observe(this, new Observer<String>() {
|
||||||
|
@Override
|
||||||
|
public void onChanged(String s) {
|
||||||
|
mUploadImageUrl = s;
|
||||||
|
Toaster.show("头像上传成功");
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -189,9 +235,96 @@ public class WriteMessageActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
private void setContactInfo(ContactInfo contactInfo) {
|
private void setContactInfo(ContactInfo contactInfo) {
|
||||||
mBinding.setContactInfo(contactInfo);
|
mBinding.setContactInfo(contactInfo);
|
||||||
|
Glide.with(this)
|
||||||
|
.load(contactInfo.getAvatar())
|
||||||
|
.error(R.drawable.ic_default_avatar)
|
||||||
|
.into(mBinding.ivAvatar);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
Log.e(TAG, "onActivityResult: " + requestCode);
|
||||||
|
if (resultCode == RESULT_OK) {
|
||||||
|
if (requestCode == REQUEST_CAMERA) {
|
||||||
|
Log.e(TAG, "onActivityResult: " + imageUri.getPath());
|
||||||
|
cropImage(imageUri);
|
||||||
|
} else if (requestCode == REQUEST_GALLERY) {
|
||||||
|
Uri uri = data.getData();
|
||||||
|
if (uri != null) {
|
||||||
|
cropImage(uri);
|
||||||
|
try {
|
||||||
|
File file = FileUtils.uriToFile(WriteMessageActivity.this, uri);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (requestCode == REQUEST_CROP) {
|
||||||
|
Bitmap bitmap = data.getParcelableExtra("data");
|
||||||
|
if (bitmap != null) {
|
||||||
|
mBinding.ivAvatar.setImageBitmap(bitmap);
|
||||||
|
File file = BitmapUtils.createImageFile(WriteMessageActivity.this, "crop_avatar_" + System.currentTimeMillis() + ".jpg");
|
||||||
|
BitmapUtils.saveBitmapToFile(bitmap, file);
|
||||||
|
mViewModel.uploadAvatar(file);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cropImage(Uri inputUri) {
|
||||||
|
Intent cropIntent = new Intent("com.android.camera.action.CROP");
|
||||||
|
cropIntent.setDataAndType(inputUri, "image/*");
|
||||||
|
cropIntent.putExtra("crop", "true");
|
||||||
|
cropIntent.putExtra("aspectX", 1); // 裁剪框宽高比
|
||||||
|
cropIntent.putExtra("aspectY", 1);
|
||||||
|
cropIntent.putExtra("outputX", 1000); // 输出图片尺寸
|
||||||
|
cropIntent.putExtra("outputY", 1000);
|
||||||
|
cropIntent.putExtra("return-data", true); // 返回Bitmap数据
|
||||||
|
startActivityForResult(cropIntent, REQUEST_CROP);
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] permissions = {Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE};
|
||||||
|
|
||||||
|
private Uri imageUri;
|
||||||
|
|
||||||
|
private void checkPermission() {
|
||||||
|
if (ContextCompat.checkSelfPermission(WriteMessageActivity.this, permissions[0]) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
ActivityCompat.requestPermissions(WriteMessageActivity.this, permissions, REQUEST_PERMISSION_CODE);
|
||||||
|
} else {
|
||||||
|
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
|
||||||
|
File photoFile = new File(getExternalCacheDir(), "temp_avatar_" + System.currentTimeMillis() + ".jpg");
|
||||||
|
imageUri = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + ".FileProvider", photoFile);
|
||||||
|
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
|
||||||
|
startActivityForResult(intent, REQUEST_CAMERA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ActivityResultLauncher<Intent> mLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback<ActivityResult>() {
|
||||||
|
@Override
|
||||||
|
public void onActivityResult(ActivityResult result) {
|
||||||
|
Log.e(TAG, "onActivityResult: " + result);
|
||||||
|
if (result != null) {
|
||||||
|
Intent intent = result.getData();
|
||||||
|
if (intent != null && result.getResultCode() == Activity.RESULT_OK) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
public class BtnClick {
|
public class BtnClick {
|
||||||
|
public void takePhoto(View view) {
|
||||||
|
checkPermission();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void openGallery(View view) {
|
||||||
|
Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
|
||||||
|
intent.setType("image/*");
|
||||||
|
startActivityForResult(intent, REQUEST_GALLERY);
|
||||||
|
}
|
||||||
|
|
||||||
public void write(View view) {
|
public void write(View view) {
|
||||||
if (mTag == null) {
|
if (mTag == null) {
|
||||||
Toaster.showLong("NFC卡片未连接");
|
Toaster.showLong("NFC卡片未连接");
|
||||||
@@ -231,6 +364,8 @@ public class WriteMessageActivity extends AppCompatActivity {
|
|||||||
contactInfo.setQq(qq);
|
contactInfo.setQq(qq);
|
||||||
contactInfo.setTag(tag);
|
contactInfo.setTag(tag);
|
||||||
contactInfo.setOperation(mOperationId);
|
contactInfo.setOperation(mOperationId);
|
||||||
|
contactInfo.setAvatar(mUploadImageUrl);
|
||||||
|
|
||||||
|
|
||||||
if (mBinding.rbEncryption.isChecked()) {
|
if (mBinding.rbEncryption.isChecked()) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,11 +1,52 @@
|
|||||||
package com.ttstd.elderlyassistant.activity.write;
|
package com.ttstd.elderlyassistant.activity.write;
|
||||||
|
|
||||||
import androidx.databinding.ObservableInt;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.lifecycle.MutableLiveData;
|
||||||
import androidx.lifecycle.ViewModel;
|
import androidx.lifecycle.ViewModel;
|
||||||
|
|
||||||
import com.ttstd.elderlyassistant.R;
|
import com.ttstd.elderlyassistant.bean.UploadImageResponse;
|
||||||
|
import com.ttstd.elderlyassistant.network.NetInterfaceManager;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import io.reactivex.rxjava3.annotations.NonNull;
|
||||||
|
import io.reactivex.rxjava3.core.Observer;
|
||||||
|
import io.reactivex.rxjava3.disposables.Disposable;
|
||||||
|
|
||||||
public class WriteMessageViewModel extends ViewModel {
|
public class WriteMessageViewModel extends ViewModel {
|
||||||
|
private static final String TAG = "WriteMessageViewModel";
|
||||||
|
|
||||||
|
public MutableLiveData<String> mUploadImageUrlData = new MutableLiveData<>();
|
||||||
|
|
||||||
|
|
||||||
|
public void uploadAvatar(File file) {
|
||||||
|
NetInterfaceManager.getInstance().getDevicesLockedStateObservable(file)
|
||||||
|
// .compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY))
|
||||||
|
.subscribe(new Observer<UploadImageResponse>() {
|
||||||
|
@Override
|
||||||
|
public void onSubscribe(@NonNull Disposable d) {
|
||||||
|
Log.e("uploadAvatar", "onSubscribe: ");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNext(@NonNull UploadImageResponse uploadImageResponse) {
|
||||||
|
Log.e("uploadAvatar", "onNext: ");
|
||||||
|
if (uploadImageResponse.isSuccess()) {
|
||||||
|
String url = uploadImageResponse.getUrl();
|
||||||
|
mUploadImageUrlData.setValue(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(@NonNull Throwable e) {
|
||||||
|
Log.e("uploadAvatar", "onError: " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onComplete() {
|
||||||
|
Log.e("uploadAvatar", "onComplete: ");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package com.ttstd.elderlyassistant.annotations;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.databinding.BindingAdapter;
|
||||||
|
|
||||||
|
import com.bumptech.glide.Glide;
|
||||||
|
import com.ttstd.elderlyassistant.R;
|
||||||
|
|
||||||
|
public class ImageViewAdapter {
|
||||||
|
@BindingAdapter("android:src")
|
||||||
|
public static void setSrc(ImageView view, Bitmap bitmap) {
|
||||||
|
view.setImageBitmap(bitmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
@BindingAdapter("android:src")
|
||||||
|
public static void setSrc(ImageView view, int resId) {
|
||||||
|
view.setImageResource(resId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@BindingAdapter("imageUrl")
|
||||||
|
public static void setSrc(ImageView imageView, String url) {
|
||||||
|
Glide.with(imageView.getContext())
|
||||||
|
.load(url)
|
||||||
|
.error(R.mipmap.ic_launcher)
|
||||||
|
.centerCrop()
|
||||||
|
.into(imageView);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义设置图片属性 - 在匹配时自定义命名空间会被忽略
|
||||||
|
*/
|
||||||
|
@BindingAdapter({"imageUrl", "error"})
|
||||||
|
public static void loadImage(ImageView imageView, String url, Drawable error) {
|
||||||
|
Glide.with(imageView.getContext())
|
||||||
|
.load(url)
|
||||||
|
.error(error)
|
||||||
|
.into(imageView);
|
||||||
|
}
|
||||||
|
|
||||||
|
@BindingAdapter({"imageAvatarUrl", "error"})
|
||||||
|
public static void loadAvatarImage(ImageView imageView, String url, Drawable error) {
|
||||||
|
Glide.with(imageView.getContext())
|
||||||
|
.load(url)
|
||||||
|
.error(error)
|
||||||
|
.into(imageView);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -15,6 +15,7 @@ public class ContactInfo implements Serializable {
|
|||||||
public String tag;
|
public String tag;
|
||||||
public String wxid;
|
public String wxid;
|
||||||
public String qq;
|
public String qq;
|
||||||
|
public String avatar;
|
||||||
/**
|
/**
|
||||||
* 1 电话 2 短信 3 微信语音 4 微信视频 5 QQ语音 6 QQ视频
|
* 1 电话 2 短信 3 微信语音 4 微信视频 5 QQ语音 6 QQ视频
|
||||||
*/
|
*/
|
||||||
@@ -73,6 +74,14 @@ public class ContactInfo implements Serializable {
|
|||||||
this.qq = qq;
|
this.qq = qq;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getAvatar() {
|
||||||
|
return avatar;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAvatar(String avatar) {
|
||||||
|
this.avatar = avatar;
|
||||||
|
}
|
||||||
|
|
||||||
public int getOperation() {
|
public int getOperation() {
|
||||||
return operation;
|
return operation;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package com.ttstd.elderlyassistant.bean;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
public class UploadImageResponse implements Serializable {
|
||||||
|
private static final long serialVersionUID = 8945363249461745018L;
|
||||||
|
|
||||||
|
boolean success;
|
||||||
|
String url;
|
||||||
|
Data data;
|
||||||
|
|
||||||
|
public boolean isSuccess() {
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSuccess(boolean success) {
|
||||||
|
this.success = success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUrl(String url) {
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Data getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setData(Data data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Data implements Serializable {
|
||||||
|
private static final long serialVersionUID = 2237301789387292709L;
|
||||||
|
|
||||||
|
String filename;
|
||||||
|
long original_size;
|
||||||
|
long compressed_size;
|
||||||
|
float compression_ratio;
|
||||||
|
|
||||||
|
public String getFilename() {
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFilename(String filename) {
|
||||||
|
this.filename = filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getOriginal_size() {
|
||||||
|
return original_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOriginal_size(long original_size) {
|
||||||
|
this.original_size = original_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getCompressed_size() {
|
||||||
|
return compressed_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCompressed_size(long compressed_size) {
|
||||||
|
this.compressed_size = compressed_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getCompression_ratio() {
|
||||||
|
return compression_ratio;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCompression_ratio(float compression_ratio) {
|
||||||
|
this.compression_ratio = compression_ratio;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
package com.ailaiban.ai.network;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
|
||||||
|
public class MD5Util {
|
||||||
|
|
||||||
|
public static String packetMD5(String str) {
|
||||||
|
MessageDigest messageDigest = null;
|
||||||
|
try {
|
||||||
|
messageDigest = MessageDigest.getInstance("MD5");
|
||||||
|
|
||||||
|
messageDigest.reset();
|
||||||
|
|
||||||
|
messageDigest.update(str.getBytes("UTF-8"));
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
System.out.println("NoSuchAlgorithmException caught!");
|
||||||
|
System.exit(-1);
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] byteArray = messageDigest.digest();
|
||||||
|
|
||||||
|
StringBuffer md5StrBuff = new StringBuffer();
|
||||||
|
|
||||||
|
for (int i = 0; i < byteArray.length; i++) {
|
||||||
|
if (Integer.toHexString(0xFF & byteArray[i]).length() == 1)
|
||||||
|
md5StrBuff.append("0").append(
|
||||||
|
Integer.toHexString(0xFF & byteArray[i]));
|
||||||
|
else
|
||||||
|
md5StrBuff.append(Integer.toHexString(0xFF & byteArray[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return md5StrBuff.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("DefaultLocale")
|
||||||
|
public static String getUpperMD5Str(String str) {
|
||||||
|
MessageDigest messageDigest = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
messageDigest = MessageDigest.getInstance("MD5");
|
||||||
|
|
||||||
|
messageDigest.reset();
|
||||||
|
|
||||||
|
messageDigest.update(str.getBytes("UTF-8"));
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
System.out.println("NoSuchAlgorithmException caught!");
|
||||||
|
System.exit(-1);
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] byteArray = messageDigest.digest();
|
||||||
|
|
||||||
|
StringBuffer md5StrBuff = new StringBuffer();
|
||||||
|
|
||||||
|
for (int i = 0; i < byteArray.length; i++) {
|
||||||
|
if (Integer.toHexString(0xFF & byteArray[i]).length() == 1)
|
||||||
|
md5StrBuff.append("0").append(
|
||||||
|
Integer.toHexString(0xFF & byteArray[i]));
|
||||||
|
else
|
||||||
|
md5StrBuff.append(Integer.toHexString(0xFF & byteArray[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return md5StrBuff.toString().toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取16位的MD5 值得
|
||||||
|
*
|
||||||
|
* @param str
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@SuppressLint("DefaultLocale")
|
||||||
|
public static String getUpperMD5Str16(String str) {
|
||||||
|
MessageDigest messageDigest = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
messageDigest = MessageDigest.getInstance("MD5");
|
||||||
|
|
||||||
|
messageDigest.reset();
|
||||||
|
|
||||||
|
messageDigest.update(str.getBytes("UTF-8"));
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
System.out.println("NoSuchAlgorithmException caught!");
|
||||||
|
System.exit(-1);
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] byteArray = messageDigest.digest();
|
||||||
|
|
||||||
|
StringBuffer md5StrBuff = new StringBuffer();
|
||||||
|
|
||||||
|
for (int i = 0; i < byteArray.length; i++) {
|
||||||
|
if (Integer.toHexString(0xFF & byteArray[i]).length() == 1)
|
||||||
|
md5StrBuff.append("0").append(
|
||||||
|
Integer.toHexString(0xFF & byteArray[i]));
|
||||||
|
else
|
||||||
|
md5StrBuff.append(Integer.toHexString(0xFF & byteArray[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return md5StrBuff.toString().toUpperCase().substring(8, 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -2,14 +2,28 @@ package com.ttstd.elderlyassistant.network;
|
|||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.ttstd.elderlyassistant.bean.UploadImageResponse;
|
||||||
|
import com.ttstd.elderlyassistant.network.api.UploadPictrueApi;
|
||||||
|
import com.ttstd.elderlyassistant.network.interceptor.RepeatRequestInterceptor;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||||
|
import io.reactivex.rxjava3.core.Observable;
|
||||||
|
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||||
import okhttp3.Cache;
|
import okhttp3.Cache;
|
||||||
import okhttp3.ConnectionPool;
|
import okhttp3.ConnectionPool;
|
||||||
import okhttp3.Dispatcher;
|
import okhttp3.Dispatcher;
|
||||||
|
import okhttp3.MediaType;
|
||||||
|
import okhttp3.MultipartBody;
|
||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
|
import okhttp3.RequestBody;
|
||||||
|
import okhttp3.logging.HttpLoggingInterceptor;
|
||||||
import retrofit2.Retrofit;
|
import retrofit2.Retrofit;
|
||||||
import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory;
|
import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory;
|
||||||
import retrofit2.converter.gson.GsonConverterFactory;
|
import retrofit2.converter.gson.GsonConverterFactory;
|
||||||
@@ -29,8 +43,8 @@ public class NetInterfaceManager {
|
|||||||
private OkHttpClient mScdnClient;
|
private OkHttpClient mScdnClient;
|
||||||
|
|
||||||
//超时时间
|
//超时时间
|
||||||
private static final int TIME_OUT = 5;
|
private static final int TIME_OUT = 30;
|
||||||
// 缓存大小
|
// 缓存大小 64M
|
||||||
private static final long CACHE_SIZE = 1024 * 1024 * 64;
|
private static final long CACHE_SIZE = 1024 * 1024 * 64;
|
||||||
|
|
||||||
private NetInterfaceManager(Context context) {
|
private NetInterfaceManager(Context context) {
|
||||||
@@ -73,6 +87,13 @@ public class NetInterfaceManager {
|
|||||||
builder.writeTimeout(TIME_OUT, TimeUnit.SECONDS);// 设置写入超时时间
|
builder.writeTimeout(TIME_OUT, TimeUnit.SECONDS);// 设置写入超时时间
|
||||||
builder.readTimeout(TIME_OUT, TimeUnit.SECONDS);// 设置读取数据超时时间
|
builder.readTimeout(TIME_OUT, TimeUnit.SECONDS);// 设置读取数据超时时间
|
||||||
builder.retryOnConnectionFailure(true);// 设置进行连接失败重试
|
builder.retryOnConnectionFailure(true);// 设置进行连接失败重试
|
||||||
|
builder.addInterceptor(new RepeatRequestInterceptor());
|
||||||
|
builder.addInterceptor(new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
|
||||||
|
@Override
|
||||||
|
public void log(@NotNull String s) {
|
||||||
|
Log.e("HttpLoggingInterceptor", "log: " + s);
|
||||||
|
}
|
||||||
|
}).setLevel(HttpLoggingInterceptor.Level.BASIC));
|
||||||
// 设置缓存文件路径
|
// 设置缓存文件路径
|
||||||
String cacheDirectory = mContext.getExternalCacheDir().getAbsolutePath() + "/OkHttpCache";
|
String cacheDirectory = mContext.getExternalCacheDir().getAbsolutePath() + "/OkHttpCache";
|
||||||
Cache cache = new Cache(new File(cacheDirectory), CACHE_SIZE);
|
Cache cache = new Cache(new File(cacheDirectory), CACHE_SIZE);
|
||||||
@@ -110,5 +131,25 @@ public class NetInterfaceManager {
|
|||||||
return mOkHttpClient;
|
return mOkHttpClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static MultipartBody.Part convertFileToJpegMultipartBody(File file, String key) {
|
||||||
|
MediaType mediaType = MediaType.Companion.parse("image/jpeg");
|
||||||
|
RequestBody requestBody = RequestBody.Companion.create(file, mediaType);
|
||||||
|
MultipartBody.Part body = MultipartBody.Part.createFormData(key, file.getName(), requestBody);
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static RequestBody convertObjectToRequestBody(Object param) {
|
||||||
|
MediaType mediaType = MediaType.Companion.parse("text/plain");
|
||||||
|
RequestBody requestBody = RequestBody.Companion.create(String.valueOf(param), mediaType);
|
||||||
|
return requestBody;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Observable<UploadImageResponse> getDevicesLockedStateObservable(File file) {
|
||||||
|
MultipartBody.Part body = convertFileToJpegMultipartBody(file, "image");
|
||||||
|
return mScdnRetrofit.create(UploadPictrueApi.class)
|
||||||
|
.uploadImg(body)
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
package com.ttstd.elderlyassistant.network;
|
package com.ttstd.elderlyassistant.network;
|
||||||
|
|
||||||
public class UrlAddress {
|
public class UrlAddress {
|
||||||
public static final String TTSTD = "https://www.ttstd.com:/65535";
|
public static final String TTSTD = "https://www.ttstd.com:65535/";
|
||||||
public static final String IMG_SCDN = "https://img.scdn.io/";
|
public static final String IMG_SCDN = "https://img.scdn.io/";
|
||||||
|
|
||||||
|
public static final String SCDN_IMG_UPLOAD = "api/v1.php";
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.ttstd.elderlyassistant.network.api;
|
||||||
|
|
||||||
|
import com.ttstd.elderlyassistant.bean.UploadImageResponse;
|
||||||
|
import com.ttstd.elderlyassistant.network.UrlAddress;
|
||||||
|
|
||||||
|
import io.reactivex.rxjava3.core.Observable;
|
||||||
|
import okhttp3.MultipartBody;
|
||||||
|
import retrofit2.http.Multipart;
|
||||||
|
import retrofit2.http.POST;
|
||||||
|
import retrofit2.http.Part;
|
||||||
|
|
||||||
|
public interface UploadPictrueApi {
|
||||||
|
@Multipart
|
||||||
|
@POST(UrlAddress.SCDN_IMG_UPLOAD)
|
||||||
|
Observable<UploadImageResponse> uploadImg(
|
||||||
|
@Part MultipartBody.Part image
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
package com.ttstd.elderlyassistant.network.interceptor;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
|
||||||
|
import com.ttstd.elderlyassistant.BuildConfig;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import okhttp3.Interceptor;
|
||||||
|
import okhttp3.Protocol;
|
||||||
|
import okhttp3.Request;
|
||||||
|
import okhttp3.RequestBody;
|
||||||
|
import okhttp3.Response;
|
||||||
|
import okhttp3.ResponseBody;
|
||||||
|
import okio.Buffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* v1.0 2022-07-15 16:16:52
|
||||||
|
*/
|
||||||
|
public class RepeatRequestInterceptor implements Interceptor {
|
||||||
|
private static final String TAG = "RepeatInterceptor";
|
||||||
|
|
||||||
|
private final ConcurrentHashMap<String, Long> requestIdsMap = new ConcurrentHashMap<>();
|
||||||
|
public static final String REPEAT_REQUEST_PROTOCOL = "OKHTTP_REPEAT_REQUEST_PROTOCOL";
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Response intercept(@NotNull Chain chain) throws IOException {
|
||||||
|
Request request = chain.request();
|
||||||
|
Response response = chain.proceed(request);
|
||||||
|
ResponseBody responseBody = response.body();
|
||||||
|
|
||||||
|
//会消费请求,导致请求多次
|
||||||
|
String content = responseBody.string();
|
||||||
|
// Response copy = response.newBuilder().body(responseBody).build();
|
||||||
|
ResponseBody copy = ResponseBody.create(responseBody.contentType(), content);
|
||||||
|
if (BuildConfig.DEBUG) {
|
||||||
|
Log.e(TAG, "请求体返回:| Response: " + request.url() + "\t body: " + content);
|
||||||
|
}
|
||||||
|
//相同的请求
|
||||||
|
String requestKey = com.ailaiban.ai.network.MD5Util.getUpperMD5Str(request.method() + request.url().toString() + requestBodyToString(request.body()));
|
||||||
|
long time = System.currentTimeMillis();//请求时间
|
||||||
|
try {
|
||||||
|
if (requestIdsMap.size() > 0 && requestIdsMap.containsKey(requestKey)) {
|
||||||
|
log("重复请求:", requestKey, request);
|
||||||
|
//下面这行写了不会抛出onerror
|
||||||
|
// chain.call().cancel();
|
||||||
|
return new Response.Builder()
|
||||||
|
.protocol(Protocol.get(REPEAT_REQUEST_PROTOCOL))
|
||||||
|
.request(request) //multi thread
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
requestIdsMap.put(requestKey, time);
|
||||||
|
log("注册请求:", requestKey, request);
|
||||||
|
// RepeatRequestInterceptor.Builder builder = request.newBuilder();
|
||||||
|
// builder.addHeader("header", jsonObject.toString());
|
||||||
|
return response.newBuilder().body(copy).build();
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "intercept: " + e.getMessage());
|
||||||
|
throw e;
|
||||||
|
} finally {
|
||||||
|
if (requestIdsMap.containsKey(requestKey) && requestIdsMap.containsValue(time)) {//请求任务完成删除map中的数据
|
||||||
|
requestIdsMap.remove(requestKey);
|
||||||
|
log("移除请求:", requestKey, request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void log(String action, String requestKey, Request request) {
|
||||||
|
if (BuildConfig.DEBUG) {
|
||||||
|
Log.e("REPEAT-REQUEST", action + requestKey + " Method @" + request.method() + " --- " + " URL = " + request.url().encodedPath() + "\t" + bodyToString(request));
|
||||||
|
} else {
|
||||||
|
Log.e("REPEAT-REQUEST", action + requestKey + " Method @" + request.method());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String bodyToString(final Request request) {
|
||||||
|
try {
|
||||||
|
final Request copy = request.newBuilder().build();
|
||||||
|
final Buffer buffer = new Buffer();
|
||||||
|
copy.body().writeTo(buffer);
|
||||||
|
if (buffer.size() > 4096) {
|
||||||
|
return "-too long";
|
||||||
|
}
|
||||||
|
return buffer.readUtf8();
|
||||||
|
} catch (Exception e) {
|
||||||
|
return "-";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String requestBodyToString(RequestBody body) {
|
||||||
|
try {
|
||||||
|
final Buffer buffer = new Buffer();
|
||||||
|
body.writeTo(buffer);
|
||||||
|
if (buffer.size() > 4096) {
|
||||||
|
return "-too long";
|
||||||
|
}
|
||||||
|
return buffer.readUtf8();
|
||||||
|
} catch (Exception e) {
|
||||||
|
return "-";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package com.ttstd.elderlyassistant.utils;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.os.Environment;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class BitmapUtils {
|
||||||
|
|
||||||
|
// 创建图片文件(自动处理目录)
|
||||||
|
public static File createImageFile(Context context, String fileName) {
|
||||||
|
// 优先使用外部存储的 Pictures 目录
|
||||||
|
File storageDir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
|
||||||
|
return new File(storageDir, fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存 Bitmap 到文件
|
||||||
|
public static boolean saveBitmapToFile(Bitmap bitmap, File file) {
|
||||||
|
try (FileOutputStream fos = new FileOutputStream(file)) {
|
||||||
|
// 根据文件名后缀选择格式(PNG 无损,JPEG 有损)
|
||||||
|
Bitmap.CompressFormat format = file.getName().endsWith(".png") ?
|
||||||
|
Bitmap.CompressFormat.PNG : Bitmap.CompressFormat.JPEG;
|
||||||
|
bitmap.compress(format, 100, fos); // 质量 100 表示最高质量
|
||||||
|
fos.flush();
|
||||||
|
return true;
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
package com.ttstd.elderlyassistant.utils;
|
||||||
|
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
import android.provider.OpenableColumns;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
public class FileUtils {
|
||||||
|
|
||||||
|
public static File uriToFile(Context context, Uri uri) throws IOException {
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||||
|
// Android 10 以下用 MediaStore 查询
|
||||||
|
File file = uriToFileLegacy(context, uri);
|
||||||
|
if (file != null) return file;
|
||||||
|
}
|
||||||
|
// Android 10+ 或查询失败时使用沙盒复制
|
||||||
|
return uriToFileModern(context, uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static File uriToFileLegacy(Context context, Uri uri) {
|
||||||
|
String[] projection = {MediaStore.Images.Media.DATA};
|
||||||
|
try (Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null)) {
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
|
||||||
|
String filePath = cursor.getString(columnIndex);
|
||||||
|
return new File(filePath);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e("URI_TO_FILE", "Query failed: " + e.getMessage());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static File uriToFileModern(Context context, Uri uri) throws IOException {
|
||||||
|
ContentResolver resolver = context.getContentResolver();
|
||||||
|
String fileName = getFileName(resolver, uri); // 获取文件名(见下文辅助方法)
|
||||||
|
File cacheDir = context.getExternalCacheDir(); // 建议使用缓存目录
|
||||||
|
File outputFile = new File(cacheDir, fileName);
|
||||||
|
|
||||||
|
try (InputStream is = resolver.openInputStream(uri);
|
||||||
|
OutputStream os = new FileOutputStream(outputFile)) {
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int len;
|
||||||
|
while ((len = is.read(buffer)) != -1) {
|
||||||
|
os.write(buffer, 0, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return outputFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取文件名辅助方法
|
||||||
|
private static String getFileName(ContentResolver resolver, Uri uri) {
|
||||||
|
String name = null;
|
||||||
|
if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
|
||||||
|
try (Cursor cursor = resolver.query(uri, null, null, null, null)) {
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
name = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (name == null) {
|
||||||
|
name = uri.getLastPathSegment(); // 备用方案
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
12
app/src/main/res/drawable/ic_default_avatar.xml
Normal file
12
app/src/main/res/drawable/ic_default_avatar.xml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="200dp"
|
||||||
|
android:height="200dp"
|
||||||
|
android:viewportWidth="1024"
|
||||||
|
android:viewportHeight="1024">
|
||||||
|
<path
|
||||||
|
android:pathData="M773.18,192.77l-6.21,-21.89 -9.54,-34.24c-7.68,-45.76 -37.25,-76.42 -68.48,-76.42h-353.92c-31.23,0 -60.8,30.66 -69.18,79.68l-14.98,52.86L126.46,192.77c-36.1,0 -66.24,32.13 -66.24,72.64v625.66c0,40.58 30.08,72.7 66.24,72.7h771.07c36.1,0 66.24,-32.13 66.24,-72.7L963.78,265.41c0,-40.51 -30.08,-72.64 -66.24,-72.64L773.12,192.77zM818.69,132.54h78.85c70.27,0 126.46,59.9 126.46,132.86v625.66c0,72.96 -56.19,132.93 -126.46,132.93L126.46,1024C56.19,1024 0,964.1 0,891.07L0,265.41c0,-72.96 56.19,-132.86 126.46,-132.86L205.44,132.54l1.73,-5.82C219.33,54.02 271.55,0 334.98,0h354.05c63.42,0 115.65,54.02 127.1,123.46l2.56,9.09z"
|
||||||
|
android:fillColor="#333333"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M512,825.6a224,224 0,1 1,0 -448,224 224,0 0,1 0,448zM512,761.6a160,160 0,1 0,0 -320,160 160,0 0,0 0,320zM800,343.49a32,32 0,1 1,0 -64h64a32,32 0,1 1,0 64h-64z"
|
||||||
|
android:fillColor="#333333"/>
|
||||||
|
</vector>
|
||||||
@@ -17,6 +17,8 @@
|
|||||||
<variable
|
<variable
|
||||||
name="viewModel"
|
name="viewModel"
|
||||||
type="com.ttstd.elderlyassistant.activity.write.WriteMessageViewModel" />
|
type="com.ttstd.elderlyassistant.activity.write.WriteMessageViewModel" />
|
||||||
|
|
||||||
|
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
@@ -33,6 +35,50 @@
|
|||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/iv_avatar"
|
||||||
|
android:layout_width="160dp"
|
||||||
|
android:layout_height="160dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:adjustViewBounds="true"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
android:src="@drawable/ic_default_avatar"
|
||||||
|
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/iv_avatar">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:onClick="@{click::takePhoto}"
|
||||||
|
android:text="拍照" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:onClick="@{click::openGallery}"
|
||||||
|
android:text="相册" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
<EditText
|
<EditText
|
||||||
android:id="@+id/et_name"
|
android:id="@+id/et_name"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ allprojects {
|
|||||||
configurations.all {
|
configurations.all {
|
||||||
resolutionStrategy {
|
resolutionStrategy {
|
||||||
force 'androidx.constraintlayout:constraintlayout:2.0.4'
|
force 'androidx.constraintlayout:constraintlayout:2.0.4'
|
||||||
|
force 'androidx.appcompat:appcompat:1.3.1'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user