Alors que j'essaie de créer un écran personnalisé pour les appels entrants, j'essaie de répondre par programme à un appel entrant. J'utilise le code suivant mais il ne fonctionne pas sous 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");
Réponses:
Mise à jour avec Android 8.0 Oreo
Même si la question a été posée à l'origine pour la prise en charge d'Android L, les gens semblent toujours répondre à cette question et à cette réponse, il vaut donc la peine 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 d' Android 8.0 Oreo , le groupe d'autorisations PHONE contient également l' autorisation ANSWER_PHONE_CALLS . Comme le nom de l'autorisation l'indique, le maintenir permet à votre application d'accepter par programme les appels entrants via un appel API approprié sans aucun piratage autour du système en utilisant la réflexion ou en simulant l'utilisateur.
Comment utilisons-nous ce changement?
Vous devez vérifier la version du système au moment de l'exécution si vous prenez en charge les anciennes versions d'Android afin de pouvoir encapsuler ce nouvel appel d'API tout en maintenant la prise en charge de ces anciennes versions d'Android. Vous devez suivre la demande d'autorisations au moment de l'exécution pour obtenir cette nouvelle autorisation pendant l'exécution, comme c'est la norme sur les nouvelles versions d'Android.
Après avoir obtenu l'autorisation, votre application n'a qu'à appeler la méthode acceptRingingCall de TelecomManager . Une invocation de base ressemble alors à ceci:
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 lorsque vous avez un contrôle illimité sur l'appareil.
Qu'est-ce que c'est?
Il existe TelephonyManager.answerRingingCall () qui est une méthode interne cachée. Cela fonctionne comme un pont pour ITelephony.answerRingingCall () qui a été discuté sur les interwebs et semble prometteur au début. Il n'est pas disponible sur 4.4.2_r1 car il a été introduit uniquement dans le commit 83da75d pour Android 4.4 KitKat ( ligne 1537 sur 4.4.3_r1 ) et plus tard "réintroduit" dans le commit f1e1e77 pour Lollipop ( ligne 3138 sur 5.0.0_r1 ) en raison de la façon dont le L'arbre Git était structuré. Cela signifie qu'à moins que vous ne preniez en charge uniquement les appareils avec Lollipop, ce qui est probablement une mauvaise décision en raison de la faible part de marché de celui-ci à l'heure actuelle, vous devez toujours fournir des méthodes de secours si vous empruntez cette voie.
Comment utiliserions-nous cela?
Comme la méthode en question est masquée de l'utilisation des applications SDK, vous devez utiliser la réflexion pour examiner et utiliser dynamiquement 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 détails sur Trail: l'API Reflection si cela vous intéresse.
Et à quoi cela ressemble-t-il 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 léger problème. Cette méthode doit être entièrement fonctionnelle, mais le responsable de la sécurité souhaite que les appelants maintiennent android.permission.MODIFY_PHONE_STATE . Cette autorisation est du domaine des fonctionnalités partiellement documentées du système car les tiers ne sont pas censés y toucher (comme vous pouvez le voir dans la documentation correspondante). Vous pouvez essayer d'ajouter un
<uses-permission>
pour cela mais cela ne servira à rien car le niveau de protection pour cette autorisation est signature | system ( voir la ligne 1201 de core / AndroidManifest sur 5.0.0_r1 ).Vous pouvez lire le problème 34785: Mise à jour de la documentation android: protectionLevel qui a été créée en 2012 pour voir qu'il nous manque des détails sur la "syntaxe de canal" spécifique, mais après avoir expérimenté, il semble qu'il doit fonctionner comme un 'ET' signifiant tout le les indicateurs spécifiés doivent être remplis pour que l'autorisation soit accordée. En travaillant sous cette hypothèse, cela signifierait que vous devez avoir votre application:
Installé en tant qu'application système.
Cela devrait être bien et pourrait être accompli en demandant aux utilisateurs d'installer à l'aide d'un ZIP lors de la récupération, par exemple lors de l'enracinement ou de l'installation d'applications Google sur des ROM personnalisées qui ne les ont pas déjà emballées.
Signé avec la même signature que les frameworks / base aka le système, aka la ROM.
C'est là que les problèmes surgissent. Pour ce faire, vous devez avoir la main sur les clés utilisées pour la signature des frameworks / base. Vous devrez non seulement accéder aux clés de Google pour les images d'usine Nexus, mais également accéder aux clés de tous les autres OEM et développeurs de ROM. Cela ne semble pas plausible, vous pouvez donc faire signer votre application avec les clés système en créant une ROM personnalisée et en demandant à vos utilisateurs d'y basculer (ce qui peut être difficile) ou en trouvant un exploit avec lequel le niveau de protection des autorisations peut être contourné (ce qui peut aussi être difficile).
En outre, ce comportement semble être lié au problème 34792: Android Jelly Bean / 4.1: android.permission.READ_LOGS ne fonctionne plus qui utilise le même niveau de protection avec un indicateur de développement non documenté.
Travailler avec TelephonyManager semble bon, mais ne fonctionnera que si vous obtenez l'autorisation appropriée, ce qui n'est pas si facile à faire en pratique.
Qu'en est-il de l'utilisation de TelephonyManager d'une autre manière?
Malheureusement, il semble que vous deviez détenir le fichier android.permission.MODIFY_PHONE_STATE pour utiliser les outils sympas, 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 exécutée sur l'appareil fonctionnera avec le code spécifié.
Sans pouvoir interagir avec le TelephonyManager, il y a aussi la possibilité d'interagir avec le service via l'
service
exécutable.Comment cela marche-t-il?
C'est assez simple, mais il y a 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 téléphone .
Cela 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 qu'il ait été basé sur IBinder.FIRST_CALL_TRANSACTION + 5 pour de nombreuses versions maintenant (de 1.5_r4 à 4.4.4_r1 ) mais lors des tests locaux, le code 5 a fonctionné pour répondre à un appel entrant. Comme Lollipo est une mise à jour massive tout autour, il est compréhensible que les internes aient également changé ici.
Cela se traduit par une commande de
service call phone 5
.Comment utilisons-nous cela par programme?
Java
Le code suivant est une implémentation approximative conçue pour fonctionner comme une preuve de concept. Si vous voulez vraiment continuer et utiliser cette méthode, vous voudrez probablement consulter les directives pour une utilisation sans problème de su et éventuellement passer au libsuperuser plus développé 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"/>
Cela nécessite-t-il vraiment un accès root?
Malheureusement, cela semble être le cas. Vous pouvez essayer d'utiliser Runtime.exec dessus, mais je n'ai pas pu avoir de chance avec cette route.
À quel point est-ce stable?
Je suis content que vous ayez posé la question. En raison de l'absence de documentation, cela peut se rompre entre différentes versions, comme illustré par la différence de code apparente ci-dessus. Le nom du service devrait probablement rester téléphone dans diverses versions, mais pour autant que nous sachions, la valeur du code peut changer entre plusieurs versions de la même version (modifications internes par, par exemple, le skin de l'OEM), à son tour cassant la méthode utilisée. Il convient donc de mentionner que les tests ont eu lieu sur un Nexus 4 (mako / occam). Je vous déconseille personnellement d'utiliser cette méthode, mais comme je ne suis pas en mesure de trouver une méthode plus stable, je pense que c'est la meilleure solution.
Méthode originale: intentions de code-clé du casque
Pour les moments où vous devez vous installer.
La section suivante a été fortement influencée par cette réponse par Riley C .
La méthode d'intention de casque simulée telle que publiée dans la question d'origine semble être diffusée exactement comme on pouvait s'y attendre, mais elle ne semble pas atteindre l'objectif de répondre à l'appel. Bien qu'il semble y avoir du code en place qui devrait gérer ces intentions, ils ne sont tout simplement pas pris en compte, ce qui doit signifier qu'il doit y avoir une sorte de nouvelles contre-mesures en place contre cette méthode. Le journal ne montre rien d'intéressant non plus et je ne pense pas personnellement que creuser dans la source Android pour cela vaudra la peine juste en raison de la possibilité que Google introduise un léger changement qui rompt facilement la méthode utilisée de toute façon.
Y a-t-il quelque chose que nous pouvons 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 keycode, pour lequel nous passons simplement 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 dans le grand public, mais il y a un petit inconvénient dans la méthode - l'événement de pression sur le bouton du casque ne peut pas être spécifié pour exiger une autorisation, ce qui signifie qu'il fonctionne comme un vrai appuyer sur un bouton et faire des bulles tout au long de la chaîne, ce qui signifie que vous devez être prudent quant au moment de simuler la pression sur le bouton car cela pourrait, par exemple, déclencher le lecteur de musique pour démarrer la lecture si personne d'autre de priorité plus élevée 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 belle API publique pour Android 8.0 Oreo et versions ultérieures.
Il n'y a pas d'API publique avant Android 8.0 Oreo. Les API internes sont interdites ou simplement sans documentation. Vous devez procéder avec prudence.
la source
La solution entièrement fonctionnelle est basée sur le code @Valter Strods.
Pour le faire fonctionner, vous devez afficher une activité (invisible) sur l'é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>
Activité d'acceptation d'appel
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);
la source
Ce qui suit est une approche alternative qui a fonctionné pour moi. Il envoie l'événement clé au serveur de télécommunications directement à l'aide de l'API MediaController. Cela nécessite que l'application dispose de l' autorisation BIND_NOTIFICATION_LISTENER_SERVICE et que l'utilisateur accorde explicitement l'accès à la notification:
@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 être juste 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>
Étant donné que la cible de l'événement est explicite, cela devrait probablement éviter tout effet secondaire du déclenchement du lecteur multimédia.
Remarque: le serveur de télécommunications peut ne pas être immédiatement actif après l'événement de sonnerie. Pour que cela fonctionne de manière fiable, il peut être utile que l'application implémente MediaSessionManager.OnActiveSessionsChangedListener pour surveiller le moment où le serveur de télécommunications devient actif, avant d'envoyer l'événement.
Mise à jour:
Dans Android O , il faut simuler
ACTION_DOWN
avantACTION_UP
, sinon ce qui précède n'a aucun effet. c'est-à-dire que ce qui suit est nécessaire:m.dispatchMediaButtonEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HEADSETHOOK)); m.dispatchMediaButtonEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK));
Mais comme un appel officiel pour répondre à un appel est disponible depuis Android O (voir la réponse du haut), il se peut que ce hack ne soit plus nécessaire, à moins que l'on ne soit coincé avec un ancien niveau d'API de compilation avant Android O.
la source
Pour élaborer un tout petit peu sur la réponse de @Muzikant, et pour la modifier un peu pour qu'elle fonctionne un peu plus propre sur mon appareil, essayez
input keyevent 79
la constante de KeyEvent.KEYCODE_HEADSETHOOK . Très grosso modo: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 assez mauvaises conventions de codage, je ne connais pas trop bien les appels à Runtime.exec (). Notez que mon appareil n'est pas rooté et que je ne demande pas de privilèges root.
Le problème avec cette approche est qu'elle ne fonctionne que sous certaines conditions (pour moi). Autrement dit, si j'exécute le fil ci-dessus à partir d'une option de menu que l'utilisateur sélectionne pendant qu'un appel sonne, l'appel répond très bien. Si je l'exécute à partir d'un récepteur qui surveille l'état des appels entrants, il est totalement ignoré.
Ainsi, sur mon Nexus 5, cela fonctionne bien pour la réponse axée sur l'utilisateur et devrait convenir à l'objectif d'un écran d'appel personnalisé. Cela ne fonctionnera tout simplement pas pour aucune sorte d'applications de type contrôle d'appel automatisé.
Il convient également de noter toutes les mises en garde possibles, notamment le fait que cela cessera probablement de fonctionner dans une ou deux mises à jour.
la source
input keyevent 79
fonctionne bien sur Sony Xperia 5.0. Fonctionne lors d'un appel depuis une activité ou depuis un récepteur de diffusion.via les commandes adb Comment prendre un appel par adb
Gardez à l'esprit qu'Android est Linux avec une JVM massive sur le front-end. Vous pouvez télécharger une application de ligne de commande et rooter le téléphone et maintenant vous avez un ordinateur Linux classique et une ligne de commande qui fait toutes les choses normales. Exécutez des scripts, vous pouvez même y ssh (astuce OpenVPN)
la source
Merci @notz la réponse de travaille pour moi sur le Lolillop. Pour que ce code fonctionne avec l'ancien SDK Android, vous pouvez faire ce code:
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(); } } }
la source
Comment activer le téléphone à 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'être publié ici, car le cas d'utilisation de la réponse automatique à un appel téléphonique nécessiterait souvent également un haut-parleur pour être utile. Merci encore à tout le monde sur ce fil, quel travail formidable.
Cela fonctionne pour moi sur Android 5.1.1 sur mon Nexus 4 sans ROOT. ;)
Autorisation 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 }
la source
Exécutez la commande suivante en tant que root:
input keyevent 5
Plus de détails sur la simulation d'événements clés ici .
Vous pouvez utiliser cette classe de base que j'ai créée pour exécuter des commandes en tant que root depuis votre application.
la source
testez ceci: ajoutez d'abord les permissions puis utilisez killCall () pour raccrocher utilisez answerCall () pour répondre à l'appel
<uses-permission android:name="android.permission.READ_PHONE_STATE"></uses-permission> <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"></uses-permission> public void killCall() { try { TelephonyManager telephonyManager = (TelephonyManager) getContext().getSystemService(Context.TELEPHONY_SERVICE); Class classTelephony = Class.forName(telephonyManager.getClass().getName()); Method methodGetITelephony = classTelephony.getDeclaredMethod("getITelephony"); methodGetITelephony.setAccessible(true); Object telephonyInterface = methodGetITelephony.invoke(telephonyManager); Class telephonyInterfaceClass = Class.forName(telephonyInterface.getClass().getName()); Method methodEndCall = telephonyInterfaceClass.getDeclaredMethod("endCall"); methodEndCall.invoke(telephonyInterface); } catch (Exception ex) { Log.d(TAG, "PhoneStateReceiver **" + ex.toString()); } } public void answerCall() { try { Runtime.getRuntime().exec("input keyevent " + Integer.toString(KeyEvent.KEYCODE_HEADSETHOOK)); } catch (IOException e) { answerRingingCallWithIntent(); } } public void answerRingingCallWithIntent() { try { Intent localIntent1 = new Intent(Intent.ACTION_HEADSET_PLUG); localIntent1.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); localIntent1.putExtra("state", 1); localIntent1.putExtra("microphone", 1); localIntent1.putExtra("name", "Headset"); getContext().sendOrderedBroadcast(localIntent1, "android.permission.CALL_PRIVILEGED"); Intent localIntent2 = new Intent(Intent.ACTION_MEDIA_BUTTON); KeyEvent localKeyEvent1 = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HEADSETHOOK); localIntent2.putExtra(Intent.EXTRA_KEY_EVENT, localKeyEvent1); getContext().sendOrderedBroadcast(localIntent2, "android.permission.CALL_PRIVILEGED"); Intent localIntent3 = new Intent(Intent.ACTION_MEDIA_BUTTON); KeyEvent localKeyEvent2 = new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK); localIntent3.putExtra(Intent.EXTRA_KEY_EVENT, localKeyEvent2); getContext().sendOrderedBroadcast(localIntent3, "android.permission.CALL_PRIVILEGED"); Intent localIntent4 = new Intent(Intent.ACTION_HEADSET_PLUG); localIntent4.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); localIntent4.putExtra("state", 0); localIntent4.putExtra("microphone", 1); localIntent4.putExtra("name", "Headset"); getContext().sendOrderedBroadcast(localIntent4, "android.permission.CALL_PRIVILEGED"); } catch (Exception e2) { e2.printStackTrace(); } }
la source
Pour info, si vous souhaitez mettre fin à un appel en cours sur Android O, Valter
Method 1: TelephonyManager.answerRingingCall()
fonctionne si vous modifiez la méthode que vous invoquez pour êtreendCall
.Il ne nécessite que la
android.permission.CALL_PHONE
permission.Voici le code:
// set the logging tag constant; you probably want to change this final String LOG_TAG = "TelephonyAnswer"; public void endCall() { 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("endCall").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); } }
la source