今天产品又有特大喜讯啦,App要添加新功能了普(ma)天(de)同(zhi)庆(zhang)~~~
登陆页面就强制用户更新。。。
脑壳疼+1
写吧
首先是三个工具类
apk
public class InstallApk {
Activity context;
public InstallApk(Activity context) {
this.context = context;
}
public void installApk(File apkFile) {
Intent intent = new Intent(Intent.ACTION_VIEW);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
boolean b = context.getPackageManager().canRequestPackageInstalls();
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Uri contentUri = FileProvider.getUriForFile(context.getApplicationContext(),
BuildConfig.APPLICATION_ID+".fileProvider", apkFile);
intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
context.startActivity(intent);
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Uri contentUri = FileProvider.getUriForFile(context.getApplicationContext(),
BuildConfig.APPLICATION_ID+".fileProvider", apkFile);
intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
context.startActivity(intent);
} else {
intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
}
}
}
头布局
public class DownFileHelper {
private static final String TAG = DownFileHelper.class.getSimpleName();
Handler handler;
Context mContext;
NotificationManager mNotifyManager;
Notification.Builder builder;
private CommonProgressDialog mDialog;
private RemoteViews contentView;
public DownFileHelper(Context mContext, Handler handler) {
this.handler = handler;
this.mContext = mContext;
}
private void createNotification(final long total, final long current) {
mNotifyManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext);
builder.setSmallIcon(R.mipmap.ic_launcher);//必须要设置这个属性,否则不显示
contentView = new RemoteViews(mContext.getPackageName(), R.layout.common_progress_dialog);
contentView.setProgressBar(R.id.progress, (int) total, (int) current, true);
builder.setOngoing(true);//设置左右滑动不能删除
Notification notification = builder.build();
notification.contentView = contentView;
mNotifyManager.notify(R.layout.common_progress_dialog, notification);//发送通知
}
/**
* 下载最新版本的apk
*
* @param path apk下载地址
*/
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
public void downFile(final String path) {
mDialog = new CommonProgressDialog(mContext);
mDialog.setMessage("正在下载");
mDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mDialog.setMax(100);
mDialog.setIndeterminate(true);
mDialog.setCancelable(true);
mDialog.show();
mNotifyManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
Bitmap btm = BitmapFactory.decodeResource(mContext.getResources(), R.mipmap.ic_launcher);//可以换成你的app的logo
if (Build.VERSION.SDK_INT >= 26) {
//创建 通知通道 channelid和channelname是必须的(自己命名就好)
@SuppressLint("WrongConstant")
NotificationChannel channel = new NotificationChannel("1",
"Channel1", NotificationManager.IMPORTANCE_LOW);
// channel.enableLights(true);//是否在桌面icon右上角展示小红点
channel.setLightColor(Color.GREEN);//小红点颜色
channel.setShowBadge(true); //是否在久按桌面图标时显示此渠道的通知
channel.enableLights(false);
channel.enableVibration(false);
// channel.setVibrationPattern(new long[]{0});
channel.setSound(null, null);
mNotifyManager.createNotificationChannel(channel);
builder = new Notification.Builder(mContext, "1");
//设置通知显示图标、文字等
builder.setSmallIcon(R.mipmap.ic_launcher)//可以换成你的app的logo
.setLargeIcon(btm)
.setTicker("正在下载")
.setContentTitle("我的app")
.setAutoCancel(true)
.build();
mNotifyManager.notify(1, builder.build());
} else {
builder = new Notification.Builder(mContext);
builder.setSmallIcon(R.drawable.ic_danmuku_off)//可以换成你的app的logo
.setLargeIcon(btm)
.setTicker("正在下载")
.setContentTitle("我的app")
.setAutoCancel(true)//可以滑动删除通知栏
.build();
mNotifyManager.notify(1, builder.build());
}
new Thread() {
public void run() {
try {
URL url = new URL(path);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setReadTimeout(5000);
con.setConnectTimeout(5000);
con.setRequestProperty("Charset", "UTF-8");
con.setRequestMethod("GET");
if (con.getResponseCode() == 200) {
int length = con.getContentLength();// 获取文件大小
InputStream is = con.getInputStream();
FileOutputStream fileOutputStream = null;
if (is != null) {
//对apk进行保存
File file = new File(Environment.getExternalStorageDirectory()
.getPath(), "your_app_name.apk");
fileOutputStream = new FileOutputStream(file);
byte[] buf = new byte[1024];
int ch;
int process = 0;
NumberFormat numberFormat = NumberFormat.getInstance();
// 设置精确到小数点后2位
numberFormat.setMaximumFractionDigits(2);
String result;
while ((ch = is.read(buf)) != -1) {
fileOutputStream.write(buf, 0, ch);
process += ch;
//更新进度条
result = numberFormat.format((float) process / (float) length * 100);
mDialog.setProgress((int) ((float) process / (float) length * 100));
builder.setContentText("下载进度:" + result + "%");
builder.setProgress(length, process, false);
mNotifyManager.notify(1, builder.build());
}
}
if (fileOutputStream != null) {
fileOutputStream.flush();
fileOutputStream.close();
}
//apk下载完成,使用Handler()通知安装apk
builder.setProgress(length, length, false);
builder.setContentText("已经下载完成");
mNotifyManager.notify(1, builder.build());
mNotifyManager.cancelAll();
handler.sendEmptyMessage(0);
} else {
Log.e(TAG, "run: ResponseCode"+ con.getResponseCode());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
}
Dialog
public class CommonProgressDialog extends AlertDialog {
private ProgressBar mProgress;
private TextView mProgressNumber;
private TextView mProgressPercent;
private TextView mProgressMessage;
private Handler mViewUpdateHandler;
private int mMax;
private CharSequence mMessage;
private boolean mHasStarted;
private int mProgressVal;
private String TAG = "CommonProgressDialog";
private String mProgressNumberFormat;
private NumberFormat mProgressPercentFormat;
private TextView tvCancel;
public CommonProgressDialog(Context context) {
super(context);
// TODO Auto-generated constructor stub
initFormats();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.common_progress_dialog);
mProgress = (ProgressBar) findViewById(R.id.progress);
mProgressNumber = (TextView) findViewById(R.id.progress_number);
mProgressPercent = (TextView) findViewById(R.id.progress_percent);
mProgressMessage = (TextView) findViewById(R.id.progress_message);
// LayoutInflater inflater = LayoutInflater.from(getContext());
mViewUpdateHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
int progress = mProgress.getProgress();
int max = mProgress.getMax();
double dProgress = (double)progress;
double dMax = (double)max;
if (mProgressNumberFormat != null) {
String format = mProgressNumberFormat;
mProgressNumber.setText(String.format(format, dProgress, dMax));
} else {
mProgressNumber.setText("");
}
if (mProgressPercentFormat != null) {
double percent = (double) progress / (double) max;
SpannableString tmp = new SpannableString(mProgressPercentFormat.format(percent));
tmp.setSpan(new StyleSpan(android.graphics.Typeface.BOLD),
0, tmp.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
mProgressPercent.setText(tmp);
} else {
mProgressPercent.setText("");
}
}
};
onProgressChanged();
if (mMessage != null) {
setMessage(mMessage);
}
if (mMax > 0) {
setMax(mMax);
}
if (mProgressVal > 0) {
setProgress(mProgressVal);
}
}
private void initFormats() {
mProgressNumberFormat = "%s/%s";
mProgressPercentFormat = NumberFormat.getPercentInstance();
mProgressPercentFormat.setMaximumFractionDigits(0);
}
private void onProgressChanged() {
mViewUpdateHandler.sendEmptyMessage(0);
}
public void setProgressStyle(int style) {
//mProgressStyle = style;
}
public int getMax() {
if (mProgress != null) {
return mProgress.getMax();
}
return mMax;
}
public void setMax(int max) {
if (mProgress != null) {
mProgress.setMax(max);
onProgressChanged();
} else {
mMax = max;
}
}
public void setIndeterminate(boolean indeterminate) {
if (mProgress != null) {
mProgress.setIndeterminate(indeterminate);
}
}
public void setProgress(int value) {
if (mHasStarted) {
mProgress.setProgress(value);
onProgressChanged();
} else {
mProgressVal = value;
}
}
@Override
public void setMessage(CharSequence message) {
// TODO Auto-generated method stub
if(mProgressMessage!=null){
mProgressMessage.setText(message);
}
else{
mMessage = message;
}
}
@Override
protected void onStart() {
// TODO Auto-generated method stub
super.onStart();
mHasStarted = true;
}
@Override
protected void onStop() {
// TODO Auto-generated method stub
super.onStop();
mHasStarted = false;
}
}
然后是布局类
activity_update.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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="match_parent"
>
<Button
android:gravity="center"
android:id="@+id/btn_install"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="安装"
/>
</android.support.constraint.ConstraintLayout>
common_progress_dialog.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!--@drawable/common_progress_dialog_background"-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="#ffffff">
<!--Title-->
<TextView
android:id="@+id/progress_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:layout_marginTop="30dp"
android:layout_gravity="center_horizontal"
android:textColor="#000"
android:text="234"
/>
<!--进度-->
<ProgressBar
android:id="@+id/progress"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="10dp"
android:layout_marginTop="10dp"
android:layout_marginLeft="30dp"
android:layout_marginRight="30dp"
android:layout_centerHorizontal="true"
android:progressDrawable="@drawable/seekbar_style"
/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="30dp"
android:layout_marginRight="30dp"
>
<!--左边的-->
<TextView
android:id="@+id/progress_percent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:textSize="18sp"
android:gravity="center_horizontal"
android:textColor="#000"
android:text="3243"
/>
<!--右边的-->
<TextView
android:id="@+id/progress_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="30dp"
android:textSize="16sp"
android:gravity="center_horizontal"
android:textColor="#000"
android:text="23424sdfsf2"
android:layout_alignParentRight="true"
/>
</RelativeLayout>
</LinearLayout>
</FrameLayout>
drawable文件夹资源文件seekbar_style.xml
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:id="@android:id/background">
<shape >
<corners android:radius="10dip" />
<gradient
android:angle="270"
android:centerColor="#151515"
android:centerY="0.45"
android:endColor="#151515"
android:startColor="#151515" />
</shape>
</item>
<item android:id="@android:id/secondaryProgress">
<clip >
<shape >
<corners android:radius="10dip" />
<gradient
android:angle="270"
android:centerColor="#333333"
android:centerY="0.45"
android:endColor="#333333"
android:startColor="#333333" />
</shape>
</clip>
</item>
<item android:id="@android:id/progress" >
<clip >
<shape >
<corners android:radius="10dip" />
<gradient
android:angle="270"
android:centerColor="#a07e5d"
android:centerY="0.45"
android:endColor="#a07e5d"
android:startColor="#a07e5d"
/>
</shape>
</clip>
</item>
</layer-list>
res内新建一个xml文件夹内设置file_provider.xml
<?xml version="1.0" encoding="utf-8"?>
<paths>
<!--1、对应内部内存卡根目录:Context.getFileDir()-->
<!--<files-path-->
<!--name="int_root"-->
<!--path="/" />-->
<!--2、对应应用默认缓存根目录:Context.getCacheDir()-->
<!--<cache-path-->
<!--name="app_cache"-->
<!--path="/" />-->
<!--3、对应外部内存卡根目录:Environment.getExternalStorageDirectory()-->
<!--<external-path-->
<!--name="ext_root"-->
<!--path="pictures/" />-->
<!--4、对应外部内存卡根目录下的APP公共目录:Context.getExternalFileDir(String)-->
<!--<external-files-path-->
<!--name="ext_pub"-->
<!--path="/" />-->
<!--5、对应外部内存卡根目录下的APP缓存目录:Context.getExternalCacheDir()-->
<!--<external-cache-path-->
<!--name="ext_cache"-->
<!--path="/" />-->
<external-path path="Android/com.guorentong.learn.myapplication/" name="files_root" />
<external-path path="." name="external_storage_root" />
</paths>
注意path="Android/com.guorentong.learn.myapplication/"要和自己的报名一致
然后清单文件里application标签内设置
<!-- FileProvider配置访问路径,适配7.0及其以上 -->
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.guorentong.learn.myapplication.fileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_provider" />
</provider>
在Activity中设置
//版本更新
private String[] permissionStr = new String[]{
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 0:
new InstallApk(HomeActivity.this)
.installApk(new File(Environment.getExternalStorageDirectory(), "your_app_name.apk"));
break;
}
}
};
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
public void permissionsCheckAndDownload() {
if (Build.VERSION.SDK_INT >= 23) {
applyPermission();
} else {
new DownFileHelper(HomeActivity.this, handler)
.downFile(mUrl);
}
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
private void applyPermission() {
/**
* 第 1 步: 检查是否有相应的权限
*/
boolean isAllGranted = checkPermissionAllGranted(permissionStr);
// 如果这3个权限全都拥有, 则直接执行备份代码
if (!isAllGranted) {
/**
* 第 2 步: 请求权限
*/
// 一次请求多个权限, 如果其他有权限是已经授予的将会自动忽略掉
ActivityCompat.requestPermissions(this, permissionStr,1);
} else {
new DownFileHelper(HomeActivity.this, handler)
.downFile(mUrl);
}
}
/**
* 检查是否拥有指定的所有权限
*/
private boolean checkPermissionAllGranted(String[] permissions) {
for (String permission : permissions) {
if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
// 只要有一个权限没有被授予, 则直接返回 false
return false;
}
}
return true;
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
boolean isAllGranted = true;
// 判断是否所有的权限都已经授予了
for (int grant : grantResults) {
if (grant != PackageManager.PERMISSION_GRANTED) {
isAllGranted = false;
break;
}
}
if (isAllGranted) {
// 如果所有的权限都授予了, 则执行备份代码
new DownFileHelper(HomeActivity.this, handler)
.downFile(mUrl);
} else {
// 弹出对话框告诉用户需要权限的原因, 并引导用户去应用权限管理中手动打开权限按钮
openAppDetails();
}
}
private void openAppDetails() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("备份通讯录需要访问 “通讯录” 和 “外部存储器”,请到 “应用信息 -> 权限” 中授予!");
builder.setPositiveButton("去手动授权", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setData(Uri.parse("package:" + getPackageName()));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
startActivity(intent);
}
});
builder.setNegativeButton("取消", null);
builder.show();
}
使用的时候判断版本,或者返回字段调用方法
permissionsCheckAndDownload();
就可以了
别忘了设置权限允许未知程序安装
//安装权限 <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
如果需要改变样式可以自己修改
最好 其实 是不用activity开启更新,而采用非绑定式服务。这样后台运行也可以继续下载避免被杀死,防止下载停止。