个人主页编辑头像修改

This commit is contained in:
wushaocheng
2022-11-17 21:06:38 +08:00
parent b47b1c5f47
commit c5a4c80412
37 changed files with 1973 additions and 585 deletions

View File

@@ -117,6 +117,8 @@ import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import fragmentation.ISupportActivity;
import fragmentation.SupportActivityDelegate;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
@@ -126,7 +128,7 @@ import io.reactivex.disposables.CompositeDisposable;
* @author alvin hwang
*/
public abstract class BaseActivity extends RxAppCompatActivity
implements IDataStatus, DialogManagerInterface {
implements IDataStatus, DialogManagerInterface, ISupportActivity {
/**
* --------------------------------------------------
@@ -146,9 +148,12 @@ public abstract class BaseActivity extends RxAppCompatActivity
private OpenNobleGlobalNoticeDialog mNoticeDialog;
private boolean isShowingChargeDialog;
private final SupportActivityDelegate mActivityDelegate = new SupportActivityDelegate(this);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.mActivityDelegate.onCreate();
context = this;
mCompositeDisposable = new CompositeDisposable();
if (needSteepStateBar()) {
@@ -321,6 +326,21 @@ public abstract class BaseActivity extends RxAppCompatActivity
return 0;
}
@Override
public final SupportActivityDelegate getSupportDelegate() {
return this.mActivityDelegate;
}
@Override
public final void onBackPressedPage() {
this.mActivityDelegate.onBackPressedPage();
}
@Override
public void onBackPressedSupport() {
this.mActivityDelegate.onBackPressedSupport();
}
@Override
public void setContentView(int layoutResID) {
super.setContentView(layoutResID);

View File

@@ -1,104 +0,0 @@
package com.yizhuan.erban.base;
import android.content.Context;
import com.bumptech.glide.Glide;
import com.bumptech.glide.GlideBuilder;
import com.bumptech.glide.Registry;
import com.bumptech.glide.load.engine.cache.DiskCache;
import com.bumptech.glide.load.engine.cache.DiskLruCacheWrapper;
import com.bumptech.glide.manager.ConnectivityMonitor;
import com.bumptech.glide.manager.ConnectivityMonitorFactory;
import com.bumptech.glide.module.AppGlideModule;
import com.netease.nim.uikit.api.NimUIKit;
import com.netease.nim.uikit.common.util.log.LogUtil;
import com.yizhuan.xchat_android_library.utils.file.StorageUtils;
import java.io.File;
/**
* <p> 应用工程的glide 配置 </p>
* Created by Administrator on 2017/11/14.
*/
//@GlideModule
public class XchatGlideModule extends AppGlideModule {
private static final String TAG = "NIMGlideModule";
private static final int M = 1024 * 1024;
private static final int MAX_DISK_CACHE_SIZE = 256 * M;
private static DiskCache diskCache = null;
/**
* ************************ Disk Cache ************************
*/
private static synchronized DiskCache getDiskCache() {
if (diskCache == null) {
diskCache = createDiskCache();
}
return diskCache;
}
private static synchronized DiskCache createDiskCache() {
final Context context = NimUIKit.getContext();
File cacheDir = StorageUtils.getOwnCacheDirectory(context, context.getPackageName() + "/cache/image/");
if (!cacheDir.exists()) {
cacheDir.mkdirs();
}
return DiskLruCacheWrapper.get(cacheDir, MAX_DISK_CACHE_SIZE);
}
/**
* ************************ Memory Cache ************************
*/
static void clearMemoryCache(Context context) {
Glide.get(context).clearMemory();
}
/**
* ************************ GlideModule override ************************
*/
@Override
public void applyOptions(Context context, GlideBuilder builder) {
builder.setDiskCache(new DiskCache.Factory() {
@Override
public DiskCache build() {
return getDiskCache();
}
});
//广播过多问题,这里不监听链接状态
builder.setConnectivityMonitorFactory(new MyConnectivityMonitorFactory());
LogUtil.i(TAG, "NIMGlideModule apply options");
}
@Override
public void registerComponents(Context context, Glide glide, Registry registry) {
super.registerComponents(context, glide, registry);
}
public class MyConnectivityMonitorFactory implements ConnectivityMonitorFactory {
public ConnectivityMonitor build(Context context, ConnectivityMonitor.ConnectivityListener listener) {
return new ConnectivityMonitor() {
@Override
public void onStart() {
}
@Override
public void onStop() {
}
@Override
public void onDestroy() {
}
};
}
}
}

View File

@@ -14,7 +14,7 @@ import kotlinx.coroutines.Job
import com.yizhuan.xchat_android_library.EasyPermissions
import com.yizhuan.erban.R
import com.yizhuan.erban.application.XChatApplication
import com.yizhuan.erban.base.BaseDialogFragment
import base.BaseDialogFragment
import com.yizhuan.erban.common.photo.PhotoProvider
import com.yizhuan.erban.common.util.PhotoCompressCallback
import com.yizhuan.erban.common.util.PhotoCompressUtil

View File

@@ -29,7 +29,7 @@ import com.bumptech.glide.request.target.DrawableImageViewTarget
import com.bumptech.glide.request.target.Target
import com.yizhuan.erban.common.transform.AssignScaleTransformation
import com.yizhuan.erban.common.transform.ComplexTransformation
import com.yizhuan.erban.common.util.ActivityHelper
import util.ActivityHelper
import com.yizhuan.erban.common.util.Utils
import com.yizhuan.xchat_android_core.utils.Logger
import jp.wasabeef.glide.transformations.RoundedCornersTransformation

View File

@@ -11,6 +11,8 @@ import com.yizhuan.erban.application.XChatApplication;
import com.yizhuan.erban.location.LocationManager;
import com.yizhuan.xchat_android_core.auth.AuthModel;
import util.CoreUtils;
/**
* Activity生命周期工具类
*/

View File

@@ -0,0 +1,164 @@
package com.yizhuan.erban.common.util;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.Point;
import android.net.Uri;
import com.yizhuan.erban.application.XChatApplication;
import java.io.ByteArrayOutputStream;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import util.Logger;
/**
* 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();
}
}

View File

@@ -9,11 +9,14 @@ import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import com.yizhuan.erban.base.BaseActivity;
import com.yizhuan.erban.base.BaseDialogFragment;
import com.yizhuan.xchat_android_core.utils.Logger;
import java.util.List;
import base.BaseDialogFragment;
import util.ActivityHelper;
import util.ReflectionUtils;
/**
* User: wukai

View File

@@ -11,6 +11,8 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Vector;
import util.CoreUtils;
/**
* Created by logwee on 2019/4/9.
*/

View File

@@ -1,435 +0,0 @@
package com.yizhuan.erban.ui.user;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.fourmob.datetimepicker.date.DatePickerDialog;
import com.jph.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.yizhuan.erban.ErbanTakePhotoActivity;
import com.yizhuan.erban.R;
import com.yizhuan.erban.UIHelper;
import com.yizhuan.erban.audio.RecordingVoiceActivity;
import com.yizhuan.erban.base.BaseActivity;
import com.yizhuan.erban.common.permission.PermissionActivity;
import com.yizhuan.erban.ui.login.ModifyInfoActivity;
import com.yizhuan.erban.ui.utils.ImageLoadUtils;
import com.yizhuan.erban.ui.widget.ButtonItem;
import com.yizhuan.erban.utils.RegexUtil;
import com.yizhuan.xchat_android_core.auth.AuthModel;
import com.yizhuan.xchat_android_core.file.FileModel;
import com.yizhuan.xchat_android_core.user.UserModel;
import com.yizhuan.xchat_android_core.user.bean.UserInfo;
import com.yizhuan.xchat_android_core.user.bean.UserPhoto;
import com.yizhuan.xchat_android_library.utils.ResUtil;
import com.yizhuan.xchat_android_library.utils.TimeUtils;
import com.yizhuan.xchat_android_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;
/**
* @author zhouxiangfeng
*/
public class UserInfoModifyActivity extends BaseActivity
implements View.OnClickListener, TimePickerDialog.OnTimeSetListener,
DatePickerDialog.OnDateSetListener, UserPhotoAdapter.ImageClickListener,
ErbanTakePhotoActivity.TakePhotoCallBack {
private static final String TAG = "UserInfoModifyActivity";
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 boolean showAvatarAuditing;
private ImageView ivAvatarAuditiong;
@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);
}
@Override
public void click(int position, UserPhoto userPhoto, boolean isOwner) {
if (userPhoto != null) {
UserModifyPhotosActivity.startForResult(UserInfoModifyActivity.this, userId, Method.PHOTO);
}
}
private void onSetListener() {
}
private void 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);
} else {
photosRecyclerView.setVisibility(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 void setTvDesc(String desc) {
tvDesc.setText(TextUtils.isEmpty(desc) ? getString(R.string.label_hint_desc_setting) : 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);
}
@Override
public void onDateSet(DatePickerDialog datePickerDialog, int year, int month, int day) {
String monthStr;
if ((month + 1) < 10) {
monthStr = "0" + (month + 1);
} else {
monthStr = String.valueOf(month + 1);
}
String dayStr;
if ((day) < 10) {
dayStr = "0" + (day);
} else {
dayStr = String.valueOf(day);
}
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);
}
@Override
public void onTimeSet(RadialPickerLayout view, int hourOfDay, int minute) {
}
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, ResUtil.getString(R.string.ui_user_userinfomodifyactivity_01));
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, ResUtil.getString(R.string.ui_user_userinfomodifyactivity_02));
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(ResUtil.getString(R.string.ui_user_userinfomodifyactivity_03));
return;
}
ButtonItem buttonItem = new ButtonItem(ResUtil.getString(R.string.ui_user_userinfomodifyactivity_04), this::checkPermissionAndStartCamera);
ButtonItem buttonItem1 = new ButtonItem(ResUtil.getString(R.string.ui_user_userinfomodifyactivity_05), () -> {
ErbanTakePhotoActivity.startToSelectPhoto(this, this);
});
List<ButtonItem> buttonItems = new ArrayList<>();
buttonItems.add(buttonItem);
buttonItems.add(buttonItem1);
getDialogManager().showCommonPopupDialog(buttonItems, ResUtil.getString(R.string.ui_user_userinfomodifyactivity_06), false);
isAvatar = true;
break;
case 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);
}
Calendar calendar = Calendar.getInstance();
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, ResUtil.getString(R.string.ui_user_userinfomodifyactivity_07));
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:
}
}
private boolean isAvatar = false;
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);
}
}
public void onUploadFail() {
toast(ResUtil.getString(R.string.ui_user_userinfomodifyactivity_08));
getDialogManager().dismissDialog();
}
@SuppressLint("CheckResult")
@Override
public void takeSuccess(TResult result) {
getDialogManager().showProgressDialog(UserInfoModifyActivity.this, ResUtil.getString(R.string.ui_user_userinfomodifyactivity_09));
FileModel.get()
.uploadFile(result.getImage().getCompressPath())
.compose(bindToLifecycle())
.subscribe((url, throwable) -> {
if (throwable != null) {
onUploadFail();
} else {
onUpload(url);
}
});
}
@Override
public void takeFail(TResult result, String msg) {
toast(msg);
}
@Override
public void takeCancel() {
}
@Override
protected void onPause() {
super.onPause();
}
@Override
protected void onDestroy() {
super.onDestroy();
}
/**
* 观察用户信息
*/
private SingleObserver<UserInfo> userInfoUpdateObserver = new SingleObserver<UserInfo>() {
@Override
public void onSubscribe(Disposable d) {
mCompositeDisposable.add(d);
}
@Override
public void onSuccess(UserInfo info) {
if (info.getUid() == userId) {
mUserInfo = info;
initData(mUserInfo);
}
getDialogManager().dismissDialog();
if (showAvatarAuditing) {
showAvatarAuditing = false;
showAvatarAuditingDialog();
}
}
@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);
//延迟3秒重新获取用户信息更新状态
ivAvatarAuditiong.setVisibility(View.VISIBLE);
Completable.timer(3000, TimeUnit.MILLISECONDS)
.compose(bindUntilEvent(ActivityEvent.STOP))
.doOnComplete(() -> {
UserModel.get().updateCurrentUserInfo().subscribe(userInfoUpdateObserver);
}).doOnError(throwable -> {
throwable.printStackTrace();
}).subscribe();
}
}

View File

@@ -0,0 +1,620 @@
package com.yizhuan.erban.ui.user
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.jph.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.yalantis.ucrop.UCrop
import com.yizhuan.erban.ErbanTakePhotoActivity
import com.yizhuan.erban.ErbanTakePhotoActivity.TakePhotoCallBack
import com.yizhuan.erban.R
import com.yizhuan.erban.UIHelper
import com.yizhuan.erban.application.XChatApplication
import com.yizhuan.erban.audio.RecordingVoiceActivity
import com.yizhuan.erban.base.BaseViewBindingActivity
import com.yizhuan.erban.common.file.FileHelper
import com.yizhuan.erban.common.permission.PermissionActivity.CheckPermListener
import com.yizhuan.erban.common.photo.PhotoProvider
import com.yizhuan.erban.common.util.PhotoCompressCallback
import com.yizhuan.erban.common.util.PhotoCompressUtil
import com.yizhuan.erban.databinding.ActivityUserInfoModifyBinding
import com.yizhuan.erban.ui.login.ModifyInfoActivity
import com.yizhuan.erban.ui.user.UserPhotoAdapter.ImageClickListener
import com.yizhuan.erban.ui.utils.ImageLoadUtils
import com.yizhuan.erban.ui.widget.dialog.CommonTipDialog
import com.yizhuan.erban.utils.RegexUtil
import com.yizhuan.xchat_android_core.auth.AuthModel
import com.yizhuan.xchat_android_core.file.FileModel
import com.yizhuan.xchat_android_core.user.UserModel
import com.yizhuan.xchat_android_core.user.bean.UserInfo
import com.yizhuan.xchat_android_core.user.bean.UserPhoto
import com.yizhuan.xchat_android_core.utils.Logger
import com.yizhuan.xchat_android_library.EasyPermissions
import com.yizhuan.xchat_android_library.utils.ResUtil
import com.yizhuan.xchat_android_library.utils.TimeUtils
import com.yizhuan.xchat_android_library.utils.log.MLog
import io.reactivex.Completable
import io.reactivex.SingleObserver
import io.reactivex.disposables.Disposable
import kotlinx.coroutines.Job
import com.yizhuan.erban.common.util.BitmapUtil
import java.io.FileNotFoundException
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.TimeUnit
/**
* @author zhouxiangfeng
*/
class UserInfoModifyActivity : BaseViewBindingActivity<ActivityUserInfoModifyBinding>(),
View.OnClickListener,
TimePickerDialog.OnTimeSetListener, DatePickerDialog.OnDateSetListener, ImageClickListener,
TakePhotoCallBack, EasyPermissions.PermissionCallbacks {
private var datePickerDialog: DatePickerDialog? = null
private var mUserInfo: UserInfo? = null
private var userId: Long = 0
private var audioFileUrl: String? = null
private var showAvatarAuditing = false
private var mUri: Uri? = null
private var mJob: Job? = 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_LOCATION = 222
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
}
override fun init() {
initWhiteTitleBar(getString(R.string.label_title_modify_info))
findViews()
initView()
userId = intent.getLongExtra("userId", 0)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
UserModel.get().getUserInfo(userId).subscribe(userInfoUpdateObserver)
}
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.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 {
binding.rvPhotos.visibility = View.GONE
}
}
}
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 fun setTvDesc(desc: String?) {
binding.tvDesc.text =
if (TextUtils.isEmpty(desc)) getString(R.string.label_hint_desc_setting) else desc
}
private fun initView() {
val calendar = Calendar.getInstance()
datePickerDialog = DatePickerDialog.newInstance(
this, calendar[Calendar.YEAR],
calendar[Calendar.MONTH], calendar[Calendar.DAY_OF_MONTH], true
)
}
override fun onDateSet(datePickerDialog: DatePickerDialog, year: Int, month: Int, day: Int) {
val monthStr: String = if (month + 1 < 10) {
"0" + (month + 1)
} else {
(month + 1).toString()
}
val dayStr: String = if (day < 10) {
"0$day"
} else {
day.toString()
}
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
/**
* 昵称
*/
const val NICK = 3
/**
* 个人介绍
*/
const val DESC = 4
const val PHOTO = 5
}
}
var checkPermissionListener = CheckPermListener { takePhoto() }
private fun takePhoto() {
ErbanTakePhotoActivity.startToTakePhoto(this, this)
}
private fun checkPermissionAndStartCamera() {
//低版本授权检查
checkPermission(checkPermissionListener, R.string.ask_camera, Manifest.permission.CAMERA)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode != RESULT_OK) {
MLog.info(this, "return is not ok,resultCode=%d", resultCode)
return
}
if (requestCode == Method.NICK) {
dialogManager.showProgressDialog(
this@UserInfoModifyActivity,
ResUtil.getString(R.string.ui_user_userinfomodifyactivity_01)
)
val stringExtra = data!!.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)
}
if (requestCode == Method.DESC) {
dialogManager.showProgressDialog(
this@UserInfoModifyActivity,
ResUtil.getString(R.string.ui_user_userinfomodifyactivity_02)
)
val stringExtra = data!!.getStringExtra(ModifyInfoActivity.CONTENT)
setTvDesc(stringExtra)
val user = UserInfo()
user.uid = AuthModel.get().currentUid
user.userDesc = stringExtra
UserModel.get().requestUpdateUserInfo(user).subscribe(userInfoUpdateObserver)
}
// 刷新声音资源
if (requestCode == Method.AUDIO) {
// 传给上个页面
setResult(RESULT_OK, data)
// 获取数据并刷新显示
audioFileUrl = data!!.getStringExtra(RecordingVoiceActivity.AUDIO_FILE)
val audioDura = data.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)
}
if (requestCode == Method.PHOTO && data != null) {
val isChanged = data.getBooleanExtra(UserModifyPhotosActivity.FLAG_CHANGE, false)
if (isChanged) UserModel.get().getUserInfo(userId).subscribe(userInfoUpdateObserver)
}
when (requestCode) {
REQUEST_CODE_OPEN_PHOTO_PROVIDER -> data?.let {
val photos = PhotoProvider.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@UserInfoModifyActivity,
ResUtil.getString(R.string.ui_user_userinfomodifyactivity_09)
)
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)
}
}
}
}
/**
* 第三方图片裁剪框架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)
}
override fun onClick(v: View) {
when (v.id) {
R.id.layout_avatar -> {
if (mUserInfo != null && mUserInfo!!.isReview) {
toast(ResUtil.getString(R.string.ui_user_userinfomodifyactivity_03))
return
}
checkStoragePermission()
// val buttonItem =
// ButtonItem(ResUtil.getString(R.string.ui_user_userinfomodifyactivity_04)) { checkPermissionAndStartCamera() }
// val buttonItem1 =
// ButtonItem(ResUtil.getString(R.string.ui_user_userinfomodifyactivity_05)) {
// ErbanTakePhotoActivity.startToSelectPhoto(
// this,
// this
// )
// }
// val buttonItems: MutableList<ButtonItem> = ArrayList()
// buttonItems.add(buttonItem)
// buttonItems.add(buttonItem1)
// dialogManager.showCommonPopupDialog(buttonItems,
// ResUtil.getString(R.string.ui_user_userinfomodifyactivity_06),
// false
// )
// PhotoDialog photoDialog = (PhotoDialog) (DialogFragmentUtils.show("photoDialog", this, PhotoDialog.class));
// photoDialog.setOnResultCallBack(new PhotoDialog.OnResultCallBack() {
// @Override
// public void takePhotoCallBack(@Nullable String path) {
//
// }
//
// @Override
// public void choicePhotoCallBack(@Nullable List<String> paths) {
//
// }
// });
isAvatar = true
}
R.id.tv_birth -> {
if (mUserInfo != null) {
val year = TimeUtils.getYear(
mUserInfo!!.birth
)
val month = TimeUtils.getMonth(
mUserInfo!!.birth
)
val day = TimeUtils.getDayOfMonth(
mUserInfo!!.birth
)
datePickerDialog =
DatePickerDialog.newInstance(this, year, month - 1, day, true)
}
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(
{
// 点击跳转到我的声音页面
UIHelper.showMyVoiceAct(
this@UserInfoModifyActivity,
Method.AUDIO,
ResUtil.getString(R.string.ui_user_userinfomodifyactivity_07)
)
isAvatar = false
}, R.string.ask_again,
Manifest.permission.RECORD_AUDIO
)
R.id.layout_photos -> UserModifyPhotosActivity.startForResult(
this@UserInfoModifyActivity,
userId,
Method.PHOTO
)
else -> {}
}
}
private fun checkStoragePermission() {
if (context?.let {
EasyPermissions.hasPermissions(
it, Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE
)
} == false) {
context?.getString(R.string.permission_storage_rationale)?.let {
EasyPermissions.requestPermissions(
this, it, PERMISSION_CODE_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE
)
}
} else {
mUri = Uri.parse("file://${FileHelper.getRootCacheDir()?.path}/${getNowTime()}.jpg")
PhotoProvider.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)
}
private var isAvatar = false
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(ResUtil.getString(R.string.ui_user_userinfomodifyactivity_08))
dialogManager.dismissDialog()
}
@SuppressLint("CheckResult")
override fun takeSuccess(result: TResult) {
dialogManager.showProgressDialog(
this@UserInfoModifyActivity,
ResUtil.getString(R.string.ui_user_userinfomodifyactivity_09)
)
FileModel.get()
.uploadFile(result.image.compressPath)
.compose(bindToLifecycle())
.subscribe { url: String?, throwable: Throwable? ->
if (throwable != null) {
onUploadFail()
} else {
onUpload(url)
}
}
}
override fun takeFail(result: TResult, msg: String) {
toast(msg)
}
override fun takeCancel() {}
override fun onPause() {
super.onPause()
}
override fun onDestroy() {
super.onDestroy()
mJob?.cancel()
}
/**
* 观察用户信息
*/
private val userInfoUpdateObserver: SingleObserver<UserInfo> =
object : SingleObserver<UserInfo> {
override fun onSubscribe(d: Disposable) {
mCompositeDisposable.add(d)
}
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 fun needSteepStateBar(): Boolean {
return true
}
override fun setStatusBar() {
StatusBarUtil.transparencyBar(this)
StatusBarUtil.StatusBarLightMode(this)
}
private fun showAvatarAuditingDialog() {
toast(R.string.avatar_auditing)
//延迟3秒重新获取用户信息更新状态
binding.ivAvatarAuditing.visibility = View.VISIBLE
Completable.timer(3000, TimeUnit.MILLISECONDS)
.compose(bindUntilEvent<Any>(ActivityEvent.STOP))
.doOnComplete {
UserModel.get().updateCurrentUserInfo().subscribe(userInfoUpdateObserver)
}
.doOnError { throwable: Throwable -> throwable.printStackTrace() }.subscribe()
}
override fun onRequestPermissionsResult(
requestCode: Int, permissions: Array<out String>, grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this)
}
override fun onPermissionsGranted(requestCode: Int, perms: MutableList<String>) {
if (requestCode == PERMISSION_CODE_STORAGE) {
checkStoragePermission()
}
}
override fun onPermissionsDenied(requestCode: Int, perms: MutableList<String>) {
if (requestCode == PERMISSION_CODE_STORAGE) {
val requestTip = getString(R.string.permission_storage_denied)
val mPrivacyDialog = CommonTipDialog(context)
mPrivacyDialog.setTipMsg(requestTip)
mPrivacyDialog.setOkText(getString(R.string.room_perform_go_update))
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(getString(R.string.permission_storage_refused))
}
}
)
mPrivacyDialog.show()
}
}
}

View File

@@ -26,5 +26,4 @@
<item name="month_text_view" type="id"/>
<item name="viewpager_inner" type="id"/>
<item name="baseViewTagID" type="id" />
</resources>

View File

@@ -30,6 +30,7 @@ android {
'src/module_easypermission/java',
'src/module_luban/java',
'src/module_easyphoto/java',
'src/module_common/java',
]
@@ -37,6 +38,7 @@ android {
'src/main/res',
'src/module_easypermission/res',
'src/module_easyphoto/res',
'src/module_common/res',
]
@@ -122,6 +124,8 @@ dependencies {
api 'com.davemorrissey.labs:subsampling-scale-image-view-androidx:3.10.0'
api 'com.github.chrisbanes:PhotoView:2.3.0'
api 'com.github.yalantis:ucrop:2.2.7'
}
repositories {
mavenCentral()

Binary file not shown.

After

Width:  |  Height:  |  Size: 461 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 437 B

View File

@@ -0,0 +1,210 @@
package base;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.CallSuper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.viewbinding.ViewBinding;
import java.lang.reflect.Field;
import java.util.List;
import fragmentation.ISupportActivity;
import fragmentation.SupportActivityDelegate;
import util.Logger;
import util.ViewBindingUtil;
public abstract class BaseActivity<VB extends ViewBinding> extends AppCompatActivity implements ISupportActivity {
private final String TAG = getClass().getSimpleName();
private final SupportActivityDelegate mActivityDelegate = new SupportActivityDelegate(this);
@Nullable
private VB mViewBinding;
@CallSuper
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
try {
super.onActivityResult(requestCode, resultCode, data);
this.handleFragmentActivityResult(this.getSupportFragmentManager(), requestCode, resultCode, data);
} catch (Exception e) {
Logger.error(TAG, "onActivityResult", e);
}
}
private void handleFragmentActivityResult(FragmentManager fragmentManager, int requestCode, int resultCode, Intent data) {
if (fragmentManager != null) {
List<Fragment> fragmentList = fragmentManager.getFragments();
int size = fragmentList.size();
if (size > 0) {
for (int i = 0; i < size; i++) {
Fragment fragment = fragmentList.get(i);
if (fragment != null) {
fragment.onActivityResult(requestCode, resultCode, data);
this.handleFragmentActivityResult(fragment.getChildFragmentManager(), requestCode, resultCode, data);
}
}
}
}
}
@CallSuper
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.mActivityDelegate.onCreate();
this.initBefore(savedInstanceState);
mViewBinding = ViewBindingUtil.inflateWithActivity(this, getLayoutInflater());
if (mViewBinding != null) {
View mContentView = mViewBinding.getRoot();
mContentView.setTag(BaseViewTag.TAG_NAME, this);
setContentView(mContentView);
}
this.findView();
this.setView();
this.setListener();
}
@CallSuper
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
}
/**
* 建议子类相关操作写在super.onResume()之前
*/
@CallSuper
@Override
protected void onResume() {
try {
super.onResume();
} catch (Exception e) {
callUpActivity();
Logger.error(TAG, "onResume", e);
}
}
/**
* 解决onResume()不明原因导致的java.lang.IllegalArgumentException
* 参考文档https://blog.csdn.net/ahubenkui/article/details/80038381
*/
private void callUpActivity() {
try {
Class<Activity> superClass = Activity.class;
Field field = superClass.getDeclaredField("mCalled");
field.setAccessible(true);
field.setBoolean(this, true);
} catch (Exception e) {
Logger.error(TAG, "callUpActivity", e);
}
}
/**
* 建议子类相关操作写在super.onPause()之前
*/
@CallSuper
@Override
protected void onPause() {
super.onPause();
//当isFinishing()返回为true时表示当前Activity将要销毁但最终不会走Activity#onDestroy()方法。
if (this.isFinishing()) {
this.onWillDestroy();
}
}
@CallSuper
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
try {
super.onSaveInstanceState(outState);
} catch (Exception e) {
Logger.error(TAG, "onSaveInstanceState", e);
}
}
/**
* 不建议子类在Activity#onDestroy()方法里面做各种释放操作因为onDestroy()方法不一定会执行有可能会导致内存泄露请使用onWillDestroy()方法。
* 参考文档https://blog.csdn.net/wangsf1112/article/details/79108856
*/
@CallSuper
@Override
protected void onDestroy() {
super.onDestroy();
this.onWillDestroy();
}
/**
* 建议子类各种释放操作放到onWillDestroy()中方法执行因为Activity#onDestroy()方法不一定会执行,有可能会导致内存泄露。
*/
protected void onWillDestroy() {
}
@Override
public final SupportActivityDelegate getSupportDelegate() {
return this.mActivityDelegate;
}
@Override
public final void onBackPressed() {
this.mActivityDelegate.onBackPressedPage();
}
@Override
public void onBackPressedSupport() {
this.mActivityDelegate.onBackPressedSupport();
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
try {
return super.dispatchTouchEvent(ev);
} catch (Exception e) {
Logger.error(TAG, "dispatchTouchEvent", e);
return false;
}
}
@Nullable
protected final VB getBinding() {
return mViewBinding;
}
////////////////////////////////////////以下是提供给子类复写的方法////////////////////////////////////////
/**
* 该方法是在onCreate()方法里执行在setContentView()方法被调用之前触发可用于处理解析Activity#getIntent()中的数据时的场景
*/
protected void initBefore(@Nullable Bundle savedInstanceState) {
}
/**
* 该方法是在onCreate()方法里执行在setContentView()方法被调用之后触发,可用于处理控件的初始化
*/
protected void findView() {
}
/**
* 该方法是在onCreate()方法里执行在setContentView()方法被调用之后触发,可用于处理控件的加载数据
*/
protected void setView() {
}
/**
* 该方法是在onCreate()方法里执行在setContentView()方法被调用之后触发,可用于处理控件的设置监听器
*/
protected void setListener() {
}
}

View File

@@ -1,4 +1,4 @@
package com.yizhuan.erban.base;
package base;
import android.content.Context;
import android.os.Bundle;
@@ -14,13 +14,13 @@ import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentManager;
import androidx.viewbinding.ViewBinding;
import com.yizhuan.erban.base.fragmentation.ISupportActivity;
import com.yizhuan.erban.base.fragmentation.ISupportFragment;
import com.yizhuan.erban.base.fragmentation.SupportFragmentDelegate;
import com.yizhuan.erban.base.fragmentation.windowcallback.WindowCallbackProxyUtil;
import com.yizhuan.erban.base.util.ViewBindingUtil;
import com.yizhuan.erban.common.util.ActivityHelper;
import com.yizhuan.xchat_android_core.utils.Logger;
import fragmentation.ISupportActivity;
import fragmentation.ISupportFragment;
import fragmentation.SupportFragmentDelegate;
import fragmentation.windowcallback.WindowCallbackProxyUtil;
import util.ActivityHelper;
import util.Logger;
import util.ViewBindingUtil;
public abstract class BaseDialogFragment<VB extends ViewBinding> extends DialogFragment implements ISupportFragment {
private final String TAG = getClass().getSimpleName();

View File

@@ -1,12 +1,13 @@
package com.yizhuan.erban.base;
package base;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import com.yizhuan.erban.R;
import com.yizhuan.xchat_android_core.utils.Logger;
import com.yizhuan.xchat_android_library.R;
import util.Logger;
/**
* author: wushaocheng

View File

@@ -1,4 +1,4 @@
package com.yizhuan.erban.base.fragmentation;
package fragmentation;
/**
* Created by YoKey on 17/6/13.
@@ -6,7 +6,7 @@ package com.yizhuan.erban.base.fragmentation;
public interface ISupportActivity {
SupportActivityDelegate getSupportDelegate();
void onBackPressed();
void onBackPressedPage();
void onBackPressedSupport();

View File

@@ -1,4 +1,4 @@
package com.yizhuan.erban.base.fragmentation;
package fragmentation;
import android.os.Bundle;

View File

@@ -1,10 +1,10 @@
package com.yizhuan.erban.base.fragmentation;
package fragmentation;
import androidx.core.app.ActivityCompat;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import com.yizhuan.erban.base.fragmentation.queue.Action;
import fragmentation.queue.Action;
public class SupportActivityDelegate {
private final ISupportActivity mSupport;
@@ -33,7 +33,7 @@ public class SupportActivityDelegate {
/**
* 不建议复写该方法,请使用 {@link #onBackPressedSupport} 代替
*/
public void onBackPressed() {
public void onBackPressedPage() {
mTransactionDelegate.mActionQueue.enqueue(new Action(Action.ACTION_BACK, getSupportFragmentManager()) {
@Override
public void run() {

View File

@@ -1,4 +1,4 @@
package com.yizhuan.erban.base.fragmentation;
package fragmentation;
import android.app.Activity;
import android.content.Intent;
@@ -11,8 +11,8 @@ import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import com.yizhuan.erban.base.fragmentation.internal.ResultRecord;
import com.yizhuan.erban.base.fragmentation.internal.TransactionRecord;
import fragmentation.internal.ResultRecord;
import fragmentation.internal.TransactionRecord;
public class SupportFragmentDelegate {
int mContainerId;

View File

@@ -1,4 +1,4 @@
package com.yizhuan.erban.base.fragmentation;
package fragmentation;
import android.content.Context;
import android.view.View;

View File

@@ -1,4 +1,4 @@
package com.yizhuan.erban.base.fragmentation;
package fragmentation;
import android.os.Bundle;
import android.os.Handler;
@@ -9,10 +9,10 @@ import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import com.yizhuan.erban.base.fragmentation.internal.ResultRecord;
import com.yizhuan.erban.base.fragmentation.internal.TransactionRecord;
import com.yizhuan.erban.base.fragmentation.queue.Action;
import com.yizhuan.erban.base.fragmentation.queue.ActionQueue;
import fragmentation.internal.ResultRecord;
import fragmentation.internal.TransactionRecord;
import fragmentation.queue.Action;
import fragmentation.queue.ActionQueue;
import java.util.List;

View File

@@ -1,4 +1,4 @@
package com.yizhuan.erban.base.fragmentation.internal;
package fragmentation.internal;
import android.os.Bundle;
import android.os.Parcel;

View File

@@ -1,4 +1,4 @@
package com.yizhuan.erban.base.fragmentation.internal;
package fragmentation.internal;
/**
* @hide Created by YoKey on 16/11/25.

View File

@@ -1,4 +1,4 @@
package com.yizhuan.erban.base.fragmentation.queue;
package fragmentation.queue;
import androidx.fragment.app.FragmentManager;

View File

@@ -1,4 +1,4 @@
package com.yizhuan.erban.base.fragmentation.queue;
package fragmentation.queue;
import android.os.Handler;
import android.os.Looper;

View File

@@ -1,4 +1,4 @@
package com.yizhuan.erban.base.fragmentation.windowcallback
package fragmentation.windowcallback
import android.view.Window

View File

@@ -1,4 +1,4 @@
package com.yizhuan.erban.base.fragmentation.windowcallback
package fragmentation.windowcallback
import android.view.Window

View File

@@ -1,4 +1,4 @@
package com.yizhuan.erban.common.util;
package util;
import android.app.Activity;
import android.content.Context;

View File

@@ -1,6 +1,4 @@
package com.yizhuan.erban.common.util;
import com.yizhuan.xchat_android_core.utils.Logger;
package util;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.EventBusException;

View File

@@ -0,0 +1,719 @@
/**
* Log类。可以直接使用静态函数
* 也可以用某个tag生成一个logger对象
* 使用前需要先调用init初始化
* 内部使用android的Log类实现并支持写入文件
*/
package util;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import com.yizhuan.xchat_android_library.utils.StringUtils;
import com.yizhuan.xchat_android_library.utils.config.BasicConfig;
import com.yizhuan.xchat_android_library.utils.log.MLog;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author daixiang
*
*/
@SuppressLint("SimpleDateFormat")
public class Logger {
public enum LogLevel {
Verbose,
Debug,
Info,
Warn,
Error
}
// 写log文件策略
public enum LogFilePolicy {
NoLogFile, // 不写文件
PerDay, // 一天只产生一个log文件
PerLaunch // 每次运行均产生一个log文件
}
public static class LogConfig {
public String dir; // log文件目录绝对路径
public LogFilePolicy policy;
public LogLevel outputLevel; // 输出级别大于等于此级别的log才会输出
public LogLevel fileLevel; // 输出到文件的级别大于等于此级别的log才会写入文件
public int fileFlushCount; // 每次累计log超过此条数时会检查是否需要flush log文件
public int fileFlushInterval; // 定时每隔一定秒数检查是否需要flush log文件
public int fileFlushMinInterval; // 距离上次flush最少需要多少秒
public LogConfig() {
policy = LogFilePolicy.PerLaunch;
outputLevel = LogLevel.Verbose;
fileLevel = LogLevel.Info;
fileFlushCount = 10;
fileFlushInterval = 60;
fileFlushMinInterval = 10;
}
public LogConfig(LogConfig cfg) {
this.dir = cfg.dir;
this.policy = cfg.policy;
this.outputLevel = cfg.outputLevel;
this.fileLevel = cfg.fileLevel;
this.fileFlushCount = cfg.fileFlushCount;
this.fileFlushInterval = cfg.fileFlushInterval;
this.fileFlushMinInterval = cfg.fileFlushMinInterval;
}
}
private static ConcurrentHashMap<String, Logger> loggers = new ConcurrentHashMap<String, Logger>();
// private static Context context;
private static LoggerThread loggerThread; // 用于在另一个线程写log文件
private static LogConfig config = new LogConfig();
// 写文件线程未准备好的时候将可以写入文件的log先缓存起来
private static List<String> logList = Collections.synchronizedList(new ArrayList<String>());
private String myTag;
private Logger(String tag) {
myTag = tag;
}
public String getTag() {
return myTag;
}
// public static void init(Context ctx) {
//
// LogConfig cfg = new LogConfig();
// if (ctx != null) {
// File f = ctx.getExternalCacheDir();
// if (f != null
// && Environment.getExternalStorageState().equals(
// Environment.MEDIA_MOUNTED)) {
// Log.i("Logger", "cache dir = " + f.getAbsolutePath());
// cfg.dir = f.getAbsolutePath() + "/logs";
// } else {
// Log.i("Logger", "no extenal storage available");
// f = ctx.getCacheDir();
// if (f != null) {
// cfg.dir = f.getAbsolutePath() + "/logs";
// }
// }
// }
// cfg.policy = LogFilePolicy.PerLaunch;
// cfg.outputLevel = LogLevel.Verbose;
// cfg.fileLevel = LogLevel.Info;
//
// Logger.init(ctx, cfg);
// }
/**
* 使用Logger之前必须先init
* @param cfg
*/
public static void init(LogConfig cfg) {
// context = ctx;
info("Logger", "init Logger");
config = new LogConfig(cfg);
// if (config.policy != LogFilePolicy.NoLogFile && loggerThread == null) {
// loggerThread = new LoggerThread("LoggerThread", config);
// loggerThread.start();
// }
initMLog(config);
}
public static void initMLog(LogConfig cfg) {
if (cfg.policy != LogFilePolicy.NoLogFile) {
String logDir = cfg.dir;
MLog.LogOptions options = new MLog.LogOptions();
if (BasicConfig.INSTANCE.isDebuggable()) {
options.logLevel = MLog.LogOptions.LEVEL_VERBOSE;
} else {
options.logLevel = MLog.LogOptions.LEVEL_INFO;
}
options.honorVerbose = false;
options.logFileName = "logs.txt";
MLog.initialize(logDir, options);
MLog.info("Logger", "init MLog, logFilePath = " + logDir + File.separator + options.logFileName);
}
}
public static Logger getLogger(String tag) {
if (StringUtils.isEmpty(tag)) {
tag = "Default";
}
Logger logger;
try {
logger = loggers.get(tag);
if (logger == null) {
logger = new Logger(tag);
loggers.put(tag, logger);
}
} catch (Exception e) {
MLog.error("Logger", "getLogger error! " + e);
logger = new Logger(tag);
}
return logger;
}
public static Logger getLogger(Class<?> cls) {
if (cls == null) {
return Logger.getLogger("");
}
// String className = cls.getName();
// String tag = className.substring(className.lastIndexOf(".") + 1);
return Logger.getLogger(cls.getSimpleName());
}
private static boolean isLoggable(LogLevel level) {
return level.compareTo(config.outputLevel) >= 0;
}
private static String levelToString(LogLevel level) {
String str = "";
switch (level) {
case Debug:
str = "Debug";
break;
case Error:
str = "Error";
break;
case Info:
str = "Info";
break;
case Verbose:
str = "Verbose";
break;
case Warn:
str = "Warn";
break;
default:
str = "Debug";
break;
}
return str;
}
public static String getLogFilePath() {
if (loggerThread != null) {
return loggerThread.getFilePath();
} else {
return null;
}
}
private static void logToFile(String tag, LogLevel level, String message, Throwable t) {
if (config.policy != LogFilePolicy.NoLogFile) {
if (loggerThread == null || !loggerThread.isReady()) {
// 文件线程未准备好,先缓存
logList.add(LoggerThread.getFormattedString(tag, level, message));
} else {
loggerThread.logToFile(tag, level, message, t);
}
}
}
public static void log(String tag, LogLevel level, String message) {
if (Logger.isLoggable(level)) {
message = msgForTextLog(tag, message);
switch (level) {
case Debug:
// Log.d(tag, message);
MLog.debugWithoutLineNumber(tag, message);
break;
case Error:
// Log.e(tag, message);
MLog.errorWithoutLineNumber(tag, message);
break;
case Info:
// Log.i(tag, message);
MLog.infoWithoutLineNumber(tag, message);
break;
case Verbose:
// Log.v(tag, message);
MLog.verboseWithoutLineNumber(tag, message);
break;
case Warn:
// Log.w(tag, message);
MLog.warnWithoutLineNumber(tag, message);
break;
default:
// Log.d(tag, message);
MLog.debugWithoutLineNumber(tag, message);
break;
}
// logToFile(tag, level, message, null);
}
}
private static void logError(String tag, String msg, Throwable tr) {
if (Logger.isLoggable(LogLevel.Error)) {
// msg = msgForTextLog(tag, msg);
if (tr == null) {
// Log.e(tag, msg);
MLog.error(tag, msg);
} else {
// Log.e(tag, msg, tr);
MLog.error(tag, msg, tr);
}
// logToFile(tag, LogLevel.Error, msg, tr);
}
}
// public static void log(String tag, LogLevel level, String message, Throwable throwable) {
// switch (level) {
// case Debug:
// Log.d(tag, message, throwable);
// break;
// case Error:
// Log.e(tag, message, throwable);
// break;
// case Info:
// Log.i(tag, message, throwable);
// break;
// case Verbose:
// Log.v(tag, message, throwable);
// break;
// case Warn:
// Log.v(tag, message, throwable);
// break;
// default:
// Log.d(tag, message, throwable);
// break;
// }
// }
private static String msgForTextLog(String tag, String message) {
if (message == null) {
message = "null";
}
int line = -1;
String filename = null;
if (Thread.currentThread().getStackTrace().length > 4) {
line = Thread.currentThread().getStackTrace()[4].getLineNumber();
filename = Thread.currentThread().getStackTrace()[4].getFileName();
}
StringBuilder sb = new StringBuilder();
sb.append("[");
sb.append(tag);
sb.append("] ");
sb.append(message);
sb.append("(P:");
sb.append(android.os.Process.myPid());
sb.append(")");
sb.append("(T:");
if (Looper.getMainLooper() == Looper.myLooper())
sb.append("Main&");
else
sb.append(Thread.currentThread().getId());
sb.append(")");
// sb.append("(C:");
// sb.append(tag);
// sb.append(")");
if (filename != null) {
sb.append(" at (");
sb.append(filename);
}
if (line > 0) {
sb.append(":");
sb.append(line);
sb.append(")");
}
return sb.toString();
}
public static void verbose(String tag, String message) {
// message = msgForTextLog(tag, message);
Logger.log(tag, LogLevel.Verbose, message);
}
public static void debug(String tag, String message) {
// message = msgForTextLog(tag, message);
Logger.log(tag, LogLevel.Debug, message);
}
public static void info(String tag, String message) {
// message = msgForTextLog(tag, message);
Logger.log(tag, LogLevel.Info, message);
}
public static void warn(String tag, String message) {
// message = msgForTextLog(tag, message);
Logger.log(tag, LogLevel.Warn, message);
}
public static void error(String tag, String message) {
// message = msgForTextLog(tag, message);
Logger.log(tag, LogLevel.Error, message);
}
public static void error(String tag, String message, Throwable throwable) {
// message = msgForTextLog(tag, message);
Logger.logError(tag, message, throwable);
}
public void verbose(String message) {
Logger.verbose(myTag, message);
// message = msgForTextLog(myTag, message);
// MLog.verboseWithoutLineNumber(myTag, message);
}
public void debug(String message) {
Logger.debug(myTag, message);
// message = msgForTextLog(myTag, message);
// MLog.debugWithoutLineNumber(myTag, message);
}
public void info(String message) {
Logger.info(myTag, message);
// message = msgForTextLog(myTag, message);
// MLog.infoWithoutLineNumber(myTag, message);
}
public void warn(String message) {
Logger.warn(myTag, message);
// message = msgForTextLog(myTag, message);
// MLog.warnWithoutLineNumber(myTag, message);
}
public void error(String message) {
Logger.error(myTag, message);
// message = msgForTextLog(myTag, message);
// MLog.errorWithoutLineNumber(myTag, message);
}
public void error(String message, Throwable throwable) {
Logger.logError(myTag, message, throwable);
// message = msgForTextLog(myTag, message);
// MLog.errorWithoutLineNumber(myTag, message, throwable);
}
public static void onTerminate() {
if (loggerThread != null) {
loggerThread.sendFlush();
}
}
// private static class SdkLogger implements ILog {
//
// @Override
// public void verbose(String tag, String msg) {
//
// Logger.verbose(tag, msg);
// }
//
// @Override
// public void debug(String tag, String msg) {
//
// Logger.debug(tag, msg);
// }
//
// @Override
// public void info(String tag, String msg) {
//
// Logger.info(tag, msg);
// }
//
// @Override
// public void warn(String tag, String msg) {
//
// Logger.warn(tag, msg);
// }
//
// @Override
// public void error(String tag, String msg) {
//
// Logger.error(tag, msg);
// }
//
// @Override
// public void error(String tag, String msg, Throwable t) {
// Logger.error(tag, msg, t);
// }
//
// }
/**
* 用于写log文件的线程
* @author daixiang
*
*/
private static class LoggerThread extends Thread {
private static final int LogMessageType = 0;
private static final int TimerMessageType = 1;
private static final int LogThrowableType = 2;
private static final int FlushLog = 3;
private LogThreadHandler handler; // 使用此handler将log消息发到此线程处理
private LogConfig config;
private String filePath;
private boolean isReady = false;
public LoggerThread(String name, LogConfig cfg) {
super(name);
config = cfg;
}
public boolean isReady() {
return isReady;
}
private static String getFormattedString(String tag, LogLevel level, String msg) {
String thread = (Looper.getMainLooper() == Looper.myLooper()) ? "[Main]"
: ("[" + Thread.currentThread().getId() + "]");
String strLevel = "[" + Logger.levelToString(level) + "]";
String logMsg = thread + "[" + tag + "]" + strLevel + " " + msg;
return logMsg;
}
public void logToFile(String tag, LogLevel level, String msg, Throwable t) {
if ((config.policy != LogFilePolicy.NoLogFile)
&& (level.compareTo(config.fileLevel) >= 0)
&& (handler != null)) {
String logMsg = getFormattedString(tag, level, msg);
Message threadMessage = null;
if (t == null) {
threadMessage = handler.obtainMessage(LogMessageType);
threadMessage.obj = logMsg;
} else {
threadMessage = handler.obtainMessage(LogThrowableType);
threadMessage.obj = logMsg;
Bundle b = new Bundle();
b.putSerializable("throwable", t);
threadMessage.setData(b);
}
if (threadMessage != null) {
handler.sendMessage(threadMessage);
}
}
}
public void sendFlush() {
if (handler != null) {
handler.sendEmptyMessage(FlushLog);
}
}
// public void logToFile(String tag, LogLevel level, String msg) {
// logToFile(tag, level, msg, null);
// }
public String getFilePath() {
return filePath;
}
public void run() {
Looper.prepare();
File logDir = new File(config.dir);
if (!logDir.exists()) {
Logger.info("Logger", "create log dir: " + logDir.getAbsolutePath());
logDir.mkdirs();
}
SimpleDateFormat f;
if (config.policy == LogFilePolicy.PerLaunch) {
f = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss-SSS");
} else {
f = new SimpleDateFormat("yyyy-MM-dd");
}
filePath = config.dir + "/" + f.format(new Date()) + ".log";
Logger.info("Logger", "log file name: " + filePath);
handler = new LogThreadHandler(this);
isReady = true;
// 将之前缓存的log先写入文件
List<String> list = new ArrayList<String>(logList);
try {
if (list.size() > 0) {
Logger.debug("Logger", "write logs before logger thread ready to file: " + list.size());
for (String s : list) {
handler.writeLine(s);
}
handler.flush(true);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
logList.clear();
list.clear();
list = null;
Looper.loop();
}
private static class LogThreadHandler extends Handler {
private SimpleDateFormat dateFormat;
private BufferedWriter writer;
private LoggerThread loggerThread;
private int logCounter;
private long lastFlushTime;
private void writeLine(String formattedStr) throws IOException {
if (writer != null) {
writer.write(dateFormat.format(new Date()) + " " + formattedStr);
writer.newLine();
}
}
public LogThreadHandler(LoggerThread thread) {
loggerThread = thread;
dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
boolean append;
if (loggerThread.config.policy == LogFilePolicy.PerLaunch) {
append = false;
} else {
append = true;
}
try {
FileWriter fw = new FileWriter(loggerThread.filePath, append);
writer = new BufferedWriter(fw);
if (loggerThread.config.policy == LogFilePolicy.PerDay) {
writer.newLine();
}
// writer.write(dateFormat.format(new Date()) + " " + loggerThread.getFormattedString("Logger", loggerThread.config.fileLevel, "---------------------Log Begin---------------------"));
// writer.newLine();
// 在文件开头加入一个易于识别的行
writeLine(getFormattedString("Logger", loggerThread.config.fileLevel, "---------------------Log Begin---------------------"));
flush(true);
} catch (IOException e) {
writer = null;
e.printStackTrace();
}
if (writer != null && loggerThread.config.fileFlushInterval > 0) {
long time = loggerThread.config.fileFlushInterval * 1000;
new Timer().schedule(new TimerTask() {
@Override
public void run() {
Message msg = obtainMessage(TimerMessageType);
sendMessage(msg);
}
}, time, time);
}
}
public void flush(boolean force) throws IOException {
if (writer != null) {
long now = System.currentTimeMillis();
// 不要太频繁flush最低间隔
if ((now - lastFlushTime) > (loggerThread.config.fileFlushMinInterval * 1000)) {
writer.flush();
lastFlushTime = System.currentTimeMillis();
logCounter = 0;
} else {
logCounter++;
}
}
}
public void flushIfNeeded() throws IOException {
if (logCounter > loggerThread.config.fileFlushCount) {
flush(false);
} else {
logCounter++;
}
}
@Override
public void handleMessage(Message msg) {
if (writer == null) {
return;
}
try {
switch (msg.what) {
case LogMessageType:
{
// String str = dateFormat.format(new Date()) + " "
// + msg.obj;
// writer.write(str);
// writer.newLine();
writeLine((String)msg.obj);
flushIfNeeded();
break;
}
case LogThrowableType:
{
// String str = dateFormat.format(new Date()) + " "
// + msg.obj;
// writer.write(str);
// writer.newLine();
writeLine((String)msg.obj);
Bundle data = msg.getData();
if (data != null) {
Throwable t = (Throwable) data
.getSerializable("throwable");
if (t != null) {
PrintWriter pw = new PrintWriter(writer);
t.printStackTrace(pw);
//pw.close(); // 不能close否则内部的bufferedwriter也会被close
writer.newLine();
flush(true); // 异常立刻flush
} else {
flushIfNeeded();
}
} else {
flushIfNeeded();
}
break;
}
case TimerMessageType:
flush(false);
break;
case FlushLog:
flush(true);
break;
default:
break;
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}

View File

@@ -1,6 +1,4 @@
package com.yizhuan.erban.common.util;
import com.yizhuan.xchat_android_core.utils.Logger;
package util;
import java.lang.reflect.Field;

View File

@@ -0,0 +1,178 @@
package util;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.WindowManager;
import androidx.fragment.app.FragmentManager;
import java.lang.reflect.Field;
import java.util.List;
/**
* author: wushaocheng
* time: 2022/11/15
* desc: 转换帮助类
*/
public class Utils {
public static <T> boolean notEmpty(List<T> list) {
return !isEmpty(list);
}
public static <T> boolean isEmpty(List<T> list) {
if (list == null || list.size() == 0) {
return true;
}
return false;
}
public static int getNavigationBarHeight(Context context) {
int statusBarHeight = 0;
int resourceId = context.getResources().getIdentifier("config_showNavigationBar", "bool", "android");
if (resourceId != 0) {
resourceId = context.getResources().getIdentifier("navigation_bar_height", "dimen", "android");
//根据资源ID获取响应的尺寸值
statusBarHeight = context.getResources().getDimensionPixelSize(resourceId);
}
return statusBarHeight;
}
/**
* 判断底部navigator是否已经显示
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public static boolean hasSoftKeys(Context context) {
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display d = windowManager.getDefaultDisplay();
DisplayMetrics realDisplayMetrics = new DisplayMetrics();
d.getRealMetrics(realDisplayMetrics);
int realHeight = realDisplayMetrics.heightPixels;
int realWidth = realDisplayMetrics.widthPixels;
DisplayMetrics displayMetrics = new DisplayMetrics();
d.getMetrics(displayMetrics);
int displayHeight = displayMetrics.heightPixels;
int displayWidth = displayMetrics.widthPixels;
return (realWidth - displayWidth) > 0 || (realHeight - displayHeight) > 0;
}
// 将px值转换为dip或dp值
public static int px2dip(Context context, float pxValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
// 将dip或dp值转换为px值
public static int dip2px(Context context, float dipValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dipValue * scale + 0.5f);
}
// 将px值转换为sp值
public static int px2sp(Context context, float pxValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (pxValue / fontScale + 0.5f);
}
// 将sp值转换为px值
public static int sp2px(Context context, float spValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (spValue * fontScale + 0.5f);
}
// 屏幕宽度(像素)
public static int getWindowWidth(Context context) {
DisplayMetrics dm = context.getApplicationContext().getResources().getDisplayMetrics();
return dm.widthPixels;
}
// 屏幕高度(像素)
public static int getWindowHeight(Context context) {
DisplayMetrics dm = context.getApplicationContext().getResources().getDisplayMetrics();
return dm.heightPixels;
}
// 根据Unicode编码判断中文汉字和符号
private static boolean isChinese(char c) {
Character.UnicodeBlock ub = Character.UnicodeBlock.of(c);
if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS
|| ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B
|| ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS
|| ub == Character.UnicodeBlock.GENERAL_PUNCTUATION) {
return true;
}
return false;
}
// 判断中文汉字和符号
public static boolean isChinese(String strName) {
char[] ch = strName.toCharArray();
for (int i = 0; i < ch.length; i++) {
char c = ch[i];
if (isChinese(c)) {
return true;
}
}
return false;
}
public static int getScreenPxWidth(Context context) {
WindowManager wm = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics outMetrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(outMetrics);
return outMetrics.widthPixels;
}
public static int getScreenDpWidth(Context context) {
int pxWidth = getScreenPxWidth(context);
return (int) px2dip(context, pxWidth);
}
public static int getScreenPxHeight(Context context) {
WindowManager wm = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics outMetrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(outMetrics);
return outMetrics.heightPixels;
}
public static int getScreenDpHeight(Context context) {
int pxHeight = getScreenPxHeight(context);
return (int) px2dip(context, pxHeight);
}
public static void executePendingTransactionsSafely(String TAG, FragmentManager fragmentManager) {
if (fragmentManager == null) {
Logger.error(TAG, "executePendingTransactionsSafely fragmentManager == null");
return;
}
try {
fragmentManager.executePendingTransactions();
} catch (Exception e) {
Logger.error(TAG, String.valueOf(e));
try {
Field mExecutingActions = fragmentManager.getClass().getDeclaredField("mExecutingActions");
mExecutingActions.setAccessible(true);
mExecutingActions.set(fragmentManager, false);
} catch (Exception e1) {
Logger.error(TAG, "set field value fail", e1);
}
}
}
}

View File

@@ -1,9 +1,8 @@
package com.yizhuan.erban.base.util
package util
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.viewbinding.ViewBinding
import com.yizhuan.xchat_android_core.utils.Logger
import java.lang.reflect.InvocationTargetException
import java.lang.reflect.ParameterizedType

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="baseViewTagID" type="id" />
</resources>

View File

@@ -0,0 +1,4 @@
<resources>
<string name="text_bitmap_too_large">上傳失敗,圖片太大啦~</string>
<string name="text_bitmap_too_small">上傳圖片不能小於20kb</string>
</resources>