diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 11226f4c..86b0abf2 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -6,9 +6,16 @@
-
-
+
+
+
+
diff --git a/app/src/main/java/com/arialyy/simple/MainActivity.java b/app/src/main/java/com/arialyy/simple/MainActivity.java
index 3769f716..8502c8c5 100644
--- a/app/src/main/java/com/arialyy/simple/MainActivity.java
+++ b/app/src/main/java/com/arialyy/simple/MainActivity.java
@@ -17,11 +17,17 @@
package com.arialyy.simple;
import android.Manifest;
+import android.annotation.SuppressLint;
import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
+import android.os.Environment;
+import android.provider.Settings;
import android.view.View;
import android.widget.TextView;
+
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatImageView;
import androidx.lifecycle.Observer;
@@ -29,6 +35,7 @@ import androidx.lifecycle.ViewModelProviders;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
+
import com.arialyy.frame.permission.OnPermissionCallback;
import com.arialyy.frame.permission.PermissionManager;
import com.arialyy.frame.util.show.T;
@@ -49,6 +56,7 @@ import com.arialyy.simple.core.upload.SFtpUploadActivity;
import com.arialyy.simple.databinding.ActivityMainBinding;
import com.arialyy.simple.modlue.CommonModule;
import com.arialyy.simple.to.NormalTo;
+
import java.util.ArrayList;
import java.util.List;
@@ -57,130 +65,175 @@ import java.util.List;
* 首页
*/
public class MainActivity extends BaseActivity {
- public static final String KEY_MAIN_DATA = "KEY_MAIN_DATA";
+ public static final String KEY_MAIN_DATA = "KEY_MAIN_DATA";
+ private static final int REQUEST_MANAGE_EXTERNAL_STORAGE = 1001;
- @Override protected void init(Bundle savedInstanceState) {
- super.init(savedInstanceState);
- setSupportActionBar(mBar);
- mBar.setTitle("Aria Demo");
- getSupportActionBar().setDisplayHomeAsUpEnabled(false);
+ @Override
+ protected void init(Bundle savedInstanceState) {
+ super.init(savedInstanceState);
+ setSupportActionBar(mBar);
+ mBar.setTitle("Aria Demo");
+ getSupportActionBar().setDisplayHomeAsUpEnabled(false);
- getBinding().list.setLayoutManager(new LinearLayoutManager(this));
- final List data = new ArrayList<>();
- final Adapter adapter = new Adapter(this, data);
- getBinding().list.setAdapter(adapter);
- getBinding().list.addItemDecoration(
- new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
+ getBinding().list.setLayoutManager(new LinearLayoutManager(this));
+ final List data = new ArrayList<>();
+ final Adapter adapter = new Adapter(this, data);
+ getBinding().list.setAdapter(adapter);
+ getBinding().list.addItemDecoration(
+ new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
- final CommonModule module = ViewModelProviders.of(this).get(CommonModule.class);
- module.getMainData(this).observe(this, new Observer>() {
- @Override public void onChanged(@Nullable List normalTos) {
- if (normalTos != null) {
- data.addAll(normalTos);
- adapter.notifyDataSetChanged();
- }
- }
- });
-
- RvItemClickSupport.addTo(getBinding().list).setOnItemClickListener(
- new RvItemClickSupport.OnItemClickListener() {
- @Override public void onItemClicked(RecyclerView recyclerView, int position, View v) {
- switch (position) {
- case 0:
- module.startNextActivity(MainActivity.this, data.get(position),
- DownloadActivity.class);
- break;
- case 1:
- module.startNextActivity(MainActivity.this, data.get(position),
- HttpUploadActivity.class);
- break;
- case 2:
- module.startNextActivity(MainActivity.this, data.get(position),
- DownloadGroupActivity.class);
- break;
- case 3:
- module.startNextActivity(MainActivity.this, data.get(position),
- FtpDownloadActivity.class);
- break;
- case 4:
- module.startNextActivity(MainActivity.this, data.get(position),
- FTPDirDownloadActivity.class);
- break;
- case 5:
- module.startNextActivity(MainActivity.this, data.get(position),
- FtpUploadActivity.class);
- break;
- case 6:
- module.startNextActivity(MainActivity.this, data.get(position),
- M3U8VodDLoadActivity.class);
- break;
- case 7:
- module.startNextActivity(MainActivity.this, data.get(position),
- M3U8LiveDLoadActivity.class);
- break;
- case 8: // d_sftp
- module.startNextActivity(MainActivity.this, data.get(position),
- SFtpDownloadActivity.class);
- break;
- case 9: // u_sftp
- module.startNextActivity(MainActivity.this, data.get(position),
- SFtpUploadActivity.class);
- break;
+ final CommonModule module = ViewModelProviders.of(this).get(CommonModule.class);
+ module.getMainData(this).observe(this, new Observer>() {
+ @Override
+ public void onChanged(@Nullable List normalTos) {
+ if (normalTos != null) {
+ data.addAll(normalTos);
+ adapter.notifyDataSetChanged();
+ }
}
- }
});
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- boolean hasPermission = PermissionManager.getInstance()
- .checkPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
- if (!hasPermission) {
- PermissionManager.getInstance().requestPermission(this, new OnPermissionCallback() {
- @Override public void onSuccess(String... permissions) {
- }
+ RvItemClickSupport.addTo(getBinding().list).setOnItemClickListener(
+ new RvItemClickSupport.OnItemClickListener() {
+ @Override
+ public void onItemClicked(RecyclerView recyclerView, int position, View v) {
+ switch (position) {
+ case 0:
+ module.startNextActivity(MainActivity.this, data.get(position),
+ DownloadActivity.class);
+ break;
+ case 1:
+ module.startNextActivity(MainActivity.this, data.get(position),
+ HttpUploadActivity.class);
+ break;
+ case 2:
+ module.startNextActivity(MainActivity.this, data.get(position),
+ DownloadGroupActivity.class);
+ break;
+ case 3:
+ module.startNextActivity(MainActivity.this, data.get(position),
+ FtpDownloadActivity.class);
+ break;
+ case 4:
+ module.startNextActivity(MainActivity.this, data.get(position),
+ FTPDirDownloadActivity.class);
+ break;
+ case 5:
+ module.startNextActivity(MainActivity.this, data.get(position),
+ FtpUploadActivity.class);
+ break;
+ case 6:
+ module.startNextActivity(MainActivity.this, data.get(position),
+ M3U8VodDLoadActivity.class);
+ break;
+ case 7:
+ module.startNextActivity(MainActivity.this, data.get(position),
+ M3U8LiveDLoadActivity.class);
+ break;
+ case 8: // d_sftp
+ module.startNextActivity(MainActivity.this, data.get(position),
+ SFtpDownloadActivity.class);
+ break;
+ case 9: // u_sftp
+ module.startNextActivity(MainActivity.this, data.get(position),
+ SFtpUploadActivity.class);
+ break;
+ }
+ }
+ });
- @Override public void onFail(String... permissions) {
- T.showShort(MainActivity.this, "没有文件读写权限");
- finish();
- }
- }, Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE);
- }
- }
- }
-
- @Override protected int setLayoutId() {
- return R.layout.activity_main;
- }
-
- private static class Adapter extends AbsRVAdapter {
-
- Adapter(Context context, List data) {
- super(context, data);
+ checkPermission();
}
- @Override protected Holder getViewHolder(View convertView, int viewType) {
- return new Holder(convertView);
+ @Override
+ protected int setLayoutId() {
+ return R.layout.activity_main;
}
- @Override protected int setLayoutId(int type) {
- return R.layout.item_main;
+ /**
+ * Android版本适配权限检查,兼容分区存储
+ *
+ * - Android 11+ (API 30+): 需要 {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE},跳转系统设置页授权
+ * - Android 6-10 (API 23-29): 使用传统 {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} / {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} 运行时权限
+ * - Android 5.x 及以下 (API < 23): 安装时授权,无需运行时处理
+ *
+ */
+ @SuppressLint("InlinedApi")
+ private void checkPermission() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ // Android 11+:使用 MANAGE_EXTERNAL_STORAGE 获取所有文件访问权限
+ if (!Environment.isExternalStorageManager()) {
+ Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
+ intent.setData(Uri.parse("package:" + getPackageName()));
+ startActivityForResult(intent, REQUEST_MANAGE_EXTERNAL_STORAGE);
+ }
+ } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ // Android 6-10:传统存储权限
+ boolean hasPermission = PermissionManager.getInstance()
+ .checkPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
+ if (!hasPermission) {
+ PermissionManager.getInstance().requestPermission(this, new OnPermissionCallback() {
+ @Override
+ public void onSuccess(String... permissions) {
+ }
+
+ @Override
+ public void onFail(String... permissions) {
+ T.showShort(MainActivity.this, "没有文件读写权限");
+ finish();
+ }
+ }, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE);
+ }
+ }
}
- @Override protected void bindData(Holder holder, int position, NormalTo item) {
- holder.title.setText(item.title);
- holder.desc.setText(item.desc);
- holder.image.setImageResource(item.icon);
+ @SuppressLint("InlinedApi")
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == REQUEST_MANAGE_EXTERNAL_STORAGE) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ if (!Environment.isExternalStorageManager()) {
+ T.showShort(MainActivity.this, "需要授予所有文件管理权限");
+ finish();
+ }
+ }
+ }
}
- private static class Holder extends AbsHolder {
- TextView title, desc;
- AppCompatImageView image;
+ private static class Adapter extends AbsRVAdapter {
- Holder(View itemView) {
- super(itemView);
- title = findViewById(R.id.title);
- desc = findViewById(R.id.desc);
- image = findViewById(R.id.image);
- }
+ Adapter(Context context, List data) {
+ super(context, data);
+ }
+
+ @Override
+ protected Holder getViewHolder(View convertView, int viewType) {
+ return new Holder(convertView);
+ }
+
+ @Override
+ protected int setLayoutId(int type) {
+ return R.layout.item_main;
+ }
+
+ @Override
+ protected void bindData(Holder holder, int position, NormalTo item) {
+ holder.title.setText(item.title);
+ holder.desc.setText(item.desc);
+ holder.image.setImageResource(item.icon);
+ }
+
+ private static class Holder extends AbsHolder {
+ TextView title, desc;
+ AppCompatImageView image;
+
+ Holder(View itemView) {
+ super(itemView);
+ title = findViewById(R.id.title);
+ desc = findViewById(R.id.desc);
+ image = findViewById(R.id.image);
+ }
+ }
}
- }
}
diff --git a/app/src/main/java/com/arialyy/simple/core/download/service/DownloadNotification.java b/app/src/main/java/com/arialyy/simple/core/download/service/DownloadNotification.java
index 1c1e0ee1..6106da51 100644
--- a/app/src/main/java/com/arialyy/simple/core/download/service/DownloadNotification.java
+++ b/app/src/main/java/com/arialyy/simple/core/download/service/DownloadNotification.java
@@ -16,9 +16,15 @@
package com.arialyy.simple.core.download.service;
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Build;
import androidx.core.app.NotificationCompat;
+import androidx.core.content.ContextCompat;
import com.arialyy.simple.R;
/**
@@ -31,6 +37,28 @@ public class DownloadNotification {
private NotificationCompat.Builder mBuilder;
private static final int mNotifiyId = 0;
+ private static final String CHANNEL_ID = "aria_download_channel";
+ private static final String CHANNEL_NAME = "下载任务";
+
+ /**
+ * 在 Application.onCreate() 中调用,确保通知渠道在发通知之前已创建。
+ */
+ public static void createNotificationChannel(Context context) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ NotificationChannel channel = new NotificationChannel(
+ CHANNEL_ID,
+ CHANNEL_NAME,
+ NotificationManager.IMPORTANCE_LOW
+ );
+ channel.setDescription("Aria下载进度通知");
+ NotificationManager manager = (NotificationManager) context.getSystemService(
+ Context.NOTIFICATION_SERVICE);
+ if (manager != null) {
+ manager.createNotificationChannel(channel);
+ }
+ }
+ }
+
public DownloadNotification(Context context) {
mContext = context;
init();
@@ -38,17 +66,34 @@ public class DownloadNotification {
private void init() {
mManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
- mBuilder = new NotificationCompat.Builder(mContext);
+ // API 26+ 使用带 channelId 的构造函数
+ mBuilder = new NotificationCompat.Builder(mContext, CHANNEL_ID);
mBuilder.setContentTitle("Aria Download Test")
.setContentText("进度条")
.setProgress(100, 0, false)
.setSmallIcon(R.mipmap.ic_launcher);
- mManager.notify(mNotifiyId, mBuilder.build());
+ notifyIfPermitted();
}
- public void upload(int progress){
+ public void upload(int progress) {
if (mBuilder != null) {
mBuilder.setProgress(100, progress, false);
+ notifyIfPermitted();
+ }
+ }
+
+ /**
+ * Android 13+ 需要 POST_NOTIFICATIONS 运行时权限,缺少权限时静默跳过通知。
+ */
+ @SuppressLint("MissingPermission")
+ private void notifyIfPermitted() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ if (ContextCompat.checkSelfPermission(mContext, Manifest.permission.POST_NOTIFICATIONS)
+ != PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+ }
+ if (mManager != null) {
mManager.notify(mNotifiyId, mBuilder.build());
}
}