增加上传图片,从相册选取照片显示还有点问题

This commit is contained in:
2025-07-19 01:21:18 +08:00
parent 85b902882c
commit fc42bb4af6
17 changed files with 791 additions and 9 deletions

View File

@@ -1,7 +1,11 @@
package com.ttstd.elderlyassistant.activity.write;
import android.Manifest;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.net.Uri;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
@@ -9,34 +13,56 @@ import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.Ndef;
import android.os.Bundle;
import android.provider.MediaStore;
import android.text.Editable;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
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.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;
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.reflect.TypeToken;
import com.hjq.toast.Toaster;
import com.ttstd.elderlyassistant.BuildConfig;
import com.ttstd.elderlyassistant.R;
import com.ttstd.elderlyassistant.bean.ContactInfo;
import com.ttstd.elderlyassistant.databinding.ActivityWriteMessageBinding;
import com.ttstd.elderlyassistant.gson.GsonUtils;
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.WriteMessageUtils;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
public class WriteMessageActivity extends AppCompatActivity {
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 WriteMessageViewModel mViewModel;
private Gson mGson = new Gson();
@@ -45,6 +71,8 @@ public class WriteMessageActivity extends AppCompatActivity {
private Tag mTag;
private int mOperationId;
private String mUploadImageUrl;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -52,6 +80,15 @@ public class WriteMessageActivity extends AppCompatActivity {
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_write_message);
mBinding.setLifecycleOwner(this);
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适配器实例
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
@@ -60,6 +97,15 @@ public class WriteMessageActivity extends AppCompatActivity {
finish();
return;
}
mViewModel.mUploadImageUrlData.observe(this, new Observer<String>() {
@Override
public void onChanged(String s) {
mUploadImageUrl = s;
Toaster.show("头像上传成功");
}
});
}
@Override
@@ -165,7 +211,7 @@ public class WriteMessageActivity extends AppCompatActivity {
case "/encrypted":
mBinding.rbEncryption.setChecked(true);
try {
String trimString = detail.trim();
String trimString = detail.trim();
String decryptedString = AESUtil.decrypt(trimString);
ContactInfo contactInfo = mGson.fromJson(decryptedString, listType);
setContactInfo(contactInfo);
@@ -189,9 +235,96 @@ public class WriteMessageActivity extends AppCompatActivity {
private void setContactInfo(ContactInfo 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 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) {
if (mTag == null) {
Toaster.showLong("NFC卡片未连接");
@@ -231,6 +364,8 @@ public class WriteMessageActivity extends AppCompatActivity {
contactInfo.setQq(qq);
contactInfo.setTag(tag);
contactInfo.setOperation(mOperationId);
contactInfo.setAvatar(mUploadImageUrl);
if (mBinding.rbEncryption.isChecked()) {
try {

View File

@@ -1,11 +1,52 @@
package com.ttstd.elderlyassistant.activity.write;
import androidx.databinding.ObservableInt;
import android.util.Log;
import androidx.lifecycle.MutableLiveData;
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 {
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: ");
}
});
}
}

View File

@@ -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);
}
}

View File

@@ -15,6 +15,7 @@ public class ContactInfo implements Serializable {
public String tag;
public String wxid;
public String qq;
public String avatar;
/**
* 1 电话 2 短信 3 微信语音 4 微信视频 5 QQ语音 6 QQ视频
*/
@@ -73,6 +74,14 @@ public class ContactInfo implements Serializable {
this.qq = qq;
}
public String getAvatar() {
return avatar;
}
public void setAvatar(String avatar) {
this.avatar = avatar;
}
public int getOperation() {
return operation;
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}

View File

@@ -2,14 +2,28 @@ package com.ttstd.elderlyassistant.network;
import android.annotation.SuppressLint;
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.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.ConnectionPool;
import okhttp3.Dispatcher;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.RequestBody;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
@@ -29,8 +43,8 @@ public class NetInterfaceManager {
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 NetInterfaceManager(Context context) {
@@ -73,6 +87,13 @@ public class NetInterfaceManager {
builder.writeTimeout(TIME_OUT, TimeUnit.SECONDS);// 设置写入超时时间
builder.readTimeout(TIME_OUT, TimeUnit.SECONDS);// 设置读取数据超时时间
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";
Cache cache = new Cache(new File(cacheDirectory), CACHE_SIZE);
@@ -110,5 +131,25 @@ public class NetInterfaceManager {
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());
}
}

View File

@@ -1,6 +1,10 @@
package com.ttstd.elderlyassistant.network;
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 SCDN_IMG_UPLOAD = "api/v1.php";
}

View File

@@ -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
);
}

View File

@@ -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 "-";
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}