如何在Android 10中打开活动(即来电接收)

28
在Android 10中,应用程序有新的限制。我们不能再从后台启动活动。虽然对于大多数应用程序来说这可能没什么问题,但对于需要在推送通知到达后显示来电的voip应用程序来说,这是一个致命的打击。
根据https://developer.android.com/guide/components/activities/background-starts,还有一些条件可以满足,以允许打开活动,但老实说,我没有完全理解(非英语母语者)。
我绝对知道的是:
- 我没有任何正在运行的活动、任务、回退栈等。 - 应用程序甚至没有运行。
我需要实现的是:
- 应用程序的FCM服务接收来自我们服务器的推送,并呈现来电屏幕(在锁定屏幕上和所有 - 就像在Android 9及以下版本中一样)。
我该怎么做才能在Android 10中为即将到来的voip呼叫打开活动?在锁屏和所有方面,就像普通用户从电话应用程序所期望的那样。
提前感谢任何提示。

你可以使用这段代码 链接 - Dinil ps
@Grisgram,你能修复这个问题吗? - Lakshay Dulani
4个回答

13

使用具有“全屏意图”的高优先级通知。这样将会:

  • 如果设备已锁定,则调用您的“全屏意图”
  • 否则,显示一个“悬浮通知”

谢谢这个。它指引了我正确的方向。我认为这将会起作用,连同一个扩展通知和自定义布局,以防应用程序当前在前台(使用前台服务)。 - Grisgram
1
这里可以找到非常好的教程:https://developer.android.com/training/notify-user/time-sensitive。记录一下,它包含了解决此问题所需的一切内容。 - Grisgram
使用这种方法,是否可以仅显示全屏意图而不是通知?我实施了这个解决方案,但当我的全屏意图出现时,它会发出通知声音。setFullScreenIntent的描述说:“启动代替将通知发布到状态栏的意图。”我认为这不是真的。它启动全屏意图并创建通知。 - Mitulát báti
@Mitulátbáti:“是否可能只显示全屏意图而不是通知?”-- 不行。“它启动了全屏意图并创建了通知”-- 这不是引用的内容。引用说它被用于“代替将通知发布到状态栏”(强调添加)。可能仍然有其他效果,如铃声和振动。如果用户不想要这些效果,用户应该能够在设置应用中为您的频道禁用它们。 - CommonsWare
@CommonsWare,您能否提及如何为通知操作设置不同的颜色(例如拒绝为红色,回答为绿色)? - Liya
@Liya:我的脑海中暂时想不到实现这个的方法 -- 抱歉! - CommonsWare

12

要在锁屏时打开活动,您可以使用带有“全屏意图”的高通知,如CommonsWare的答案所示。但是,为了获得更多详细信息,您可以尝试我的解决方案,如下所示:

  1. 创建一个前台服务,然后在onStartCommand方法中调用buildNotification,在buildNotification方法中返回放入startForeground方法参数的通知。

 public class IncomingCallService extends Service {
     public int onStartCommand(Intent intent, int flags, int startId) {
         Notification notification = buildNotification();
         startForeground(1, notification);
         return START_NOT_STICKY;
     }
 }
  • 在 buildNotification 方法中,我们将创建一个高优先级、调用类别和全屏意图的通知。

  •  private Notification buildNotification() {
         Intent fullScreenIntent = new Intent(this, IncomingCallActivity.class);
         PendingIntent fullScreenPendingIntent = PendingIntent.getActivity(this, 0, fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT);
         NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    
         NotificationCompat.Builder notificationBuilder =
             new NotificationCompat.Builder(this)
                     .setSmallIcon(R.drawable.ic_notification_icon)
                     .setContentTitle("Incoming call")
                     .setContentText("(919) 555-1234")
                     .setPriority(NotificationCompat.PRIORITY_HIGH)
                     .setCategory(NotificationCompat.CATEGORY_CALL)
                     // Use a full-screen intent only for the highest-priority alerts where you
                     // have an associated activity that you would like to launch after the user
                     // interacts with the notification. Also, if your app targets Android 10
                     // or higher, you need to request the USE_FULL_SCREEN_INTENT permission in
                     // order for the platform to invoke this notification.
                     .setFullScreenIntent(fullScreenPendingIntent, true);
         notificationBuilder.setAutoCancel(true);
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
             notificationManager.createNotificationChannel(new NotificationChannel("123", "123", NotificationManager.IMPORTANCE_HIGH));
             notificationBuilder.setChannelId("123");
         }
         Notification incomingCallNotification = notificationBuilder.build();
         return incomingCallNotification;
     }
    
    在 onStartCommand 中,添加一行代码发送 ACTION_CLOSE_SYSTEM_DIALOGS 广播动作。 这很重要,以便启动全屏待处理意图。
     public int onStartCommand(Intent intent, int flags, int startId) {
         Notification notification = buildNotification();
         startForeground(1, notification);
         sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
         return START_NOT_STICKY;
     }
    
  • 创建全屏活动,该活动将显示在锁定屏幕上,然后您需要添加setShowWhenLocked和setTurnScreenOn以在锁定屏幕上显示。如果没有这样做,您的活动将显示在锁定屏幕后面。下面是我的示例。

  •  public class IncomingCallActivity extends AppCompatActivity {
         protected void onCreate(@Nullable Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
             setContentView(R.layout.activity_explore);
             setShowWhenLocked(true);
             setTurnScreenOn(true);
             getWindow().addFlags(
             WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
                     | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
                     | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
                     | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
                     | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON);
         }
     }
    
  • 当您从逻辑上接收到电话时,现在必须启动IncomingCallService。

  •  public void startCallService() {
         Intent intent = new Intent(context, IncomingCallService.class);
         startForegroundService(intent);
     }
    
  • 你必须在你的清单文件中声明以下活动、服务和一些权限,如下所示:

  •  <uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
     <application
        ...>
         <activity android:name=".IncomingCallActivity" />
         <service
             android:name=".IncomingCallService"
             android:enabled="true"
             android:exported="true" />
     </application>
    

    我在谷歌、三星和Vsmart手机上进行了测试,它们都正常工作。但对于小米设备,您需要按以下步骤启用一些权限:

    1. 长按您的应用图标
    2. 打开应用信息
    3. 点击“其他权限”选项
    4. 允许显示在锁定屏幕上

    现在您的应用程序将在小米设备上工作。如果您在使用我的解决方案时遇到任何问题,请在此处留言。如果我能帮助您,我会尽力而为。


    感谢您抽出时间提供如此详细的答案 - 我会仔细研究。 - Grisgram
    2
    我的解决方案已上传到Github。您可以在https://github.com/doanpt/PhoneActivity上查看。 - Trung Đoan
    请注意 - getWindow().addFlags() 应该在 SetContentView 之前调用。而且在 API >= 27 上设置这些标志可能不是一个好主意 - 但不确定。 - Michal Dobrodenka
    @MichalDobrodenka,1)实际上,我的示例可能不需要添加标志就可以工作。但我添加了它以在API 27以下的版本上工作。2)对于addFlags方法,Google表示应该在设置某些标志之前调用setContentView,而不是所有标志,因此为了最佳效果,我同意我们应该在调用setContentView之前移动addFlags。 - Trung Đoan
    有没有办法以编程方式请求小米设备的权限呢? - Xoltawn
    显示剩余3条评论

    3

    我完全按照你的示例操作,但是并没有像你展示的那样工作。通知仍然是普通的通知 - 没有大的弹出窗口,也没有全屏意图。 - Yazid
    1
    @YazidEF 在创建通知渠道时,将优先级设置为高。 - Lloyd Dcosta
    @LloydDcosta,我已经查看了你的示例,但是在我点击按钮后,HeadsUpNotificationActionReceiver没有被调用。有什么想法吗? - alext
    1
    你的例子很棒且有帮助性,但不幸的是,在针对SDK 31 (必须)时,Intent.ACTION_CLOSE_SYSTEM_DIALOGS已过时,如果使用会导致应用程序崩溃。 - Michal Dobrodenka

    1

    点击这个链接可以帮助你这里

    或者

    你需要请求"在其他应用上绘制"的权限,然后就可以像以前的版本一样做了。

      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
                if (!Settings.canDrawOverlays(this)) {
                    RequestPermission();
    
            }
            }
    
        private void RequestPermission() {
            // Check if Android P or higher
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                // Show alert dialog to the user saying a separate permission is needed
                // Launch the settings activity if the user prefers
                Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                        Uri.parse("package:" + BuildConfig.APPLICATION_ID));
                startActivityForResult(intent, 
                ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE);
            }
         }
    

    或者你可以使用我的这个answer

    https://dev59.com/PFIH5IYBdhLWcg3wYtES#63699960


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接