diff --git a/app/build.gradle b/app/build.gradle index 71e2210f6..a161775cb 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -250,6 +250,8 @@ dependencies { implementation 'com.github.mmin18:realtimeblurview:1.2.1' implementation 'com.qiyukf.unicorn:unicorn:8.2.0' + + implementation 'com.github.yalantis:ucrop:2.2.7' } repositories { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7f0e5c4f5..7200237a8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1324,6 +1324,13 @@ + + \ No newline at end of file diff --git a/app/src/main/java/com/nnbc123/app/common/util/BitmapUtil.java b/app/src/main/java/com/nnbc123/app/common/util/BitmapUtil.java new file mode 100644 index 000000000..12e1b8a3f --- /dev/null +++ b/app/src/main/java/com/nnbc123/app/common/util/BitmapUtil.java @@ -0,0 +1,163 @@ +package com.nnbc123.app.common.util; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Matrix; +import android.graphics.Point; +import android.net.Uri; + +import com.nnbc123.app.application.XChatApplication; +import com.nnbc123.core.utils.Logger; + +import java.io.ByteArrayOutputStream; +import java.io.FileDescriptor; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +/** + * Created by wushaocheng + * Date: 2022/11/17 + */ +public class BitmapUtil { + + private static final String TAG = "BitmapUtil"; + + public static Bitmap decodeSampledBitmapFromFileDescriptor(FileDescriptor fd, int reqWidth, int reqHeight) { + final BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + options.inPreferredConfig = Bitmap.Config.RGB_565; + BitmapFactory.decodeFileDescriptor(fd, null, options); + options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); + options.inJustDecodeBounds = false; + return BitmapFactory.decodeFileDescriptor(fd, null, options); + } + + private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { + if (reqWidth == 0 || reqHeight == 0) { + return 1; + } + + final int height = options.outHeight; + final int width = options.outWidth; + Logger.info(TAG, "origin, w= " + width + " h=" + height); + int inSampleSize = 1; + + if (height > reqHeight || width > reqWidth) { + final int halfHeight = height / 2; + final int halfWidth = width / 2; + + while ((halfHeight / inSampleSize) >= reqHeight + && (halfWidth / inSampleSize) >= reqWidth) { + inSampleSize *= 2; + } + } + + Logger.info(TAG, "sampleSize:" + inSampleSize); + return inSampleSize; + } + + public static Bitmap setImgSize(Bitmap bm, float newWidth, float newHeight) { + if (bm == null) { + Logger.error(TAG, "bitmap is null."); + return null; + } + // 获得图片的宽高. + int width = bm.getWidth(); + int height = bm.getHeight(); + // 计算缩放比例. + float scaleWidth = newWidth / width; + float scaleHeight = newHeight / height; + // 取得想要缩放的matrix参数. + Matrix matrix = new Matrix(); + matrix.postScale(scaleWidth, scaleHeight); + // 得到新的图片. + return Bitmap.createBitmap(bm, 0, 0, width, height, matrix, true); + } + + public static Point getImageSize(String imgPath) { + Point point = new Point(); + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeFile(imgPath, options); + point.x = options.outWidth; + point.y = options.outHeight; + return point; + } + + /** + * 获取sd卡上的bitmap的大小 + * 不加载进内存 + */ + public static long getSdBitmapSize(Uri imageUri) { + return getSdBitmapSize(imageUri, null); + } + + /** + * androidQ 以上使用path拿不到图片信息,换成Uri + * + * @param imageUri + * @param inPreferredConfig + * @return + */ + private static long getSdBitmapSize(Uri imageUri, Bitmap.Config inPreferredConfig) { + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + if (inPreferredConfig != null) { + options.inPreferredConfig = inPreferredConfig; + } + try { + InputStream stream = XChatApplication.gContext.getContentResolver().openInputStream(imageUri); + BitmapFactory.decodeStream(stream, null, options); + switch (options.inPreferredConfig) { + case ALPHA_8: + return options.outWidth * options.outHeight; + case RGB_565: + case ARGB_4444: + return options.outWidth * options.outHeight * 2; + case RGBA_F16: + return options.outWidth * options.outHeight * 8; + case ARGB_8888: + default: + return options.outWidth * options.outHeight * 4; + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + return 0; + } + } + + + public static Bitmap convertBitmap(Bitmap bitmap) { + if (bitmap == null) { + return null; + } + float density = XChatApplication.gContext.getResources().getDisplayMetrics().density; + return BitmapUtil.setImgSize(bitmap, bitmap.getWidth() * density / 3.0f, bitmap.getHeight() * density / 3.0f); + } + + public static int caculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { + int width = options.outWidth; + int height = options.outHeight; + int inSampleSize = 1; + if (width > reqWidth || height > reqHeight) { + int widthRadio = Math.round(width * 1.0f / reqWidth); + int heightRadio = Math.round(height * 1.0f / reqHeight); + inSampleSize = Math.max(widthRadio, heightRadio); + } + return inSampleSize; + } + + + public static byte[] inputStream2ByteArr(InputStream inputStream) throws IOException { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + byte[] buff = new byte[1024]; + int len = 0; + while ( (len = inputStream.read(buff)) != -1) { + outputStream.write(buff, 0, len); + } + inputStream.close(); + outputStream.close(); + return outputStream.toByteArray(); + } +} diff --git a/app/src/main/java/com/nnbc123/app/ui/user/UserInfoModifyActivity.kt b/app/src/main/java/com/nnbc123/app/ui/user/UserInfoModifyActivity.kt index 5a0b6cd69..ed55cdc40 100644 --- a/app/src/main/java/com/nnbc123/app/ui/user/UserInfoModifyActivity.kt +++ b/app/src/main/java/com/nnbc123/app/ui/user/UserInfoModifyActivity.kt @@ -1,435 +1,563 @@ -package com.nnbc123.app.ui.user; +package com.nnbc123.app.ui.user -import android.Manifest; -import android.annotation.SuppressLint; -import android.app.Activity; -import android.content.Intent; -import android.os.Bundle; - -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - -import android.text.TextUtils; -import android.view.View; -import android.widget.ImageView; -import android.widget.TextView; - -import com.fourmob.datetimepicker.date.DatePickerDialog; -import com.nnbc123.app.takephoto.model.TResult; -import com.netease.nim.uikit.StatusBarUtil; -import com.netease.nim.uikit.common.util.sys.TimeUtil; -import com.sleepbot.datetimepicker.time.RadialPickerLayout; -import com.sleepbot.datetimepicker.time.TimePickerDialog; -import com.trello.rxlifecycle3.android.ActivityEvent; -import com.nnbc123.app.ErbanTakePhotoActivity; -import com.nnbc123.app.R; -import com.nnbc123.app.UIHelper; -import com.nnbc123.app.audio.RecordingVoiceActivity; -import com.nnbc123.app.base.BaseActivity; -import com.nnbc123.app.common.permission.PermissionActivity; -import com.nnbc123.app.ui.login.ModifyInfoActivity; -import com.nnbc123.app.ui.utils.ImageLoadUtils; -import com.nnbc123.app.ui.widget.ButtonItem; -import com.nnbc123.app.utils.RegexUtil; -import com.nnbc123.core.auth.AuthModel; -import com.nnbc123.core.file.FileModel; -import com.nnbc123.core.user.UserModel; -import com.nnbc123.core.user.bean.UserInfo; -import com.nnbc123.core.user.bean.UserPhoto; -import com.nnbc123.library.utils.TimeUtils; -import com.nnbc123.library.utils.log.MLog; - -import java.util.ArrayList; -import java.util.Calendar; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import io.reactivex.Completable; -import io.reactivex.SingleObserver; -import io.reactivex.disposables.Disposable; +import android.Manifest +import android.annotation.SuppressLint +import android.content.Intent +import android.graphics.Bitmap +import android.net.Uri +import android.os.Bundle +import android.provider.Settings +import android.text.TextUtils +import android.view.View +import androidx.core.content.ContextCompat +import androidx.recyclerview.widget.LinearLayoutManager +import com.fourmob.datetimepicker.date.DatePickerDialog +import com.hjq.toast.ToastUtils +import com.netease.nim.uikit.StatusBarUtil +import com.netease.nim.uikit.common.util.sys.TimeUtil +import com.nnbc123.app.R +import com.nnbc123.app.UIHelper +import com.nnbc123.app.application.XChatApplication +import com.nnbc123.app.audio.RecordingVoiceActivity +import com.nnbc123.app.base.BaseViewBindingActivity +import com.nnbc123.app.common.util.BitmapUtil +import com.nnbc123.app.databinding.ActivityUserInfoModifyBinding +import com.nnbc123.app.ui.login.ModifyInfoActivity +import com.nnbc123.app.ui.user.UserPhotoAdapter.ImageClickListener +import com.nnbc123.app.ui.utils.ImageLoadUtils +import com.nnbc123.app.ui.widget.dialog.CommonTipDialog +import com.nnbc123.app.utils.RegexUtil +import com.nnbc123.core.auth.AuthModel +import com.nnbc123.core.file.FileModel +import com.nnbc123.core.user.UserModel +import com.nnbc123.core.user.bean.UserInfo +import com.nnbc123.core.user.bean.UserPhoto +import com.nnbc123.core.utils.Logger +import com.nnbc123.library.common.file.FileHelper +import com.nnbc123.library.common.photo.PhotoProviderNew +import com.nnbc123.library.common.util.PhotoCompressCallback +import com.nnbc123.library.common.util.PhotoCompressUtil +import com.nnbc123.library.easypermisssion.EasyPermissions +import com.nnbc123.library.utils.TimeUtils +import com.sleepbot.datetimepicker.time.RadialPickerLayout +import com.sleepbot.datetimepicker.time.TimePickerDialog +import com.trello.rxlifecycle3.android.ActivityEvent +import com.yalantis.ucrop.UCrop +import io.reactivex.Completable +import io.reactivex.SingleObserver +import io.reactivex.disposables.Disposable +import kotlinx.coroutines.Job +import java.io.FileNotFoundException +import java.text.SimpleDateFormat +import java.util.* +import java.util.concurrent.TimeUnit /** * @author zhouxiangfeng */ -public class UserInfoModifyActivity extends BaseActivity - implements View.OnClickListener, TimePickerDialog.OnTimeSetListener, - DatePickerDialog.OnDateSetListener, UserPhotoAdapter.ImageClickListener, - ErbanTakePhotoActivity.TakePhotoCallBack { +class UserInfoModifyActivity : BaseViewBindingActivity(), + View.OnClickListener, + TimePickerDialog.OnTimeSetListener, DatePickerDialog.OnDateSetListener, ImageClickListener, + EasyPermissions.PermissionCallbacks { + private var datePickerDialog: DatePickerDialog? = null + private var mUserInfo: UserInfo? = null + private var userId: Long = 0 + private var showAvatarAuditing = false - private static final String TAG = "UserInfoModifyActivity"; + private var mUri: Uri? = null + private var mJob: Job? = null - private ImageView civAvatar; - private DatePickerDialog datePickerDialog; - private TextView tvBirth; - private TextView tvNick; - private TextView tvDesc; - private UserInfo mUserInfo; - private long userId; - private TextView tvNoVoice; - private String audioFileUrl; - private RecyclerView photosRecyclerView; + private var isAvatar = false - private boolean showAvatarAuditing; - private ImageView ivAvatarAuditiong; + private var audioFileUrl: String? = null + companion object { + private const val TAG = "UserInfoModifyActivity" + private const val PERMISSION_CODE_STORAGE = 12 + private const val REQUEST_CODE_STORAGE = 42 + private const val REQUEST_CODE_OPEN_PHOTO_PROVIDER = 111 // 从相册中选择 + private const val MAX_BITMAP_SIZE = 100 * 1024 * 1024 // 剪切的图片最大为100 MB + private const val MIN_HEAD_PHOTO_SIZE = 20 * 1024 // 剪切的图片最小为20kb - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_user_info_modify); - initWhiteTitleBar(getString(R.string.label_title_modify_info)); - findViews(); - init(); - onSetListener(); - userId = getIntent().getLongExtra("userId", 0); - UserModel.get().getUserInfo(userId).subscribe(userInfoUpdateObserver); + //头像压缩后的大小不能超过大小 200KB + private const val IMAGE_MOST_COMPRESS_SIZE = 200 } - - @Override - public void click(int position, UserPhoto userPhoto, boolean isOwner) { - if (userPhoto != null) { - UserModifyPhotosActivity.startForResult(UserInfoModifyActivity.this, userId, Method.PHOTO); - } + override fun init() { + initWhiteTitleBar(getString(R.string.label_title_modify_info)) + findViews() + initView() + userId = intent.getLongExtra("userId", 0) } - private void onSetListener() { - + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + UserModel.get().getUserInfo(userId).subscribe(userInfoUpdateObserver) + PhotoCompressUtil.clearCompressCache() } - private void initData(UserInfo userInfo) { + override fun click(position: Int, userPhoto: UserPhoto, isOwner: Boolean) { + UserModifyPhotosActivity.startForResult( + this@UserInfoModifyActivity, + userId, + Method.PHOTO + ) + } + + private fun initData(userInfo: UserInfo?) { if (null != userInfo) { - audioFileUrl = userInfo.getUserVoice(); - ImageLoadUtils.loadAvatar(this, !TextUtils.isEmpty(userInfo.getNewAvatar()) ? userInfo.getNewAvatar() : userInfo.getAvatar(), civAvatar); - ivAvatarAuditiong.setVisibility(userInfo.isReview() ? View.VISIBLE : View.GONE); - String birth = TimeUtil.getDateTimeString(userInfo.getBirth(), "yyyy-MM-dd"); - tvBirth.setText(birth); - tvNick.setText(RegexUtil.getPrintableString(userInfo.getNick())); - setTvDesc(userInfo.getUserDesc()); - boolean hasVoice = userInfo.getVoiceDura() > 0; - tvNoVoice.setVisibility(hasVoice ? View.GONE : View.VISIBLE); - UserPhotoAdapter adapter = new UserPhotoAdapter(userInfo.getPrivatePhoto(), 1, userInfo.getUid()); - adapter.setSmall(true); - adapter.setImageClickListener(this); - photosRecyclerView.setAdapter(adapter); - if (userInfo.getPrivatePhoto() != null && userInfo.getPrivatePhoto().size() > 0) { - photosRecyclerView.setVisibility(View.VISIBLE); + audioFileUrl = userInfo.userVoice + ImageLoadUtils.loadAvatar( + this, + if (!TextUtils.isEmpty(userInfo.newAvatar)) userInfo.newAvatar else userInfo.avatar, + binding.civAvatar + ) + binding.ivAvatarAuditing.visibility = + if (userInfo.isReview) View.VISIBLE else View.GONE + val birth = TimeUtil.getDateTimeString(userInfo.birth, "yyyy-MM-dd") + binding.tvBirth.text = birth + binding.tvNick.text = RegexUtil.getPrintableString(userInfo.nick) + setTvDesc(userInfo.userDesc) + val hasVoice = userInfo.voiceDura > 0 + binding.tvNoVoice.visibility = if (hasVoice) View.GONE else View.VISIBLE + val adapter = UserPhotoAdapter(userInfo.privatePhoto, 1, userInfo.uid) + adapter.setSmall(true) + adapter.setImageClickListener(this) + binding.rvPhotos.adapter = adapter + if (userInfo.privatePhoto != null && userInfo.privatePhoto.size > 0) { + binding.rvPhotos.visibility = View.VISIBLE } else { - photosRecyclerView.setVisibility(View.GONE); + binding.rvPhotos.visibility = View.GONE } } } - private void findViews() { - civAvatar = findViewById(R.id.civ_avatar); - tvBirth = findViewById(R.id.tv_birth); - tvNick = findViewById(R.id.tv_nick); - tvDesc = findViewById(R.id.tv_desc); - tvNoVoice = findViewById(R.id.tv_no_voice); - photosRecyclerView = findViewById(R.id.rv_photos); - findViewById(R.id.layout_avatar).setOnClickListener(this); - tvBirth.setOnClickListener(this); - findViewById(R.id.tv_nick).setOnClickListener(this); - findViewById(R.id.layout_desc).setOnClickListener(this); - findViewById(R.id.iv_desc_more).setOnClickListener(this); - findViewById(R.id.layout_photos).setOnClickListener(this); - findViewById(R.id.rl_audio_record).setOnClickListener(this); - LinearLayoutManager mLayoutManager = - new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, true); - photosRecyclerView.setLayoutManager(mLayoutManager); - ivAvatarAuditiong = findViewById(R.id.iv_avatar_auditing); - + private fun findViews() { + binding.layoutAvatar.setOnClickListener(this) + binding.tvBirth.setOnClickListener(this) + binding.tvNick.setOnClickListener(this) + binding.layoutDesc.setOnClickListener(this) + binding.ivDescMore.setOnClickListener(this) + binding.layoutPhotos.setOnClickListener(this) + binding.rlAudioRecord.setOnClickListener(this) + val mLayoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, true) + binding.rvPhotos.layoutManager = mLayoutManager } - private void setTvDesc(String desc) { - tvDesc.setText(TextUtils.isEmpty(desc) ? getString(R.string.label_hint_desc_setting) : desc); + private fun setTvDesc(desc: String?) { + binding.tvDesc.text = + if (TextUtils.isEmpty(desc)) getString(R.string.label_hint_desc_setting) else desc } - private void init() { - Calendar calendar = Calendar.getInstance(); - datePickerDialog = DatePickerDialog.newInstance(this, calendar.get(Calendar.YEAR), - calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH), true); + private fun initView() { + val calendar = Calendar.getInstance() + datePickerDialog = DatePickerDialog.newInstance( + this, calendar[Calendar.YEAR], + calendar[Calendar.MONTH], calendar[Calendar.DAY_OF_MONTH], true + ) } - @Override - public void onDateSet(DatePickerDialog datePickerDialog, int year, int month, int day) { - String monthStr; - if ((month + 1) < 10) { - monthStr = "0" + (month + 1); + override fun onDateSet(datePickerDialog: DatePickerDialog, year: Int, month: Int, day: Int) { + val monthStr: String = if (month + 1 < 10) { + "0" + (month + 1) } else { - monthStr = String.valueOf(month + 1); + (month + 1).toString() } - String dayStr; - if ((day) < 10) { - dayStr = "0" + (day); + val dayStr: String = if (day < 10) { + "0$day" } else { - dayStr = String.valueOf(day); + day.toString() } - String birth = String.valueOf(year) + "-" + monthStr + "-" + dayStr; - UserInfo user = new UserInfo(); - user.setUid(AuthModel.get().getCurrentUid()); - user.setBirthStr(birth); - UserModel.get().requestUpdateUserInfo(user).subscribe(userInfoUpdateObserver); + val birth = "$year-$monthStr-$dayStr" + val user = UserInfo() + user.uid = AuthModel.get().currentUid + user.birthStr = birth + UserModel.get().requestUpdateUserInfo(user).subscribe(userInfoUpdateObserver) } + override fun onTimeSet(view: RadialPickerLayout, hourOfDay: Int, minute: Int) {} + interface Method { + companion object { + /** + * 录音 + */ + const val AUDIO = 2 - @Override - public void onTimeSet(RadialPickerLayout view, int hourOfDay, int minute) { + /** + * 昵称 + */ + const val NICK = 3 + /** + * 个人介绍 + */ + const val DESC = 4 + + /** + * 拍照 + */ + const val PHOTO = 5 + } } - - public interface Method { - /** - * 录音 - */ - int AUDIO = 2; - /** - * 昵称 - */ - int NICK = 3; - /** - * 个人介绍 - */ - int DESC = 4; - - int PHOTO = 5; - } - - - PermissionActivity.CheckPermListener checkPermissionListener = new PermissionActivity.CheckPermListener() { - @Override - public void superPermission() { - takePhoto(); - } - }; - - private void takePhoto() { - ErbanTakePhotoActivity.startToTakePhoto(this, this); - } - - private void checkPermissionAndStartCamera() { - //低版本授权检查 - checkPermission(checkPermissionListener, R.string.ask_camera, Manifest.permission.CAMERA); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - if (resultCode != Activity.RESULT_OK) { - MLog.info(this, "return is not ok,resultCode=%d", resultCode); - return; - } - - if (requestCode == Method.NICK) { - getDialogManager().showProgressDialog(UserInfoModifyActivity.this, "请稍后"); - String stringExtra = data.getStringExtra(ModifyInfoActivity.CONTENT_NICK); - tvNick.setText(stringExtra); - UserInfo user = new UserInfo(); - user.setUid(AuthModel.get().getCurrentUid()); - user.setNick(stringExtra); - UserModel.get().requestUpdateUserInfo(user).subscribe(userInfoUpdateObserver); - - } - - if (requestCode == Method.DESC) { - getDialogManager().showProgressDialog(UserInfoModifyActivity.this, "请稍后"); - String stringExtra = data.getStringExtra(ModifyInfoActivity.CONTENT); - setTvDesc(stringExtra); - UserInfo user = new UserInfo(); - user.setUid(AuthModel.get().getCurrentUid()); - user.setUserDesc(stringExtra); - UserModel.get().requestUpdateUserInfo(user).subscribe(userInfoUpdateObserver); - } - - // 刷新声音资源 - if (requestCode == Method.AUDIO) { - // 传给上个页面 - setResult(RESULT_OK, data); - - // 获取数据并刷新显示 - audioFileUrl = data.getStringExtra(RecordingVoiceActivity.AUDIO_FILE); - int audioDura = data.getIntExtra(RecordingVoiceActivity.AUDIO_DURA, 0); - - // 更新用户信息 - UserInfo user = new UserInfo(); - user.setUid(AuthModel.get().getCurrentUid()); - user.setUserVoice(audioFileUrl); - user.setVoiceDura(audioDura); - UserModel.get().requestUpdateUserInfo(user).subscribe(userInfoUpdateObserver); - } - - if (requestCode == Method.PHOTO && data != null) { - boolean isChanged = data.getBooleanExtra(UserModifyPhotosActivity.FLAG_CHANGE, false); - if (isChanged) - UserModel.get().getUserInfo(userId).subscribe(userInfoUpdateObserver); - - } - - } - - @Override - public void onClick(View v) { - switch (v.getId()) { - case R.id.layout_avatar: - if (mUserInfo != null && mUserInfo.isReview()) { - toast("头像正在审核中"); - return; + override fun onClick(v: View) { + when (v.id) { + R.id.layout_avatar -> { + if (mUserInfo != null && mUserInfo?.isReview == true) { + toast("头像正在审核中") + return } - ButtonItem buttonItem = new ButtonItem("拍照上传", this::checkPermissionAndStartCamera); - ButtonItem buttonItem1 = new ButtonItem("本地相册", () -> { - ErbanTakePhotoActivity.startToSelectPhoto(this, this); - }); - List buttonItems = new ArrayList<>(); - buttonItems.add(buttonItem); - buttonItems.add(buttonItem1); - getDialogManager().showCommonPopupDialog(buttonItems, "取消", false); - - isAvatar = true; - break; - case R.id.tv_birth: + checkStoragePermission() + isAvatar = true + } + R.id.tv_birth -> { if (mUserInfo != null) { - int year = TimeUtils.getYear(mUserInfo.getBirth()); - int month = TimeUtils.getMonth(mUserInfo.getBirth()); - int day = TimeUtils.getDayOfMonth(mUserInfo.getBirth()); - datePickerDialog = DatePickerDialog.newInstance(this, year, (month - 1), day, true); + val year = TimeUtils.getYear( + mUserInfo?.birth ?: 0L + ) + val month = TimeUtils.getMonth( + mUserInfo?.birth ?: 0L + ) + val day = TimeUtils.getDayOfMonth( + mUserInfo?.birth ?: 0L + ) + datePickerDialog = + DatePickerDialog.newInstance(this, year, month - 1, day, true) } - Calendar calendar = Calendar.getInstance(); + val calendar = Calendar.getInstance() + datePickerDialog?.apply { + setVibrate(true) + setYearRange(1945, calendar[Calendar.YEAR] - 18) + show(supportFragmentManager, "DATEPICKER_TAG_1") + } + } + R.id.tv_nick -> UIHelper.showModifyInfoAct( + this@UserInfoModifyActivity, + Method.NICK, + ModifyInfoActivity.NICK_MODIFY + ) + R.id.iv_desc_more, R.id.layout_desc -> UIHelper.showModifyInfoAct( + this@UserInfoModifyActivity, + Method.DESC, + ModifyInfoActivity.CONTENT_MODIFY + ) + R.id.rl_audio_record -> checkPermission( + { - datePickerDialog.setVibrate(true); - datePickerDialog.setYearRange(1945, calendar.get(Calendar.YEAR) - 18); - datePickerDialog.show(getSupportFragmentManager(), "DATEPICKER_TAG_1"); - break; - case R.id.tv_nick: - UIHelper.showModifyInfoAct(UserInfoModifyActivity.this, Method.NICK, ModifyInfoActivity.NICK_MODIFY); - break; - - case R.id.iv_desc_more: - case R.id.layout_desc: - UIHelper.showModifyInfoAct(UserInfoModifyActivity.this, Method.DESC, ModifyInfoActivity.CONTENT_MODIFY); - break; - - case R.id.rl_audio_record: - checkPermission(() -> { - // 点击跳转到我的声音页面 - UIHelper.showMyVoiceAct(UserInfoModifyActivity.this, Method.AUDIO, "编辑资料"); - isAvatar = false; - }, R.string.ask_again, - Manifest.permission.RECORD_AUDIO); - - break; - case R.id.layout_photos: - UserModifyPhotosActivity.startForResult(UserInfoModifyActivity.this, userId, Method.PHOTO); - break; - default: + // 点击跳转到我的声音页面 + UIHelper.showMyVoiceAct(this@UserInfoModifyActivity, Method.AUDIO, "编辑资料") + isAvatar = false + }, R.string.ask_again, + Manifest.permission.RECORD_AUDIO + ) + R.id.layout_photos -> UserModifyPhotosActivity.startForResult( + this@UserInfoModifyActivity, + userId, + Method.PHOTO + ) + else -> {} } } - private boolean isAvatar = false; + override fun onRequestPermissionsResult( + requestCode: Int, permissions: Array, grantResults: IntArray + ) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this) + } - public void onUpload(String url) { - if (isAvatar) { - UserInfo user = new UserInfo(); - user.setUid(AuthModel.get().getCurrentUid()); - user.setAvatar(url); - showAvatarAuditing = true; - UserModel.get().requestUpdateUserInfo(user).subscribe(userInfoUpdateObserver); + override fun onPermissionsGranted(requestCode: Int, perms: MutableList) { + if (requestCode == PERMISSION_CODE_STORAGE) { + checkStoragePermission() } } - public void onUploadFail() { - toast("上传失败"); - getDialogManager().dismissDialog(); - } - - - @SuppressLint("CheckResult") - @Override - public void takeSuccess(TResult result) { - getDialogManager().showProgressDialog(UserInfoModifyActivity.this, "请稍后"); - FileModel.get() - .uploadFile(result.getImage().getCompressPath()) - .compose(bindToLifecycle()) - .subscribe((url, throwable) -> { - if (throwable != null) { - onUploadFail(); - } else { - onUpload(url); + override fun onPermissionsDenied(requestCode: Int, perms: MutableList) { + if (requestCode == PERMISSION_CODE_STORAGE) { + val requestTip = "为了能正常加载您本地的文件内容,请前往应用权限设置界面打开存储空间权限。" + val mPrivacyDialog = CommonTipDialog(context) + mPrivacyDialog.setTipMsg(requestTip) + mPrivacyDialog.setOkText("去设置") + mPrivacyDialog.setOnActionListener( + object : CommonTipDialog.OnActionListener { + override fun onOk() { + //同意跳到应用详情页面 + val packageUri = + Uri.parse("package:${packageName}") + val intent = Intent( + Settings.ACTION_APPLICATION_DETAILS_SETTINGS, + packageUri + ) + startActivityForResult( + intent, REQUEST_CODE_STORAGE + ) } - }); + + override fun onCancel() { + super.onCancel() + //取消跳到应用详情页面 + ToastUtils.show("您已拒绝提供存储空间权限,将无法正常加载您本地的文件内容。") + } + } + ) + mPrivacyDialog.show() + } } - @Override - public void takeFail(TResult result, String msg) { - toast(msg); + private fun checkStoragePermission() { + if (!EasyPermissions.hasPermissions( + this, + Manifest.permission.WRITE_EXTERNAL_STORAGE, + Manifest.permission.READ_EXTERNAL_STORAGE + ) + ) { + EasyPermissions.requestPermissions( + this, + "请您再次考虑授予存储空间权限,否则将无法正常加载您本地的文件内容。", + PERMISSION_CODE_STORAGE, + Manifest.permission.WRITE_EXTERNAL_STORAGE, + Manifest.permission.READ_EXTERNAL_STORAGE + ) + } else { + mUri = Uri.parse("file://${FileHelper.getRootCacheDir()?.path}/${getNowTime()}.jpg") + PhotoProviderNew.photoProvider( + this, + resultCode = REQUEST_CODE_OPEN_PHOTO_PROVIDER + ) + } } - @Override - public void takeCancel() { - + private fun getNowTime(): String { + val date = Date(System.currentTimeMillis()) + val dateFormat = SimpleDateFormat("MMddHHmmssSS") + return dateFormat.format(date) } - @Override - protected void onPause() { - super.onPause(); + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + if (requestCode == REQUEST_CODE_STORAGE) { + checkStoragePermission() + } else if (resultCode == RESULT_OK) { + when (requestCode) { + Method.NICK -> { + data?.let { + dialogManager.showProgressDialog( + this@UserInfoModifyActivity, + "请稍后" + ) + val stringExtra = it.getStringExtra(ModifyInfoActivity.CONTENT_NICK) + binding.tvNick.text = stringExtra + val user = UserInfo() + user.uid = AuthModel.get().currentUid + user.nick = stringExtra + UserModel.get().requestUpdateUserInfo(user) + .subscribe(userInfoUpdateObserver) + } + } + Method.DESC -> { + data?.let { + dialogManager.showProgressDialog( + this@UserInfoModifyActivity, + "请稍后" + ) + val stringExtra = it.getStringExtra(ModifyInfoActivity.CONTENT) + setTvDesc(stringExtra) + val user = UserInfo() + user.uid = AuthModel.get().currentUid + user.userDesc = stringExtra + UserModel.get().requestUpdateUserInfo(user) + .subscribe(userInfoUpdateObserver) + } + } + Method.AUDIO -> { + data?.let { + // 传给上个页面 + setResult(RESULT_OK, it) + + // 获取数据并刷新显示 + audioFileUrl = it.getStringExtra(RecordingVoiceActivity.AUDIO_FILE) ?: "" + val audioDura = it.getIntExtra(RecordingVoiceActivity.AUDIO_DURA, 0) + + // 更新用户信息 + val user = UserInfo() + user.uid = AuthModel.get().currentUid + user.userVoice = audioFileUrl + user.voiceDura = audioDura + UserModel.get().requestUpdateUserInfo(user) + .subscribe(userInfoUpdateObserver) + } + } + Method.PHOTO -> { + data?.let { + val isChanged = + it.getBooleanExtra(UserModifyPhotosActivity.FLAG_CHANGE, false) + if (isChanged) UserModel.get().getUserInfo(userId) + .subscribe(userInfoUpdateObserver) + } + } + REQUEST_CODE_OPEN_PHOTO_PROVIDER -> data?.let { + val photos = PhotoProviderNew.getResultPhotoList(it) ?: return + if (photos.isNotEmpty()) { + val photo = photos[0] + crop(photo.uri, photo.size, mUri) + } + } + UCrop.REQUEST_CROP -> mUri?.path?.let { + try { + mJob?.cancel() + mJob = PhotoCompressUtil.compress( + this, + it, + PhotoCompressUtil.getCompressCachePath(), + object : PhotoCompressCallback { + @SuppressLint("CheckResult") + override fun onSuccess(compressedImg: String) { + dialogManager.showProgressDialog( + this@UserInfoModifyActivity, + "请稍后" + ) + FileModel.get() + .uploadFile(compressedImg) + .compose(bindToLifecycle()) + .subscribe { url: String?, throwable: Throwable? -> + if (throwable != null) { + onUploadFail() + } else { + onUpload(url) + } + } + } + + override fun onFail(e: Throwable) { + toast(e.message) + } + }, + mostCompressSize = IMAGE_MOST_COMPRESS_SIZE + ) + } catch (e: FileNotFoundException) { + Logger.error(TAG, e.message) + } + } + } + } } - @Override - protected void onDestroy() { - super.onDestroy(); + /** + * 第三方图片裁剪框架Ucrop + */ + private fun crop(sourceUri: Uri?, sourceSize: Long, destinationUri: Uri?) { + if (sourceUri == null || destinationUri == null) { + return + } //防止too large导致oom,大于100m不处理,内存大小 + if (BitmapUtil.getSdBitmapSize(sourceUri) >= MAX_BITMAP_SIZE) { + toast(R.string.text_bitmap_too_large) + return + } + if (sourceSize > 0) { + //不能上传图片的最小文件大小 + Logger.debug(TAG, "sourceSize: $sourceSize") + if (sourceSize < MIN_HEAD_PHOTO_SIZE) { + toast(R.string.text_bitmap_too_small) + return + } + } + val options = UCrop.Options().apply { + setCompressionQuality(100) + setShowCropGrid(false) + setToolbarColor( + ContextCompat.getColor( + XChatApplication.gContext, + android.R.color.black + ) + ) + setStatusBarColor( + ContextCompat.getColor( + XChatApplication.gContext, + android.R.color.black + ) + ) + setHideBottomControls(true) + setCompressionFormat(Bitmap.CompressFormat.JPEG) + setToolbarCancelDrawable(R.drawable.user_ucrop_ic_closs) + setToolbarCropDrawable(R.drawable.user_ucrop_ic_sure) + setToolbarWidgetColor( + ContextCompat.getColor( + XChatApplication.gContext, + R.color.color_white + ) + ) + } + UCrop.of(sourceUri, destinationUri).withOptions(options).withAspectRatio(1f, 1f) + .withMaxResultSize(800, 800).start(this) } + private fun onUpload(url: String?) { + if (isAvatar) { + val user = UserInfo() + user.uid = AuthModel.get().currentUid + user.avatar = url + showAvatarAuditing = true + UserModel.get().requestUpdateUserInfo(user).subscribe(userInfoUpdateObserver) + } + } + + private fun onUploadFail() { + toast("上传失败") + dialogManager.dismissDialog() + } /** * 观察用户信息 */ - private SingleObserver userInfoUpdateObserver = new SingleObserver() { - @Override - public void onSubscribe(Disposable d) { - mCompositeDisposable.add(d); - } - - @Override - public void onSuccess(UserInfo info) { - if (info.getUid() == userId) { - mUserInfo = info; - initData(mUserInfo); + private val userInfoUpdateObserver: SingleObserver = + object : SingleObserver { + override fun onSubscribe(d: Disposable) { + mCompositeDisposable.add(d) } - getDialogManager().dismissDialog(); - if (showAvatarAuditing) { - showAvatarAuditing = false; - showAvatarAuditingDialog(); + + override fun onSuccess(info: UserInfo) { + if (info.uid == userId) { + mUserInfo = info + initData(mUserInfo) + } + dialogManager.dismissDialog() + if (showAvatarAuditing) { + showAvatarAuditing = false + showAvatarAuditingDialog() + } + } + + override fun onError(e: Throwable) { + dialogManager.dismissDialog() + toast(e.message) } } - @Override - public void onError(Throwable e) { - getDialogManager().dismissDialog(); - toast(e.getMessage()); - } - }; - - @Override - protected boolean needSteepStateBar() { - return true; - } - - @Override - protected void setStatusBar() { - StatusBarUtil.transparencyBar(this); - StatusBarUtil.StatusBarLightMode(this); - } - - private void showAvatarAuditingDialog() { - toast(R.string.avatar_auditing); + private fun showAvatarAuditingDialog() { + toast(R.string.avatar_auditing) //延迟3秒重新获取用户信息更新状态 - ivAvatarAuditiong.setVisibility(View.VISIBLE); + binding.ivAvatarAuditing.visibility = View.VISIBLE Completable.timer(3000, TimeUnit.MILLISECONDS) - .compose(bindUntilEvent(ActivityEvent.STOP)) - .doOnComplete(() -> { - UserModel.get().updateCurrentUserInfo().subscribe(userInfoUpdateObserver); - }).doOnError(throwable -> { - throwable.printStackTrace(); - }).subscribe(); + .compose(bindUntilEvent(ActivityEvent.STOP)) + .doOnComplete { + UserModel.get().updateCurrentUserInfo().subscribe(userInfoUpdateObserver) + } + .doOnError { throwable: Throwable -> throwable.printStackTrace() }.subscribe() } -} + + override fun needSteepStateBar(): Boolean { + return true + } + + override fun setStatusBar() { + StatusBarUtil.transparencyBar(this) + StatusBarUtil.StatusBarLightMode(this) + } + + override fun onDestroy() { + super.onDestroy() + dialogManager.dismissDialog() + mJob?.cancel() + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/nnbc123/app/ui/user/UserModifyPhotosActivity.kt b/app/src/main/java/com/nnbc123/app/ui/user/UserModifyPhotosActivity.kt index 926079c55..643eff998 100644 --- a/app/src/main/java/com/nnbc123/app/ui/user/UserModifyPhotosActivity.kt +++ b/app/src/main/java/com/nnbc123/app/ui/user/UserModifyPhotosActivity.kt @@ -1,261 +1,393 @@ -package com.nnbc123.app.ui.user; +package com.nnbc123.app.ui.user -import android.annotation.SuppressLint; -import android.app.Activity; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.view.View; -import android.widget.GridView; - -import com.nnbc123.app.takephoto.app.TakePhotoActivity; -import com.nnbc123.app.takephoto.compress.CompressConfig; -import com.nnbc123.app.takephoto.model.TResult; -import com.netease.nim.uikit.StatusBarUtil; -import com.orhanobut.logger.Logger; -import com.trello.rxlifecycle3.android.ActivityEvent; -import com.nnbc123.app.R; -import com.nnbc123.app.base.TitleBar; -import com.nnbc123.app.common.permission.PermissionActivity; -import com.nnbc123.app.ui.widget.ButtonItem; -import com.nnbc123.core.file.FileModel; -import com.nnbc123.core.user.UserModel; -import com.nnbc123.core.user.bean.UserInfo; -import com.nnbc123.core.user.bean.UserPhoto; -import com.nnbc123.core.utils.net.BeanObserver; -import com.nnbc123.library.utils.file.JXFileUtils; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; +import android.Manifest +import android.annotation.SuppressLint +import android.app.Activity +import android.content.Intent +import android.graphics.Bitmap +import android.net.Uri +import android.os.Bundle +import android.provider.Settings +import android.view.View +import android.widget.GridView +import androidx.core.content.ContextCompat +import com.hjq.toast.ToastUtils +import com.netease.nim.uikit.StatusBarUtil +import com.nnbc123.app.R +import com.nnbc123.app.application.XChatApplication +import com.nnbc123.app.base.TitleBar +import com.nnbc123.app.common.util.BitmapUtil +import com.nnbc123.app.takephoto.app.TakePhotoActivity +import com.nnbc123.app.ui.user.UserModifyPhotosAdapter.PhotoItemClickListener +import com.nnbc123.app.ui.widget.dialog.CommonTipDialog +import com.nnbc123.core.file.FileModel +import com.nnbc123.core.user.UserModel +import com.nnbc123.core.user.bean.UserInfo +import com.nnbc123.core.user.bean.UserPhoto +import com.nnbc123.core.utils.net.BeanObserver +import com.nnbc123.library.common.file.FileHelper +import com.nnbc123.library.common.photo.PhotoProviderNew +import com.nnbc123.library.common.util.PhotoCompressCallback +import com.nnbc123.library.common.util.PhotoCompressUtil +import com.nnbc123.library.easypermisssion.EasyPermissions +import com.orhanobut.logger.Logger +import com.trello.rxlifecycle3.android.ActivityEvent +import com.yalantis.ucrop.UCrop +import kotlinx.coroutines.Job +import java.io.FileNotFoundException +import java.text.SimpleDateFormat +import java.util.* /** * Created by chenran on 2017/7/24. */ +class UserModifyPhotosActivity : TakePhotoActivity(), PhotoItemClickListener, + EasyPermissions.PermissionCallbacks { + private var userId: Long = 0 + private var userInfo: UserInfo? = null + private var photoGridView: GridView? = null + private var isEditMode = false + private var adapter: UserModifyPhotosAdapter? = null + private var mActivity: UserModifyPhotosActivity? = null -public class UserModifyPhotosActivity extends TakePhotoActivity implements UserModifyPhotosAdapter.PhotoItemClickListener { + private var mUri: Uri? = null + private var mJob: Job? = null - public static final String FLAG_CHANGE = "isChanged"; + companion object { + const val FLAG_CHANGE = "isChanged" + private const val TAG = "UserModifyPhotosActivit" + fun startForResult(activity: Activity, userId: Long, requestCode: Int) { + val intent = Intent(activity, UserModifyPhotosActivity::class.java) + intent.putExtra("userId", userId) + activity.startActivityForResult(intent, requestCode) + } - private long userId; - private UserInfo userInfo; - private GridView photoGridView; - private boolean isEditMode; - private UserModifyPhotosAdapter adapter; - private UserModifyPhotosActivity mActivity; + private const val PERMISSION_CODE_STORAGE = 12 + private const val REQUEST_CODE_STORAGE = 42 + private const val REQUEST_CODE_OPEN_PHOTO_PROVIDER = 111 // 从相册中选择 + private const val MAX_BITMAP_SIZE = 100 * 1024 * 1024 // 剪切的图片最大为100 MB + private const val MIN_HEAD_PHOTO_SIZE = 20 * 1024 // 剪切的图片最小为20kb + + //头像压缩后的大小不能超过大小 200KB + private const val IMAGE_MOST_COMPRESS_SIZE = 200 + } // 判断相册是否做过增减操作 - private boolean isChanged = false; - - public static void startForResult(Activity activity, long userId, int requestCode) { - Intent intent = new Intent(activity, UserModifyPhotosActivity.class); - intent.putExtra("userId", userId); - activity.startActivityForResult(intent, requestCode); - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_user_photos_modify); - initView(); - mActivity = this; - userId = getIntent().getLongExtra("userId", 0); - userInfo = UserModel.get().getCacheLoginUserInfo(); - adapter = new UserModifyPhotosAdapter(this, new ArrayList(), this); - photoGridView.setAdapter(adapter); + private var isChanged = false + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_user_photos_modify) + PhotoCompressUtil.clearCompressCache() + initView() + mActivity = this + userId = intent.getLongExtra("userId", 0) + userInfo = UserModel.get().cacheLoginUserInfo + adapter = UserModifyPhotosAdapter( + this, + ArrayList(), + this + ) + photoGridView!!.adapter = adapter if (userInfo != null) { - updateView(); + updateView() } } - private void initView() { - initWhiteTitleBar("我的相册"); - TitleBar titleBar = findViewById(R.id.title_bar); - titleBar.addAction(new TitleBar.TextAction("编辑",getResources().getColor(R.color.text_normal_282828)) { - @Override - public void performAction(View view) { - notifyEditMode(); + private fun initView() { + initWhiteTitleBar("我的相册") + val titleBar = findViewById(R.id.title_bar) + titleBar.addAction(object : TitleBar.TextAction( + "编辑", + ContextCompat.getColor(this, R.color.text_normal_282828) + ) { + override fun performAction(view: View) { + notifyEditMode() } - }); - photoGridView = findViewById(R.id.gridView); + }) + photoGridView = findViewById(R.id.gridView) } - @Override - protected void onLeftClickListener() { - onBackPressed(); + override fun onLeftClickListener() { + onBackPressed() } - private void updateView() { - adapter.setData(userInfo.getPrivatePhoto()); - adapter.notifyDataSetChanged(); + private fun updateView() { + adapter!!.setData(userInfo!!.privatePhoto) + adapter!!.notifyDataSetChanged() } - private void notifyEditMode() { - adapter.setEditMode(!isEditMode); - isEditMode = !isEditMode; - adapter.notifyDataSetChanged(); + private fun notifyEditMode() { + adapter!!.setEditMode(!isEditMode) + isEditMode = !isEditMode + adapter!!.notifyDataSetChanged() } - private void takePhoto() { - File cameraOutFile = JXFileUtils.getTempFile(this, "picture_" + System.currentTimeMillis() + ".jpg"); - if (!cameraOutFile.getParentFile().exists()) { - cameraOutFile.getParentFile().mkdirs(); - } - Uri uri = Uri.fromFile(cameraOutFile); - CompressConfig compressConfig = new CompressConfig.Builder().create(); - compressConfig.setMaxSize(500 * 1024); - getTakePhoto().onEnableCompress(compressConfig, false); - getTakePhoto().onPickFromCapture(uri); - } - - PermissionActivity.CheckPermListener checkPermissionListener = new PermissionActivity.CheckPermListener() { - @Override - public void superPermission() { - takePhoto(); - } - }; - - private void checkPermissionAndStartCamera() { - //低版本授权检查 - checkPermission(checkPermissionListener, R.string.ask_camera, android.Manifest.permission.CAMERA); - } - - @Override - public void onPhotoDeleteClick(int position) { - getDialogManager().showProgressDialog(this, "请稍后"); + override fun onPhotoDeleteClick(position: Int) { + dialogManager.showProgressDialog(this, "请稍后") if (position != 0) { - UserPhoto userPhoto = userInfo.getPrivatePhoto().get(position - 1); - UserModel.get().requestDeletePhoto(userPhoto.getPid()) - .subscribe(new BeanObserver() { - @Override - public void onErrorMsg(String error) { - toast("操作失败,请检查网络"); - getDialogManager().dismissDialog(); - } + val userPhoto = userInfo!!.privatePhoto[position - 1] + UserModel.get().requestDeletePhoto(userPhoto.pid) + .subscribe(object : BeanObserver() { + override fun onErrorMsg(error: String) { + toast("操作失败,请检查网络") + dialogManager.dismissDialog() + } - @Override - public void onSuccess(UserInfo info) { - isChanged = true; - getDialogManager().dismissDialog(); - if (info.getUid() == userId) { - userInfo = info; - updateView(); - - } + override fun onSuccess(info: UserInfo) { + isChanged = true + dialogManager.dismissDialog() + if (info.uid == userId) { + userInfo = info + updateView() } - }); + } + }) } } - @Override - public void onPhotoItemClick(int position) { + override fun onPhotoItemClick(position: Int) { if (userInfo == null) { - return; + return } if (position == 0) { - if (userInfo.getPrivatePhoto() != null && userInfo.getPrivatePhoto().size() == 8) { - toast("照片已达到最大上传数"); - return; + if (userInfo!!.privatePhoto != null && userInfo!!.privatePhoto.size == 8) { + toast("照片已达到最大上传数") + return } - ButtonItem upItem = new ButtonItem("拍照上传", new ButtonItem.OnClickListener() { - @Override - public void onClick() { - checkPermissionAndStartCamera(); - } - }); - ButtonItem loaclItem = new ButtonItem("本地相册", new ButtonItem.OnClickListener() { - @Override - public void onClick() { - CompressConfig compressConfig = new CompressConfig.Builder().create(); - compressConfig.setMaxSize(500 * 1024); - getTakePhoto().onEnableCompress(compressConfig, true); - getTakePhoto().onPickFromGallery(); - } - }); - List buttonItemList = new ArrayList<>(); - buttonItemList.add(upItem); - buttonItemList.add(loaclItem); - getDialogManager().showCommonPopupDialog(buttonItemList, "取消", false); + checkStoragePermission() } else { - ArrayList userPhotos1 = new ArrayList<>(); - userPhotos1.addAll(userInfo.getPrivatePhoto()); - Intent intent = new Intent(mActivity, ShowPhotoActivity.class); - int position1 = position - 1; - intent.putExtra("position", position1); - intent.putExtra("photoList", userPhotos1); - startActivity(intent); + val userPhotos1 = ArrayList() + userPhotos1.addAll(userInfo!!.privatePhoto) + val intent = Intent(mActivity, ShowPhotoActivity::class.java) + val position1 = position - 1 + intent.putExtra("position", position1) + intent.putExtra("photoList", userPhotos1) + startActivity(intent) } } - public void onUploadPhoto(String url) { - Logger.d("onUploadPhoto:" + url); + override fun onRequestPermissionsResult( + requestCode: Int, permissions: Array, grantResults: IntArray + ) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this) + } + + override fun onPermissionsGranted(requestCode: Int, perms: MutableList) { + if (requestCode == PERMISSION_CODE_STORAGE) { + checkStoragePermission() + } + } + + override fun onPermissionsDenied(requestCode: Int, perms: MutableList) { + if (requestCode == PERMISSION_CODE_STORAGE) { + val requestTip = "为了能正常加载您本地的文件内容,请前往应用权限设置界面打开存储空间权限。" + val mPrivacyDialog = CommonTipDialog(context) + mPrivacyDialog.setTipMsg(requestTip) + mPrivacyDialog.setOkText("去设置") + mPrivacyDialog.setOnActionListener( + object : CommonTipDialog.OnActionListener { + override fun onOk() { + //同意跳到应用详情页面 + val packageUri = + Uri.parse("package:${packageName}") + val intent = Intent( + Settings.ACTION_APPLICATION_DETAILS_SETTINGS, + packageUri + ) + startActivityForResult( + intent, REQUEST_CODE_STORAGE + ) + } + + override fun onCancel() { + super.onCancel() + //取消跳到应用详情页面 + ToastUtils.show("您已拒绝提供存储空间权限,将无法正常加载您本地的文件内容。") + } + } + ) + mPrivacyDialog.show() + } + } + + private fun checkStoragePermission() { + if (!EasyPermissions.hasPermissions( + this, + Manifest.permission.WRITE_EXTERNAL_STORAGE, + Manifest.permission.READ_EXTERNAL_STORAGE + ) + ) { + EasyPermissions.requestPermissions( + this, + "请您再次考虑授予存储空间权限,否则将无法正常加载您本地的文件内容。", + PERMISSION_CODE_STORAGE, + Manifest.permission.WRITE_EXTERNAL_STORAGE, + Manifest.permission.READ_EXTERNAL_STORAGE + ) + } else { + mUri = Uri.parse("file://${FileHelper.getRootCacheDir()?.path}/${getNowTime()}.jpg") + PhotoProviderNew.photoProvider( + this, + resultCode = REQUEST_CODE_OPEN_PHOTO_PROVIDER + ) + } + } + + private fun getNowTime(): String { + val date = Date(System.currentTimeMillis()) + val dateFormat = SimpleDateFormat("MMddHHmmssSS") + return dateFormat.format(date) + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + if (requestCode == REQUEST_CODE_STORAGE) { + checkStoragePermission() + } else if (resultCode == RESULT_OK) { + when (requestCode) { + REQUEST_CODE_OPEN_PHOTO_PROVIDER -> data?.let { + val photos = PhotoProviderNew.getResultPhotoList(data) ?: return + if (photos.isNotEmpty()) { + val photo = photos[0] + crop(photo.uri, photo.size, mUri) + } + } + UCrop.REQUEST_CROP -> mUri?.path?.let { + try { + mJob?.cancel() + mJob = PhotoCompressUtil.compress( + XChatApplication.gContext, + it, + PhotoCompressUtil.getCompressCachePath(), + object : PhotoCompressCallback { + @SuppressLint("CheckResult") + override fun onSuccess(compressedImg: String) { + dialogManager.showProgressDialog( + this@UserModifyPhotosActivity, + "请稍后" + ) + FileModel.get() + .uploadFile(compressedImg) + .compose(bindToLifecycle()) + .subscribe { url: String?, throwable: Throwable? -> + if (throwable != null) { + onUploadFail() + } else { + onUpload(url) + } + } + } + + override fun onFail(e: Throwable) { + toast(e.message) + } + }, + mostCompressSize = IMAGE_MOST_COMPRESS_SIZE + ) + } catch (e: FileNotFoundException) { + com.nnbc123.core.utils.Logger.error(TAG, e.message) + } + } + } + } + } + + /** + * 第三方图片裁剪框架Ucrop + */ + private fun crop(sourceUri: Uri?, sourceSize: Long, destinationUri: Uri?) { + if (sourceUri == null || destinationUri == null) { + return + } //防止too large导致oom,大于100m不处理,内存大小 + if (BitmapUtil.getSdBitmapSize(sourceUri) >= MAX_BITMAP_SIZE) { + toast(R.string.text_bitmap_too_large) + return + } + if (sourceSize > 0) { + //不能上传图片的最小文件大小 + com.nnbc123.core.utils.Logger.debug(TAG, "sourceSize: $sourceSize") + if (sourceSize < MIN_HEAD_PHOTO_SIZE) { + toast(R.string.text_bitmap_too_small) + return + } + } + val options = UCrop.Options().apply { + setCompressionQuality(100) + setShowCropGrid(false) + setToolbarColor( + ContextCompat.getColor( + XChatApplication.gContext, + android.R.color.black + ) + ) + setStatusBarColor( + ContextCompat.getColor( + XChatApplication.gContext, + android.R.color.black + ) + ) + setHideBottomControls(true) + setCompressionFormat(Bitmap.CompressFormat.JPEG) + setToolbarCancelDrawable(R.drawable.user_ucrop_ic_closs) + setToolbarCropDrawable(R.drawable.user_ucrop_ic_sure) + setToolbarWidgetColor( + ContextCompat.getColor( + XChatApplication.gContext, + R.color.color_white + ) + ) + } + UCrop.of(sourceUri, destinationUri).withOptions(options).withAspectRatio(1f, 1f) + .withMaxResultSize(800, 800).start(this) + } + + private fun onUpload(url: String?) { + Logger.d("onUploadPhoto:$url") UserModel.get().requestAddPhoto(url) - .compose(bindUntilEvent(ActivityEvent.DESTROY)) - .subscribe(new BeanObserver() { - @Override - public void onErrorMsg(String error) { - toast("操作失败,请检查网络"); - getDialogManager().dismissDialog(); + .compose(bindUntilEvent(ActivityEvent.DESTROY)) + .subscribe(object : BeanObserver() { + override fun onErrorMsg(error: String) { + toast("操作失敗,請檢查網絡") + dialogManager.dismissDialog() + } + + override fun onSuccess(info: UserInfo) { + toast("相片已送審,審核成功後自動上傳相片") + isChanged = true + dialogManager.dismissDialog() + if (info.uid == userId) { + userInfo = info + updateView() } - - @Override - public void onSuccess(UserInfo info) { - toast("相片已送审,审核成功后自动上传相片"); - isChanged = true; - getDialogManager().dismissDialog(); - if (info.getUid() == userId) { - userInfo = info; - updateView(); - - } - } - }); + } + }) } - public void onUploadPhotoFail() { - toast("操作失败,请检查网络"); - getDialogManager().dismissDialog(); + private fun onUploadFail() { + toast("操作失敗,請檢查網絡") + dialogManager.dismissDialog() } - @SuppressLint("CheckResult") - @Override - public void takeSuccess(TResult result) { - getDialogManager().showProgressDialog(this, "请稍后"); - FileModel.get() - .uploadFile(result.getImage().getCompressPath()) - .compose(bindToLifecycle()) - .subscribe((url, throwable) -> { - if (throwable != null) { - onUploadPhotoFail(); - } else { - onUploadPhoto(url); - } - }); + override fun onBackPressed() { + val intent = Intent() + intent.putExtra(FLAG_CHANGE, isChanged) + setResult(RESULT_OK, intent) + super.onBackPressed() } - @Override - public void onBackPressed() { - Intent intent = new Intent(); - intent.putExtra(FLAG_CHANGE, isChanged); - setResult(RESULT_OK, intent); - - super.onBackPressed(); + override fun needSteepStateBar(): Boolean { + return true } - @Override - public void takeFail(TResult result, String msg) { - toast(msg); + override fun setStatusBar() { + StatusBarUtil.transparencyBar(this) + StatusBarUtil.StatusBarLightMode(this) } - @Override - protected boolean needSteepStateBar() { - return true; + override fun onDestroy() { + super.onDestroy() + mJob?.cancel() } - @Override - protected void setStatusBar() { - StatusBarUtil.transparencyBar(this); - StatusBarUtil.StatusBarLightMode(this); - } - -} +} \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 227d729ee..50a77bfb5 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -572,4 +572,8 @@ @anim/anim_right_out + + diff --git a/library/src/main/res/drawable-xhdpi/user_ucrop_ic_closs.webp b/library/src/main/res/drawable-xhdpi/user_ucrop_ic_closs.webp new file mode 100644 index 000000000..d7b4aeb46 Binary files /dev/null and b/library/src/main/res/drawable-xhdpi/user_ucrop_ic_closs.webp differ diff --git a/library/src/main/res/drawable-xhdpi/user_ucrop_ic_sure.webp b/library/src/main/res/drawable-xhdpi/user_ucrop_ic_sure.webp new file mode 100644 index 000000000..eb6c5557c Binary files /dev/null and b/library/src/main/res/drawable-xhdpi/user_ucrop_ic_sure.webp differ