diff --git a/app/build.gradle b/app/build.gradle
index 564f97e6..3353b2eb 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -89,12 +89,11 @@ dependencies {
//兼容 Glide
implementation "com.android.support:support-annotations:28.0.0"
- implementation "androidx.work:work-runtime:2.5.0"
kapt "com.android.support:support-annotations:28.0.0"
//AndroidX
implementation "androidx.appcompat:appcompat:1.3.1"
- implementation "androidx.core:core-ktx:1.6.0"
+ implementation "androidx.core:core-ktx:1.7.0"
implementation "androidx.annotation:annotation:1.3.0"
implementation "androidx.constraintlayout:constraintlayout:2.1.1"
implementation "androidx.preference:preference-ktx:1.1.1"
@@ -138,7 +137,7 @@ dependencies {
implementation "com.scwang.smart:refresh-header-material:2.0.1"
implementation 'com.github.lwj1994:Matisse:0.5.3-20220115'
- implementation "com.yanzhenjie:permission:2.0.3"
+ implementation 'com.github.getActivity:XXPermissions:13.6'
implementation "com.gyf.immersionbar:immersionbar:3.0.0"
implementation "com.github.yalantis:ucrop:2.2.7"
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index c7111725..eb400685 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -92,6 +92,9 @@
+
{
- try {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
- String relativePath = Environment.DIRECTORY_PICTURES + File.separator + "Tieba Lite" + File.separator + "shareTemp";
- String where = MediaStore.Images.Media.RELATIVE_PATH + " like \"" + relativePath + "%" + "\"";
- int i = getContentResolver().deleteAll(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, where, null);
- } else {
- if (AndPermission.hasPermissions(this, Permission.Group.STORAGE)) {
- File shareTemp = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getAbsoluteFile(), "Tieba Lite" + File.separator + "shareTemp");
- if (shareTemp.exists() && shareTemp.deleteAll()) {
- FileUtil.deleteAllFiles(shareTemp);
- }
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }, 100);
- */
}
override fun recreate() {
diff --git a/app/src/main/java/com/huanchengfly/tieba/post/activities/ReplyActivity.kt b/app/src/main/java/com/huanchengfly/tieba/post/activities/ReplyActivity.kt
index 367538b2..8ed37bb2 100644
--- a/app/src/main/java/com/huanchengfly/tieba/post/activities/ReplyActivity.kt
+++ b/app/src/main/java/com/huanchengfly/tieba/post/activities/ReplyActivity.kt
@@ -19,6 +19,7 @@ import android.widget.FrameLayout
import android.widget.GridView
import android.widget.RelativeLayout
import android.widget.Toast
+import androidx.activity.result.ActivityResultLauncher
import androidx.appcompat.widget.Toolbar
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
@@ -49,10 +50,10 @@ import com.huanchengfly.tieba.post.utils.*
import com.huanchengfly.tieba.post.widgets.edittext.widget.UndoableEditText
import com.huanchengfly.tieba.post.widgets.theme.TintConstraintLayout
import com.huanchengfly.tieba.post.widgets.theme.TintImageView
-import com.zhihu.matisse.Matisse
import org.litepal.LitePal.where
-class ReplyActivity : BaseActivity(), View.OnClickListener {
+class ReplyActivity : BaseActivity(), View.OnClickListener,
+ InsertPhotoAdapter.MatisseLauncherProvider {
@BindView(R.id.activity_reply_edit_text)
lateinit var editText: UndoableEditText
@@ -84,7 +85,9 @@ class ReplyActivity : BaseActivity(), View.OnClickListener {
lateinit var toolbar: Toolbar
private var replyInfoBean: ReplyInfoBean? = null
private var loadingDialog: LoadingDialog? = null
- private var insertPhotoAdapter: InsertPhotoAdapter? = null
+ private val insertPhotoAdapter: InsertPhotoAdapter by lazy {
+ InsertPhotoAdapter(this)
+ }
private var sendItem: MenuItem? = null
private var replySuccess = false
private var content: String? = null
@@ -92,6 +95,17 @@ class ReplyActivity : BaseActivity(), View.OnClickListener {
override val isNeedImmersionBar: Boolean
get() = false
+ @JvmField
+ val matisseLauncher = registerForActivityResult(InsertPhotoAdapter.MatisseResultContract()) {
+ val photoInfoBeans = insertPhotoAdapter.getFileList().toMutableList()
+ for (uri in it) {
+ photoInfoBeans.add(PhotoInfoBean(this, uri))
+ }
+ insertPhotoAdapter.setFileList(photoInfoBeans)
+ }
+
+ override fun getMatisseLauncher(): ActivityResultLauncher = matisseLauncher
+
override fun getLayoutId(): Int {
return R.layout.activity_reply
}
@@ -217,11 +231,11 @@ class ReplyActivity : BaseActivity(), View.OnClickListener {
): Int {
var dragFlags = 0
var swiped = 0
- if (viewHolder.adapterPosition < insertPhotoAdapter!!.itemCount - 1) {
+ if (viewHolder.adapterPosition < insertPhotoAdapter.itemCount - 1) {
swiped = ItemTouchHelper.UP or ItemTouchHelper.DOWN
- if (viewHolder.adapterPosition < insertPhotoAdapter!!.itemCount - 2 && viewHolder.adapterPosition > 0) {
+ if (viewHolder.adapterPosition < insertPhotoAdapter.itemCount - 2 && viewHolder.adapterPosition > 0) {
dragFlags = ItemTouchHelper.RIGHT or ItemTouchHelper.LEFT
- } else if (viewHolder.adapterPosition == insertPhotoAdapter!!.itemCount - 2) {
+ } else if (viewHolder.adapterPosition == insertPhotoAdapter.itemCount - 2) {
dragFlags = ItemTouchHelper.LEFT
} else if (viewHolder.adapterPosition == 0) {
dragFlags = ItemTouchHelper.RIGHT
@@ -237,17 +251,17 @@ class ReplyActivity : BaseActivity(), View.OnClickListener {
): Boolean {
val oldPosition = viewHolder.adapterPosition
val newPosition = target.adapterPosition
- if (newPosition < insertPhotoAdapter!!.itemCount - 1) {
+ if (newPosition < insertPhotoAdapter.itemCount - 1) {
if (oldPosition < newPosition) {
for (i in oldPosition until newPosition) {
- insertPhotoAdapter!!.swap(i, i + 1)
+ insertPhotoAdapter.swap(i, i + 1)
}
} else {
for (i in oldPosition downTo newPosition + 1) {
- insertPhotoAdapter!!.swap(i, i - 1)
+ insertPhotoAdapter.swap(i, i - 1)
}
}
- insertPhotoAdapter!!.notifyItemMoved(oldPosition, newPosition)
+ insertPhotoAdapter.notifyItemMoved(oldPosition, newPosition)
return true
}
return false
@@ -255,7 +269,7 @@ class ReplyActivity : BaseActivity(), View.OnClickListener {
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
val position = viewHolder.adapterPosition
- insertPhotoAdapter!!.remove(position)
+ insertPhotoAdapter.remove(position)
}
})
mItemTouchHelper.attachToRecyclerView(insertView)
@@ -267,7 +281,6 @@ class ReplyActivity : BaseActivity(), View.OnClickListener {
} else {
insertImageBtn.visibility = View.INVISIBLE
}
- insertPhotoAdapter = InsertPhotoAdapter(this)
insertView.adapter = insertPhotoAdapter
val layoutManager = LinearLayoutManager(this)
layoutManager.orientation = RecyclerView.HORIZONTAL
@@ -315,7 +328,7 @@ class ReplyActivity : BaseActivity(), View.OnClickListener {
private fun canSend(): Boolean {
return editText.text.toString().isNotEmpty() ||
- insertPhotoAdapter!!.fileList.size > 0
+ insertPhotoAdapter.getFileList().isNotEmpty()
}
private fun needUpload(): Boolean {
@@ -323,7 +336,7 @@ class ReplyActivity : BaseActivity(), View.OnClickListener {
if (replyInfoBean!!.isSubFloor) {
return false
}
- for (photoInfoBean in insertPhotoAdapter!!.fileList) {
+ for (photoInfoBean in insertPhotoAdapter.getFileList()) {
if (photoInfoBean.webUploadPicBean == null) {
needUpload = true
break
@@ -352,12 +365,13 @@ class ReplyActivity : BaseActivity(), View.OnClickListener {
val builder = StringBuilder()
if (hasPhoto()) {
if (!needUpload()) {
- for (photoInfoBean in insertPhotoAdapter!!.fileList) {
+ for (photoInfoBean in insertPhotoAdapter.getFileList()) {
if (photoInfoBean.webUploadPicBean != null) {
builder.append(photoInfoBean.webUploadPicBean.imageInfo)
- if (insertPhotoAdapter!!.fileList.size - 1 > insertPhotoAdapter!!.fileList.indexOf(
- photoInfoBean
- )
+ if (insertPhotoAdapter.getFileList().size - 1 > insertPhotoAdapter.getFileList()
+ .indexOf(
+ photoInfoBean
+ )
) {
builder.append("|")
}
@@ -367,7 +381,7 @@ class ReplyActivity : BaseActivity(), View.OnClickListener {
return
}
UploadHelper.with(this)
- .setFileList(insertPhotoAdapter!!.fileList)
+ .setFileList(insertPhotoAdapter.getFileList())
.setCallback(object : UploadCallback {
override fun onSuccess(photoInfoBeans: List) {
for (photoInfoBean in photoInfoBeans) {
@@ -401,7 +415,8 @@ class ReplyActivity : BaseActivity(), View.OnClickListener {
}
private fun hasPhoto(): Boolean {
- return insertPhotoAdapter!!.fileList != null && insertPhotoAdapter!!.fileList.size > 0
+ return insertPhotoAdapter.getFileList()
+ .isNotEmpty()
}
private fun setEnabled(imageButton: TintImageView, enable: Boolean) {
@@ -570,20 +585,6 @@ class ReplyActivity : BaseActivity(), View.OnClickListener {
})
}
- override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
- super.onActivityResult(requestCode, resultCode, data)
- if (requestCode == REQUEST_CODE_CHOOSE && resultCode == RESULT_OK) {
- val uriList = Matisse.obtainResult(data)
- val photoInfoBeans = insertPhotoAdapter!!.fileList
- for (uri in uriList) {
- val infoBean = PhotoInfoBean(this, uri)
- photoInfoBeans.add(infoBean)
- }
- insertPhotoAdapter!!.fileList = photoInfoBeans
- sendItem!!.isEnabled = true
- }
- }
-
override fun onClick(v: View) {
when (v.id) {
R.id.activity_reply_root -> finish()
diff --git a/app/src/main/java/com/huanchengfly/tieba/post/activities/TranslucentThemeActivity.kt b/app/src/main/java/com/huanchengfly/tieba/post/activities/TranslucentThemeActivity.kt
index 6101c8d3..9d9be18f 100644
--- a/app/src/main/java/com/huanchengfly/tieba/post/activities/TranslucentThemeActivity.kt
+++ b/app/src/main/java/com/huanchengfly/tieba/post/activities/TranslucentThemeActivity.kt
@@ -10,7 +10,10 @@ import android.graphics.drawable.Drawable
import android.net.Uri
import android.os.Build
import android.os.Bundle
-import android.view.*
+import android.view.MenuItem
+import android.view.MotionEvent
+import android.view.View
+import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.SeekBar
import android.widget.SeekBar.OnSeekBarChangeListener
@@ -47,8 +50,6 @@ import com.huanchengfly.tieba.post.widgets.theme.TintMaterialButton
import com.jrummyapps.android.colorpicker.ColorPickerDialog
import com.jrummyapps.android.colorpicker.ColorPickerDialogListener
import com.yalantis.ucrop.UCrop
-import com.yanzhenjie.permission.Action
-import com.yanzhenjie.permission.runtime.Permission
import com.zhihu.matisse.Matisse
import com.zhihu.matisse.MimeType
import com.zhihu.matisse.engine.impl.GlideEngine
@@ -460,7 +461,7 @@ class TranslucentThemeActivity : BaseActivity(), View.OnClickListener, OnSeekBar
.choose(MimeType.ofImage())
.theme(if (ThemeUtil.isNightMode(this)) R.style.Matisse_Dracula else R.style.Matisse_Zhihu)
.imageEngine(GlideEngine())
- .forResult(REQUEST_CODE_CHOOSE)
+ .forResult(REQUEST_CODE_CHOOSE)
}
R.id.custom_color -> {
val primaryColorPicker = ColorPickerDialog.newBuilder()
@@ -485,14 +486,23 @@ class TranslucentThemeActivity : BaseActivity(), View.OnClickListener, OnSeekBar
}
}
- private fun askPermission(granted: Action>) {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
- PermissionUtil.askPermission(this, granted, R.string.toast_no_permission_insert_photo,
- PermissionUtil.Permission(Permission.Group.STORAGE, getString(R.string.tip_permission_storage)))
- } else {
- PermissionUtil.askPermission(this, granted, R.string.toast_no_permission_insert_photo,
- PermissionUtil.Permission(Permission.READ_EXTERNAL_STORAGE, getString(R.string.tip_permission_storage)))
- }
+ private fun askPermission(granted: () -> Unit) {
+ PermissionUtils.askPermission(
+ this,
+ PermissionUtils.Permission(
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
+ listOf(
+ PermissionUtils.READ_EXTERNAL_STORAGE,
+ PermissionUtils.WRITE_EXTERNAL_STORAGE
+ )
+ } else {
+ listOf(PermissionUtils.READ_EXTERNAL_STORAGE)
+ },
+ getString(R.string.tip_permission_storage)
+ ),
+ R.string.toast_no_permission_insert_photo,
+ granted
+ )
}
interface SavePicCallback {
diff --git a/app/src/main/java/com/huanchengfly/tieba/post/adapters/InsertPhotoAdapter.kt b/app/src/main/java/com/huanchengfly/tieba/post/adapters/InsertPhotoAdapter.kt
index 4adf96b6..a1b4fb6c 100644
--- a/app/src/main/java/com/huanchengfly/tieba/post/adapters/InsertPhotoAdapter.kt
+++ b/app/src/main/java/com/huanchengfly/tieba/post/adapters/InsertPhotoAdapter.kt
@@ -1,128 +1,143 @@
-package com.huanchengfly.tieba.post.adapters;
+package com.huanchengfly.tieba.post.adapters
-import android.app.Activity;
-import android.content.Context;
-import android.os.Build;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.Toast;
+import android.app.Activity.RESULT_OK
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import android.os.Build
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.Toast
+import androidx.activity.result.ActivityResultLauncher
+import androidx.activity.result.contract.ActivityResultContract
+import androidx.appcompat.app.AppCompatActivity
+import androidx.recyclerview.widget.RecyclerView
+import com.bumptech.glide.Glide
+import com.bumptech.glide.request.RequestOptions
+import com.huanchengfly.tieba.post.R
+import com.huanchengfly.tieba.post.components.MyViewHolder
+import com.huanchengfly.tieba.post.components.transformations.RadiusTransformation
+import com.huanchengfly.tieba.post.models.PhotoInfoBean
+import com.huanchengfly.tieba.post.utils.PermissionUtils
+import com.huanchengfly.tieba.post.utils.ThemeUtil
+import com.zhihu.matisse.Matisse
+import com.zhihu.matisse.MimeType
+import com.zhihu.matisse.engine.impl.GlideEngine
+import java.util.*
-import androidx.annotation.NonNull;
-import androidx.recyclerview.widget.RecyclerView;
+class InsertPhotoAdapter(private val mContext: Context) : RecyclerView.Adapter() {
+ private var fileList: MutableList = ArrayList()
-import com.bumptech.glide.Glide;
-import com.bumptech.glide.request.RequestOptions;
-import com.huanchengfly.tieba.post.R;
-import com.huanchengfly.tieba.post.activities.ReplyActivity;
-import com.huanchengfly.tieba.post.components.MyViewHolder;
-import com.huanchengfly.tieba.post.components.transformations.RadiusTransformation;
-import com.huanchengfly.tieba.post.models.PhotoInfoBean;
-import com.huanchengfly.tieba.post.utils.PermissionUtil;
-import com.huanchengfly.tieba.post.utils.ThemeUtil;
-import com.yanzhenjie.permission.Action;
-import com.yanzhenjie.permission.runtime.Permission;
-import com.zhihu.matisse.Matisse;
-import com.zhihu.matisse.MimeType;
-import com.zhihu.matisse.engine.impl.GlideEngine;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-public class InsertPhotoAdapter extends RecyclerView.Adapter {
- public static final int TYPE_IMAGE = 0;
- public static final int TYPE_INSERT = 1;
- private static final String TAG = "InsertPhotoAdapter";
- private Context mContext;
- private List fileList;
-
- public InsertPhotoAdapter(Context context) {
- super();
- this.mContext = context;
- this.fileList = new ArrayList<>();
+ fun remove(position: Int) {
+ fileList.removeAt(position)
+ notifyItemRemoved(position)
}
- public void remove(int position) {
- fileList.remove(position);
- notifyItemRemoved(position);
+ fun swap(oldPosition: Int, newPosition: Int) {
+ Collections.swap(fileList, oldPosition, newPosition)
}
- public void swap(int oldPosition, int newPosition) {
- Collections.swap(fileList, oldPosition, newPosition);
+ fun getFileList(): List {
+ return fileList
}
- public List getFileList() {
- return fileList;
+ fun setFileList(fileList: MutableList) {
+ this.fileList = fileList
+ notifyDataSetChanged()
}
- public void setFileList(List fileList) {
- this.fileList = fileList;
- notifyDataSetChanged();
+ override fun getItemViewType(position: Int): Int {
+ return if (position >= fileList.size) TYPE_INSERT else TYPE_IMAGE
}
- @Override
- public int getItemViewType(int position) {
- if (position >= fileList.size()) return TYPE_INSERT;
- return TYPE_IMAGE;
- }
-
- @NonNull
- @Override
- public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
if (viewType == TYPE_INSERT) {
- View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_insert_more, parent, false);
- return new MyViewHolder(view);
+ val view: View = LayoutInflater.from(parent.context)
+ .inflate(R.layout.item_insert_more, parent, false)
+ return MyViewHolder(view)
}
- View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_insert_image, parent, false);
- return new MyViewHolder(view);
+ val view: View = LayoutInflater.from(parent.context)
+ .inflate(R.layout.item_insert_image, parent, false)
+ return MyViewHolder(view)
}
- @Override
- public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
- if (holder.getItemViewType() == TYPE_IMAGE) {
- PhotoInfoBean photoInfoBean = fileList.get(position);
- ImageView imageView = holder.getView(R.id.image_preview);
+ override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
+ if (holder.itemViewType == TYPE_IMAGE) {
+ val photoInfoBean: PhotoInfoBean = fileList[position]
+ val imageView: ImageView = holder.getView(R.id.image_preview)
Glide.with(mContext)
- .load(photoInfoBean.getFileUri())
- .apply(RequestOptions.bitmapTransform(new RadiusTransformation()))
- .into(imageView);
- } else if (holder.getItemViewType() == TYPE_INSERT) {
- if (fileList.size() < 10) {
- holder.setItemOnClickListener((View view) -> {
- if (mContext instanceof Activity) {
- askPermission(data -> Matisse.from((Activity) mContext)
+ .load(photoInfoBean.fileUri)
+ .apply(RequestOptions.bitmapTransform(RadiusTransformation()))
+ .into(imageView)
+ } else if (holder.itemViewType == TYPE_INSERT) {
+ if (fileList.size < 10) {
+ holder.setItemOnClickListener {
+ if (mContext is AppCompatActivity && mContext is MatisseLauncherProvider) {
+ askPermission {
+ Matisse.from(mContext)
.choose(MimeType.ofImage())
.countable(true)
- .maxSelectable(10 - fileList.size())
- .theme(ThemeUtil.isNightMode(mContext) ? R.style.Matisse_Dracula : R.style.Matisse_Zhihu)
- .imageEngine(new GlideEngine())
- .forResult(ReplyActivity.REQUEST_CODE_CHOOSE));
+ .maxSelectable(10 - fileList.size)
+ .theme(if (ThemeUtil.isNightMode(mContext)) R.style.Matisse_Dracula else R.style.Matisse_Zhihu)
+ .imageEngine(GlideEngine())
+ .forResult(mContext.getMatisseLauncher())
+ }
}
- });
+ }
} else {
- Toast.makeText(mContext, R.string.toast_max_selectable, Toast.LENGTH_SHORT).show();
+ Toast.makeText(mContext, R.string.toast_max_selectable, Toast.LENGTH_SHORT).show()
}
}
}
- private void askPermission(Action> granted) {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
- PermissionUtil.askPermission(mContext, granted, R.string.toast_no_permission_insert_photo,
- new PermissionUtil.Permission(Permission.Group.STORAGE, mContext.getString(R.string.tip_permission_storage)));
- } else {
- PermissionUtil.askPermission(mContext, granted, R.string.toast_no_permission_insert_photo,
- new PermissionUtil.Permission(Permission.READ_EXTERNAL_STORAGE, mContext.getString(R.string.tip_permission_storage)));
+ private fun askPermission(granted: () -> Unit) {
+ PermissionUtils.askPermission(
+ mContext,
+ PermissionUtils.Permission(
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
+ listOf(
+ PermissionUtils.READ_EXTERNAL_STORAGE,
+ PermissionUtils.WRITE_EXTERNAL_STORAGE
+ )
+ } else {
+ listOf(PermissionUtils.READ_EXTERNAL_STORAGE)
+ },
+ mContext.getString(R.string.tip_permission_storage)
+ ),
+ R.string.toast_no_permission_insert_photo,
+ granted
+ )
+ }
+
+ override fun getItemCount(): Int {
+ return fileList.size + 1
+ }
+
+ fun getItem(position: Int): PhotoInfoBean {
+ return fileList[position]
+ }
+
+ interface MatisseLauncherProvider {
+ fun getMatisseLauncher(): ActivityResultLauncher
+ }
+
+ class MatisseResultContract : ActivityResultContract>() {
+ override fun createIntent(context: Context, input: Intent): Intent {
+ return input
+ }
+
+ override fun parseResult(resultCode: Int, intent: Intent?): List {
+ if (resultCode != RESULT_OK) {
+ return emptyList()
+ }
+ return Matisse.obtainResult(intent)
}
}
- @Override
- public int getItemCount() {
- return fileList.size() + 1;
- }
-
- public PhotoInfoBean getItem(int position) {
- return fileList.get(position);
+ companion object {
+ const val TYPE_IMAGE = 0
+ const val TYPE_INSERT = 1
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/huanchengfly/tieba/post/api/retrofit/interceptors/FailureResponseInterceptor.kt b/app/src/main/java/com/huanchengfly/tieba/post/api/retrofit/interceptors/FailureResponseInterceptor.kt
index be6713b2..1342c123 100644
--- a/app/src/main/java/com/huanchengfly/tieba/post/api/retrofit/interceptors/FailureResponseInterceptor.kt
+++ b/app/src/main/java/com/huanchengfly/tieba/post/api/retrofit/interceptors/FailureResponseInterceptor.kt
@@ -1,10 +1,12 @@
package com.huanchengfly.tieba.post.api.retrofit.interceptors
+import android.util.Log
import com.google.gson.Gson
import com.huanchengfly.tieba.post.api.models.CommonResponse
import com.huanchengfly.tieba.post.api.retrofit.exception.TiebaApiException
import okhttp3.Interceptor
import okhttp3.Response
+import java.nio.charset.StandardCharsets
object FailureResponseInterceptor : Interceptor {
private val gson = Gson()
@@ -22,9 +24,18 @@ object FailureResponseInterceptor : Interceptor {
contentType.charset(Charsets.UTF_8)!!
}
- val inputStreamReader = body.source().also {
+ val inputStream = body.source().also {
it.request(Long.MAX_VALUE)
- }.buffer.clone().inputStream().reader(charset)
+ }.buffer.clone().inputStream()
+
+ val length: Int = inputStream.available()
+ val buffer = ByteArray(length)
+ inputStream.read(buffer)
+ val bodyString = String(buffer, StandardCharsets.UTF_8)
+
+ Log.i("ResponseI", bodyString)
+
+ val inputStreamReader = inputStream.reader(charset)
val jsonObject = try {
gson.fromJson(
diff --git a/app/src/main/java/com/huanchengfly/tieba/post/components/dialogs/RequestPermissionTipDialog.kt b/app/src/main/java/com/huanchengfly/tieba/post/components/dialogs/RequestPermissionTipDialog.kt
new file mode 100644
index 00000000..04eaffcb
--- /dev/null
+++ b/app/src/main/java/com/huanchengfly/tieba/post/components/dialogs/RequestPermissionTipDialog.kt
@@ -0,0 +1,43 @@
+package com.huanchengfly.tieba.post.components.dialogs
+
+import android.app.AlertDialog
+import android.content.Context
+import android.view.Gravity
+import android.view.View
+import android.view.WindowManager
+import android.widget.TextView
+import androidx.core.view.setPadding
+import com.huanchengfly.tieba.post.R
+import com.huanchengfly.tieba.post.dpToPx
+import com.huanchengfly.tieba.post.utils.PermissionUtils
+
+
+class RequestPermissionTipDialog(context: Context, permission: PermissionUtils.Permission) :
+ AlertDialog(context, R.style.Dialog_RequestPermissionTip) {
+ val title: TextView
+ val message: TextView
+
+ init {
+ setCancelable(false)
+ setView(View.inflate(context, R.layout.dialog_request_permission_tip, null).also {
+ title = it.findViewById(R.id.request_permission_tip_dialog_title)
+ message = it.findViewById(R.id.request_permission_tip_dialog_message)
+ })
+ val permissionName = PermissionUtils.transformText(context, permission.permissions).first()
+ title.text = context.getString(R.string.title_request_permission_tip_dialog, permissionName)
+ message.text =
+ context.getString(R.string.message_request_permission_tip_dialog, permission.desc)
+ }
+
+ override fun show() {
+ super.show()
+ window?.let {
+ it.attributes = it.attributes.apply {
+ width = WindowManager.LayoutParams.MATCH_PARENT
+ height = WindowManager.LayoutParams.WRAP_CONTENT
+ it.decorView.setPadding(16f.dpToPx())
+ }
+ it.setGravity(Gravity.TOP or Gravity.CENTER_HORIZONTAL)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/huanchengfly/tieba/post/fragments/WebViewFragment.java b/app/src/main/java/com/huanchengfly/tieba/post/fragments/WebViewFragment.java
index 7dddfe17..4cf2774a 100644
--- a/app/src/main/java/com/huanchengfly/tieba/post/fragments/WebViewFragment.java
+++ b/app/src/main/java/com/huanchengfly/tieba/post/fragments/WebViewFragment.java
@@ -1,5 +1,7 @@
package com.huanchengfly.tieba.post.fragments;
+import static com.huanchengfly.tieba.post.utils.FileUtil.FILE_TYPE_DOWNLOAD;
+
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
@@ -33,6 +35,7 @@ import androidx.annotation.Nullable;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.google.android.material.snackbar.Snackbar;
+import com.hjq.permissions.Permission;
import com.huanchengfly.tieba.post.R;
import com.huanchengfly.tieba.post.components.dialogs.PermissionDialog;
import com.huanchengfly.tieba.post.interfaces.OnReceivedTitleListener;
@@ -43,17 +46,14 @@ import com.huanchengfly.tieba.post.utils.AssetUtil;
import com.huanchengfly.tieba.post.utils.DialogUtil;
import com.huanchengfly.tieba.post.utils.FileUtil;
import com.huanchengfly.tieba.post.utils.NavigationHelper;
+import com.huanchengfly.tieba.post.utils.PermissionUtils;
import com.huanchengfly.tieba.post.utils.ThemeUtil;
import com.huanchengfly.tieba.post.utils.TiebaLiteJavaScript;
import com.huanchengfly.tieba.post.utils.Util;
-import com.yanzhenjie.permission.AndPermission;
-import com.yanzhenjie.permission.runtime.Permission;
import org.jetbrains.annotations.NotNull;
-import java.util.List;
-
-import static com.huanchengfly.tieba.post.utils.FileUtil.FILE_TYPE_DOWNLOAD;
+import java.util.Arrays;
//TODO: 代码太烂,需要重写
public class WebViewFragment extends BaseFragment implements DownloadListener {
@@ -380,18 +380,26 @@ public class WebViewFragment extends BaseFragment implements DownloadListener {
getAttachContext().getString(R.string.title_ask_permission, uri.getHost(), getAttachContext().getString(R.string.permission_name_location)),
R.drawable.ic_round_location_on))
.setOnGrantedCallback(isForever -> {
- AndPermission.with(getAttachContext())
- .runtime()
- .permission(Permission.ACCESS_COARSE_LOCATION, Permission.ACCESS_FINE_LOCATION)
- .onGranted((List permissions) -> {
+ PermissionUtils.INSTANCE.askPermission(
+ getAttachContext(),
+ new PermissionUtils.Permission(
+ Arrays.asList(Permission.ACCESS_COARSE_LOCATION, Permission.ACCESS_FINE_LOCATION),
+ getAttachContext().getString(R.string.usage_webview_location_permission)
+ ),
+ R.string.tip_no_permission,
+ () -> {
if (isEnabledLocationFunction()) {
callback.invoke(origin, true, isForever);
} else {
callback.invoke(origin, false, false);
}
- })
- .onDenied((List permissions) -> callback.invoke(origin, false, false))
- .start();
+ return null;
+ },
+ () -> {
+ callback.invoke(origin, false, false);
+ return null;
+ }
+ );
})
.setOnDeniedCallback(isForever -> callback.invoke(origin, false, false))
.show();
diff --git a/app/src/main/java/com/huanchengfly/tieba/post/fragments/intro/PermissionFragment.kt b/app/src/main/java/com/huanchengfly/tieba/post/fragments/intro/PermissionFragment.kt
index f261fa1e..0eae1aed 100644
--- a/app/src/main/java/com/huanchengfly/tieba/post/fragments/intro/PermissionFragment.kt
+++ b/app/src/main/java/com/huanchengfly/tieba/post/fragments/intro/PermissionFragment.kt
@@ -1,67 +1,44 @@
-package com.huanchengfly.tieba.post.fragments.intro;
+package com.huanchengfly.tieba.post.fragments.intro
-import android.os.Build;
-import android.view.ViewGroup;
+import android.os.Build
+import android.view.ViewGroup
+import com.huanchengfly.tieba.post.R
+import com.huanchengfly.tieba.post.ui.intro.fragments.BaseIntroFragment
+import com.huanchengfly.tieba.post.ui.theme.utils.ThemeUtils
+import com.huanchengfly.tieba.post.utils.PermissionUtils
-import androidx.annotation.Nullable;
+class PermissionFragment : BaseIntroFragment() {
+ override fun getIconRes(): Int = R.drawable.ic_round_warning
+ override fun getTitle(): CharSequence =
+ attachContext.getString(R.string.title_fragment_permission)
-import com.huanchengfly.tieba.post.R;
-import com.huanchengfly.tieba.post.ui.intro.fragments.BaseIntroFragment;
-import com.huanchengfly.tieba.post.ui.theme.utils.ThemeUtils;
-import com.huanchengfly.tieba.post.utils.PermissionUtil;
-import com.yanzhenjie.permission.runtime.Permission;
+ override fun getSubtitle(): CharSequence =
+ attachContext.getString(R.string.subtitle_fragment_permission)
-public class PermissionFragment extends BaseIntroFragment {
- @Override
- public int getIconRes() {
- return R.drawable.ic_round_warning;
+ override fun getIconColor(): Int = ThemeUtils.getColorByAttr(attachContext, R.attr.colorAccent)
+ override fun getTitleTextColor(): Int =
+ ThemeUtils.getColorByAttr(attachContext, R.attr.colorText)
+
+ override fun getSubtitleTextColor(): Int =
+ ThemeUtils.getColorByAttr(attachContext, R.attr.colorTextSecondary)
+
+ override fun getCustomLayoutResId(): Int = R.layout.layout_fragment_permission
+
+ override fun initCustomLayout(container: ViewGroup) {
+ super.initCustomLayout(container)
}
- @Nullable
- @Override
- protected CharSequence getTitle() {
- return getAttachContext().getString(R.string.title_fragment_permission);
- }
-
- @Nullable
- @Override
- protected CharSequence getSubtitle() {
- return getAttachContext().getString(R.string.subtitle_fragment_permission);
- }
-
- @Override
- protected int getIconColor() {
- return ThemeUtils.getColorByAttr(getAttachContext(), R.attr.colorAccent);
- }
-
- @Override
- protected int getTitleTextColor() {
- return ThemeUtils.getColorByAttr(getAttachContext(), R.attr.colorText);
- }
-
- @Override
- protected int getSubtitleTextColor() {
- return ThemeUtils.getColorByAttr(getAttachContext(), R.attr.colorTextSecondary);
- }
-
- @Override
- protected int getCustomLayoutResId() {
- return R.layout.layout_fragment_permission;
- }
-
- @Override
- protected void initCustomLayout(ViewGroup container) {
- super.initCustomLayout(container);
- }
-
- @Override
- public boolean onNext() {
+ override fun onNext(): Boolean {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
- PermissionUtil.askPermission(getAttachContext(), data -> next(),
- new PermissionUtil.Permission(Permission.READ_PHONE_STATE, getString(R.string.tip_permission_phone)),
- new PermissionUtil.Permission(Permission.Group.STORAGE, getString(R.string.tip_permission_storage)));
- return true;
+ PermissionUtils.askPermission(
+ attachContext,
+ PermissionUtils.Permission(
+ listOf(PermissionUtils.READ_PHONE_STATE),
+ getString(R.string.tip_permission_phone)
+ )
+ ) { next() }
+ return true
}
- return false;
+ return false
}
-}
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/huanchengfly/tieba/post/utils/FileUtil.java b/app/src/main/java/com/huanchengfly/tieba/post/utils/FileUtil.java
index e624d497..f3233429 100644
--- a/app/src/main/java/com/huanchengfly/tieba/post/utils/FileUtil.java
+++ b/app/src/main/java/com/huanchengfly/tieba/post/utils/FileUtil.java
@@ -16,7 +16,6 @@ import android.text.TextUtils;
import android.webkit.URLUtil;
import com.huanchengfly.tieba.post.R;
-import com.yanzhenjie.permission.runtime.Permission;
import java.io.File;
import java.io.FileInputStream;
@@ -24,6 +23,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
public class FileUtil {
public static final int FILE_TYPE_DOWNLOAD = 0;
@@ -197,10 +197,17 @@ public class FileUtil {
downloadBySystemWithPermission(context, fileType, url, fileName);
return;
}
- PermissionUtil.askPermission(context,
- data -> downloadBySystemWithPermission(context, fileType, url, fileName),
- R.string.toast_without_permission_download,
- new PermissionUtil.Permission(Permission.WRITE_EXTERNAL_STORAGE, context.getString(R.string.tip_permission_storage_download)));
+ PermissionUtils.INSTANCE.askPermission(
+ context,
+ new PermissionUtils.Permission(
+ Arrays.asList(PermissionUtils.READ_EXTERNAL_STORAGE, PermissionUtils.WRITE_EXTERNAL_STORAGE),
+ context.getString(R.string.tip_permission_storage_download)
+ ),
+ () -> {
+ downloadBySystemWithPermission(context, fileType, url, fileName);
+ return null;
+ }
+ );
}
public static String readFile(File file) {
diff --git a/app/src/main/java/com/huanchengfly/tieba/post/utils/ImageUtil.java b/app/src/main/java/com/huanchengfly/tieba/post/utils/ImageUtil.java
index 913b4460..8700fa6a 100644
--- a/app/src/main/java/com/huanchengfly/tieba/post/utils/ImageUtil.java
+++ b/app/src/main/java/com/huanchengfly/tieba/post/utils/ImageUtil.java
@@ -45,7 +45,6 @@ import com.huanchengfly.tieba.post.R;
import com.huanchengfly.tieba.post.activities.PhotoViewActivity;
import com.huanchengfly.tieba.post.components.transformations.RadiusTransformation;
import com.huanchengfly.tieba.post.models.PhotoViewBean;
-import com.yanzhenjie.permission.runtime.Permission;
import com.zhihu.matisse.MimeType;
import java.io.ByteArrayOutputStream;
@@ -254,10 +253,17 @@ public class ImageUtil {
downloadAboveQ(context, url, forShare, taskCallback);
return;
}
- PermissionUtil.askPermission(context,
- data -> downloadBelowQ(context, url, forShare, taskCallback),
+ PermissionUtils.INSTANCE.askPermission(
+ context,
+ new PermissionUtils.Permission(
+ Arrays.asList(PermissionUtils.READ_EXTERNAL_STORAGE, PermissionUtils.WRITE_EXTERNAL_STORAGE),
+ context.getString(R.string.tip_permission_storage)
+ ),
R.string.toast_no_permission_save_photo,
- new PermissionUtil.Permission(Permission.Group.STORAGE, context.getString(R.string.tip_permission_storage)));
+ () -> {
+ downloadBelowQ(context, url, forShare, taskCallback);
+ return null;
+ });
}
private static void downloadAboveQ(Context context, String url, boolean forShare, @Nullable ShareTaskCallback taskCallback) {
@@ -328,6 +334,9 @@ public class ImageUtil {
}
}
String fileName = URLUtil.guessFileName(url, null, MimeType.JPEG.toString());
+ if (isGifFile(file)) {
+ fileName = changeFileExtension(fileName, "gif");
+ }
File destFile = new File(appDir, fileName);
if (destFile.exists()) {
return;
diff --git a/app/src/main/java/com/huanchengfly/tieba/post/utils/PermissionUtil.java b/app/src/main/java/com/huanchengfly/tieba/post/utils/PermissionUtil.java
deleted file mode 100644
index 50b4e98f..00000000
--- a/app/src/main/java/com/huanchengfly/tieba/post/utils/PermissionUtil.java
+++ /dev/null
@@ -1,153 +0,0 @@
-package com.huanchengfly.tieba.post.utils;
-
-import android.annotation.SuppressLint;
-import android.content.Context;
-import android.text.TextUtils;
-import android.widget.Toast;
-
-import androidx.annotation.Nullable;
-import androidx.annotation.StringRes;
-
-import com.huanchengfly.tieba.post.R;
-import com.yanzhenjie.permission.Action;
-import com.yanzhenjie.permission.AndPermission;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-public class PermissionUtil {
- public static void askPermission(Context context, Permission... permissions) {
- askPermission(context, null, permissions);
- }
-
- public static void askPermission(Context context, Action> action, Permission... permissions) {
- askPermission(context, action, context.getString(R.string.tip_no_permission), permissions);
- }
-
- public static void askPermission(Context context, Action> action, @StringRes int deniedTip, Permission... permissions) {
- askPermission(context, action, context.getString(deniedTip), permissions);
- }
-
- private static String getMessage(Context context, Permission... permissions) {
- StringBuilder stringBuilder = new StringBuilder(context.getString(R.string.message_need_permission));
- for (Permission permission : permissions) {
- if (permission.isGroup()) {
- if (!AndPermission.hasPermissions(context, permission.getPermissionGroup())) {
- stringBuilder.append(context.getString(R.string.message_permission_desc, com.yanzhenjie.permission.runtime.Permission.transformText(context, permission.getPermissionGroup()).get(0), permission.getDesc()));
- }
- } else {
- if (!AndPermission.hasPermissions(context, permission.getPermission())) {
- stringBuilder.append(context.getString(R.string.message_permission_desc, com.yanzhenjie.permission.runtime.Permission.transformText(context, permission.getPermission()).get(0), permission.getDesc()));
- }
- }
- }
- stringBuilder.append(context.getString(R.string.tip_regrant_permission, context.getString(R.string.button_sure_default)));
- return stringBuilder.toString();
- }
-
- @SuppressLint("WrongConstant")
- public static void askPermission(Context context, @Nullable Action> action, @Nullable String deniedTip, Permission... permissions) {
- List permissionList = new ArrayList<>();
- for (Permission permission : permissions) {
- if (permission.isGroup()) {
- permissionList.addAll(Arrays.asList(permission.getPermissionGroup()));
- } else {
- permissionList.add(permission.getPermission());
- }
- }
- String[] permissionArray = permissionList.toArray(new String[0]);
- if (AndPermission.hasPermissions(context, permissionArray) && action != null) {
- action.onAction(permissionList);
- return;
- }
- AndPermission.with(context)
- .runtime()
- .permission(permissionArray)
- .rationale((c, data, executor) -> {
- DialogUtil.build(c)
- .setCancelable(false)
- .setTitle(R.string.title_dialog_permission)
- .setMessage(getMessage(c, permissions))
- .setPositiveButton(R.string.button_sure_default, (dialog, which) -> {
- executor.execute();
- })
- .setNegativeButton(R.string.button_cancel, (dialog, which) -> {
- executor.cancel();
- })
- .create()
- .show();
- })
- .onGranted(action)
- .onDenied(data -> {
- if (!TextUtils.isEmpty(deniedTip))
- Toast.makeText(context, deniedTip, Toast.LENGTH_SHORT).show();
- boolean hasAlwaysDeniedPermission = AndPermission.hasAlwaysDeniedPermission(context, data);
- if (hasAlwaysDeniedPermission) {
- AndPermission.with(context)
- .runtime()
- .setting()
- .start(0);
- }
- /*
- DialogUtil.build(context)
- .setCancelable(false)
- .setTitle(R.string.title_dialog_permission)
- .setMessage(getMessage(context, permissions))
- .setPositiveButton(R.string.button_sure_default, (dialog, which) -> {
- if (hasAlwaysDeniedPermission) {
- AndPermission.with(context)
- .runtime()
- .setting()
- .onComeback(() -> askPermission(context, action, deniedTip, permissions))
- .start();
- } else {
- askPermission(context, action, deniedTip, permissions);
- }
- })
- .setNegativeButton(R.string.button_cancel, (dialog, which) -> {
- if (!TextUtils.isEmpty(deniedTip))
- Toast.makeText(context, deniedTip, Toast.LENGTH_SHORT).show();
- })
- .create()
- .show();
- */
- })
- .start();
- }
-
- public static class Permission {
- private boolean isGroup;
- private String permission;
- private String[] permissionGroup;
- private String desc;
-
- public Permission(String permission, String desc) {
- this.isGroup = false;
- this.permission = permission;
- this.desc = desc;
- }
-
- public Permission(String[] permissionGroup, String desc) {
- this.isGroup = true;
- this.permissionGroup = permissionGroup;
- this.desc = desc;
- }
-
- public String getDesc() {
- return desc;
- }
-
- public boolean isGroup() {
- return isGroup;
- }
-
- public String getPermission() {
- return permission;
- }
-
- public String[] getPermissionGroup() {
- return permissionGroup;
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/huanchengfly/tieba/post/utils/PermissionUtils.kt b/app/src/main/java/com/huanchengfly/tieba/post/utils/PermissionUtils.kt
new file mode 100644
index 00000000..6b4d2385
--- /dev/null
+++ b/app/src/main/java/com/huanchengfly/tieba/post/utils/PermissionUtils.kt
@@ -0,0 +1,190 @@
+package com.huanchengfly.tieba.post.utils
+
+import android.content.Context
+import android.os.Build
+import com.hjq.permissions.OnPermissionCallback
+import com.hjq.permissions.XXPermissions
+import com.huanchengfly.tieba.post.R
+import com.huanchengfly.tieba.post.components.dialogs.RequestPermissionTipDialog
+import com.huanchengfly.tieba.post.toastShort
+
+
+object PermissionUtils {
+ const val READ_CALENDAR = "android.permission.READ_CALENDAR"
+ const val WRITE_CALENDAR = "android.permission.WRITE_CALENDAR"
+
+ const val CAMERA = "android.permission.CAMERA"
+
+ const val READ_CONTACTS = "android.permission.READ_CONTACTS"
+ const val WRITE_CONTACTS = "android.permission.WRITE_CONTACTS"
+ const val GET_ACCOUNTS = "android.permission.GET_ACCOUNTS"
+
+ const val ACCESS_FINE_LOCATION = "android.permission.ACCESS_FINE_LOCATION"
+ const val ACCESS_COARSE_LOCATION = "android.permission.ACCESS_COARSE_LOCATION"
+ const val ACCESS_BACKGROUND_LOCATION = "android.permission.ACCESS_BACKGROUND_LOCATION"
+
+ const val RECORD_AUDIO = "android.permission.RECORD_AUDIO"
+
+ const val READ_PHONE_STATE = "android.permission.READ_PHONE_STATE"
+ const val CALL_PHONE = "android.permission.CALL_PHONE"
+ const val USE_SIP = "android.permission.USE_SIP"
+ const val READ_PHONE_NUMBERS = "android.permission.READ_PHONE_NUMBERS"
+ const val ANSWER_PHONE_CALLS = "android.permission.ANSWER_PHONE_CALLS"
+ const val ADD_VOICEMAIL = "com.android.voicemail.permission.ADD_VOICEMAIL"
+
+ const val READ_CALL_LOG = "android.permission.READ_CALL_LOG"
+ const val WRITE_CALL_LOG = "android.permission.WRITE_CALL_LOG"
+ const val PROCESS_OUTGOING_CALLS = "android.permission.PROCESS_OUTGOING_CALLS"
+
+ const val BODY_SENSORS = "android.permission.BODY_SENSORS"
+ const val ACTIVITY_RECOGNITION = "android.permission.ACTIVITY_RECOGNITION"
+
+ const val SEND_SMS = "android.permission.SEND_SMS"
+ const val RECEIVE_SMS = "android.permission.RECEIVE_SMS"
+ const val READ_SMS = "android.permission.READ_SMS"
+ const val RECEIVE_WAP_PUSH = "android.permission.RECEIVE_WAP_PUSH"
+ const val RECEIVE_MMS = "android.permission.RECEIVE_MMS"
+
+ const val READ_EXTERNAL_STORAGE = "android.permission.READ_EXTERNAL_STORAGE"
+ const val WRITE_EXTERNAL_STORAGE = "android.permission.WRITE_EXTERNAL_STORAGE"
+
+ /**
+ * Turn permissions into text.
+ */
+ fun transformText(context: Context, permissions: List): List {
+ val textList: MutableList = ArrayList()
+ for (permission in permissions) {
+ when (permission) {
+ READ_CALENDAR, WRITE_CALENDAR -> {
+ val message = context.getString(R.string.permission_name_calendar)
+ if (!textList.contains(message)) {
+ textList.add(message)
+ }
+ }
+ CAMERA -> {
+ val message = context.getString(R.string.permission_name_camera)
+ if (!textList.contains(message)) {
+ textList.add(message)
+ }
+ }
+ GET_ACCOUNTS, READ_CONTACTS, WRITE_CONTACTS -> {
+ val message = context.getString(R.string.permission_name_contacts)
+ if (!textList.contains(message)) {
+ textList.add(message)
+ }
+ }
+ ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION -> {
+ val message = context.getString(R.string.permission_name_location)
+ if (!textList.contains(message)) {
+ textList.add(message)
+ }
+ }
+ RECORD_AUDIO -> {
+ val message = context.getString(R.string.permission_name_microphone)
+ if (!textList.contains(message)) {
+ textList.add(message)
+ }
+ }
+ READ_PHONE_STATE, CALL_PHONE, ADD_VOICEMAIL, USE_SIP, READ_PHONE_NUMBERS, ANSWER_PHONE_CALLS -> {
+ val message = context.getString(R.string.permission_name_phone)
+ if (!textList.contains(message)) {
+ textList.add(message)
+ }
+ }
+ READ_CALL_LOG, WRITE_CALL_LOG, PROCESS_OUTGOING_CALLS -> {
+ val messageId: Int =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) R.string.permission_name_call_log else R.string.permission_name_phone
+ val message = context.getString(messageId)
+ if (!textList.contains(message)) {
+ textList.add(message)
+ }
+ }
+ BODY_SENSORS -> {
+ val message = context.getString(R.string.permission_name_sensors)
+ if (!textList.contains(message)) {
+ textList.add(message)
+ }
+ }
+ ACTIVITY_RECOGNITION -> {
+ val message = context.getString(R.string.permission_name_activity_recognition)
+ if (!textList.contains(message)) {
+ textList.add(message)
+ }
+ }
+ SEND_SMS, RECEIVE_SMS, READ_SMS, RECEIVE_WAP_PUSH, RECEIVE_MMS -> {
+ val message = context.getString(R.string.permission_name_sms)
+ if (!textList.contains(message)) {
+ textList.add(message)
+ }
+ }
+ READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE -> {
+ val message = context.getString(R.string.permission_name_storage)
+ if (!textList.contains(message)) {
+ textList.add(message)
+ }
+ }
+ }
+ }
+ return textList
+ }
+
+ fun askPermission(context: Context, permission: Permission, onGranted: () -> Unit) {
+ askPermission(context, permission, R.string.tip_no_permission, onGranted)
+ }
+
+ fun askPermission(
+ context: Context,
+ permission: Permission,
+ deniedToast: Int,
+ onGranted: () -> Unit
+ ) {
+ askPermission(context, permission, context.getString(deniedToast), onGranted, null)
+ }
+
+ fun askPermission(
+ context: Context,
+ permission: Permission,
+ deniedToast: Int,
+ onGranted: () -> Unit,
+ onDenied: (() -> Unit)?
+ ) {
+ askPermission(context, permission, context.getString(deniedToast), onGranted, onDenied)
+ }
+
+ @JvmOverloads
+ fun askPermission(
+ context: Context,
+ permission: Permission,
+ deniedToast: String,
+ onGranted: () -> Unit,
+ onDenied: (() -> Unit)? = null
+ ) {
+ if (XXPermissions.isGranted(context, permission.permissions)) {
+ onGranted()
+ } else {
+ val dialog = RequestPermissionTipDialog(context, permission)
+ XXPermissions.with(context)
+ .permission(permission.permissions)
+ .request(object : OnPermissionCallback {
+ override fun onGranted(permissions: List, all: Boolean) {
+ if (all) {
+ onGranted()
+ } else {
+ context.toastShort(deniedToast)
+ onDenied?.invoke()
+ }
+ dialog.dismiss()
+ }
+
+ override fun onDenied(permissions: List, never: Boolean) {
+ context.toastShort(deniedToast)
+ onDenied?.invoke()
+ dialog.dismiss()
+ }
+ })
+ dialog.show()
+ }
+ }
+
+ data class Permission(val permissions: List, val desc: String)
+}
diff --git a/app/src/main/java/com/huanchengfly/tieba/post/widgets/edittext/widget/UndoableEditText.java b/app/src/main/java/com/huanchengfly/tieba/post/widgets/edittext/widget/UndoableEditText.java
index 2ccee903..bca53808 100644
--- a/app/src/main/java/com/huanchengfly/tieba/post/widgets/edittext/widget/UndoableEditText.java
+++ b/app/src/main/java/com/huanchengfly/tieba/post/widgets/edittext/widget/UndoableEditText.java
@@ -4,9 +4,15 @@ import android.content.Context;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.AttributeSet;
+import android.view.ActionMode;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
import androidx.appcompat.widget.AppCompatEditText;
+import androidx.core.widget.TextViewCompat;
+import com.huanchengfly.tieba.post.R;
import com.huanchengfly.tieba.post.widgets.edittext.OperationManager;
public class UndoableEditText extends AppCompatEditText {
@@ -50,6 +56,28 @@ public class UndoableEditText extends AppCompatEditText {
}
private void init() {
+ TextViewCompat.setCustomSelectionActionModeCallback(this, new ActionMode.Callback() {
+ @Override
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ MenuInflater menuInflater = mode.getMenuInflater();
+ menuInflater.inflate(R.menu.menu_undoable_edit_text, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ return false;
+ }
+
+ @Override
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ return false;
+ }
+
+ @Override
+ public void onDestroyActionMode(ActionMode mode) {
+ }
+ });
addTextChangedListener(mgr);
}
diff --git a/app/src/main/res/layout/dialog_request_permission_tip.xml b/app/src/main/res/layout/dialog_request_permission_tip.xml
new file mode 100644
index 00000000..11cdf29c
--- /dev/null
+++ b/app/src/main/res/layout/dialog_request_permission_tip.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/menu_undoable_edit_text.xml b/app/src/main/res/menu/menu_undoable_edit_text.xml
new file mode 100644
index 00000000..bef1c3b6
--- /dev/null
+++ b/app/src/main/res/menu/menu_undoable_edit_text.xml
@@ -0,0 +1,9 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index a567b8ce..b803c483 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -504,4 +504,19 @@
“电池优化”已忽略
已忽略电池优化
复制崩溃报告
+ 重做
+ 日历
+ 相机
+ 手机账号 / 通讯录
+ 位置信息
+ 麦克风
+ 电话
+ 通话记录
+ 身体传感器
+ 健身运动
+ 短信
+ 存储空间
+ 贴吧 Lite 正在请求“%1$s”权限
+ 授权后,将%1$s
+ 用于向当前访问的网页提供你的位置信息,以便该网页向你提供相关服务。
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 78981f46..7c39006e 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -143,6 +143,13 @@
- @style/Animation.Bottom
+
+