当前位置:   article > 正文

Android 10(Android Q) 适配_setfullscreenintent

setfullscreenintent

官方网站-沙盒存储

Android Q 中的隐私权-重大隐私权变更

官方网站-展示时间敏感的通知

1. 设备硬件信息读取限制

在Android10中, 系统不允许普通App请求android.permission.READ_PHONE_STATE权限, 故新版App需要取消该动态权限的申请。

当前获取设备唯一ID的方式为使用SSAID, 若获取为空的话则使用UUID.randomUUID().toString()获得一个随机ID并存储起来, 该ID保证唯一, 但App卸载重装之后就会改变

SSAID的获取方式为:

  1. String id = android.provider.Settings.Secure.getString(context.getContentResolver(), android.provider.Settings.Secure.ANDROID_ID);
  2. 复制代码

2. 后台App启动限制

Android10中, 当App无前台显示的Activity时,其启动Activity会被系统拦截, 导致启动无效。

个人觉得官方这样做的目的是在用户使用的过程中, 不希望被其他App强制打断, 但这样就对闹钟类, 带呼叫功能的App不太友好了。

对此官方给予的折中方案是使用全屏Intent(full-screen intent), 既创建通知栏通知时, 加入full-screen intent 设置, 示例代码如下(基于官方文档修改):

  1. Intent fullScreenIntent = new Intent(this, CallActivity.class);
  2. PendingIntent fullScreenPendingIntent = PendingIntent.getActivity(this, 0,
  3. fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT);
  4. NotificationCompat.Builder notificationBuilder =
  5. new NotificationCompat.Builder(this, CHANNEL_ID)
  6. .setSmallIcon(R.drawable.notification_icon)
  7. .setContentTitle("Incoming call")
  8. .setContentText("(919) 555-1234")
  9. //以下为关键的3行
  10. .setPriority(NotificationCompat.PRIORITY_HIGH)
  11. .setCategory(NotificationCompat.CATEGORY_CALL)
  12. .setFullScreenIntent(fullScreenPendingIntent, true);
  13. NotificationManager notifyManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
  14. notifyManager.notify(notifyId, builder.build());
  15. 复制代码

注意在Target SDk为29及以上时,需要在AndroidManifest上增加USE_FULL_SCREEN_INTENT申明

  1. //AndroidManifest中
  2. <uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
  3. 复制代码

测试下来, 当手机处于亮屏状态时, 会显示一个通知栏, 当手机处于锁屏或者灭屏状态时,会亮屏并直接进入到CallActivity中

3. App沙盒化存储

在Android10上, 当App的target sdk为29及以上时或者在AndroidManifest中申明时, App即使有外部存储的写入权限, 也无法直接通过路径访问外部存储

疑惑

网上有些其他的文章有一些说法

新的存储权限取消并替换了READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE权限,如果要访问沙盒外的媒体共享文件,比如照片,音乐,视频等,需要申请新的媒体权限:READ_MEDIA_IMAGES,READ_MEDIA_VIDEO,READ_MEDIA_AUDIO,申请方法同原来的存储权限

我在实际测试中并没有找到这3个权限类型, 对此查看了官方文档

官方文档的原文为:

 

Snipaste_2019-09-19_21-58-41.png

 

 

大致意思是当你访问沙盒内部时,是不需要READ_EXTERNAL_STORAGE 或 WRITE_EXTERNAL_STORAGE权限的, 若要访问外部存储, 需要拥有READ_EXTERNAL_STORAGE 权限并且使用MediaStore来进行访问

所以我觉得在Android 10上, 若要访问外部文件, 还是需要进行READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE 动态权限的申请的

适配方案

对应App的数据, 需要存储到App的沙盒中, 对应的路径为下

文件类型地址
视频文件context.getExternalFilesDir(Environment.DIRECTORY_MOVIES)
音频文件context.getExternalFilesDir(Environment.DIRECTORY_MUSIC)
图片文件context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
  • 存储到沙盒中的这些地址的实际路径在/data/user/0/包名/中,所以这些文件在App卸载之后会被清除
  • 在这些目录里的文件受系统保护, 其他App无法直接对其访问

若需要将沙盒中的视频或图片文件保存在外部存储时, 需要使用ContentValues和MediaStore来进行操作,对此我封装了一个工具类:

  1. public static boolean SavePictureFile(Context context, File file) {
  2. if (file == null) {
  3. return false;
  4. }
  5. Uri uri = insertFileIntoMediaStore(context, file, true);
  6. return SaveFile(context, file, uri);
  7. }
  8. public static boolean SaveVideoFile(Context context, File file) {
  9. if (file == null) {
  10. return false;
  11. }
  12. Uri uri = insertFileIntoMediaStore(context, file, false);
  13. return SaveFile(context, file, uri);
  14. }
  15. private static boolean SaveFile(Context context, File file, Uri uri) {
  16. if (uri == null) {
  17. LogUtil.e("url is null");
  18. return false;
  19. }
  20. LogUtil.i("SaveFile: " + file.getName());
  21. ContentResolver contentResolver = context.getContentResolver();
  22. ParcelFileDescriptor parcelFileDescriptor = null;
  23. try {
  24. parcelFileDescriptor = contentResolver.openFileDescriptor(uri, "w");
  25. } catch (FileNotFoundException e) {
  26. e.printStackTrace();
  27. }
  28. if (parcelFileDescriptor == null) {
  29. LogUtil.e("parcelFileDescriptor is null");
  30. return false;
  31. }
  32. FileOutputStream outputStream = new FileOutputStream(parcelFileDescriptor.getFileDescriptor());
  33. FileInputStream inputStream;
  34. try {
  35. inputStream = new FileInputStream(file);
  36. } catch (FileNotFoundException e) {
  37. LogUtil.e(e.toString());
  38. try {
  39. outputStream.close();
  40. } catch (IOException ex) {
  41. LogUtil.e(ex.toString());
  42. }
  43. return false;
  44. }
  45. try {
  46. copy(inputStream, outputStream);
  47. } catch (IOException e) {
  48. LogUtil.e(e.toString());
  49. return false;
  50. } finally {
  51. try {
  52. outputStream.close();
  53. } catch (IOException e) {
  54. LogUtil.e(e.toString());
  55. }
  56. try {
  57. inputStream.close();
  58. } catch (IOException e) {
  59. LogUtil.e(e.toString());
  60. }
  61. }
  62. return true;
  63. }
  64. //注意当文件比较大时该方法耗时会比较大
  65. private static void copy(InputStream ist, OutputStream ost) throws IOException {
  66. byte[] buffer = new byte[4096];
  67. int byteCount;
  68. while ((byteCount = ist.read(buffer)) != -1) {
  69. ost.write(buffer, 0, byteCount);
  70. }
  71. ost.flush();
  72. }
  73. //创建视频或图片的URI
  74. private static Uri insertFileIntoMediaStore(Context context, File file, boolean isPicture) {
  75. ContentValues contentValues = new ContentValues();
  76. contentValues.put(MediaStore.Video.Media.DISPLAY_NAME, file.getName());
  77. contentValues.put(MediaStore.Video.Media.MIME_TYPE, isPicture ? "image/jpeg" : "video/mp4");
  78. if (Build.VERSION.SDK_INT >= 29) {
  79. contentValues.put(MediaStore.Video.Media.DATE_TAKEN, file.lastModified());
  80. }
  81. Uri uri = null;
  82. try {
  83. uri = context.getContentResolver().insert(
  84. (isPicture ? MediaStore.Images.Media.EXTERNAL_CONTENT_URI : MediaStore.Video.Media.EXTERNAL_CONTENT_URI)
  85. , contentValues
  86. );
  87. } catch (Exception e) {
  88. e.printStackTrace();
  89. }
  90. return uri;
  91. }
  92. 复制代码
  • Android 10 之前, 往系统文件夹写入文件需要文件读写权限, Android10中, 可以无需权限直接往系统文件夹写入文件
  • 默认保存的地址, 视频默认在Movies中, 图片默认在Pictures中, 若想保持到对应的子文件夹中, 则需要以下设置
  1. //注意MediaStore.Images.Media.RELATIVE_PATH需要compileSdkVersion=29,
  2. //故该方法只可在Android10的手机上执行
  3. //图片, 对应存储的地址为 Pictures/test
  4. contentValues.put(MediaStore.Images.Media.RELATIVE_PATH,
  5. Environment.DIRECTORY_PICTURES + File.separator + test);
  6. //视频, 对应存储的地址为 Movies/test
  7. contentValues.put(MediaStore.Video.Media.RELATIVE_PATH,
  8. Environment.DIRECTORY_MOVIES + File.separator + test);
  9. 复制代码

结语

以上是在适配中的总结。

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/247631
推荐阅读
相关标签
  

闽ICP备14008679号