Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
【头像信息修改完善(相机、选图、动态权限初步完成)】
ui下新建一个camera包(用于处理照片),下新建一个CameraHandler、XiaoYunCamera、RequestCodes、CameraImageBean;(后面移动到core/ui)
core下引入uCrop库、权限库PermissionsDispatcher、工具库AndroidUtilCode;
Camera包转移到core/ui下(相关文件、依赖库同);
Drawable、layout、style、anim文件也移动过去(具体移动的文件目录见项目文档);
core/res下,新建,anim下push_bottom_in.xml、push_bottom_out.xml;drawable下新建btn_border.xml、btn_border_nativephoto.xml、btn_border_takephoto.xml;layout下dialog_camera_panel.xml;
更新Configurator、PermissionCheckerDelegate、FileUtil(注释)、style、AndroidManifest(权限声明)、UserProfileClickListener、UserProfileDelegate、PersonalDelegate
  • Loading branch information
aaLiweipeng committed Mar 4, 2020
1 parent 94a3c5e commit 5ed0d2d
Show file tree
Hide file tree
Showing 31 changed files with 603 additions and 19 deletions.
Binary file modified xiaoyun_annotations/build/libs/xiaoyun_annotations.jar
Binary file not shown.
Binary file modified xiaoyun_compiler/build/libs/xiaoyun_compiler.jar
Binary file not shown.
9 changes: 9 additions & 0 deletions xiaoyun_core/build.gradle
Expand Up @@ -120,4 +120,13 @@ dependencies {

// 引入multidex库
implementation 'com.android.support:multidex:1.0.3'

//工具包
implementation 'com.blankj:utilcode:1.7.1'
//动态权限处理
api 'com.github.hotchemi:permissionsdispatcher:3.0.1'
annotationProcessor 'com.github.hotchemi:permissionsdispatcher-processor:3.0.1'
//图片剪裁
implementation 'com.github.yalantis:ucrop:2.2.1-native'
}

22 changes: 21 additions & 1 deletion xiaoyun_core/src/main/AndroidManifest.xml
Expand Up @@ -3,7 +3,27 @@
xmlns:tools="http://schemas.android.com/tools"
package="com.lwp.xiaoyun_core">

<uses-permission android:name="android.permission.INTERNET"/>
<!--网络权限-->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

<!--拨号权限-->
<uses-permission android:name="android.permission.CALL_PHONE" />

<!--文件权限-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<!--相机权限-->
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.CAMERA" />

<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />

<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

<application
android:allowBackup="true"
Expand Down
Expand Up @@ -4,6 +4,7 @@
import android.os.Handler;
import android.support.annotation.NonNull;

import com.blankj.utilcode.util.Utils;
import com.joanzapata.iconify.IconFontDescriptor;
import com.joanzapata.iconify.Iconify;
import com.lwp.xiaoyun_core.delegates.web.event.Event;
Expand Down Expand Up @@ -127,6 +128,7 @@ public final Configurator withWebHost(String host) {
public final void configure() {
initIcons();//字体图标库是非常通用的,写在这里保证初始化完成时图标库也初始化完成
XIAOYUN_CONFIGS.put(ConfigKeys.CONFIG_READY, true);
Utils.init(XiaoYun.getApplicationContext());//初始化工具包库
}


Expand Down
@@ -1,5 +1,20 @@
package com.lwp.xiaoyun_core.delegates;

import android.Manifest;
import android.content.DialogInterface;
import android.support.annotation.NonNull;
import android.support.v7.app.AlertDialog;
import android.widget.Toast;

import com.lwp.xiaoyun_core.ui.camera.XiaoYunCamera;

import permissions.dispatcher.NeedsPermission;
import permissions.dispatcher.OnNeverAskAgain;
import permissions.dispatcher.OnPermissionDenied;
import permissions.dispatcher.OnShowRationale;
import permissions.dispatcher.PermissionRequest;
import permissions.dispatcher.RuntimePermissions;

/**
* <pre>
* author : 李蔚蓬(简书_凌川江雪)
Expand All @@ -8,5 +23,74 @@
* 用来进行(基于Android 6.0之后的动态权限)权限的判定
* </pre>
*/
//Register an Activity or Fragment to handle permissions 在框架中注册一个需要处理权限的活动或者碎片
@RuntimePermissions
public abstract class PermissionCheckerDelegate extends BaseDelegate {

//本方法不是 直接拿来调用的,是用来生成代码的
// (这里具体实现,在 生成类PermissionCheckerDelegatePermissionsDispatcher中 抽象调用)
// 动态申请权限成功后,执行本方法
// .
// 所以它不能是private static,否则annotationProcessor或者注解 没办法正常读取解析
//@NeedsPermission注解的意义:
// Annotate a method which performs the action that requires one or more permissions
// 注释执行需要一个或多个权限的操作的方法
@NeedsPermission({Manifest.permission.CAMERA,Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE})
void startCamera() {
// 动态申请权限(相机、读写)成功后!!! 执行本方法!!!
XiaoYunCamera.start(this);
}

//开始请求权限
public void startCameraWithCheck() {
//开始请求权限(相机、读写)
PermissionCheckerDelegatePermissionsDispatcher.startCameraWithPermissionCheck(this);
}

//注解在当用户拒绝了权限请求时需要调用的方法上
@OnPermissionDenied({Manifest.permission.CAMERA,Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE})
void onCameraDenied() {
Toast.makeText(getContext(), "不允许拍照", Toast.LENGTH_LONG).show();
}

//注解在当用户选中了 授权窗口中的 不再询问复选框 后 并拒绝了权限请求时需要调用的方法,
// 一般可以向用户解释 为何申请此权限,并根据 实际需求 决定 是否再次弹出权限请求对话框
@OnNeverAskAgain({Manifest.permission.CAMERA,Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE})
void onCameraNever() {
Toast.makeText(getContext(), "拒绝使用相机,不再询问,下次使用请在系统设置", Toast.LENGTH_LONG).show();
}

//注解在用于向用户解释为什么需要调用该权限的方法上,
//只有当第一次请求权限被用户拒绝,下次请求权限之前会调用
@OnShowRationale({Manifest.permission.CAMERA,Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE})
void onCameraRationale(PermissionRequest request) {
showRationaleDialog(request);
}
private void showRationaleDialog(final PermissionRequest request) {
new AlertDialog.Builder(getContext())
.setPositiveButton("同意", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
request.proceed();
}
})
.setNegativeButton("拒绝", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
request.cancel();
}
})
.setCancelable(false)
.setMessage("更新头像需要使用相机或者访问本地相册喔~\n上次被您拒绝了,现在麻烦同意一下权限可以嘛老铁?")
.show();
}

//请求权限的结果处理
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);

//PermissionCheckerDelegatePermissionsDispatcher 是注解自动生成的!!
PermissionCheckerDelegatePermissionsDispatcher.onRequestPermissionsResult(this, requestCode, grantResults);
}
}
@@ -0,0 +1,134 @@
package com.lwp.xiaoyun_core.ui.camera;

import android.content.ContentValues;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.net.Uri;
import android.os.Build;
import android.provider.MediaStore;
import android.support.v7.app.AlertDialog;
import android.view.Gravity;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;

import com.blankj.utilcode.util.FileUtils;
import com.lwp.xiaoyun_core.R;
import com.lwp.xiaoyun_core.delegates.PermissionCheckerDelegate;
import com.lwp.xiaoyun_core.util.file.FileUtil;

import java.io.File;

/**
* <pre>
* author : 李蔚蓬(简书_凌川江雪)
* time : 2020/3/2 5:36
* desc : 照片处理类
* </pre>
*/
public class CameraHandler implements View.OnClickListener {

private final AlertDialog DIALOG;
private final PermissionCheckerDelegate DELEGATE;

public CameraHandler(PermissionCheckerDelegate delegate) {
this.DELEGATE = delegate;
DIALOG = new AlertDialog.Builder(delegate.getContext()).create();
}

//弹出Dialog弹框 内容:三个按钮,拍照 选图 取消
final void beginCameraDialog() {
DIALOG.show();

final Window window = DIALOG.getWindow();
if (window != null) {
window.setContentView(R.layout.dialog_camera_panel);//设置弹框布局
window.setGravity(Gravity.BOTTOM);
window.setWindowAnimations(R.style.anim_panel_up_from_bottom);
window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));

//设置属性
final WindowManager.LayoutParams params = window.getAttributes();
params.width = WindowManager.LayoutParams.MATCH_PARENT;
params.flags = WindowManager.LayoutParams.FLAG_DIM_BEHIND;
params.dimAmount = 0.5f;//设置幕布黑暗程度
window.setAttributes(params);

//设置弹框中 组件的监听
window.findViewById(R.id.photodialog_btn_cancel).setOnClickListener(this);//取消按钮
window.findViewById(R.id.photodialog_btn_take).setOnClickListener(this);//拍照按钮
window.findViewById(R.id.photodialog_btn_native).setOnClickListener(this);//本地按钮
}
}

private String getPhotoName() {
//获取一个 模板格式化后的 文件名(模板:文件头_当前时间.后缀)
return FileUtil.getFileNameByTime("IMG", "jpg");
}
//拍照取图
public void takePhoto() {
final String currentPhotoName = getPhotoName();
//拍照意图
final Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
//File 临时文件句柄 临时文件:这里是系统相册目录下的当前文件名的文件临时句柄
//CAMERA_PHOTO_DIR 系统相册目录
final File tempFile = new File(FileUtil.CAMERA_PHOTO_DIR, currentPhotoName);

//兼容7.0及以上的写法
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
final ContentValues contentValues = new ContentValues(1);
contentValues.put(MediaStore.Images.Media.DATA, tempFile.getPath());
//使用 ContentProvider 的方式
final Uri uri = DELEGATE.getContext().getContentResolver().
insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);

//需要讲Uri路径转化为实际路径
final File realFile =
FileUtils.getFileByPath(FileUtil.getRealFilePath(DELEGATE.getContext(), uri));

final Uri realUri = Uri.fromFile(realFile);
CameraImageBean.getInstance().setPath(realUri);
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
} else {

final Uri fileUri = Uri.fromFile(tempFile);
CameraImageBean.getInstance().setPath(fileUri);
intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
}

//使用startActivityForResult()的形式 启动Activity
DELEGATE.startActivityForResult(intent, RequestCodes.TAKE_PHOTO);
}

//本地取图
private void pickPhoto() {
final Intent intent = new Intent();
intent.setType("image/*");//所有的Image类型
intent.setAction(Intent.ACTION_GET_CONTENT);//获取内容
intent.addCategory(Intent.CATEGORY_OPENABLE);

//使用startActivityForResult()的形式 启动Activity
// createChooser 创建选择器
DELEGATE.startActivityForResult
(Intent.createChooser(intent, "选择获取图片的方式"), RequestCodes.PICK_PHOTO);
}


@Override
public void onClick(View v) {
int id = v.getId();

if (id == R.id.photodialog_btn_cancel) {
DIALOG.cancel();
} else if (id == R.id.photodialog_btn_take) {
//拍照取图
takePhoto();
DIALOG.cancel();
} else if (id == R.id.photodialog_btn_native) {
//本地取图
pickPhoto();
DIALOG.cancel();
}
}
}
@@ -0,0 +1,29 @@
package com.lwp.xiaoyun_core.ui.camera;

import android.net.Uri;

/**
* <pre>
* author : 李蔚蓬(简书_凌川江雪)
* time : 2020/3/2 5:39
* desc : 存储中间值
* </pre>
*/
public final class CameraImageBean {

private Uri mPath = null;

//单例模式
private static final CameraImageBean INSTANCE = new CameraImageBean();
public static CameraImageBean getInstance(){
return INSTANCE;
}

public Uri getPath() {
return mPath;
}

public void setPath(Uri mPath) {
this.mPath = mPath;
}
}
@@ -0,0 +1,19 @@
package com.lwp.xiaoyun_core.ui.camera;


import com.yalantis.ucrop.UCrop;

/**
* <pre>
* author : 李蔚蓬(简书_凌川江雪)
* time : 2020/3/2 5:38
* desc : 请求码存储
* </pre>
*/
public class RequestCodes {
public static final int TAKE_PHOTO = 4;//拍照
public static final int PICK_PHOTO = 5;//选择图片
public static final int CROP_PHOTO = UCrop.REQUEST_CROP;//剪裁
public static final int CROP_ERROR = UCrop.RESULT_ERROR;//剪裁错误
public static final int SCAN = 7;//扫描二维码
}
@@ -0,0 +1,26 @@
package com.lwp.xiaoyun_core.ui.camera;

import android.net.Uri;

import com.lwp.xiaoyun_core.delegates.PermissionCheckerDelegate;
import com.lwp.xiaoyun_core.util.file.FileUtil;

/**
* <pre>
* author : 李蔚蓬(简书_凌川江雪)
* time : 2020/3/2 5:37
* desc : 照相机功能模块函数调用等
* </pre>
*/
public class XiaoYunCamera {

//需要剪裁的文件
public static Uri createCropFile() {
return Uri.parse(FileUtil.createFile("crop_image",
FileUtil.getFileNameByTime("IMG", "jpg")).getPath());
}

public static void start(PermissionCheckerDelegate delegate) {
new CameraHandler(delegate).beginCameraDialog();//弹出Dialog弹框 内容:三个按钮,拍照 选图 取消
}
}

0 comments on commit 5ed0d2d

Please sign in to comment.