3 Commits

50 changed files with 788 additions and 1498 deletions

View File

@@ -156,13 +156,12 @@ android {
}
debug {
println("minifyEnabled = " + minify_enabled)
buildConfigField "String", "BASE_URL", "\"http://beta.api.nnbc123.cn/\""
buildConfigField "String", "WEB_URL", "\"http://beta.api.nnbc123.cn\""
buildConfigField "String", "BASE_URL_DEBUG", "BASE_URL"
buildConfigField "String", "BASE_URL_STAGING", "\"https://api.nnbc123.cn/\""
buildConfigField "String", "BASE_URL_RELEASE", "\"https://api.nnbc123.cn/\""
minifyEnabled minify_enabled.toBoolean() // 是否混淆
minifyEnabled false
shrinkResources false
signingConfig signingConfigs.v2
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
@@ -221,11 +220,6 @@ android {
abiFilters 'arm64-v8a'
}
}
rongyao {
ndk {
abiFilters 'arm64-v8a'
}
}
kuaishou_01 {
ndk {
abiFilters 'armeabi-v7a', 'arm64-v8a'

View File

@@ -677,7 +677,14 @@ open class BaseRoomFragment<V : IBaseRoomView?, P : BaseRoomPresenter<V>?> :
} else {
val roomQueueInfo = AvRoomDataManager.get()
.getRoomQueueMemberInfoByAccount(myUid.toString())
if (roomQueueInfo?.mChatRoomMember != null
if (roomQueueInfo?.mChatRoomMember?.isNoProhibitMic == true && roomQueueInfo.mRoomMicInfo?.isMicMute == false) {
bottomView.setMicBtnEnable(true)
if (AudioEngineManager.get().isMute) {
bottomView.setMicBtnOpen(false)
} else {
bottomView.setMicBtnOpen(true)
}
} else if (roomQueueInfo?.mChatRoomMember != null
&& myUid.toString() == roomQueueInfo.mChatRoomMember.account
&& roomQueueInfo.mRoomMicInfo?.isMicMute == true
) {

View File

@@ -137,7 +137,7 @@ public abstract class BaseActivity extends RxAppCompatActivity
*/
protected static final String STATUS_TAG = "STATUS_TAG";
public final RxPermissions rxPermissions = new RxPermissions(this);
protected final RxPermissions rxPermissions = new RxPermissions(this);
protected TitleBar mTitleBar;
protected DefaultToolBar mToolBar;
protected CompositeDisposable mCompositeDisposable;

View File

@@ -1,22 +1,11 @@
package com.nnbc123.app.skill
import android.Manifest
import android.annotation.SuppressLint
import android.content.Intent
import android.net.Uri
import android.provider.Settings
import androidx.core.util.Consumer
import com.nnbc123.app.R
import com.nnbc123.app.base.BaseActivity
import com.nnbc123.app.common.permission.PermissionHelper.isAllGranted
import com.nnbc123.app.skill.dialog.SkillSelectionDialog
import com.nnbc123.app.skill.repository.SkillDataManager
import com.nnbc123.app.skill.repository.SkillModel
import com.nnbc123.app.skill.widget.*
import com.nnbc123.app.ui.widget.dialog.CommonTipDialog
import com.nnbc123.app.ui.widget.dialog.RequestPermissionPromptDialog
import com.nnbc123.app.ui.widget.dialog.RequestPermissionPromptDialog.Companion.dismissCurrentDialog
import com.nnbc123.app.ui.widget.dialog.RequestPermissionPromptDialog.Companion.isNeedPrompt
import com.nnbc123.core.file.FileModel
import com.nnbc123.core.skill.entity.PropRefEntity
import com.nnbc123.core.skill.entity.PropsEntity
@@ -24,7 +13,6 @@ import com.nnbc123.core.skill.entity.SkillPostServerEntity
import com.nnbc123.core.skill.entity.SkillPropertyEntity
import com.nnbc123.core.skill.event.SkillEvent
import com.nnbc123.core.utils.toast
import com.nnbc123.library.utils.ResUtil
import org.greenrobot.eventbus.EventBus
import java.io.File
@@ -210,53 +198,4 @@ class SkillDataDelegate(private val skillView: SkillCardView, private val activi
activity.toast(th.message)
})
}
override fun requestMicPermissions(consumer: Consumer<Boolean>) {
super.requestMicPermissions(consumer)
val permissions = arrayOf(
Manifest.permission.RECORD_AUDIO,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE
)
val sb = StringBuffer()
sb.append("1.").append(ResUtil.getString(R.string.permission_denied_tips_mic))
.append("\n2.").append(ResUtil.getString(R.string.permission_denied_tips_image))
val tips = sb.toString()
if (isNeedPrompt()
&& !isAllGranted(activity.rxPermissions, *permissions)
) {
RequestPermissionPromptDialog(activity, tips).show()
}
activity.checkPermission(Consumer<Boolean> { isGranted: Boolean ->
dismissCurrentDialog()
if (isGranted) {
consumer.accept(true)
} else {
consumer.accept(false)
showPermissionDeniedTipsDialog(tips)
}
}, *permissions)
}
private fun showPermissionDeniedTipsDialog(message: String) {
val mPrivacyDialog = CommonTipDialog(activity)
mPrivacyDialog.setTipMsg(message)
mPrivacyDialog.setOkText("去设置")
mPrivacyDialog.setOnActionListener(
object : CommonTipDialog.OnActionListener {
override fun onOk() {
//同意跳到应用详情页面
val packageUri = Uri.parse("package:" + activity.packageName)
val intent = Intent(
Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
packageUri
)
activity.startActivityForResult(
intent, 0
)
}
}
)
mPrivacyDialog.show()
}
}

View File

@@ -1,6 +1,5 @@
package com.nnbc123.app.skill.widget
import androidx.core.util.Consumer
import com.nnbc123.core.skill.entity.PropRefEntity
import java.io.File
@@ -32,7 +31,6 @@ interface ItemEventListener {
fun onItemClick(item: SkillItem) {}
fun onRecordSuccess(audioFile: File?, duration: Int) {}
fun onDeleteRecordClick() {}
fun requestMicPermissions(consumer: Consumer<Boolean>) {}
}

View File

@@ -3,7 +3,6 @@ package com.nnbc123.app.skill.widget
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import androidx.core.util.Consumer
import com.netease.nim.uikit.common.util.sys.ScreenUtil
import com.netease.nimlib.sdk.media.record.RecordType
import com.nnbc123.app.R
@@ -13,7 +12,6 @@ import com.nnbc123.app.databinding.LayoutSkillAudioBinding
import com.nnbc123.app.ui.widget.dialog.CommonTipDialog
import com.nnbc123.core.utils.TextUtils
import com.nnbc123.core.utils.toast
import com.tbruyelle.rxpermissions2.RxPermissions
import java.io.File
class RecordIResourceItem(private val itemAttribute: ItemAttribute) : SkillItem,
@@ -121,13 +119,6 @@ class RecordIResourceItem(private val itemAttribute: ItemAttribute) : SkillItem,
setItemByState(RECORD_STATE_READY)
}
override fun onClickRecord() {
itemAttribute.itemEventListener?.requestMicPermissions {
if (it) {
binding.recordView.startRecord()
}
}
}
/**
* 根据状态设置View

View File

@@ -50,7 +50,7 @@ class TimerRecorderView(context: Context, @Nullable attrs: AttributeSet?, defSty
setOnClickListener {
when (state) {
STATE_PAUSED -> {
recordListener?.onClickRecord()
startRecord()
}
STATE_PLAYED -> {
endAudioRecord(false)
@@ -82,7 +82,7 @@ class TimerRecorderView(context: Context, @Nullable attrs: AttributeSet?, defSty
//开始录制
fun startRecord() {
private fun startRecord() {
if (audioMessageHelper == null) {
val options = NimUIKitImpl.getOptions()
options.audioRecordMaxTime = recordDuration
@@ -175,6 +175,5 @@ class TimerRecorderView(context: Context, @Nullable attrs: AttributeSet?, defSty
fun onRecordCancel()
fun onRecordSuccess(file: File?)
fun onRecordFail()
fun onClickRecord()
}
}

View File

@@ -9,11 +9,8 @@ import android.text.SpannableString;
import android.text.TextUtils;
import android.view.View;
import androidx.core.util.Consumer;
import androidx.databinding.DataBindingUtil;
import com.nnbc123.app.ui.setting.invite.InviteInputConfirmDialog;
import com.nnbc123.app.ui.setting.invite.InviteInputDialog;
import com.nnbc123.core.UriProvider;
import com.nnbc123.core.auth.AuthModel;
import com.nnbc123.core.auth.event.LogoutEvent;
@@ -58,8 +55,6 @@ public class SettingActivity extends BaseActivity implements View.OnClickListene
private ActivitySettingBinding settingBinding;
private WithdrawInfo withdrawInfos;
private InviteInputDialog inviteInputDialog;
private InviteInputConfirmDialog inviteInputConfirmDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -81,7 +76,6 @@ public class SettingActivity extends BaseActivity implements View.OnClickListene
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
dismissInviteDialog();
}
@SuppressLint("CheckResult")
@@ -238,9 +232,6 @@ public class SettingActivity extends BaseActivity implements View.OnClickListene
case R.id.rly_permission:
PermissionGuideActivity.Companion.start(context);
break;
case R.id.layout_invite_code:
showInviteInputDialog();
break;
}
}
@@ -294,11 +285,7 @@ public class SettingActivity extends BaseActivity implements View.OnClickListene
} else {
settingBinding.tvPayModify.setText("修改");
}
if (cacheLoginUserInfo.isCanRefillInviteCode()) {
settingBinding.layoutInviteCode.setVisibility(View.VISIBLE);
} else {
settingBinding.layoutInviteCode.setVisibility(View.GONE);
}
}
public static class MissingPermissionException extends RuntimeException {
@@ -324,60 +311,5 @@ public class SettingActivity extends BaseActivity implements View.OnClickListene
finish();
}
public void showInviteInputDialog(){
inviteInputDialog = new InviteInputDialog(this);
inviteInputDialog.setOnNext(new Consumer<String>() {
@Override
public void accept(String s) {
showInviteInputConfirmDialog(s);
}
});
inviteInputDialog.show();
}
public void showInviteInputConfirmDialog(String code) {
inviteInputConfirmDialog = new InviteInputConfirmDialog(this, code);
inviteInputConfirmDialog.setOnNext(new Consumer<String>() {
@Override
public void accept(String s) {
inviteInputConfirmDialog.dismiss();
submitInviteCode(s);
}
});
inviteInputConfirmDialog.show();
}
private void dismissInviteDialog() {
if (inviteInputDialog != null && inviteInputDialog.isShowing()) {
inviteInputDialog.dismiss();
}
if (inviteInputConfirmDialog != null && inviteInputConfirmDialog.isShowing()) {
inviteInputConfirmDialog.dismiss();
}
}
private void submitInviteCode(String code) {
getDialogManager().showProgressDialog(this);
Disposable d = UserModel.get().refillInviteCode(code)
.compose(bindToLifecycle())
.subscribe(info -> {
getDialogManager().dismissDialog();
if (info.isSuccess()) {
dismissInviteDialog();
toast(R.string.invite_input_fill_in_success);
settingBinding.layoutInviteCode.setVisibility(View.GONE);
} else {
String message = info.getMessage();
if (message != null) {
toast(message);
}
// INVITE_CODE_DEVICE_DUPLICATED(211185,"该设备已填写过邀请码"),
// TIME_OUT_TO_REFILL_INVITE_CODE(211186,"超出补充填时间,无法补填邀请码"),
if (info.getCode() == 211185 || info.getCode() == 211186) {
settingBinding.layoutInviteCode.setVisibility(View.GONE);
dismissInviteDialog();
}
}
});
}
}

View File

@@ -1,57 +0,0 @@
package com.nnbc123.app.ui.setting.invite
import android.content.Context
import android.graphics.Typeface
import android.os.Bundle
import android.view.Gravity
import android.view.WindowManager
import android.widget.TextView
import androidx.core.util.Consumer
import com.chuhai.utils.ktx.getColorById
import com.chuhai.utils.spannable.SpannableTextBuilder
import com.nnbc123.app.R
import com.nnbc123.app.databinding.InviteInputConfirmDialogBinding
import com.nnbc123.app.ui.widget.dialog.BaseDialog
import com.nnbc123.app.utils.SpannableBuilder
import java.time.format.TextStyle
/**
* Created by Max on 2024/3/25 20:05
* Desc:
**/
class InviteInputConfirmDialog(context: Context, val code: String) :
BaseDialog(context, R.style.dialog) {
var onNext: Consumer<String>? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setCancelable(false)
val binding = InviteInputConfirmDialogBinding.inflate(layoutInflater)
setContentView(binding.root)
window?.attributes?.let {
it.width = WindowManager.LayoutParams.MATCH_PARENT
it.height = WindowManager.LayoutParams.WRAP_CONTENT
window?.attributes = it
}
SpannableTextBuilder(binding.tvTitle).appendText(context.getString(R.string.invite_input_confirm_title))
.appendText(
text = context.getString(R.string.invite_input_confirm_format).format(code),
textColor = context.getColorById(R.color.color_2B2D33)
)
.appendText("?").apply()
SpannableTextBuilder(binding.tvTips).appendText(context.getString(R.string.invite_input_tips3))
.appendText(
text = "1次",
textSize = 16,
textStyle = Typeface.BOLD,
textColor = context.getColorById(R.color.color_theme)
).apply()
binding.tvCancel.setOnClickListener {
dismiss()
}
binding.tvSave.setOnClickListener {
onNext?.accept(code)
}
}
}

View File

@@ -1,49 +0,0 @@
package com.nnbc123.app.ui.setting.invite
import android.content.Context
import android.os.Bundle
import android.view.View
import android.view.WindowManager
import androidx.core.util.Consumer
import com.chuhai.utils.ktx.setOnInputChangedListener
import com.nnbc123.app.R
import com.nnbc123.app.databinding.InviteInputDialogBinding
import com.nnbc123.app.ui.widget.dialog.BaseDialog
/**
* Created by Max on 2024/3/25 20:05
* Desc:
**/
class InviteInputDialog(context: Context) : BaseDialog(context, R.style.dialog) {
var onNext: Consumer<String>? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = InviteInputDialogBinding.inflate(layoutInflater)
setContentView(binding.root)
window?.attributes?.let {
it.width = WindowManager.LayoutParams.MATCH_PARENT
it.height = WindowManager.LayoutParams.WRAP_CONTENT
window?.attributes = it
}
binding.etCode.setOnInputChangedListener {
resetSaveBtn(binding)
true
}
binding.tvSave.setOnClickListener {
onNext?.accept(binding.etCode.text.toString().trim())
}
resetSaveBtn(binding)
}
private fun resetSaveBtn(binding: InviteInputDialogBinding) {
if (binding.etCode.length() > 0) {
binding.tvSave.isEnabled = true
binding.tvSave.setBackgroundResource(R.drawable.shape_theme_26)
} else {
binding.tvSave.isEnabled = false
binding.tvSave.setBackgroundResource(R.drawable.shape_33ffda24_26)
}
}
}

View File

@@ -154,6 +154,16 @@ public class UserInfoActivity extends BaseBindingActivity<ActivityUserInfoBindin
if (userId != AuthModel.get().getCurrentUid() && !VipHelper.isHideLookUser()) {
UserModel.get().visitUserDetail(userId).subscribe();
}
viewModel.getHallData().observe(this, clanAndHallInfo -> {
if (clanAndHallInfo != null && clanAndHallInfo.getClan() != null && clanAndHallInfo.getClan().getId() != 0) {
mBinding.tvHallDesc.setVisibility(View.VISIBLE);
mBinding.tvHallDesc.setOnClickListener(v -> {
ModuleClanActivity.start(context, userId);
});
} else {
mBinding.tvHallDesc.setVisibility(View.GONE);
}
});
if (userId != AuthModel.get().getCurrentUid()) {
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) mBinding.viewPager.getLayoutParams();
@@ -167,6 +177,7 @@ public class UserInfoActivity extends BaseBindingActivity<ActivityUserInfoBindin
protected void onResume() {
super.onResume();
viewModel.getUserInfoDetail();
viewModel.getUserHallAndClan();
viewModel.getUserSkillData(viewModel.getUserId());
}

View File

@@ -1,6 +1,5 @@
package com.nnbc123.app.ui.webview;
import android.Manifest;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
@@ -14,7 +13,6 @@ import android.os.Bundle;
import android.os.Handler;
import android.text.TextUtils;
import android.view.View;
import android.webkit.PermissionRequest;
import android.webkit.SslErrorHandler;
import android.webkit.ValueCallback;
import android.webkit.WebBackForwardList;
@@ -33,9 +31,6 @@ import com.google.gson.Gson;
import com.netease.nim.uikit.StatusBarUtil;
import com.netease.nim.uikit.common.util.log.LogUtil;
import com.netease.nim.uikit.common.util.string.StringUtil;
import com.nnbc123.app.common.permission.PermissionHelper;
import com.nnbc123.app.ui.widget.dialog.RequestPermissionPromptDialog;
import com.nnbc123.library.utils.ResUtil;
import com.orhanobut.logger.Logger;
import com.trello.rxlifecycle3.android.ActivityEvent;
import com.nnbc123.app.R;
@@ -226,8 +221,6 @@ public class CommonWebViewActivity extends BaseActivity implements ShareDialog.O
jsInterface.setPosition(mPosition);
webView.addJavascriptInterface(jsInterface, "androidJsObj");
webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
// 允许自动播放
webView.getSettings().setMediaPlaybackRequiresUserGesture(false);
webView.setWebViewClient(new WebViewClient() {
@@ -319,35 +312,6 @@ public class CommonWebViewActivity extends BaseActivity implements ShareDialog.O
//获取webviewtitle作为titlebar的title
wvcc = new WebChromeClient() {
@Override
public void onPermissionRequest(PermissionRequest request) {
String tips = null;
String[] permissions = null;
for (String item : request.getResources()) {
if (item != null && item.equals(PermissionRequest.RESOURCE_VIDEO_CAPTURE)) {
tips = ResUtil.getString(R.string.permission_denied_tips_camera);
permissions = new String[]{Manifest.permission.CAMERA,
Manifest.permission.RECORD_AUDIO};
}
}
if (permissions == null) {
request.grant(request.getResources());
return;
}
if (RequestPermissionPromptDialog.Companion.isNeedPrompt()
&& !PermissionHelper.INSTANCE.isAllGranted(rxPermissions, permissions)) {
new RequestPermissionPromptDialog(context, tips).show();
}
checkPermission(isGranted -> {
RequestPermissionPromptDialog.Companion.dismissCurrentDialog();
if (isGranted) {
request.grant(request.getResources());
} else {
request.deny();
}
}, permissions);
}
@Override
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);

View File

@@ -31,7 +31,7 @@ class RequestPermissionPromptDialog(context: Context, private val message: Strin
* 是否需要展示权限申请说明
*/
fun isNeedPrompt(): Boolean {
return (BasicConfig.INSTANCE.channel == "huawei") || (BasicConfig.INSTANCE.channel == "rongyao")
return (BasicConfig.INSTANCE.channel == "huawei")
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="26dp"/>
<solid android:color="#33FFDA24"/>
</shape>

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="8dp"/>
<solid android:color="#F3F5FA"/>
</shape>

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="26dp"/>
<solid android:color="@color/color_theme"/>
</shape>

View File

@@ -308,35 +308,6 @@
android:textColor="@color/text_title_282828"
android:textSize="14sp" />
<LinearLayout
android:id="@+id/layout_invite_code"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{click}"
android:orientation="vertical"
android:visibility="gone"
tools:visibility="visible">
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_marginStart="@dimen/dp_15"
android:layout_marginEnd="15dp"
android:background="@color/line_353548" />
<TextView
android:id="@+id/tv_invite_code"
android:layout_width="match_parent"
android:layout_height="50dp"
android:drawableEnd="@drawable/arrow_right"
android:gravity="center_vertical"
android:paddingLeft="@dimen/dp_15"
android:paddingRight="@dimen/dp_15"
android:text="@string/text_setting_invite_code"
android:textColor="@color/text_title_282828"
android:textSize="14sp" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="10dp"

View File

@@ -232,6 +232,16 @@
android:orientation="horizontal"
app:layout_constraintTop_toBottomOf="@id/ll_id">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_hall_desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="@dimen/dp_4"
android:visibility="gone"
app:drawableStartCompat="@drawable/ic_userinfo_hall"
tools:visibility="visible" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_user_level"
android:layout_width="wrap_content"

View File

@@ -1,72 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="34dp"
android:background="@drawable/shape_white_20dp_round">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:textColor="#2B2D33"
android:textSize="14dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="@string/invite_input_confirm_format" />
<TextView
android:id="@+id/tv_tips"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="28dp"
android:text="@string/invite_input_tips3"
android:textColor="#2B2D33"
android:textSize="14dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_title" />
<TextView
android:id="@+id/tv_cancel"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_marginStart="22dp"
android:layout_marginTop="28dp"
android:layout_marginBottom="24dp"
android:background="@drawable/shape_33ffda24_26"
android:gravity="center"
android:text="@string/cancel"
android:textColor="#2B2D33"
android:textSize="14dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/tv_save"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_tips" />
<TextView
android:id="@+id/tv_save"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_marginStart="15dp"
android:layout_marginEnd="22dp"
android:background="@drawable/shape_theme_26"
android:gravity="center"
android:text="@string/dialog_privilege_15"
android:textColor="#2B2D33"
android:textSize="14dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/tv_cancel"
app:layout_constraintTop_toBottomOf="@id/tv_tips" />
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>

View File

@@ -1,83 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="34dp"
android:background="@drawable/shape_white_20dp_round">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="28dp"
android:text="@string/invite_input_title"
android:textColor="#2B2D33"
android:textSize="16dp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/et_code"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_marginHorizontal="26dp"
android:layout_marginTop="26dp"
android:background="@drawable/shape_f3f5fa_8dp"
android:digits="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
android:gravity="center"
android:hint="@string/invite_input_hint"
android:maxLength="10"
android:singleLine="true"
android:textColor="#878B9C"
android:textColorHint="#878B9C"
android:textSize="14dp"
app:layout_constraintTop_toBottomOf="@id/tv_title" />
<TextView
android:id="@+id/tv_tips_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="28dp"
android:text="@string/invite_input_tips"
android:textColor="#2B2D33"
android:textSize="14dp"
app:layout_constraintStart_toStartOf="@id/et_code"
app:layout_constraintTop_toBottomOf="@id/et_code" />
<TextView
android:id="@+id/tv_tips"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/invite_input_tips2"
android:textColor="#A2A7B8"
android:textSize="12dp"
app:layout_constraintStart_toStartOf="@id/tv_tips_title"
app:layout_constraintTop_toBottomOf="@id/tv_tips_title" />
<TextView
android:id="@+id/tv_save"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_marginHorizontal="32dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="24dp"
android:background="@drawable/shape_theme_26"
android:gravity="center"
android:text="@string/dialog_privilege_15"
android:textColor="#2B2D33"
android:textSize="14dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_tips" />
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>

View File

@@ -680,7 +680,6 @@
<string name="text_setting_pay_password">支付密码</string>
<string name="text_setting_feedback">我要反馈</string>
<string name="text_setting_black_management">黑名单管理</string>
<string name="text_setting_invite_code">填写邀请码</string>
<string name="text_setting_community_norms">社区规范</string>
<string name="text_setting_contact_us">联系官方</string>
<string name="text_setting_help">帮助</string>
@@ -992,7 +991,7 @@
<string name="diamond_inning">%d钻/局</string>
<string name="permission_denied_tips_mic">为了实现连麦及语音输入等功能,请您允许应用向您获取“麦克风”权限</string>
<string name="permission_denied_tips_image">访问你的本地内容,以能正常使用图片上传、视频发送等应用功能</string>
<string name="permission_denied_tips_camera">用于拍摄更新个人头像、发布动态、实名认证、与客服反馈问题时进行照片和视频的拍摄录制</string>
<string name="permission_denied_tips_camera">用于拍摄更新个人头像、发布动态、与客服反馈问题时进行照片和视频的拍摄录制</string>
<string name="all_service_gift_room_go">去围观</string>
<string name="all_service_gift_room_go_title">前往围观</string>
@@ -1000,13 +999,4 @@
<string name="all_service_gift_room_go_ignore">下次不再出现此提示</string>
<string name="all_service_gift_room_go_cancel">留在这</string>
<string name="all_service_gift_room_go_go">立即前往</string>
<string name="invite_input_title">填写邀请码</string>
<string name="invite_input_hint">请输入要填写的邀请码</string>
<string name="invite_input_tips">填写说明:</string>
<string name="invite_input_tips2">请填写邀请人的邀请码;\n邀请码只能填写一次请勿随意填写</string>
<string name="invite_input_confirm_title">确认填写 </string>
<string name="invite_input_confirm_format">邀请码【%s】</string>
<string name="invite_input_tips3">注意邀请码只能填写 </string>
<string name="invite_input_fill_in_success">填写成功</string>
</resources>

View File

@@ -38,8 +38,6 @@ public class CustomItem implements Parcelable, Serializable {
*/
private int fileType;
private String format;
private int width;
private int height;
public CustomItem(String path, int fileType) {
this(path, fileType, "jpeg");
@@ -52,16 +50,12 @@ public class CustomItem implements Parcelable, Serializable {
this.path = path;
this.fileType = fileType;
this.format = format;
this.width = 0;
this.height = 0;
}
protected CustomItem(Parcel in) {
this.path = in.readString();
this.fileType = in.readInt();
this.format = in.readString();
this.width = in.readInt();
this.height = in.readInt();
}
public boolean isImage() {
@@ -110,22 +104,6 @@ public class CustomItem implements Parcelable, Serializable {
this.format = format;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
@Override
public int describeContents() {
return 0;
@@ -136,7 +114,5 @@ public class CustomItem implements Parcelable, Serializable {
dest.writeString(this.path);
dest.writeInt(this.fileType);
dest.writeString(this.format);
dest.writeInt(this.width);
dest.writeInt(this.height);
}
}

View File

@@ -22,7 +22,6 @@ import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.provider.MediaStore;
import androidx.annotation.Nullable;
import com.zhihu.matisse.MimeType;
@@ -47,10 +46,8 @@ public class Item implements Parcelable {
public final Uri uri;
public final long size;
public final long duration; // only for video, in ms
public final int width;
public final int height;
private Item(long id, String mimeType, long size, long duration,int width,int height) {
private Item(long id, String mimeType, long size, long duration) {
this.id = id;
this.mimeType = mimeType;
Uri contentUri;
@@ -65,18 +62,14 @@ public class Item implements Parcelable {
this.uri = ContentUris.withAppendedId(contentUri, id);
this.size = size;
this.duration = duration;
this.width = width;
this.height = height;
}
public Item(String mimeType, Uri uri, long size, long duration, int width, int height) {
public Item(String mimeType, Uri uri, long size, long duration) {
this.id = 1;
this.mimeType = mimeType;
this.uri = uri;
this.size = size;
this.duration = duration;
this.width = width;
this.height = height;
}
private Item(Parcel source) {
@@ -85,44 +78,13 @@ public class Item implements Parcelable {
uri = source.readParcelable(Uri.class.getClassLoader());
size = source.readLong();
duration = source.readLong();
width = source.readInt();
height = source.readInt();
}
public static Item valueOf(Cursor cursor) {
return new Item(getCursorLong(cursor, MediaStore.Files.FileColumns._ID, 0),
getCursorString(cursor, MediaStore.MediaColumns.MIME_TYPE, ""),
getCursorLong(cursor, MediaStore.MediaColumns.SIZE, 0),
getCursorLong(cursor, "duration", 0),
getCursorInt(cursor, MediaStore.MediaColumns.WIDTH, 0),
getCursorInt(cursor, MediaStore.MediaColumns.HEIGHT, 0));
}
private static long getCursorLong(Cursor cursor, String key, long defaultValue) {
int index = cursor.getColumnIndex(key);
long value = defaultValue;
if (index >= 0) {
value = cursor.getLong(index);
}
return value;
}
private static String getCursorString(Cursor cursor, String key, String defaultValue) {
int index = cursor.getColumnIndex(key);
String value = defaultValue;
if (index >= 0) {
value = cursor.getString(index);
}
return value;
}
private static int getCursorInt(Cursor cursor, String key, int defaultValue) {
int index = cursor.getColumnIndex(key);
int value = defaultValue;
if (index >= 0) {
value = cursor.getInt(index);
}
return value;
return new Item(cursor.getLong(cursor.getColumnIndex(MediaStore.Files.FileColumns._ID)),
cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.MIME_TYPE)),
cursor.getLong(cursor.getColumnIndex(MediaStore.MediaColumns.SIZE)),
cursor.getLong(cursor.getColumnIndex("duration")));
}
@Override
@@ -137,8 +99,6 @@ public class Item implements Parcelable {
dest.writeParcelable(uri, 0);
dest.writeLong(size);
dest.writeLong(duration);
dest.writeInt(width);
dest.writeInt(height);
}
public Uri getContentUri() {
@@ -174,9 +134,7 @@ public class Item implements Parcelable {
&& (uri != null && uri.equals(other.uri)
|| (uri == null && other.uri == null))
&& size == other.size
&& duration == other.duration
&& width == other.width
&& height == other.height;
&& duration == other.duration;
}
@Override
@@ -189,8 +147,6 @@ public class Item implements Parcelable {
result = 31 * result + uri.hashCode();
result = 31 * result + Long.valueOf(size).hashCode();
result = 31 * result + Long.valueOf(duration).hashCode();
result = 31 * result + Integer.valueOf(width).hashCode();
result = 31 * result + Integer.valueOf(height).hashCode();
return result;
}
}

View File

@@ -39,8 +39,6 @@ public class AlbumMediaLoader extends CursorLoader {
MediaStore.MediaColumns.DISPLAY_NAME,
MediaStore.MediaColumns.MIME_TYPE,
MediaStore.MediaColumns.SIZE,
MediaStore.MediaColumns.WIDTH,
MediaStore.MediaColumns.HEIGHT,
"duration"};
// === params for album ALL && showSingleMediaType: false ===
@@ -145,7 +143,7 @@ public class AlbumMediaLoader extends CursorLoader {
return result;
}
MatrixCursor dummy = new MatrixCursor(PROJECTION);
dummy.addRow(new Object[]{Item.ITEM_ID_CAPTURE, Item.ITEM_DISPLAY_NAME_CAPTURE, "", 0, 0, 0, 0});
dummy.addRow(new Object[]{Item.ITEM_ID_CAPTURE, Item.ITEM_DISPLAY_NAME_CAPTURE, "", 0, 0});
return new MergeCursor(new Cursor[]{dummy, result});
}

View File

@@ -165,9 +165,6 @@ public class SelectedItemCollection {
CustomItem customItem = new CustomItem();
customItem.setPath(PathUtils.getPath(mContext, item.getContentUri()));
customItem.setFileType(item.isGif() ? 1 : 0);
customItem.setWidth(item.width);
customItem.setHeight(item.height);
customItem.setFormat(item.mimeType);
paths.add(customItem);
}
return paths;

View File

@@ -21,8 +21,6 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.TypedArray;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.net.Uri;
@@ -252,9 +250,6 @@ public class MatisseActivity extends AppCompatActivity implements
for (Item item : selected) {
selectedUris.add(item.getContentUri());
CustomItem customItem = new CustomItem();
customItem.setFormat(item.mimeType);
customItem.setWidth(item.width);
customItem.setHeight(item.height);
customItem.setFileType(item.isGif() ? 1 : 0);
customItem.setPath(PathUtils.getPath(this, item.getContentUri()));
selectedPaths.add(customItem);
@@ -285,7 +280,6 @@ public class MatisseActivity extends AppCompatActivity implements
CustomItem customItem = new CustomItem();
customItem.setFileType(0);
customItem.setPath(path);
trySetImageInfo(customItem);
selectedPath.add(customItem);
Intent result = new Intent();
result.putParcelableArrayListExtra(EXTRA_RESULT_SELECTION, selected);
@@ -304,9 +298,6 @@ public class MatisseActivity extends AppCompatActivity implements
ArrayList<CustomItem> selectedPath = new ArrayList<>(1);
Item item = selected.get(0);
CustomItem customItem = new CustomItem();
customItem.setFormat(item.mimeType);
customItem.setWidth(item.width);
customItem.setHeight(item.height);
customItem.setFileType(2);
customItem.setPath(item.uri.toString());
selectedPath.add(customItem);
@@ -318,26 +309,6 @@ public class MatisseActivity extends AppCompatActivity implements
}
}
// 补充图片信息
private void trySetImageInfo(CustomItem customItem) {
try {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
Bitmap bitmap = BitmapFactory.decodeFile(customItem.getPath(), options);
if (customItem.getWidth() <= 0) {
customItem.setWidth(options.outWidth);
}
if (customItem.getHeight() <= 0) {
customItem.setHeight(options.outHeight);
}
if (customItem.getFormat() == null || customItem.getFormat().length() == 0) {
customItem.setFormat(options.outMimeType);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void updateBottomToolbar() {
int selectedCount = mSelectedCollection.count();
if (selectedCount == 0) {

View File

@@ -48,9 +48,9 @@ public class PublishPresenter extends BaseMvpPresenter<IPublishView> {
private MiniWorldChooseInfo miniWorldChooseInfo = new MiniWorldChooseInfo();
public void publishDy(List<DynamicMedia> list, long worldId, String content, boolean isOriginalImage) {
public void publishDy(List<String> list, long worldId, String content, boolean isOriginalImage) {
publishBody = new PublishBody();
List<DynamicMedia> uploadList = new ArrayList<>(list);
List<String> uploadList = new ArrayList<>(list);
int type = ListUtils.isListEmpty(uploadList) ? 0 : 2;
publishBody.setType(type);
publishBody.setUid(AuthModel.get().getCurrentUid());
@@ -77,7 +77,7 @@ public class PublishPresenter extends BaseMvpPresenter<IPublishView> {
});
}
private Single<String> uploadImage(List<DynamicMedia> imagePaths) {
private Single<String> uploadImage(List<String> imagePaths) {
upload(imagePaths);
return Single.create(emitter ->
mImageUploadSubscribe = Observable.interval(500, TimeUnit.MILLISECONDS)
@@ -91,34 +91,22 @@ public class PublishPresenter extends BaseMvpPresenter<IPublishView> {
}));
}
private void upload(List<DynamicMedia> imagePaths) {
private void upload(List<String> imagePaths) {
if (imagePaths == null || imagePaths.size() == 0) {
return;
}
DynamicMedia item = imagePaths.get(0);
String file = item.getLocalFilePath();
String file = imagePaths.get(0);
Single.create((SingleOnSubscribe<String>) e -> {
long fileLength = JXFileUtils.getFileLength(file);
LogUtil.print("文件大小:" + fileLength);
String compressFile = null;
if (!isOriginalImage && fileLength > ImageUploadConfig.MAX_FILE_SIZE) {
JXImageUtils.CompressResult result = JXImageUtils.compressImagePxAndQuality(
compressFile = JXImageUtils.compressImagePxAndQuality(
file, DirectoryHelper.get().getDynamicDir(),
"dynamic_" + System.currentTimeMillis() + ".jpg",
ImageUploadConfig.EXPECT_MIN_WIDTH,
ImageUploadConfig.EXPECT_COMPRESS_SIZE);
if (result != null) {
compressFile = result.getPath();
if (result.getWidth() > 0 && item.getWidth() != result.getWidth()) {
item.setWidth(result.getWidth());
}
if (result.getHeight() > 0 && item.getHeight() != result.getHeight()) {
item.setHeight(result.getHeight());
}
if (result.getFormat() != null) {
item.setFormat(result.getFormat());
}
}
LogUtil.print("压缩后:" + compressFile);
}
if (!TextUtils.isEmpty(compressFile)) {
@@ -130,22 +118,21 @@ public class PublishPresenter extends BaseMvpPresenter<IPublishView> {
}
})
.compose(RxHelper.handleSchedulers())
.flatMap((Function<String, SingleSource<String>>)
path -> FileModel.get().uploadFile(path))
.flatMap((Function<String, SingleSource<DynamicMedia>>)
path -> FileModel.get().uploadFileReturnImageInfo(path))
.compose(bindUntilEvent(PresenterEvent.DESTROY))
.subscribe(new DontWarnObserver<String>() {
.subscribe(new DontWarnObserver<DynamicMedia>() {
@Override
public void acceptThrowable(String url, Throwable throwable) {
super.acceptThrowable(url, throwable);
public void acceptThrowable(DynamicMedia media, Throwable throwable) {
super.acceptThrowable(media, throwable);
if (throwable != null) {
if (mImageUploadSubscribe != null) {
mImageUploadSubscribe.dispose();
}
dealUploadFileError(throwable);
} else {
item.setResUrl(url);
LogUtil.print("上传成功:", item);
publishBody.addDynamicMedia(item);
LogUtil.print("上传成功:", media);
publishBody.addDynamicMedia(media);
imagePaths.remove(0);
upload(imagePaths);
}

View File

@@ -337,7 +337,7 @@ public class PublishActivity extends BaseMvpActivity<IPublishView, PublishPresen
tvPublish.setEnabled(false);
getDialogManager().showProgressDialog(context);
getMvpPresenter().publishDy(
ObjectTypeHelper.customToMediaList(uploadList),
ObjectTypeHelper.customToStringList(uploadList),
getMvpPresenter().getWorldId(), etContent.getText().toString(), isOriginalImage);
}
@@ -442,7 +442,6 @@ public class PublishActivity extends BaseMvpActivity<IPublishView, PublishPresen
public void onPublishFailed(Throwable throwable) {
getDialogManager().dismissDialog();
toast(throwable.getMessage());
updatePublishStatus();
}
@Override

View File

@@ -22,22 +22,6 @@ public class ObjectTypeHelper {
return resultList;
}
public static List<DynamicMedia> customToMediaList(List<CustomItem> paramsList) {
List<DynamicMedia> resultList = new ArrayList<>();
if (paramsList == null) {
return resultList;
}
for (CustomItem item : paramsList) {
DynamicMedia media = new DynamicMedia();
media.setLocalFilePath(item.getPath());
media.setWidth(item.getWidth());
media.setHeight(item.getHeight());
media.setFormat(item.getFormat());
resultList.add(media);
}
return resultList;
}
public static List<CustomItem> stringToCustomList(List<String> paramsList) {
List<CustomItem> resultList = new ArrayList<>();
if (paramsList == null) {
@@ -56,8 +40,6 @@ public class ObjectTypeHelper {
}
for (DynamicMedia media : paramsList) {
CustomItem item = new CustomItem();
item.setWidth(media.getWidth());
item.setHeight(media.getHeight());
item.setPath(media.getResUrl());
item.setFileType(CustomItem.UNKOWN);
if (media.isJpgOrPng()) {

View File

@@ -100,7 +100,6 @@ dependencies {
api 'com.alipay.sdk:alipaysdk-android:+@aar'
api 'com.qcloud.cos:cos-android:5.9.23'
}
repositories {
mavenCentral()

View File

@@ -2158,9 +2158,6 @@ public final class IMNetEaseManager {
PkModel.get().syncPkList(userInfo, chatRoomMember.getGroupType());
noticePKInfo();
int oldMicPosition = AvRoomDataManager.get().getMicPosition(AuthModel.get().getCurrentUid());
boolean oldIsOnMic = AvRoomDataManager.get().isOwnerOnMic();
int size = mMicQueueMemberMap.size();
if (size > 0) {
for (int j = 0; j < size; j++) {
@@ -2204,14 +2201,7 @@ public final class IMNetEaseManager {
//开麦
AudioEngineManager.get().setRole(Constants.CLIENT_ROLE_BROADCASTER);
if (!roomQueueInfo.mChatRoomMember.isNoProhibitMic()) {
/*
修复相亲房自动闭麦bug
原因:相亲房的某些麦位状态更新,走的是上麦通道,导致默认闭麦了
临时解决:若是相亲房且之前在麦或麦位没变化->不做闭麦操作
*/
if (!AvRoomDataManager.get().isDatingMode() || (AvRoomDataManager.get().isDatingMode() && (!oldIsOnMic || oldMicPosition != micPosition))) {
AudioEngineManager.get().setMute(roomQueueInfo.mRoomMicInfo.isMicMute() || AvRoomDataManager.get().mIsNeedOpenMic);
}
AudioEngineManager.get().setMute(roomQueueInfo.mRoomMicInfo.isMicMute() || AvRoomDataManager.get().mIsNeedOpenMic);
}
}
noticeUpMic(Integer.parseInt(key), chatRoomMember.getAccount());

View File

@@ -2,19 +2,22 @@ package com.nnbc123.core.file;
import android.text.TextUtils;
import com.chuhai.utils.AppUtils;
import com.chuhai.utils.PathUtils;
import com.nnbc123.core.file.cos.CosClient;
import com.nnbc123.core.file.cos.CosToken;
import com.netease.nim.uikit.common.util.log.LogUtil;
import com.qiniu.android.common.FixedZone;
import com.qiniu.android.storage.Configuration;
import com.qiniu.android.storage.UploadManager;
import com.nnbc123.core.base.BaseModel;
import com.nnbc123.core.bean.response.ServiceResult;
import com.nnbc123.core.community.bean.DynamicMedia;
import com.nnbc123.core.exception.ErrorThrowable;
import com.nnbc123.core.utils.net.RxHelper;
import com.nnbc123.library.net.rxnet.RxNet;
import org.json.JSONObject;
import java.io.File;
import java.util.UUID;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import retrofit2.http.GET;
public class FileModel extends BaseModel implements IFileModel {
@@ -24,50 +27,119 @@ public class FileModel extends BaseModel implements IFileModel {
}
private FileModel() {
Configuration config = new Configuration.Builder()
.zone(FixedZone.zone2) // 设置区域不指定会自动选择。指定不同区域的上传域名、备用域名、备用IP。
.build();
uploadManager = new UploadManager(config);
}
public static FileModel get() {
return Helper.INSTANCE;
}
private UploadManager uploadManager;
private final Api api = RxNet.create(Api.class);
private CosToken cosToken;
private Single<CosToken> getCosToken() {
if (cosToken != null && cosToken.isValid()) {
return Single.just(cosToken);
} else {
return api.getCosToken().compose(RxHelper.handleSchedulers())
.compose(RxHelper.handleBeanData()).map(cosToken -> {
FileModel.get().cosToken = cosToken;
return cosToken;
});
}
}
@Override
public Single<String> uploadFile(String path) {
File file;
if (TextUtils.isEmpty(path) || !((file = new File(path)).exists())) {
return Single.error(new ErrorThrowable(path + " 为空或者该文件不存在!"));
}
File finalFile = file;
String outName = UUID.randomUUID().toString() + PathUtils.INSTANCE.getSuffixType(finalFile.getName());
return getCosToken().flatMap(token -> CosClient.INSTANCE.upload(AppUtils.getApp(), finalFile, outName, token).map(cosXmlResult -> cosXmlResult.accessUrl))
.observeOn(AndroidSchedulers.mainThread());
return api.getUploadToken()
.compose(RxHelper.handleSchedulers())
.compose(RxHelper.handleBeanData())
.flatMap(uploadToken -> Single.create(singleEmitter ->
uploadManager.put(finalFile, uploadToken.getKey(), uploadToken.getToken(),
(key, info, response) -> {
if (info.isOK()) {
try {
String imgUrl = response.getString("path");
singleEmitter.onSuccess(imgUrl);
} catch (Exception e) {
singleEmitter.onError(e);
}
} else {
singleEmitter.onError(new Throwable(info.error));
}
}, null)));
}
@Override
public Single<DynamicMedia> uploadFileReturnImageInfo(String path) {
return uploadFileReturnImageInfo(path, null);
}
@Override
public Single<DynamicMedia> uploadFileReturnImageInfo(String path, String qiniuName) {
File file;
if (TextUtils.isEmpty(path) || !((file = new File(path)).exists())) {
return Single.error(new ErrorThrowable(path + " 为空或者该文件不存在!"));
}
File finalFile = file;
return api.getUploadToken()
.compose(RxHelper.handleSchedulers())
.compose(RxHelper.handleBeanData())
.flatMap(uploadToken -> Single.create(singleEmitter ->
uploadManager.put(finalFile, uploadToken.getKey(), uploadToken.getToken(),
(key, info, response) -> {
if (info.isOK()) {
try {
LogUtil.print("上传成功");
LogUtil.print(response);
DynamicMedia media = responseToMeia(response);
if (media != null) {
singleEmitter.onSuccess(media);
return;
}
} catch (Exception e) {
e.printStackTrace();
}
singleEmitter.onError(new Throwable("qiniu json error"));
} else {
singleEmitter.onError(new Throwable(info.error));
}
}, null)));
}
@Override
public Single<String> downloadFile(String url) {
return null;
}
private DynamicMedia responseToMeia(JSONObject response) {
DynamicMedia media = new DynamicMedia();
try {
String imgNamePath = response.getString("path");
media.setResUrl(imgNamePath);
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
try {
media.setFormat(response.getString("format"));
media.setWidth(response.getInt("w"));
media.setHeight(response.getInt("h"));
} catch (Exception ex) {
ex.printStackTrace();
}
LogUtil.print("七牛上传", media);
return media;
}
interface Api {
/**
* 获取钱包
*
* @return
*/
@GET("/tencent/cos/getToken")
Single<ServiceResult<CosToken>> getCosToken();
@GET("/qiniu/upload/getUploadToken")
Single<ServiceResult<UploadToken>> getUploadToken();
}
}

View File

@@ -23,4 +23,8 @@ public interface IFileModel extends IModel {
*/
Single<String> downloadFile(String url);
Single<DynamicMedia> uploadFileReturnImageInfo(String path);
Single<DynamicMedia> uploadFileReturnImageInfo(String path, String qiniuName);
}

View File

@@ -0,0 +1,71 @@
package com.nnbc123.core.file;
public class UploadToken {
private String key;
private String token;
//<editor-fold defaultstate="collapsed" desc="delombok">
@SuppressWarnings("all")
public UploadToken() {
}
@SuppressWarnings("all")
public String getKey() {
return this.key;
}
@SuppressWarnings("all")
public String getToken() {
return this.token;
}
@SuppressWarnings("all")
public void setKey(final String key) {
this.key = key;
}
@SuppressWarnings("all")
public void setToken(final String token) {
this.token = token;
}
@Override
@SuppressWarnings("all")
public boolean equals(final Object o) {
if (o == this) return true;
if (!(o instanceof UploadToken)) return false;
final UploadToken other = (UploadToken) o;
if (!other.canEqual((Object) this)) return false;
final Object this$key = this.getKey();
final Object other$key = other.getKey();
if (this$key == null ? other$key != null : !this$key.equals(other$key)) return false;
final Object this$token = this.getToken();
final Object other$token = other.getToken();
if (this$token == null ? other$token != null : !this$token.equals(other$token)) return false;
return true;
}
@SuppressWarnings("all")
protected boolean canEqual(final Object other) {
return other instanceof UploadToken;
}
@Override
@SuppressWarnings("all")
public int hashCode() {
final int PRIME = 59;
int result = 1;
final Object $key = this.getKey();
result = result * PRIME + ($key == null ? 43 : $key.hashCode());
final Object $token = this.getToken();
result = result * PRIME + ($token == null ? 43 : $token.hashCode());
return result;
}
@Override
@SuppressWarnings("all")
public String toString() {
return "UploadToken(key=" + this.getKey() + ", token=" + this.getToken() + ")";
}
//</editor-fold>
}

View File

@@ -1,131 +0,0 @@
package com.nnbc123.core.file.cos
import android.content.Context
import android.net.Uri
import android.util.Log
import com.nnbc123.core.Env
import com.tencent.cos.xml.CosXmlService
import com.tencent.cos.xml.CosXmlServiceConfig
import com.tencent.cos.xml.exception.CosXmlClientException
import com.tencent.cos.xml.exception.CosXmlServiceException
import com.tencent.cos.xml.listener.CosXmlResultListener
import com.tencent.cos.xml.model.CosXmlRequest
import com.tencent.cos.xml.model.CosXmlResult
import com.tencent.cos.xml.transfer.COSXMLUploadTask
import com.tencent.cos.xml.transfer.TransferConfig
import com.tencent.cos.xml.transfer.TransferManager
import io.reactivex.Single
import java.io.File
/**
* Created by Max on 2024/1/16 11:09
* Desc:
**/
object CosClient {
private var cosXmlClient: CosXmlService? = null
private var credentialProvider: CosCredentialProvider? = null
private fun getCosXmlClient(context: Context, token: CosToken): CosXmlService {
var client = this.cosXmlClient
if (client != null && credentialProvider != null) {
credentialProvider?.updateCredentials(token.toCredential())
return client
}
// 创建 CosXmlServiceConfig 对象,根据需要修改默认的配置参数
val serviceConfig: CosXmlServiceConfig = CosXmlServiceConfig.Builder()
.setRegion(token.region)
.isHttps(true) // 使用 HTTPS 请求, 默认为 HTTP 请求
.builder()
val credentials = token.toCredential()
credentialProvider = CosCredentialProvider(credentials)
// 初始化 COS Service获取实例
client = CosXmlService(
context,
serviceConfig, credentialProvider
)
cosXmlClient = client
return client
}
/**
* 上传文件
* @param outName 远端文件名
*/
fun upload(
context: Context,
file: File,
outName: String,
token: CosToken
): Single<CosXmlResult> {
if (Env.isDebug()) {
Log.e("CosClient", "upload file:${file.absolutePath} outName:${outName}")
}
return Single.create {
uploadFile(context, file, outName, token).apply {
setCosXmlResultListener(object : CosXmlResultListener {
override fun onSuccess(request: CosXmlRequest?, result: CosXmlResult) {
transformDomain(result, token)
if (Env.isDebug()) {
Log.e("CosClient", "upload onSuccess result:${result.accessUrl}")
}
it.onSuccess(result)
}
override fun onFail(
request: CosXmlRequest?,
clientException: CosXmlClientException?,
serviceException: CosXmlServiceException?
) {
if (Env.isDebug()) {
Log.e("CosClient", "upload onFail clientException:$clientException")
Log.e("CosClient", "upload onFail serviceException:$serviceException")
}
it.onError(CosException(clientException, serviceException))
}
})
}
}
}
private fun transformDomain(result: CosXmlResult, token: CosToken) {
try {
if (result.accessUrl.isNullOrEmpty()) {
return
}
if (token.customDomain.isNullOrEmpty()) {
return
}
val newUri = Uri.parse(token.customDomain)
result.accessUrl = Uri.parse(result.accessUrl).buildUpon().scheme(newUri.scheme)
.authority(newUri.authority).build().toString()
} catch (e: Exception) {
e.printStackTrace()
}
}
/**
* 上传文件
* @param outName 远端文件名
*/
private fun uploadFile(
context: Context,
file: File,
outName: String,
token: CosToken
): COSXMLUploadTask {
val cosXmlService = getCosXmlClient(context, token)
// 初始化 TransferConfig这里使用默认配置如果需要定制请参考 SDK 接口文档
val transferConfig = TransferConfig.Builder().build()
// 初始化 TransferManager
val transferManager = TransferManager(
cosXmlService,
transferConfig
)
return transferManager.upload(
token.bucket, outName,
file.absolutePath, null
)
}
}

View File

@@ -1,20 +0,0 @@
package com.nnbc123.core.file.cos
import com.tencent.qcloud.core.auth.BasicLifecycleCredentialProvider
import com.tencent.qcloud.core.auth.QCloudLifecycleCredentials
/**
* Created by Max on 2024/1/16 11:28
* Desc:
**/
class CosCredentialProvider(private var credentials: QCloudLifecycleCredentials) :
BasicLifecycleCredentialProvider() {
fun updateCredentials(credentials: QCloudLifecycleCredentials) {
this.credentials = credentials
}
override fun fetchNewCredentials(): QCloudLifecycleCredentials {
return credentials
}
}

View File

@@ -1,21 +0,0 @@
package com.nnbc123.core.file.cos
import com.tencent.cos.xml.exception.CosXmlClientException
import com.tencent.cos.xml.exception.CosXmlServiceException
/**
* Created by Max on 2024/1/16 15:49
* Desc:
**/
class CosException : Exception {
var clientException: CosXmlClientException? = null
var serviceException: CosXmlServiceException? = null
constructor(
clientException: CosXmlClientException?,
serviceException: CosXmlServiceException?
) : super(clientException ?: serviceException) {
this.clientException = clientException
this.serviceException = serviceException
}
}

View File

@@ -1,44 +0,0 @@
package com.nnbc123.core.file.cos
import com.nnbc123.core.utils.CurrentTimeUtils
import com.tencent.qcloud.core.auth.QCloudLifecycleCredentials
import com.tencent.qcloud.core.auth.SessionQCloudCredentials
/**
* Created by Max on 2024/1/16 11:44
* Desc:
**/
data class CosToken(
val secretId: String?,
val secretKey: String?,
val sessionToken: String?,
val bucket: String?,
val region: String?,
val startTime: Long?,
val expireTime: Long?,
val customDomain: String?
) {
/**
* 是否有效
*/
fun isValid(): Boolean {
if (expireTime == null) {
return false
}
val currentTime = CurrentTimeUtils.getCurrentTime() / 1000
// 预留一点安全时长
var safeTime = 30
if (safeTime >= (expireTime - (startTime ?: 0))) {
safeTime = 0
}
return currentTime <= (expireTime - safeTime)
}
fun toCredential(): QCloudLifecycleCredentials {
return SessionQCloudCredentials(
secretId ?: "", secretKey ?: "",
sessionToken ?: "", startTime ?: 0, expireTime ?: 0
)
}
}

View File

@@ -239,8 +239,6 @@ public interface IUserModel extends IModel {
Single<ServiceResult> deleteInRoomRecord(@Nullable String roomUid);
Single<ServiceResult> refillInviteCode(@Nullable String inviteCode);
/**
* 随机头像昵称开关
*/

View File

@@ -793,12 +793,6 @@ public final class UserModel extends BaseModel implements IUserModel {
.compose(RxHelper.handleSchAndExce());
}
@Override
public Single<ServiceResult> refillInviteCode(@Nullable String inviteCode) {
return api.refillInviteCode(inviteCode)
.compose(RxHelper.handleSchAndExce());
}
/**
* 随机头像昵称开关
*/
@@ -1112,9 +1106,5 @@ public final class UserModel extends BaseModel implements IUserModel {
@GET("/uservisitrecord/visitUserList")
Single<ServiceResult<List<VisitorInfo>>> getVisitorUserList(@Query("pageNum") int pageNum,
@Query("pageSize") int pageSize);
@FormUrlEncoded
@POST("/inviteCode/refillInviteCode")
Single<ServiceResult<String>> refillInviteCode(@Field("inviteCode") String inviteCode);
}
}

View File

@@ -228,17 +228,6 @@ public class UserInfo implements Serializable {
private boolean voicePlaying;
private boolean lock;
// 是否可以补填邀请码
private boolean canRefillInviteCode;
public boolean isCanRefillInviteCode() {
return canRefillInviteCode;
}
public void setCanRefillInviteCode(boolean canRefillInviteCode) {
this.canRefillInviteCode = canRefillInviteCode;
}
public boolean isLock() {
return lock;
}

View File

@@ -0,0 +1,10 @@
package com.nnbc123.core.utils;
/**
* create by lvzebiao @2019/11/20
*/
public class UploadUtils {
}

View File

@@ -2,8 +2,6 @@ package com.nnbc123.core.community.bean;
import android.text.TextUtils;
import kotlin.jvm.Transient;
/**
* create by lvzebiao @2019/11/21
*/
@@ -23,17 +21,6 @@ public class DynamicMedia {
private int width;
private int height;
@Transient
private String localFilePath;
public String getLocalFilePath() {
return localFilePath;
}
public void setLocalFilePath(String localFilePath) {
this.localFilePath = localFilePath;
}
/**
* 是否是网络图片
* @return -
@@ -51,14 +38,14 @@ public class DynamicMedia {
if (TextUtils.isEmpty(format)) {
return false;
}
return format.toLowerCase().contains("jpeg") || format.toLowerCase().contains("jpg") || format.toLowerCase().contains("png");
return format.toLowerCase().equals("jpeg") || format.toLowerCase().equals("jpg") || format.toLowerCase().equals("png");
}
public boolean isGif() {
if (TextUtils.isEmpty(format)) {
return false;
}
return format.toLowerCase().contains("gif");
return format.toLowerCase().equals("gif");
}
//<editor-fold defaultstate="collapsed" desc="delombok">

View File

@@ -6,11 +6,8 @@
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx4096m -XX:MaxPermSize=2048m
org.gradle.daemon=true
org.gradle.jvmargs=-Xmx4096m \
--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \
--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED \
--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
@@ -31,6 +28,5 @@ COMPILE_SDK_VERSION=32
MIN_SDK_VERSION=21
TARGET_SDK_VERSION=32
version_name=2.1.6
version_code=2106
minify_enabled=false
version_name=2.1.1
version_code=2101

View File

@@ -116,6 +116,8 @@ dependencies {
api "com.orhanobut:logger:${loggerVersion}"
api "com.qiniu:qiniu-android-sdk:${qiniu}"
api "org.greenrobot:eventbus:${eventbusVersion}"
api "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"

View File

@@ -19,7 +19,6 @@ import android.graphics.RectF;
import android.media.ExifInterface;
import android.util.Log;
import com.nnbc123.library.utils.LogUtil;
import com.nnbc123.library.utils.StringUtils;
import com.nnbc123.library.utils.file.JXFileUtils;
@@ -691,79 +690,78 @@ public class JXImageUtils {
return baos.toByteArray();
}
public static CompressResult compressImagePxAndQuality(String inPath, File outDir, String outFileName, int expectWidth, long expectSize) {
try {
if (outDir == null) {
return null;
}
if (!outDir.exists()) {
//创建目录
outDir.mkdirs();
}
if (!outDir.exists()) {
return null;
}
//读取原图的旋转角度,并写入到压缩图片中
ExifInterface exif = new ExifInterface(inPath);
String orientation = exif.getAttribute(ExifInterface.TAG_ORIENTATION);
LogUtil.print("原图:" + inPath);
// LogUtil.print("原图旋转角度:" + getPicRotate(inPath));
public static String compressImagePxAndQuality(String inPath, File outDir, String outFileName, int expectWidth, long expectSize) {
try {
if (outDir == null) {
return null;
}
if (!outDir.exists()) {
//创建目录
outDir.mkdirs();
}
if (!outDir.exists()) {
return null;
}
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inPreferredConfig = Config.RGB_565;
bmOptions.inSampleSize = getExpectInSampleSize(inPath, expectWidth);
Bitmap bitmap = BitmapFactory.decodeFile(inPath, bmOptions);
//读取原图的旋转角度,并写入到压缩图片中
ExifInterface exif = new ExifInterface(inPath);
String orientation = exif.getAttribute(ExifInterface.TAG_ORIENTATION);
LogUtil.print("原图:" + inPath);
LogUtil.print("原图旋转角度:" + getPicRotate(inPath));
LogUtil.print("宽高," + bitmap.getWidth() + "," + bitmap.getHeight());
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inPreferredConfig = Config.RGB_565;
bmOptions.inSampleSize = getExpectInSampleSize(inPath, expectWidth);
Bitmap bitmap = BitmapFactory.decodeFile(inPath, bmOptions);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int options = 100;
int minQuality = 20;
bitmap.compress(Bitmap.CompressFormat.JPEG, options, baos);//质量压缩方法把压缩后的数据存放到baos中 (100表示不压缩0表示压缩到最小)
Log.e("mouse_debug", "压缩前质量:" + baos.toByteArray().length);
while (baos.toByteArray().length > expectSize) {//循环判断如果压缩后图片是否大于指定大小,大于继续压缩
baos.reset();//重置baos即让下一次的写入覆盖之前的内容
options -= 5;//图片质量每次减少5
LogUtil.print("宽高," + bitmap.getWidth() + "," + bitmap.getHeight());
if (options <= minQuality) options = minQuality;//如果图片质量小于5为保证压缩后的图片质量图片最底压缩质量为5
LogUtil.print("压缩参数:" + options);
bitmap.compress(Bitmap.CompressFormat.JPEG, options, baos);//将压缩后的图片保存到baos中
if (options == minQuality) break;//如果图片的质量已降到最低则,不再进行压缩
}
if (!bitmap.isRecycled()) {
bitmap.recycle();//回收内存中的图片
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int options = 100;
int minQuality = 20;
bitmap.compress(Bitmap.CompressFormat.JPEG, options, baos);//质量压缩方法把压缩后的数据存放到baos中 (100表示不压缩0表示压缩到最小)
Log.e("mouse_debug", "压缩前质量:" + baos.toByteArray().length);
while (baos.toByteArray().length > expectSize) {//循环判断如果压缩后图片是否大于指定大小,大于继续压缩
baos.reset();//重置baos即让下一次的写入覆盖之前的内容
options -= 5;//图片质量每次减少5
File thumbnailFile = new File(outDir, outFileName);
if (thumbnailFile.exists()) {
thumbnailFile.delete();
}
thumbnailFile.createNewFile();
FileOutputStream fos = new FileOutputStream(thumbnailFile);//将压缩后的图片保存的本地上指定路径中
fos.write(baos.toByteArray());
fos.flush();
fos.close();
if (thumbnailFile.exists()) {
String compressPath = thumbnailFile.getPath();
exif = new ExifInterface(compressPath);
if (orientation != null) {
exif.setAttribute(ExifInterface.TAG_ORIENTATION, orientation);
exif.saveAttributes();
}
int width = exif.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, 0);
int height = exif.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, 0);
// LogUtil.print("压缩图旋转角度:" + getPicRotate(compressPath));
return new CompressResult(compressPath, width, height, "image/jpeg");
}
} catch (Exception ex) {
Log.e("mouse_debug", "压缩失败...");
ex.printStackTrace();
} catch (OutOfMemoryError error) {
LogUtil.print("压缩图片OOM了");
}
return null;
}
if (options <= minQuality) options = minQuality;//如果图片质量小于5为保证压缩后的图片质量图片最底压缩质量为5
LogUtil.print("压缩参数:" + options);
bitmap.compress(Bitmap.CompressFormat.JPEG, options, baos);//将压缩后的图片保存到baos中
if (options == minQuality) break;//如果图片的质量已降到最低则,不再进行压缩
}
if (!bitmap.isRecycled()) {
bitmap.recycle();//回收内存中的图片
}
File thumbnailFile = new File(outDir, outFileName);
if (thumbnailFile.exists()) {
thumbnailFile.delete();
}
thumbnailFile.createNewFile();
FileOutputStream fos = new FileOutputStream(thumbnailFile);//将压缩后的图片保存的本地上指定路径中
fos.write(baos.toByteArray());
fos.flush();
fos.close();
if (thumbnailFile.exists()) {
String compressPath = thumbnailFile.getPath();
if (orientation != null) {
exif = new ExifInterface(compressPath);
exif.setAttribute(ExifInterface.TAG_ORIENTATION, orientation);
exif.saveAttributes();
}
LogUtil.print("压缩图旋转角度:" + getPicRotate(compressPath));
return compressPath;
}
} catch (Exception ex) {
Log.e("mouse_debug", "压缩失败...");
ex.printStackTrace();
} catch (OutOfMemoryError error) {
LogUtil.print("压缩图片OOM了");
}
return null;
}
public static int getExpectInSampleSize(String path, int expectWidth) {
//先获取图片旋转角度如果是90或者270°则以高作为参考值否则以宽作为参考值
@@ -816,33 +814,5 @@ public class JXImageUtils {
return degree;
}
public static class CompressResult {
private String path;
private int width;
private int height;
private String format;
public CompressResult(String path, int width, int height, String format) {
this.path = path;
this.width = width;
this.height = height;
this.format = format;
}
public String getPath() {
return path;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public String getFormat() {
return format;
}
}
}

View File

@@ -5,9 +5,8 @@ import android.net.Uri;
import android.os.Environment;
import android.text.TextUtils;
import androidx.core.text.TextUtilsCompat;
import com.nnbc123.library.utils.FP;
import com.qiniu.android.utils.StringUtils;
import com.nnbc123.library.common.application.BaseApp;
import com.nnbc123.library.common.util.Logger;
@@ -878,4 +877,41 @@ public class FileHelper {
}
return content;
}
/**
* 文件转换成字符串
*
* @param filePath 文件路径
* @return 字符串内容
*/
public static String getTxtFileContent(String filePath) {
String content = "";
if (!StringUtils.isNullOrEmpty(filePath)) {
File file = new File(filePath);
if (file.isFile()) {
FileInputStream inputStream = null;
try {
inputStream = new FileInputStream(file);
String line;
StringBuilder sb = new StringBuilder();
BufferedReader buffReader = new BufferedReader(new InputStreamReader(inputStream));
while ((line = buffReader.readLine()) != null) {
sb.append(line).append("\n");
}
content = sb.toString();
} catch (Exception e) {
Logger.error(TAG, "getTxtFileContent read fail, e = " + e);
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (Exception ignore) {
}
}
}
}
}
return content;
}
}

View File

@@ -45,475 +45,451 @@ import java.io.File
* 路径 工具类 By https://github.com/Blankj/AndroidUtilCode -> PathUtils.java
* Created by Max on 2018/12/12.
*/
object PathUtils {
/**
* Return the path of /system.
*
* @return the path of /system
*/
val rootPath: String
get() = Environment.getRootDirectory().absolutePath
/**
* Return the path of /data.
*
* @return the path of /data
*/
val dataPath: String
get() = Environment.getDataDirectory().absolutePath
/**
* Return the path of /cache.
*
* @return the path of /cache
*/
val downloadCachePath: String
get() = Environment.getDownloadCacheDirectory().absolutePath
/**
* Return the path of /data/data/package.
*
* @return the path of /data/data/package
*/
fun getInternalAppDataPath(application: Application): String {
return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
application.applicationInfo.dataDir
} else application.dataDir.absolutePath
class PathUtils private constructor() {
init {
throw UnsupportedOperationException("u can't instantiate me...")
}
/**
* Return the path of /data/data/package/code_cache.
*
* @return the path of /data/data/package/code_cache
*/
fun getInternalAppCodeCacheDir(application: Application): String {
return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
application.applicationInfo.dataDir + "/code_cache"
} else application.codeCacheDir.absolutePath
}
companion object {
/**
* Return the path of /system.
*
* @return the path of /system
*/
val rootPath: String
get() = Environment.getRootDirectory().absolutePath
/**
* Return the path of /data/data/package/cache.
*
* @return the path of /data/data/package/cache
*/
fun getInternalAppCachePath(application: Application): String {
return application.cacheDir.absolutePath
}
/**
* Return the path of /data.
*
* @return the path of /data
*/
val dataPath: String
get() = Environment.getDataDirectory().absolutePath
/**
* Return the path of /data/data/package/databases.
*
* @return the path of /data/data/package/databases
*/
fun getInternalAppDbsPath(application: Application): String {
return application.applicationInfo.dataDir + "/databases"
}
/**
* Return the path of /cache.
*
* @return the path of /cache
*/
val downloadCachePath: String
get() = Environment.getDownloadCacheDirectory().absolutePath
/**
* Return the path of /data/data/package/databases/name.
*
* @param name The name of database.
* @return the path of /data/data/package/databases/name
*/
fun getInternalAppDbPath(application: Application, name: String?): String {
return application.getDatabasePath(name).absolutePath
}
/**
* Return the path of /data/data/package.
*
* @return the path of /data/data/package
*/
fun getInternalAppDataPath(application: Application): String {
return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
application.applicationInfo.dataDir
} else application.dataDir.absolutePath
}
/**
* Return the path of /data/data/package/files.
*
* @return the path of /data/data/package/files
*/
fun getInternalAppFilesPath(application: Application): String {
return application.filesDir.absolutePath
}
/**
* Return the path of /data/data/package/code_cache.
*
* @return the path of /data/data/package/code_cache
*/
fun getInternalAppCodeCacheDir(application: Application): String {
return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
application.applicationInfo.dataDir + "/code_cache"
} else application.codeCacheDir.absolutePath
}
/**
* Return the path of /data/data/package/shared_prefs.
*
* @return the path of /data/data/package/shared_prefs
*/
fun getInternalAppSpPath(application: Application): String {
return application.applicationInfo.dataDir + "shared_prefs"
}
/**
* Return the path of /data/data/package/cache.
*
* @return the path of /data/data/package/cache
*/
fun getInternalAppCachePath(application: Application): String {
return application.cacheDir.absolutePath
}
/**
* Return the path of /data/data/package/no_backup.
*
* @return the path of /data/data/package/no_backup
*/
fun getInternalAppNoBackupFilesPath(application: Application): String {
return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
application.applicationInfo.dataDir + "no_backup"
} else application.noBackupFilesDir.absolutePath
}
/**
* Return the path of /data/data/package/databases.
*
* @return the path of /data/data/package/databases
*/
fun getInternalAppDbsPath(application: Application): String {
return application.applicationInfo.dataDir + "/databases"
}
/**
* Return the path of /storage/emulated/0.
*
* @return the path of /storage/emulated/0
*/
val externalStoragePath: String?
get() = if (isExternalStorageDisable) null else Environment.getExternalStorageDirectory().absolutePath
/**
* Return the path of /data/data/package/databases/name.
*
* @param name The name of database.
* @return the path of /data/data/package/databases/name
*/
fun getInternalAppDbPath(application: Application, name: String?): String {
return application.getDatabasePath(name).absolutePath
}
/**
* Return the path of /storage/emulated/0/Music.
*
* @return the path of /storage/emulated/0/Music
*/
val externalMusicPath: String?
get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_MUSIC
).absolutePath
/**
* Return the path of /data/data/package/files.
*
* @return the path of /data/data/package/files
*/
fun getInternalAppFilesPath(application: Application): String {
return application.filesDir.absolutePath
}
/**
* Return the path of /storage/emulated/0/Podcasts.
*
* @return the path of /storage/emulated/0/Podcasts
*/
val externalPodcastsPath: String?
get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PODCASTS
).absolutePath
/**
* Return the path of /data/data/package/shared_prefs.
*
* @return the path of /data/data/package/shared_prefs
*/
fun getInternalAppSpPath(application: Application): String {
return application.applicationInfo.dataDir + "shared_prefs"
}
/**
* Return the path of /storage/emulated/0/Ringtones.
*
* @return the path of /storage/emulated/0/Ringtones
*/
val externalRingtonesPath: String?
get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_RINGTONES
).absolutePath
/**
* Return the path of /data/data/package/no_backup.
*
* @return the path of /data/data/package/no_backup
*/
fun getInternalAppNoBackupFilesPath(application: Application): String {
return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
application.applicationInfo.dataDir + "no_backup"
} else application.noBackupFilesDir.absolutePath
}
/**
* Return the path of /storage/emulated/0/Alarms.
*
* @return the path of /storage/emulated/0/Alarms
*/
val externalAlarmsPath: String?
get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_ALARMS
).absolutePath
/**
* Return the path of /storage/emulated/0.
*
* @return the path of /storage/emulated/0
*/
val externalStoragePath: String?
get() = if (isExternalStorageDisable) null else Environment.getExternalStorageDirectory().absolutePath
/**
* Return the path of /storage/emulated/0/Notifications.
*
* @return the path of /storage/emulated/0/Notifications
*/
val externalNotificationsPath: String?
get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_NOTIFICATIONS
).absolutePath
/**
* Return the path of /storage/emulated/0/Music.
*
* @return the path of /storage/emulated/0/Music
*/
val externalMusicPath: String?
get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_MUSIC
).absolutePath
/**
* Return the path of /storage/emulated/0/Pictures.
*
* @return the path of /storage/emulated/0/Pictures
*/
val externalPicturesPath: String?
get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES
).absolutePath
/**
* Return the path of /storage/emulated/0/Podcasts.
*
* @return the path of /storage/emulated/0/Podcasts
*/
val externalPodcastsPath: String?
get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PODCASTS
).absolutePath
/**
* Return the path of /storage/emulated/0/Movies.
*
* @return the path of /storage/emulated/0/Movies
*/
val externalMoviesPath: String?
get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_MOVIES
).absolutePath
/**
* Return the path of /storage/emulated/0/Ringtones.
*
* @return the path of /storage/emulated/0/Ringtones
*/
val externalRingtonesPath: String?
get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_RINGTONES
).absolutePath
/**
* Return the path of /storage/emulated/0/Download.
*
* @return the path of /storage/emulated/0/Download
*/
val externalDownloadsPath: String?
get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOWNLOADS
).absolutePath
/**
* Return the path of /storage/emulated/0/Alarms.
*
* @return the path of /storage/emulated/0/Alarms
*/
val externalAlarmsPath: String?
get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_ALARMS
).absolutePath
/**
* Return the path of /storage/emulated/0/DCIM.
*
* @return the path of /storage/emulated/0/DCIM
*/
val externalDcimPath: String?
get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DCIM
).absolutePath
/**
* Return the path of /storage/emulated/0/Notifications.
*
* @return the path of /storage/emulated/0/Notifications
*/
val externalNotificationsPath: String?
get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_NOTIFICATIONS
).absolutePath
/**
* Return the path of /storage/emulated/0/Documents.
*
* @return the path of /storage/emulated/0/Documents
*/
val externalDocumentsPath: String?
get() {
/**
* Return the path of /storage/emulated/0/Pictures.
*
* @return the path of /storage/emulated/0/Pictures
*/
val externalPicturesPath: String?
get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES
).absolutePath
/**
* Return the path of /storage/emulated/0/Movies.
*
* @return the path of /storage/emulated/0/Movies
*/
val externalMoviesPath: String?
get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_MOVIES
).absolutePath
/**
* Return the path of /storage/emulated/0/Download.
*
* @return the path of /storage/emulated/0/Download
*/
val externalDownloadsPath: String?
get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOWNLOADS
).absolutePath
/**
* Return the path of /storage/emulated/0/DCIM.
*
* @return the path of /storage/emulated/0/DCIM
*/
val externalDcimPath: String?
get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DCIM
).absolutePath
/**
* Return the path of /storage/emulated/0/Documents.
*
* @return the path of /storage/emulated/0/Documents
*/
val externalDocumentsPath: String?
get() {
if (isExternalStorageDisable) return null
return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
Environment.getExternalStorageDirectory().absolutePath + "/Documents"
} else Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOCUMENTS
).absolutePath
}
/**
* Return the path of /storage/emulated/0/Android/data/package.
*
* @return the path of /storage/emulated/0/Android/data/package
*/
fun getExternalAppDataPath(application: Application): String? {
return if (isExternalStorageDisable) null else application.externalCacheDir?.parentFile?.absolutePath
}
/**
* Return the path of /storage/emulated/0/Android/data/package/cache.
*
* @return the path of /storage/emulated/0/Android/data/package/cache
*/
fun getExternalAppCachePath(application: Application): String? {
return if (isExternalStorageDisable) null else application.externalCacheDir?.absolutePath
}
/**
* Return the path of /storage/emulated/0/Android/data/package/files.
*
* @return the path of /storage/emulated/0/Android/data/package/files
*/
fun getExternalAppFilesPath(application: Application): String? {
return if (isExternalStorageDisable) null else application.getExternalFilesDir(null)?.absolutePath
}
/**
* Return the path of /storage/emulated/0/Android/data/package/files/Music.
*
* @return the path of /storage/emulated/0/Android/data/package/files/Music
*/
fun getExternalAppMusicPath(application: Application): String? {
return if (isExternalStorageDisable) null else application.getExternalFilesDir(
Environment.DIRECTORY_MUSIC
)?.absolutePath
}
/**
* Return the path of /storage/emulated/0/Android/data/package/files/Podcasts.
*
* @return the path of /storage/emulated/0/Android/data/package/files/Podcasts
*/
fun getExternalAppPodcastsPath(application: Application): String? {
return if (isExternalStorageDisable) null else application.getExternalFilesDir(
Environment.DIRECTORY_PODCASTS
)?.absolutePath
}
/**
* Return the path of /storage/emulated/0/Android/data/package/files/Ringtones.
*
* @return the path of /storage/emulated/0/Android/data/package/files/Ringtones
*/
fun getExternalAppRingtonesPath(application: Application): String? {
return if (isExternalStorageDisable) null else application.getExternalFilesDir(
Environment.DIRECTORY_RINGTONES
)?.absolutePath
}
/**
* Return the path of /storage/emulated/0/Android/data/package/files/Alarms.
*
* @return the path of /storage/emulated/0/Android/data/package/files/Alarms
*/
fun getExternalAppAlarmsPath(application: Application): String? {
return if (isExternalStorageDisable) null else application.getExternalFilesDir(
Environment.DIRECTORY_ALARMS
)?.absolutePath
}
/**
* Return the path of /storage/emulated/0/Android/data/package/files/Notifications.
*
* @return the path of /storage/emulated/0/Android/data/package/files/Notifications
*/
fun getExternalAppNotificationsPath(application: Application): String? {
return if (isExternalStorageDisable) null else application.getExternalFilesDir(
Environment.DIRECTORY_NOTIFICATIONS
)?.absolutePath
}
/**
* Return the path of /storage/emulated/0/Android/data/package/files/Pictures.
*
* @return path of /storage/emulated/0/Android/data/package/files/Pictures
*/
fun getExternalAppPicturesPath(application: Application): String? {
return if (isExternalStorageDisable) null else application.getExternalFilesDir(
Environment.DIRECTORY_PICTURES
)?.absolutePath
}
/**
* Return the path of /storage/emulated/0/Android/data/package/files/Movies.
*
* @return the path of /storage/emulated/0/Android/data/package/files/Movies
*/
fun getExternalAppMoviesPath(application: Application): String? {
return if (isExternalStorageDisable) null else application.getExternalFilesDir(
Environment.DIRECTORY_MOVIES
)?.absolutePath
}
/**
* Return the path of /storage/emulated/0/Android/data/package/files/Download.
*
* @return the path of /storage/emulated/0/Android/data/package/files/Download
*/
fun getExternalAppDownloadPath(application: Application): String? {
return if (isExternalStorageDisable) null else application.getExternalFilesDir(
Environment.DIRECTORY_DOWNLOADS
)?.absolutePath
}
/**
* Return the path of /storage/emulated/0/Android/data/package/files/DCIM.
*
* @return the path of /storage/emulated/0/Android/data/package/files/DCIM
*/
fun getExternalAppDcimPath(application: Application): String? {
return if (isExternalStorageDisable) null else application.getExternalFilesDir(
Environment.DIRECTORY_DCIM
)?.absolutePath
}
/**
* Return the path of /storage/emulated/0/Android/data/package/files/Documents.
*
* @return the path of /storage/emulated/0/Android/data/package/files/Documents
*/
fun getExternalAppDocumentsPath(application: Application): String? {
if (isExternalStorageDisable) return null
return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
Environment.getExternalStorageDirectory().absolutePath + "/Documents"
} else Environment.getExternalStoragePublicDirectory(
application.getExternalFilesDir(null)?.absolutePath + "/Documents"
} else application.getExternalFilesDir(
Environment.DIRECTORY_DOCUMENTS
).absolutePath
)?.absolutePath
}
/**
* Return the path of /storage/emulated/0/Android/data/package.
*
* @return the path of /storage/emulated/0/Android/data/package
*/
fun getExternalAppDataPath(application: Application): String? {
return if (isExternalStorageDisable) null else application.externalCacheDir?.parentFile?.absolutePath
}
/**
* Return the path of /storage/emulated/0/Android/data/package/cache.
*
* @return the path of /storage/emulated/0/Android/data/package/cache
*/
fun getExternalAppCachePath(application: Application): String? {
return if (isExternalStorageDisable) null else application.externalCacheDir?.absolutePath
}
/**
* Return the path of /storage/emulated/0/Android/data/package/files.
*
* @return the path of /storage/emulated/0/Android/data/package/files
*/
fun getExternalAppFilesPath(application: Application): String? {
return if (isExternalStorageDisable) null else application.getExternalFilesDir(null)?.absolutePath
}
/**
* Return the path of /storage/emulated/0/Android/data/package/files/Music.
*
* @return the path of /storage/emulated/0/Android/data/package/files/Music
*/
fun getExternalAppMusicPath(application: Application): String? {
return if (isExternalStorageDisable) null else application.getExternalFilesDir(
Environment.DIRECTORY_MUSIC
)?.absolutePath
}
/**
* Return the path of /storage/emulated/0/Android/data/package/files/Podcasts.
*
* @return the path of /storage/emulated/0/Android/data/package/files/Podcasts
*/
fun getExternalAppPodcastsPath(application: Application): String? {
return if (isExternalStorageDisable) null else application.getExternalFilesDir(
Environment.DIRECTORY_PODCASTS
)?.absolutePath
}
/**
* Return the path of /storage/emulated/0/Android/data/package/files/Ringtones.
*
* @return the path of /storage/emulated/0/Android/data/package/files/Ringtones
*/
fun getExternalAppRingtonesPath(application: Application): String? {
return if (isExternalStorageDisable) null else application.getExternalFilesDir(
Environment.DIRECTORY_RINGTONES
)?.absolutePath
}
/**
* Return the path of /storage/emulated/0/Android/data/package/files/Alarms.
*
* @return the path of /storage/emulated/0/Android/data/package/files/Alarms
*/
fun getExternalAppAlarmsPath(application: Application): String? {
return if (isExternalStorageDisable) null else application.getExternalFilesDir(
Environment.DIRECTORY_ALARMS
)?.absolutePath
}
/**
* Return the path of /storage/emulated/0/Android/data/package/files/Notifications.
*
* @return the path of /storage/emulated/0/Android/data/package/files/Notifications
*/
fun getExternalAppNotificationsPath(application: Application): String? {
return if (isExternalStorageDisable) null else application.getExternalFilesDir(
Environment.DIRECTORY_NOTIFICATIONS
)?.absolutePath
}
/**
* Return the path of /storage/emulated/0/Android/data/package/files/Pictures.
*
* @return path of /storage/emulated/0/Android/data/package/files/Pictures
*/
fun getExternalAppPicturesPath(application: Application): String? {
return if (isExternalStorageDisable) null else application.getExternalFilesDir(
Environment.DIRECTORY_PICTURES
)?.absolutePath
}
/**
* Return the path of /storage/emulated/0/Android/data/package/files/Movies.
*
* @return the path of /storage/emulated/0/Android/data/package/files/Movies
*/
fun getExternalAppMoviesPath(application: Application): String? {
return if (isExternalStorageDisable) null else application.getExternalFilesDir(
Environment.DIRECTORY_MOVIES
)?.absolutePath
}
/**
* Return the path of /storage/emulated/0/Android/data/package/files/Download.
*
* @return the path of /storage/emulated/0/Android/data/package/files/Download
*/
fun getExternalAppDownloadPath(application: Application): String? {
return if (isExternalStorageDisable) null else application.getExternalFilesDir(
Environment.DIRECTORY_DOWNLOADS
)?.absolutePath
}
/**
* Return the path of /storage/emulated/0/Android/data/package/files/DCIM.
*
* @return the path of /storage/emulated/0/Android/data/package/files/DCIM
*/
fun getExternalAppDcimPath(application: Application): String? {
return if (isExternalStorageDisable) null else application.getExternalFilesDir(
Environment.DIRECTORY_DCIM
)?.absolutePath
}
/**
* Return the path of /storage/emulated/0/Android/data/package/files/Documents.
*
* @return the path of /storage/emulated/0/Android/data/package/files/Documents
*/
fun getExternalAppDocumentsPath(application: Application): String? {
if (isExternalStorageDisable) return null
return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
application.getExternalFilesDir(null)?.absolutePath + "/Documents"
} else application.getExternalFilesDir(
Environment.DIRECTORY_DOCUMENTS
)?.absolutePath
}
/**
* Return the path of /storage/emulated/0/Android/obb/package.
*
* @return the path of /storage/emulated/0/Android/obb/package
*/
fun getExternalAppObbPath(application: Application): String? {
return if (isExternalStorageDisable) null else application.obbDir.absolutePath
}
private val isExternalStorageDisable: Boolean
private get() = Environment.MEDIA_MOUNTED != Environment.getExternalStorageState()
/**
* 判断sub是否在parent之下的文件或子文件夹<br></br>
*
* @param parent
* @param sub
* @return
*/
fun isSub(parent: File, sub: File): Boolean {
return try {
sub.absolutePath.startsWith(parent.absolutePath)
} catch (e: Exception) {
false
/**
* Return the path of /storage/emulated/0/Android/obb/package.
*
* @return the path of /storage/emulated/0/Android/obb/package
*/
fun getExternalAppObbPath(application: Application): String? {
return if (isExternalStorageDisable) null else application.obbDir.absolutePath
}
}
/**
* 获取子绝对路径与父绝对路径的相对路径
*
* @param parentPath
* @param subPath
* @return
*/
fun getRelativePath(parentPath: String?, subPath: String?): String? {
return try {
if (parentPath == null || subPath == null) {
return null
private val isExternalStorageDisable: Boolean
private get() = Environment.MEDIA_MOUNTED != Environment.getExternalStorageState()
/**
* 判断sub是否在parent之下的文件或子文件夹<br></br>
*
* @param parent
* @param sub
* @return
*/
fun isSub(parent: File, sub: File): Boolean {
return try {
sub.absolutePath.startsWith(parent.absolutePath)
} catch (e: Exception) {
false
}
if (subPath.startsWith(parentPath)) {
subPath.substring(parentPath.length)
} else {
}
/**
* 获取子绝对路径与父绝对路径的相对路径
*
* @param parentPath
* @param subPath
* @return
*/
fun getRelativePath(parentPath: String?, subPath: String?): String? {
return try {
if (parentPath == null || subPath == null) {
return null
}
if (subPath.startsWith(parentPath)) {
subPath.substring(parentPath.length)
} else {
null
}
} catch (e: Exception) {
null
}
} catch (e: Exception) {
null
}
}
/**
* 拼接两个路径
*
* @param pathA 路径A
* @param pathB 路径B
* @return 拼接后的路径
*/
fun plusPath(pathA: String?, pathB: String?): String? {
if (pathA == null) {
return pathB
}
if (pathB == null) {
return pathA
}
return plusPathNotNull(pathA, pathB)
}
/**
* 拼接两个路径
*
* @param pathA 路径A
* @param pathB 路径B
* @return 拼接后的路径
*/
fun plusPathNotNull(pathA: String, pathB: String): String {
val pathAEndSeparator = pathA.endsWith(File.separator)
val pathBStartSeparator = pathB.startsWith(File.separator)
return if (pathAEndSeparator && pathBStartSeparator) {
pathA + pathB.substring(1)
} else if (pathAEndSeparator || pathBStartSeparator) {
pathA + pathB
} else {
pathA + File.separator + pathB
}
}
/**
* 获取后缀名称
* @param path 路径
* @return 后缀格式 .mp4 .gif 等
*/
fun getSuffixType(path: String): String? {
if (path.isEmpty()) {
return null
}
val dotIndex = path.indexOfLast {
'.' == it
}
val separatorIndex = path.indexOfLast {
'/' == it
}
if (dotIndex >= 0 && dotIndex > separatorIndex) {
val suffix = path.substring(dotIndex)
val askIndex = suffix.indexOfLast {
'?' == it
/**
* 拼接两个路径
*
* @param pathA 路径A
* @param pathB 路径B
* @return 拼接后的路径
*/
fun plusPath(pathA: String?, pathB: String?): String? {
if (pathA == null) {
return pathB
}
return if (askIndex >= 0) {
suffix.substring(0, askIndex)
if (pathB == null) {
return pathA
}
return plusPathNotNull(pathA, pathB)
}
/**
* 拼接两个路径
*
* @param pathA 路径A
* @param pathB 路径B
* @return 拼接后的路径
*/
fun plusPathNotNull(pathA: String, pathB: String): String {
val pathAEndSeparator = pathA.endsWith(File.separator)
val pathBStartSeparator = pathB.startsWith(File.separator)
return if (pathAEndSeparator && pathBStartSeparator) {
pathA + pathB.substring(1)
} else if (pathAEndSeparator || pathBStartSeparator) {
pathA + pathB
} else {
suffix
pathA + File.separator + pathB
}
}
return null
}
}

View File

@@ -0,0 +1,25 @@
package com.chuhai.utils
import android.os.SystemClock
/**
* Created by Max on 2023/10/24 15:11
* Desc:服务器时间
*/
object ServiceTime {
// 服务器时间与系统开机时间的时差
private var serviceTimeDiff: Long? = null
val time
get() = if (serviceTimeDiff == null) System.currentTimeMillis()
else SystemClock.elapsedRealtime() + serviceTimeDiff!!
/**
* 刷新服务器时间
*/
fun refreshServiceTime(time: Long) {
//serviceTimeDiff = 服务器时间 - 此刻系统启动时间
serviceTimeDiff = time - SystemClock.elapsedRealtime()
}
}