pref: 优化权限申请流程
This commit is contained in:
parent
db03c543e1
commit
8c07ef51ed
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -92,6 +92,9 @@
|
|||
<meta-data
|
||||
android:name="use_miui_font"
|
||||
android:value="true" />
|
||||
<meta-data
|
||||
android:name="ScopedStorage"
|
||||
android:value="true" />
|
||||
|
||||
<activity
|
||||
android:name=".activities.MainActivity"
|
||||
|
|
|
|||
|
|
@ -253,26 +253,6 @@ open class MainActivity : BaseActivity(), NavigationBarView.OnItemSelectedListen
|
|||
} else if (!AccountUtil.isLoggedIn(this)) {
|
||||
navigationHelper.navigationByData(NavigationHelper.ACTION_LOGIN)
|
||||
}
|
||||
/*
|
||||
handler.postDelayed(() -> {
|
||||
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() {
|
||||
|
|
|
|||
|
|
@ -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<Intent> = 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,10 +365,11 @@ 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(
|
||||
if (insertPhotoAdapter.getFileList().size - 1 > insertPhotoAdapter.getFileList()
|
||||
.indexOf(
|
||||
photoInfoBean
|
||||
)
|
||||
) {
|
||||
|
|
@ -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<PhotoInfoBean>) {
|
||||
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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -485,14 +486,23 @@ class TranslucentThemeActivity : BaseActivity(), View.OnClickListener, OnSeekBar
|
|||
}
|
||||
}
|
||||
|
||||
private fun askPermission(granted: Action<List<String?>>) {
|
||||
private fun askPermission(granted: () -> Unit) {
|
||||
PermissionUtils.askPermission(
|
||||
this,
|
||||
PermissionUtils.Permission(
|
||||
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)))
|
||||
listOf(
|
||||
PermissionUtils.READ_EXTERNAL_STORAGE,
|
||||
PermissionUtils.WRITE_EXTERNAL_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)))
|
||||
}
|
||||
listOf(PermissionUtils.READ_EXTERNAL_STORAGE)
|
||||
},
|
||||
getString(R.string.tip_permission_storage)
|
||||
),
|
||||
R.string.toast_no_permission_insert_photo,
|
||||
granted
|
||||
)
|
||||
}
|
||||
|
||||
interface SavePicCallback<T> {
|
||||
|
|
|
|||
|
|
@ -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<MyViewHolder>() {
|
||||
private var fileList: MutableList<PhotoInfoBean> = ArrayList<PhotoInfoBean>()
|
||||
|
||||
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<MyViewHolder> {
|
||||
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<PhotoInfoBean> 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<PhotoInfoBean> {
|
||||
return fileList
|
||||
}
|
||||
|
||||
public List<PhotoInfoBean> getFileList() {
|
||||
return fileList;
|
||||
fun setFileList(fileList: MutableList<PhotoInfoBean>) {
|
||||
this.fileList = fileList
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
public void setFileList(List<PhotoInfoBean> 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<List<String>> granted) {
|
||||
private fun askPermission(granted: () -> Unit) {
|
||||
PermissionUtils.askPermission(
|
||||
mContext,
|
||||
PermissionUtils.Permission(
|
||||
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)));
|
||||
listOf(
|
||||
PermissionUtils.READ_EXTERNAL_STORAGE,
|
||||
PermissionUtils.WRITE_EXTERNAL_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)));
|
||||
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<Intent>
|
||||
}
|
||||
|
||||
class MatisseResultContract : ActivityResultContract<Intent, List<Uri>>() {
|
||||
override fun createIntent(context: Context, input: Intent): Intent {
|
||||
return input
|
||||
}
|
||||
|
||||
override fun parseResult(resultCode: Int, intent: Intent?): List<Uri> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
@ -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<CommonResponse>(
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<String> 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<String> permissions) -> callback.invoke(origin, false, false))
|
||||
.start();
|
||||
return null;
|
||||
},
|
||||
() -> {
|
||||
callback.invoke(origin, false, false);
|
||||
return null;
|
||||
}
|
||||
);
|
||||
})
|
||||
.setOnDeniedCallback(isForever -> callback.invoke(origin, false, false))
|
||||
.show();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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<List<String>> action, Permission... permissions) {
|
||||
askPermission(context, action, context.getString(R.string.tip_no_permission), permissions);
|
||||
}
|
||||
|
||||
public static void askPermission(Context context, Action<List<String>> 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<List<String>> action, @Nullable String deniedTip, Permission... permissions) {
|
||||
List<String> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<String>): List<String> {
|
||||
val textList: MutableList<String> = 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<String>, all: Boolean) {
|
||||
if (all) {
|
||||
onGranted()
|
||||
} else {
|
||||
context.toastShort(deniedToast)
|
||||
onDenied?.invoke()
|
||||
}
|
||||
dialog.dismiss()
|
||||
}
|
||||
|
||||
override fun onDenied(permissions: List<String>, never: Boolean) {
|
||||
context.toastShort(deniedToast)
|
||||
onDenied?.invoke()
|
||||
dialog.dismiss()
|
||||
}
|
||||
})
|
||||
dialog.show()
|
||||
}
|
||||
}
|
||||
|
||||
data class Permission(val permissions: List<String>, val desc: String)
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.huanchengfly.tieba.post.widgets.theme.TintConstraintLayout 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"
|
||||
android:layout_margin="16dp"
|
||||
android:background="@drawable/bg_radius_16dp"
|
||||
android:padding="16dp"
|
||||
app:backgroundTint="@color/default_color_background">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/request_permission_tip_dialog_icon"
|
||||
android:layout_width="@dimen/size_avatar_small"
|
||||
android:layout_height="@dimen/size_avatar_small"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.0"
|
||||
app:srcCompat="@mipmap/ic_launcher_round" />
|
||||
|
||||
<com.huanchengfly.tieba.post.widgets.theme.TintTextView
|
||||
android:id="@+id/request_permission_tip_dialog_title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toTopOf="@+id/request_permission_tip_dialog_message"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toEndOf="@+id/request_permission_tip_dialog_icon"
|
||||
app:layout_constraintTop_toTopOf="@+id/request_permission_tip_dialog_icon"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
app:tint="@color/default_color_text"
|
||||
tools:text="贴吧 Lite 正在请求“存储空间”权限" />
|
||||
|
||||
<com.huanchengfly.tieba.post.widgets.theme.TintTextView
|
||||
android:id="@+id/request_permission_tip_dialog_message"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/request_permission_tip_dialog_icon"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toStartOf="@+id/request_permission_tip_dialog_title"
|
||||
app:layout_constraintTop_toBottomOf="@+id/request_permission_tip_dialog_title"
|
||||
app:tint="@color/default_color_text_secondary"
|
||||
tools:text="授权后,将用于保存图片" />
|
||||
|
||||
</com.huanchengfly.tieba.post.widgets.theme.TintConstraintLayout>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:id="@+id/action_undo"
|
||||
android:title="@string/button_undo" />
|
||||
<item
|
||||
android:id="@+id/action_redo"
|
||||
android:title="@string/button_redo" />
|
||||
</menu>
|
||||
|
|
@ -504,4 +504,19 @@
|
|||
<string name="summary_battery_optimization_ignored">“电池优化”已忽略</string>
|
||||
<string name="toast_ignore_battery_optimization_already">已忽略电池优化</string>
|
||||
<string name="button_copy_crash">复制崩溃报告</string>
|
||||
<string name="button_redo">重做</string>
|
||||
<string name="permission_name_calendar">日历</string>
|
||||
<string name="permission_name_camera">相机</string>
|
||||
<string name="permission_name_contacts">手机账号 / 通讯录</string>
|
||||
<string name="permission_name_location">位置信息</string>
|
||||
<string name="permission_name_microphone">麦克风</string>
|
||||
<string name="permission_name_phone">电话</string>
|
||||
<string name="permission_name_call_log">通话记录</string>
|
||||
<string name="permission_name_sensors">身体传感器</string>
|
||||
<string name="permission_name_activity_recognition">健身运动</string>
|
||||
<string name="permission_name_sms">短信</string>
|
||||
<string name="permission_name_storage">存储空间</string>
|
||||
<string name="title_request_permission_tip_dialog">贴吧 Lite 正在请求“%1$s”权限</string>
|
||||
<string name="message_request_permission_tip_dialog">授权后,将%1$s</string>
|
||||
<string name="usage_webview_location_permission">用于向当前访问的网页提供你的位置信息,以便该网页向你提供相关服务。</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -143,6 +143,13 @@
|
|||
<item name="android:windowAnimationStyle">@style/Animation.Bottom</item>
|
||||
</style>
|
||||
|
||||
<style name="Dialog.RequestPermissionTip" parent="Theme.MaterialComponents.DayNight.Dialog">
|
||||
<item name="android:windowFrame">@null</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
<item name="android:windowAnimationStyle">@style/Animation.Fade</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.ScrollCollapsingToolbar" parent="android:Widget">
|
||||
<item name="expandedTitleMargin">32dp</item>
|
||||
<item name="statusBarScrim">?attr/colorPrimaryDark</item>
|
||||
|
|
|
|||
Loading…
Reference in New Issue