web-dev-qa-db-fra.com

Comment répondre aux appels entrants par programmation dans Android 5.0 (Lollipop)?

Alors que je tente de créer un écran personnalisé pour les appels entrants, je tente de répondre par programme à un appel entrant. J'utilise le code suivant mais cela ne fonctionne pas dans Android 5.0.

// Simulate a press of the headset button to pick up the call
Intent buttonDown = new Intent(Intent.ACTION_MEDIA_BUTTON);             
buttonDown.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HEADSETHOOK));
context.sendOrderedBroadcast(buttonDown, "Android.permission.CALL_PRIVILEGED");

// froyo and beyond trigger on buttonUp instead of buttonDown
Intent buttonUp = new Intent(Intent.ACTION_MEDIA_BUTTON);               
buttonUp.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK));
context.sendOrderedBroadcast(buttonUp, "Android.permission.CALL_PRIVILEGED");
83
maveroid

Mise à jour avec Android 8.0 Oreo

Même si la question avait à l'origine été posée sur le support de Android L, les gens semblent toujours avoir du mal à répondre à cette question. Il est donc utile de décrire les améliorations apportées à Android 8.0 Oreo. Les méthodes rétrocompatibles sont toujours décrites ci-dessous.

Qu'est ce qui a changé?

À partir de Android 8.0 Oreo , le [~ # ~] téléphone [~ # ~] groupe de permissions contient également le ANSWER_PHONE_CALLS permission . Comme le nom de l'autorisation le suggère, son maintien permet à votre application d'accepter par programmation les appels entrants via un appel d'API approprié, sans aucun piratage du système à l'aide de la réflexion ou de la simulation de l'utilisateur.

Comment utilisons-nous ce changement?

Vous devez vérifier la version du système à l'exécution } _ si vous prenez en charge les versions antérieures Android afin de pouvoir encapsuler ce nouvel appel d'API tout en maintenant la prise en charge de ces versions antérieures Android. Vous devez suivre demander des autorisations au moment de l'exécution pour obtenir cette nouvelle autorisation lors de l'exécution, comme il est standard sur les versions les plus récentes Android.

Après avoir obtenu l'autorisation, votre application doit simplement appeler la méthode acceptRingingCall de TelecomManager. Une invocation de base se présente comme suit:

TelecomManager tm = (TelecomManager) mContext
        .getSystemService(Context.TELECOM_SERVICE);

if (tm == null) {
    // whether you want to handle this is up to you really
    throw new NullPointerException("tm == null");
}

tm.acceptRingingCall();

Méthode 1: TelephonyManager.answerRingingCall ()

Pour quand vous avez un contrôle illimité sur le périphérique.

Qu'est-ce que c'est?

Il existe TelephonyManager.answerRingingCall () qui est une méthode interne masquée. Cela fonctionne comme un pont pour ITelephony.answerRingingCall () qui a été discuté sur les interwebs et semble prometteur au début. Il est non disponible sur 4.4.2_r1 comme il a été introduit uniquement dans commit 83da75d pour Android 4.4 KitKat ( ligne 1537 sur 4.4.3_r1 ) et plus tard "réintroduit" dans commit f1e1e77 pour Lollipop ( ligne 3138 sur 5.0.0_r1 ) en raison de comment l'arbre Git a été structuré. Cela signifie que, sauf si vous ne prenez en charge que des appareils avec Lollipop, ce qui est probablement une mauvaise décision compte tenu de sa toute petite part de marché, vous devez toujours fournir des méthodes de secours si vous suivez cette voie.

Comment l'utiliserions-nous?

Comme la méthode en question est masquée par les applications du SDK, vous devez utiliser réflexion pour examiner et utiliser de manière dynamique la méthode pendant l'exécution. Si vous n'êtes pas familier avec la réflexion, vous pouvez lire rapidement Qu'est-ce que la réflexion et pourquoi est-ce utile? }. Vous pouvez également approfondir les spécificités à l'adresse { Trail: l'API de Reflection } si vous le souhaitez.

Et à quoi ça ressemble dans le code?

// set the logging tag constant; you probably want to change this
final String LOG_TAG = "TelephonyAnswer";

TelephonyManager tm = (TelephonyManager) mContext
        .getSystemService(Context.TELEPHONY_SERVICE);

try {
    if (tm == null) {
        // this will be easier for debugging later on
        throw new NullPointerException("tm == null");
    }

    // do reflection magic
    tm.getClass().getMethod("answerRingingCall").invoke(tm);
} catch (Exception e) {
    // we catch it all as the following things could happen:
    // NoSuchMethodException, if the answerRingingCall() is missing
    // SecurityException, if the security manager is not happy
    // IllegalAccessException, if the method is not accessible
    // IllegalArgumentException, if the method expected other arguments
    // InvocationTargetException, if the method threw itself
    // NullPointerException, if something was a null value along the way
    // ExceptionInInitializerError, if initialization failed
    // something more crazy, if anything else breaks

    // TODO decide how to handle this state
    // you probably want to set some failure state/go to fallback
    Log.e(LOG_TAG, "Unable to use the Telephony Manager directly.", e);
}

C'est trop beau pour être vrai!

En fait, il y a un petit problème. Cette méthode devrait être entièrement fonctionnelle, mais le responsable de la sécurité souhaite que les appelants tiennent Android.permission.MODIFY_PHONE_STATE . Cette autorisation ne concerne que les fonctionnalités partiellement documentées du système, car les tiers ne sont pas censés y toucher (comme vous pouvez le constater à la documentation correspondante). Vous pouvez essayer d’ajouter un <uses-permission> , Mais cela ne servira à rien car le niveau de protection de cette permission est signature | system ( voir la ligne 1201 de core/AndroidManifest sur 5.0.0_r1 ).

Vous pouvez lire Numéro 34785: Mise à jour d'Android: documentation de protectionLevel } qui a été créée en 2012 pour constater que des informations manquantes concernant la "syntaxe de canal" spécifique sont manquantes. 'AND' signifie que tous les drapeaux spécifiés doivent être remplis pour que l'autorisation soit accordée. En travaillant dans cette hypothèse, cela signifierait que vous devez avoir votre application:

  1. Installé en tant qu'application système.

    Cela devrait suffire et pourrait être accompli en demandant aux utilisateurs d'installer à l'aide d'un fichier Zip en cours de récupération, par exemple lors de la création ou de la création d'une application Google sur des ROM personnalisées qui ne les ont pas déjà emballées.

  2. Signé avec la même signature que frameworks/base, alias le système, alias la ROM.

    C'est là que les problèmes surgissent. Pour ce faire, vous devez avoir les mains sur les clés utilisées pour la signature des cadres/bases. Vous devez non seulement accéder aux clés de Google pour les images d'usine Nexus, mais également aux clés de tous les autres constructeurs et ROM développeurs. Cela ne semble pas plausible, vous pouvez donc faire signer votre application avec les clés système en créant un ROM personnalisé et en demandant à vos utilisateurs d’y basculer (ce qui peut être difficile), ou en trouvant un exploit avec lequel la protection des permissions niveau peut être contourné (ce qui peut être difficile aussi).

De plus, ce comportement semble être lié à Numéro 34792: Android Jelly Bean/4.1: Android.permission.READ_LOGS ne fonctionne plus } _ qui utilise le même niveau de protection ainsi qu'un indicateur de développement non documenté. .

Travailler avec TelephonyManager semble bien, mais ne fonctionnera que si vous obtenez l'autorisation appropriée, ce qui n'est pas si facile à faire dans la pratique.

Qu'en est-il de l'utilisation de TelephonyManager d'une autre manière?

Malheureusement, il semble que vous deviez utiliser (Android.permission.MODIFY_PHONE_STATE } pour utiliser les outils intéressants, ce qui signifie que vous aurez du mal à accéder à ces méthodes.


Méthode 2: appel de service CODE DE SERVICE

Pour quand vous pouvez tester que la construction en cours d'exécution sur le périphérique fonctionnera avec le code spécifié.

Sans pouvoir interagir avec TelephonyManager, il existe également la possibilité d'interagir avec le service via l'exécutable service.

Comment cela marche-t-il?

C'est assez simple, mais il existe encore moins de documentation sur cette route que d'autres. Nous savons avec certitude que l'exécutable prend deux arguments: le nom du service et le code.

  • Le nom du service que nous voulons utiliser est phone .

    Ceci peut être vu en exécutant service list.

  • Le code que nous voulons utiliser semble avoir été 6 mais semble maintenant être 5 .

    Il semble que cela soit basé sur IBinder.FIRST_CALL_TRANSACTION + 5 pour plusieurs versions (de 1.5_r4 à 4.4.4_r1 ), mais lors du test local du code 5 travaillé pour répondre à un appel entrant. Comme Lollipo est une mise à jour massive tout autour, il est compréhensible que les éléments internes aient également changé ici.

Cela se traduit par une commande de service call phone 5.

Comment utilisons-nous cela par programmation?

Java

Le code suivant est une implémentation grossière conçue pour fonctionner comme une preuve de concept. Si vous voulez réellement utiliser cette méthode, vous voudrez probablement consulter conseils pour une utilisation sans problème du s } et éventuellement passer à la version plus développée libsuperuser de Chainfire .

try {
    Process proc = Runtime.getRuntime().exec("su");
    DataOutputStream os = new DataOutputStream(proc.getOutputStream());

    os.writeBytes("service call phone 5\n");
    os.flush();

    os.writeBytes("exit\n");
    os.flush();

    if (proc.waitFor() == 255) {
        // TODO handle being declined root access
        // 255 is the standard code for being declined root for SU
    }
} catch (IOException e) {
    // TODO handle I/O going wrong
    // this probably means that the device isn't rooted
} catch (InterruptedException e) {
    // don't swallow interruptions
    Thread.currentThread().interrupt();
}

Manifeste

<!-- Inform the user we want them root accesses. -->
<uses-permission Android:name="Android.permission.ACCESS_SUPERUSER"/>

Est-ce que cela nécessite vraiment un accès root?

Malheureusement, il semble que oui. Vous pouvez essayer d'utiliser Runtime.exec , mais je n'ai pas réussi à avoir de la chance avec cette route.

Est-ce que c'est stable?

Je suis content que vous ayez demandé. Du fait qu’il n’est pas documenté, cela peut toucher plusieurs versions, comme l’illustre l’apparente différence de code ci-dessus. Le nom du service devrait probablement rester phone dans différentes versions, mais la valeur du code peut changer pour plusieurs versions de la même version (modifications internes apportées, par exemple, par l’apparence du constructeur). méthode utilisée. Il convient donc de mentionner que les tests ont eu lieu sur un Nexus 4 (mako/occam). Personnellement, je vous conseillerais de ne pas utiliser cette méthode, mais comme je ne parviens pas à trouver une méthode plus stable, je pense que c'est la meilleure solution.


Méthode originale: Intentions de code de touche pour casque

Pour les moments où vous devez vous installer.

La section suivante était fortement influencée par cette réponse par Riley C .

La méthode d'intention de casque simulé, telle que publiée dans la question initiale, semble être diffusée comme on pourrait s'y attendre, mais elle ne semble pas atteindre l'objectif de répondre à l'appel. Bien qu'il semble exister un code en place qui devrait gérer ces intentions, on ne s'en préoccupe tout simplement pas, ce qui signifie nécessairement qu'il doit y avoir une sorte de nouvelle contre-mesure en place contre cette méthode. Le journal ne montre rien d'intéressant non plus et je ne crois pas personnellement que fouiller dans la source Android en vaut la peine, simplement en raison de la possibilité que Google introduit un léger changement qui casse facilement la méthode utilisée de toute façon.

Y a-t-il quelque chose que nous puissions faire maintenant?

Le comportement peut être reproduit de manière cohérente à l'aide de l'exécutable d'entrée. Il prend un argument de code clé, pour lequel nous passons simplement dans KeyEvent.KEYCODE_HEADSETHOOK . La méthode ne nécessite même pas d'accès root, ce qui la rend adaptée aux cas d'utilisation courants du grand public, mais la méthode présente un petit inconvénient: l'événement de pression du bouton du casque ne peut pas être spécifié pour nécessiter une autorisation, ce qui signifie qu'elle fonctionne comme un véritable une pression sur un bouton et des bulles dans toute la chaîne, ce qui signifie que vous devez faire attention au moment de simuler la pression d'un bouton, par exemple, le lecteur pourrait commencer la lecture si personne d'autre de priorité supérieure n'est prêt à gérer. l'événement.

Code?

new Thread(new Runnable() {

    @Override
    public void run() {
        try {
            Runtime.getRuntime().exec("input keyevent " +
                    Integer.toString(KeyEvent.KEYCODE_HEADSETHOOK));
        } catch (IOException e) {
            // Runtime.exec(String) had an I/O problem, try to fall back
            String enforcedPerm = "Android.permission.CALL_PRIVILEGED";
            Intent btnDown = new Intent(Intent.ACTION_MEDIA_BUTTON).putExtra(
                    Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN,
                            KeyEvent.KEYCODE_HEADSETHOOK));
            Intent btnUp = new Intent(Intent.ACTION_MEDIA_BUTTON).putExtra(
                    Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP,
                            KeyEvent.KEYCODE_HEADSETHOOK));

            mContext.sendOrderedBroadcast(btnDown, enforcedPerm);
            mContext.sendOrderedBroadcast(btnUp, enforcedPerm);
        }
    }

}).start();

tl; dr

Il existe une API publique Nice pour Android 8.0 Oreo et versions ultérieures.

Il n'y a pas d'API publique antérieure à Android 8.0 Oreo. Les API internes sont interdites ou tout simplement sans documentation. Vous devriez procéder avec prudence.

137
Valter Jansons

La solution entièrement opérationnelle est basée sur le code @Valter Strods.

Pour le faire fonctionner, vous devez afficher une activité (invisible) sur un écran de verrouillage où le code est exécuté.

AndroidManifest.xml

<uses-permission Android:name="Android.permission.WAKE_LOCK" />
<uses-permission Android:name="Android.permission.DISABLE_KEYGUARD" />

<activity Android:name="com.mysms.Android.lib.activity.AcceptCallActivity"
        Android:launchMode="singleTop"
        Android:excludeFromRecents="true"
        Android:taskAffinity=""
        Android:configChanges="orientation|keyboardHidden|screenSize"
        Android:theme="@style/Mysms.Invisible">
    </activity>

Appel accepter l'activité

package com.mysms.Android.lib.activity;

import Android.app.Activity;
import Android.app.KeyguardManager;
import Android.content.BroadcastReceiver;
import Android.content.Context;
import Android.content.Intent;
import Android.content.IntentFilter;
import Android.media.AudioManager;
import Android.os.Build;
import Android.os.Bundle;
import Android.telephony.TelephonyManager;
import Android.view.KeyEvent;
import Android.view.WindowManager;

import org.Apache.log4j.Logger;

import Java.io.IOException;

public class AcceptCallActivity extends Activity {

     private static Logger logger = Logger.getLogger(AcceptCallActivity.class);

     private static final String MANUFACTURER_HTC = "HTC";

     private KeyguardManager keyguardManager;
     private AudioManager audioManager;
     private CallStateReceiver callStateReceiver;

     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);

         keyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
         audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
     }

     @Override
     protected void onResume() {
         super.onResume();

         registerCallStateReceiver();
         updateWindowFlags();
         acceptCall();
     }

     @Override
     protected void onPause() {
         super.onPause();

         if (callStateReceiver != null) {
              unregisterReceiver(callStateReceiver);
              callStateReceiver = null;
         }
     }

     private void registerCallStateReceiver() {
         callStateReceiver = new CallStateReceiver();
         IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
         registerReceiver(callStateReceiver, intentFilter);
     }

     private void updateWindowFlags() {
         if (keyguardManager.inKeyguardRestrictedInputMode()) {
              getWindow().addFlags(
                       WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD |
                                WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON |
                                WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
         } else {
              getWindow().clearFlags(
                       WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD |
                                WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
                                WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
         }
     }

     private void acceptCall() {

         // for HTC devices we need to broadcast a connected headset
         boolean broadcastConnected = MANUFACTURER_HTC.equalsIgnoreCase(Build.MANUFACTURER)
                  && !audioManager.isWiredHeadsetOn();

         if (broadcastConnected) {
              broadcastHeadsetConnected(false);
         }

         try {
              try {
                  logger.debug("execute input keycode headset hook");
                  Runtime.getRuntime().exec("input keyevent " +
                           Integer.toString(KeyEvent.KEYCODE_HEADSETHOOK));

              } catch (IOException e) {
                  // Runtime.exec(String) had an I/O problem, try to fall back
                  logger.debug("send keycode headset hook intents");
                  String enforcedPerm = "Android.permission.CALL_PRIVILEGED";
                  Intent btnDown = new Intent(Intent.ACTION_MEDIA_BUTTON).putExtra(
                           Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN,
                                    KeyEvent.KEYCODE_HEADSETHOOK));
                  Intent btnUp = new Intent(Intent.ACTION_MEDIA_BUTTON).putExtra(
                           Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP,
                                    KeyEvent.KEYCODE_HEADSETHOOK));

                  sendOrderedBroadcast(btnDown, enforcedPerm);
                  sendOrderedBroadcast(btnUp, enforcedPerm);
              }
         } finally {
              if (broadcastConnected) {
                  broadcastHeadsetConnected(false);
              }
         }
     }

     private void broadcastHeadsetConnected(boolean connected) {
         Intent i = new Intent(Intent.ACTION_HEADSET_PLUG);
         i.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
         i.putExtra("state", connected ? 1 : 0);
         i.putExtra("name", "mysms");
         try {
              sendOrderedBroadcast(i, null);
         } catch (Exception e) {
         }
     }

     private class CallStateReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
              finish();
         }
     }
}

Style

<style name="Mysms.Invisible">
    <item name="Android:windowFrame">@null</item>
    <item name="Android:windowBackground">@Android:color/transparent</item>
    <item name="Android:windowContentOverlay">@null</item>
    <item name="Android:windowNoTitle">true</item>
    <item name="Android:windowIsTranslucent">true</item>
    <item name="Android:windowAnimationStyle">@null</item>
</style>

Enfin appelez la magie!

Intent intent = new Intent(context, AcceptCallActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK
            | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
context.startActivity(intent);
33
notz

Ce qui suit est une approche alternative qui a fonctionné pour moi. Il envoie l'événement de clé au serveur de télécommunication directement à l'aide de l'API MediaController. Cela nécessite que l'application possède BIND_NOTIFICATION_LISTENER_SERVICE permission et reçoit l'autorisation explicite d'accéder à la notification de la part de l'utilisateur:

@TargetApi(Build.VERSION_CODES.Lollipop) 
void sendHeadsetHookLollipop() {
    MediaSessionManager mediaSessionManager =  (MediaSessionManager) getApplicationContext().getSystemService(Context.MEDIA_SESSION_SERVICE);

    try {
        List<MediaController> mediaControllerList = mediaSessionManager.getActiveSessions 
                     (new ComponentName(getApplicationContext(), NotificationReceiverService.class));

        for (MediaController m : mediaControllerList) {
             if ("com.Android.server.telecom".equals(m.getPackageName())) {
                 m.dispatchMediaButtonEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK));
                 log.info("HEADSETHOOK sent to telecom server");
                 break;
             }
        }
    } catch (SecurityException e) {
        log.error("Permission error. Access to notification not granted to the app.");      
    }  
}

NotificationReceiverService.class dans le code ci-dessus pourrait n'être qu'une classe vide.

import Android.service.notification.NotificationListenerService;

public class NotificationReceiverService extends NotificationListenerService{
     public NotificationReceiverService() {
     }
}

Avec la section correspondante dans le manifeste:

    <service Android:name=".NotificationReceiverService" Android:permission="Android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
        Android:enabled="true" Android:exported="true">
    <intent-filter>
         <action Android:name="Android.service.notification.NotificationListenerService" />
    </intent-filter>

Puisque la cible de l'événement est explicite, cela devrait probablement éviter tout effet secondaire de déclenchement du lecteur multimédia.

Remarque: le serveur de télécommunication peut ne pas être immédiatement actif après la sonnerie. Pour que cela fonctionne de manière fiable, il peut être utile que l'application implémente MediaSessionManager.OnActiveSessionsChangedListener de surveiller le moment où le serveur de télécommunication devient actif avant d'envoyer l'événement.

Mise à jour:

Dans Android O , il faut simuler ACTION_DOWN avant ACTION_UP, sinon ce qui précède n’a aucun effet. c'est-à-dire qu'il faut:

m.dispatchMediaButtonEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HEADSETHOOK));
m.dispatchMediaButtonEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK));

Mais étant donné qu'un appel officiel pour répondre à un appel est disponible depuis Android O (voir la réponse du haut)), il peut ne plus être nécessaire d'utiliser ce hack, à moins que l'un d'entre eux ne soit bloqué avec un ancien niveau d'API de compilation avant Android O.

13
headuck

Pour élaborer un petit peu sur la réponse de @Muzikant et la modifier un peu pour qu'elle fonctionne plus propre sur mon appareil, essayez input keyevent 79, la constante de KeyEvent.KEYCODE_HEADSETHOOK . Très à peu près:

    new Thread(new Runnable() {

        @Override
        public void run() {

            try {

                Runtime.getRuntime().exec( "input keyevent " + KeyEvent.KEYCODE_HEADSETHOOK );
            }
            catch (Throwable t) {

                // do something proper here.
            }
        }
    }).start();

Pardonnez les conventions de codage assez mauvaises, je ne suis pas trop au courant des appels à Runtime.exec (). Notez que mon appareil n'est pas root et que je ne demande pas les privilèges root.

Le problème avec cette approche est qu’elle ne fonctionne que sous certaines conditions (pour moi). C'est-à-dire que si j'exécute le fil de discussion ci-dessus à partir d'une option de menu que l'utilisateur sélectionne pendant la sonnerie d'un appel, l'appel répond parfaitement. Si je le lance depuis un récepteur qui surveille le statut des appels entrants, il sera totalement ignoré.

Donc, sur mon Nexus 5, cela fonctionne bien pour la réponse par utilisateur et devrait convenir à un écran d’appel personnalisé. Cela ne fonctionnera tout simplement pas pour toutes sortes d'applications automatisées du type à contrôle d'appel.

Il convient également de noter toutes les mises en garde possibles, y compris le fait que cela aussi cessera probablement de fonctionner dans une mise à jour ou deux.

9
Riley C

Merci @notz la réponse de travaille pour moi sur le Lolillop. Pour que ce code fonctionne avec l'ancien SDK Android, vous pouvez utiliser le code suivant:

if (Build.VERSION.SDK_INT >= 21) {  
    Intent answerCalintent = new Intent(context, AcceptCallActivity.class);  
    answerCalintent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | 
                             Intent.FLAG_ACTIVITY_CLEAR_TASK  | 
                             Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
    context.startActivity(answerCalintent);  
}  
else {  
  if (telephonyService != null) {  
    try {  
        telephonyService.answerRingingCall();  
    }  
    catch (Exception e) {  
        answerPhoneHeadsethook();  
    }  
  }  
}  
0
Khac Quyet Dang

via les commandes adb Comment prendre un appel avec adb

N'oubliez pas que Android est Linux avec une JVM massive en front-end. Vous pouvez télécharger une application en ligne de commande et rooter le téléphone. Vous disposez maintenant d'un ordinateur Linux standard et d'une ligne de commande les choses normales. Exécutez des scripts, vous pouvez même ssh (truc OpenVPN)

0
user1544207

Comment activer le haut-parleur après avoir répondu automatiquement aux appels.

J'ai résolu mon problème ci-dessus avec setSpeakerphoneOn. Je pense que cela vaut la peine d’écrire ici, étant donné que, dans le cas d’une réponse automatique à un appel téléphonique, il faudrait souvent aussi utiliser un haut-parleur pour être utile. Merci encore à tout le monde sur ce fil, quel travail génial.

Cela fonctionne pour moi sur Android 5.1.1 sur mon Nexus 4 sans racine.;)

Permission requise:

<uses-permission Android:name="Android.permission.MODIFY_AUDIO_SETTINGS"/>

Code Java:

// this means the phone has answered
if(state==TelephonyManager.CALL_STATE_OFFHOOK)
{
    // try and turn on speaker phone
    final Handler mHandler = new Handler();
    mHandler.postDelayed(new Runnable() {
        @Override
        public void run() {
            AudioManager audioManager = (AudioManager) localContext.getSystemService(Context.AUDIO_SERVICE);

            // this doesnt work without Android.permission.MODIFY_PHONE_STATE
            // audioManager.setMode(AudioManager.MODE_IN_CALL);

            // weirdly this works
            audioManager.setMode(AudioManager.MODE_NORMAL); // this is important
            audioManager.setSpeakerphoneOn(true);

            // note the phone interface won't show speaker phone is enabled
            // but the phone speaker will be on
            // remember to turn it back off when your done ;)
        }
    }, 500); // half a second delay is important or it might fail
}
0
Madhava Jay