package com.duqing.missions.base.activities;
|
|
import android.Manifest;
|
import android.app.AlertDialog;
|
import android.app.ProgressDialog;
|
import android.content.Context;
|
import android.content.DialogInterface;
|
import android.content.Intent;
|
import android.content.pm.ActivityInfo;
|
import android.net.Uri;
|
import android.os.Build;
|
import android.os.Bundle;
|
import android.os.Environment;
|
import android.provider.Settings;
|
import android.util.Log;
|
import android.view.LayoutInflater;
|
import android.view.MotionEvent;
|
import android.view.View;
|
import android.view.WindowManager;
|
import android.view.inputmethod.InputMethodManager;
|
import android.widget.EditText;
|
import android.widget.Toast;
|
|
import androidx.annotation.ColorRes;
|
import androidx.annotation.Nullable;
|
import androidx.annotation.StringRes;
|
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.core.content.FileProvider;
|
import androidx.lifecycle.ViewModel;
|
import androidx.lifecycle.ViewModelProvider;
|
import androidx.viewbinding.ViewBinding;
|
|
import com.duqing.missions.MyApplication;
|
import com.duqing.missions.R;
|
import com.duqing.missions.data.ApkUpGradeResult;
|
import com.duqing.missions.util.MyLog;
|
import com.duqing.missions.util.ResPonse;
|
import com.duqing.missions.util.SpUtils;
|
import com.permissionx.guolindev.PermissionX;
|
import com.permissionx.guolindev.callback.ExplainReasonCallbackWithBeforeParam;
|
import com.permissionx.guolindev.callback.ForwardToSettingsCallback;
|
import com.permissionx.guolindev.callback.RequestCallback;
|
import com.permissionx.guolindev.request.ExplainScope;
|
import com.permissionx.guolindev.request.ForwardScope;
|
|
import java.io.File;
|
import java.io.FileOutputStream;
|
import java.io.IOException;
|
import java.io.InputStream;
|
import java.lang.reflect.Method;
|
import java.lang.reflect.ParameterizedType;
|
import java.util.Date;
|
import java.util.List;
|
|
import okhttp3.Call;
|
import okhttp3.Callback;
|
import okhttp3.OkHttpClient;
|
import okhttp3.Request;
|
import okhttp3.Response;
|
import okhttp3.ResponseBody;
|
|
/**
|
* activity 封装
|
* Created by Administrator on 2021/10/27 0027.
|
*/
|
public abstract class BaseActivity<B extends ViewBinding,VM extends ViewModel> extends AppCompatActivity {
|
|
protected B binding;
|
protected VM viewModel;
|
protected String TAG ;
|
public final String[] FILE_PERMISSIONS = new String []{Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE};
|
public final String[] LOCATION_PERMISSIONS = new String []{Manifest.permission.ACCESS_FINE_LOCATION,Manifest.permission.ACCESS_COARSE_LOCATION};
|
public final String[] CAMERA_PERMISSIONS = new String[]{ FILE_PERMISSIONS[0],FILE_PERMISSIONS[1], Manifest.permission.CAMERA};
|
public final String[] CAMERA_RECORD_PERMISSIONS = new String[]{ FILE_PERMISSIONS[0],FILE_PERMISSIONS[1], Manifest.permission.CAMERA,Manifest.permission.RECORD_AUDIO};
|
|
public static final int RESULT_LOGIN = 100,RESULT_LOGIN_RECREATE = 103,RESULT_BIND = 101,RESULT_SENDEDFILES = 105,RESULT_DISSCONNECT = 104,
|
RESULT_UPDATEUSER = 115,RESULT_LOGOUT = 113, REQUEST_CODE_ACTIVITY = 333;
|
public static final int REQUEST_CODE_LOGOUT = 20009,/*退出*/
|
REQUEST_CODE_ORDER = 10010,/*订单详情*/
|
REQUEST_CODE_LOGIN = 20010,/*登录*/
|
REQUEST_CODE_SCAN = 20011/*扫描请求*/,
|
REQUEST_CODE_PIC = 20012,/*选择图片*/
|
REQUEST_CODE_PAYPASS = 20112,/*支付验证*/
|
REQUEST_CODE_PAYPASS_FOR_ALIPAY = 20110,/*支付宝修改支付验证*/
|
REQUEST_CODE_PAYPASS_FOR_REALNAME = 20111,/*支付宝修改支付验证*/
|
REQUEST_CODE_PERMISSION = 20013,/*请求权限*/
|
REQUEST_VERSION_PERMISSION = 20013,/*检查更新*/
|
REQUEST_INSTALL_PERMISSION = 20014,/*请求安装权限*/
|
REQUEST_CODE_MANAGE_GROUP = 20015,/*选择分组*/
|
REQUEST_CODE_MANAGE_DEL = 20016,/*选择删除*/
|
REQUEST_CODE_MANAGE_EDIT = 20017,/*选择编辑*/
|
REQUEST_CODE_MANAGE_ADD = 20018,/*选择添加*/
|
REQUEST_CODE_MANAGE_DRAFT = 20019,/*选择草稿编辑*/
|
REQUEST_CODE_WEPAY = 20020,/*微信支付*/
|
REQUEST_CODE_ALIPAY = 20021,/*支付宝*/
|
REQUEST_CODE_PINKAGE = 20022,/*包邮设置*/
|
REQUEST_INSTALL_APK = 20200,/*请求安装权限*/
|
REQUEST_CODE_COMMENT_REFRESH = 20222,/*请求安装权限*/
|
RESULT_CODE_DRAFT = 4041/*草稿*/,
|
RESULT_CODE_UPDATED = 4042/*更新*/,
|
RESULT_CODE_FAILD = 4044/*失败*/,
|
RESULT_CODE_SUCESS = 4046/*成功*/,
|
RESULT_CODE_CANCEL = 4043/*取消*/;
|
protected Context mContext;
|
|
|
@Override
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
super.onCreate(savedInstanceState);
|
// get genericity "B"
|
setStatusBarBgColor(R.color.white);
|
setStatusBarTextColor(true);
|
try {
|
final ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass();
|
Class<B> entityClass = (Class<B>) type.getActualTypeArguments()[0];
|
Method method = entityClass.getMethod("inflate", LayoutInflater.class);//get method from name "inflate";
|
binding = (B) method.invoke(entityClass,getLayoutInflater());//execute method to create a objct of viewbind;
|
Class<VM> vmClass = (Class<VM>) type.getActualTypeArguments()[1];
|
viewModel = new ViewModelProvider(this).get(vmClass);
|
} catch (Exception e) {
|
e.printStackTrace();
|
}
|
setContentView(binding.getRoot());
|
mContext = this;
|
try {
|
//设置坚屏 一定要放到try catch里面,否则会崩溃
|
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
} catch (Exception e) {
|
}
|
TAG = this.getClass().getSimpleName();
|
initViews();
|
}
|
|
public abstract void initViews();
|
|
|
public void setStatusBarTransparent(boolean isBlack){
|
//透明状态栏
|
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
|
//透明导航栏
|
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
|
}
|
|
/**
|
* 修改状态栏颜色,支持4.4以上版本
|
* @param colorId
|
*/
|
public void setStatusBarBgColor(@ColorRes int colorId) {
|
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
|
getWindow().setStatusBarColor(getResources().getColor(colorId));
|
}
|
|
/**
|
* 修改状态栏文本颜色
|
* @param isBlack
|
*/
|
public void setStatusBarTextColor(boolean isBlack){
|
View decor = getWindow().getDecorView();
|
if (isBlack) {
|
decor.setSystemUiVisibility( View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
|
} else {
|
decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
|
}
|
}
|
|
/**
|
* 隐藏虚拟按键
|
*/
|
public void hideBottomUIMenu() {
|
//隐藏虚拟按键
|
if (Build.VERSION.SDK_INT > 11 && Build.VERSION.SDK_INT < 19) { // lower api
|
View v = getWindow().getDecorView();
|
v.setSystemUiVisibility(View.GONE);
|
} else if (Build.VERSION.SDK_INT >= 19) {
|
//for new api versions.
|
View decorView = getWindow().getDecorView();
|
int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY ;
|
decorView.setSystemUiVisibility(uiOptions);
|
}
|
|
}
|
|
@Override
|
public boolean dispatchTouchEvent(MotionEvent ev) {
|
if (ev.getAction() == MotionEvent.ACTION_DOWN) { //把操作放在用户点击的时候
|
View v = getCurrentFocus(); //得到当前页面的焦点,ps:有输入框的页面焦点一般会被输入框占据
|
if (isShouldHideKeyboard(v, ev)) { //判断用户点击的是否是输入框以外的区域
|
hideSoftKeyboard (); //收起键盘
|
}
|
}
|
return super.dispatchTouchEvent(ev);
|
}
|
|
/**
|
* 根据EditText所在坐标和用户点击的坐标相对比,来判断是否隐藏键盘,因为当用户点击EditText时则不能隐藏
|
*
|
* @param v
|
* @param event
|
* @return
|
*/
|
private boolean isShouldHideKeyboard(View v, MotionEvent event) {
|
if (v != null && (v instanceof EditText)) { //判断得到的焦点控件是否包含EditText
|
int[] l = {0, 0};
|
v.getLocationInWindow(l);
|
int left = l[0], //得到输入框在屏幕中上下左右的位置
|
top = l[1],
|
bottom = top + v.getHeight(),
|
right = left + v.getWidth();
|
if (event.getX() > left && event.getX() < right
|
&& event.getY() > top && event.getY() < bottom) {
|
// 点击位置如果是EditText的区域,忽略它,不收起键盘。
|
return false;
|
} else {
|
return true;
|
}
|
}
|
// 如果焦点不是EditText则忽略
|
return false;
|
}
|
/**
|
* 判断软键盘输入法是否弹出
|
*/
|
public boolean isKeyboardVisbility(Context context, View v) {
|
InputMethodManager imm = (InputMethodManager) context.getSystemService(context.INPUT_METHOD_SERVICE);
|
if (imm.hideSoftInputFromWindow(v.getWindowToken(), 0)) {
|
imm.showSoftInput(v, 0);
|
return true;//键盘显示中
|
} else {
|
return false;//键盘未显示
|
}
|
}
|
protected void hideSoftKeyboard() {
|
if (getWindow().getAttributes().softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN) {
|
if (getCurrentFocus() != null)
|
((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow(getCurrentFocus().getWindowToken(),
|
InputMethodManager.HIDE_NOT_ALWAYS);
|
}
|
}
|
/**
|
* 状态栏高度
|
* @return
|
*/
|
public int getStatusBarHeight() {
|
int result = 0;
|
int resId = getResources().getIdentifier("status_bar_height", "dimen", "android");
|
if (resId > 0) {
|
result = getResources().getDimensionPixelOffset(resId);
|
}
|
return result;
|
}
|
|
|
long mExitTime= 0 ;
|
/**
|
* 返回键退出程序
|
*/
|
public void backExit() {
|
if ((System.currentTimeMillis() - mExitTime) > 2000) {
|
Toast.makeText(this, "再按一次退出", Toast.LENGTH_SHORT).show();
|
mExitTime = System.currentTimeMillis();
|
} else {
|
//此方法导致app关闭后重启
|
MyApplication.getApplication().quitApp();
|
System.exit(0);
|
//quitApp();
|
}
|
}
|
public void showToast(String message){
|
Toast.makeText(this,message,Toast.LENGTH_SHORT).show();
|
}
|
|
public void showToast(@StringRes int msg){
|
showToast(getString(msg));
|
}
|
ApkUpGradeResult.AppInfo apkUpGrade;
|
ProgressDialog progressDialog ;
|
|
public void showUpdateDialog(){
|
if(apkUpGrade == null){
|
return;
|
}
|
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
builder.setTitle("新版本").setMessage("有新版更新\n"+apkUpGrade.content)
|
.setPositiveButton("", new DialogInterface.OnClickListener() {
|
@Override
|
public void onClick(DialogInterface dialog, int which) {
|
dialog.dismiss();
|
okHttpDownload(apkUpGrade.url, new ResPonse<String>() {
|
@Override
|
public void doSuccess(String path) {
|
apkUpGrade.remark = path;
|
openAPK(new File(path));
|
}
|
|
@Override
|
public void doError(String path) {
|
if(apkUpGrade.isImportant==1){
|
showUpdateDialog();
|
}else {
|
if(path != null) {
|
apkUpGrade.remark = path;
|
openAPK(new File(path));
|
}
|
}
|
}
|
});
|
}
|
})
|
.setNegativeButton(apkUpGrade.isImportant==1?"退出":"取消", new DialogInterface.OnClickListener() {
|
@Override
|
public void onClick(DialogInterface dialog, int which) {
|
if(apkUpGrade.isImportant==1){
|
MyApplication.getApplication().quitApp();
|
}else {
|
SpUtils.getInstance().putLongValOfProject(mContext,"version", new Date().getTime());//记录今天已经请求过更新 不在自动请求
|
}
|
}
|
});
|
}
|
|
/**
|
* okhttp下载文件
|
* @param downloadUrl
|
* @param resPonse
|
*/
|
protected void okHttpDownload(final String downloadUrl, final ResPonse<String> resPonse){
|
PermissionX.init(this).permissions(FILE_PERMISSIONS).onExplainRequestReason(new ExplainReasonCallbackWithBeforeParam() {
|
@Override
|
public void onExplainReason(ExplainScope scope, List<String> deniedList, boolean beforeRequest) {
|
scope.showRequestReasonDialog(deniedList, "即将申请的权限是程序必须依赖的权限", "继续申请","我已明白");
|
}
|
}).onForwardToSettings(new ForwardToSettingsCallback() {
|
@Override
|
public void onForwardToSettings(ForwardScope scope, List<String> deniedList) {
|
scope.showForwardToSettingsDialog(deniedList, "您需要去应用程序设置当中手动开启权限", "去设置","我已明白");
|
}
|
}).request(new RequestCallback() {
|
@Override
|
public void onResult(boolean allGranted, List<String> grantedList, List<String> deniedList) {
|
if(allGranted){
|
|
OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
|
|
Request request = new Request.Builder()
|
.url(downloadUrl)
|
.get()
|
.build();
|
|
okHttpClient.newCall(request).enqueue(new Callback() {
|
@Override
|
public void onFailure(Call call, IOException e) {
|
Log.e("tag", "onFailure: " + e.getMessage());
|
}
|
|
@Override
|
public void onResponse(Call call, Response response) throws IOException {
|
ResponseBody body = response.body(); // 获取到请求体
|
InputStream inputStream = body.byteStream(); // 转换成字节流
|
progressDialog = new ProgressDialog(mContext);
|
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
|
progressDialog.setTitle("正在下载...");
|
progressDialog.setCancelable(false);
|
progressDialog.show();
|
saveFile(inputStream, getSaveFilePath(downloadUrl), body.contentLength(),resPonse);
|
}
|
});
|
} else {
|
Toast.makeText(mContext, "您拒绝了权限申请!", Toast.LENGTH_SHORT).show();
|
}
|
}
|
});
|
}
|
|
/**
|
* @param inputStream
|
* @param filePath 存放的地址
|
* @param l 文件的长度
|
*/
|
private void saveFile(InputStream inputStream, final String filePath, final long l, final ResPonse<String> resPonse) {
|
runOnUiThread(new Runnable() {
|
@Override
|
public void run() {
|
progressDialog.setMax((int)l);// 设置进度条最大值
|
}
|
});
|
long count = 0;
|
try {
|
File file = new File(filePath);
|
Log.i(TAG, "文件保存路径:" + filePath+" 文件是否存在:"+file.exists());
|
if (!file.exists()) {//文件不存在
|
file.createNewFile();
|
}else{
|
file.delete();
|
}
|
// 获取到输出流,写入到的地址
|
FileOutputStream outputStream = new FileOutputStream(file);
|
int length = -1;
|
byte[] bytes = new byte[1024 * 10];
|
while ((length = inputStream.read(bytes)) != -1) {
|
// 写入文件
|
outputStream.write(bytes, 0, length);
|
count += length;
|
|
final long finalCount = count;
|
final int finalLenght = length;
|
runOnUiThread(new Runnable() {
|
@Override
|
public void run() {
|
progressDialog.setProgress((int) finalCount); // 设置进度
|
}
|
});
|
}
|
inputStream.close(); // 关闭输入流
|
outputStream.close(); // 关闭输出流
|
runOnUiThread(new Runnable() {
|
@Override
|
public void run() {
|
// 如果写入的进度值完毕,Toast
|
progressDialog.dismiss();
|
resPonse.doSuccess(filePath);
|
}
|
});
|
} catch (Exception e) {
|
e.printStackTrace();
|
}
|
}
|
|
|
/**
|
* 获取文件保存路径 sdcard根目录/download/文件名称
|
* @param fileUrl
|
* @return
|
*/
|
public static String getSaveFilePath(String fileUrl){
|
String fileName=fileUrl.substring(fileUrl.lastIndexOf("/")+1,fileUrl.length());//获取文件名称
|
String storePath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "Download";
|
File appDir = new File(storePath);
|
if (!appDir.exists()) {
|
appDir.mkdirs();
|
}
|
return storePath + File.separator +fileName;
|
}
|
|
/**
|
* 打开apk文件
|
* @param file
|
*/
|
public void openAPK(final File file){
|
|
//android 8及以上需要设置权限
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !getPackageManager().canRequestPackageInstalls()) {
|
// 申请安装权限。
|
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
builder.setTitle("需要允许安装权限").setMessage("应用需要手动设置,才能打开安装包升级应用").setCancelable(false)
|
.setNegativeButton("", new DialogInterface.OnClickListener() {
|
@Override
|
public void onClick(DialogInterface dialog, int which) {
|
Uri packageUri = Uri.parse("package:" + getPackageName());
|
Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, packageUri);
|
startActivityForResult(intent, REQUEST_INSTALL_PERMISSION);
|
dialog.dismiss();
|
}
|
})
|
.setPositiveButton(apkUpGrade.isImportant==1?"退出":"取消", new DialogInterface.OnClickListener() {
|
@Override
|
public void onClick(DialogInterface dialog, int which) {
|
if(apkUpGrade.isImportant==1){
|
MyApplication.getApplication().quitApp();
|
}
|
}
|
}).create().show();
|
return;
|
}
|
|
Intent intent = new Intent();
|
intent.setAction(Intent.ACTION_VIEW);
|
Uri photoURI = null ;
|
if(Build.VERSION.SDK_INT>= Build.VERSION_CODES.N) { //判读版本是否在7.0以上
|
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
photoURI = FileProvider.getUriForFile(mContext, mContext.getApplicationContext().getPackageName() + ".provider", file);//添加这一句表示对目标应用临时授权该Uri所代表的文件
|
|
}else{
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
photoURI = Uri.fromFile(file);
|
}
|
intent.setDataAndType(photoURI,"application/vnd.android.package-archive");
|
startActivityForResult(intent,REQUEST_INSTALL_APK);
|
}
|
|
@Override
|
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
|
super.onActivityResult(requestCode, resultCode, data);
|
MyLog.i(TAG,String.format("onActivityResult requestCode:%s, resultCode:%s, data:%s",requestCode, resultCode, data));
|
if(requestCode == REQUEST_INSTALL_PERMISSION){
|
openAPK(new File(apkUpGrade.remark));
|
}else if(requestCode == REQUEST_INSTALL_APK && apkUpGrade.isImportant==1){
|
/*showDialog("强制更新", "需要安装新版本才能继续使用","安装","退出", new ResPonse() {
|
@Override
|
public void doSuccess(Object obj) {
|
|
}
|
|
@Override
|
public void doError(Object obj) {
|
super.doError(obj);
|
}
|
});*/
|
}
|
}
|
|
|
protected boolean onBackKeyDown() {
|
onBackPressed();
|
return false;
|
}
|
|
}
|