एंड्रॉइड 5.0 (लॉलीपॉप) में इनकमिंग कॉल का जवाब कैसे दिया जा सकता है?


87

जैसा कि मैं आने वाली कॉल के लिए एक कस्टम स्क्रीन बनाने की कोशिश कर रहा हूं, मैं आने वाली कॉल का जवाब देने की कोशिश कर रहा हूं। मैं निम्नलिखित कोड का उपयोग कर रहा हूं लेकिन यह एंड्रॉइड 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");

अरे यार, इसमें क्यों भाग रहा है, जरा स्लाइड करो यार! मेरे लिए आसान लगता है \ n /
nobalG

मैं Android उपयोगकर्ताओं के लिए एक कस्टम इनकमिंग कॉल स्क्रीन बना रहा हूं।
मैवरोइड

2
किसी को? मुझे भी इसमें दिलचस्पी है! बहुत सी चीजों की कोशिश की, लेकिन उन्होंने काम नहीं किया: /
आर्थर

1
@nobalG वह प्रोग्रामेटिक रूप से कह रहे हैं
9

1
@maveroid, क्या आप Android 5.0 के लिए वर्कअराउंड लेकर आए हैं?
आर्थरफ्रीयर

जवाबों:


155

Android 8.0 Oreo के साथ अपडेट करें

यद्यपि यह प्रश्न मूल रूप से एंड्रॉइड L सपोर्ट के लिए पूछा गया था, फिर भी लोग इस प्रश्न और उत्तर को टालते हुए प्रतीत होते हैं, इसलिए यह एंड्रॉइड 8.0 ओरियो में पेश किए गए सुधारों का वर्णन करने के लायक है। पिछड़े संगत तरीके अभी भी नीचे वर्णित हैं।

किया बदल गया?

Android 8.0 Oreo के साथ शुरू , PHONE अनुमति समूह में ANSWER_PHONE_CALLS अनुमति भी है । जैसा कि अनुमति के नाम से पता चलता है, इसे रखने से आपका ऐप प्रोग्राम को उपयोगकर्ता के प्रतिबिंब या अनुकरण के बिना किसी भी हैकिंग के बिना उचित एपीआई कॉल के माध्यम से आने वाली कॉल को स्वीकार करने की अनुमति देता है।

हम इस परिवर्तन का उपयोग कैसे करते हैं?

यदि आप पुराने एंड्रॉइड संस्करणों का समर्थन कर रहे हैं, तो आपको रनटाइम पर सिस्टम संस्करण की जांच करनी चाहिए ताकि आप उन पुराने एंड्रॉइड संस्करणों के लिए समर्थन बनाए रखते हुए इस नए एपीआई कॉल को इनकैप्सुलेट कर सकें। आपको रन-टाइम के दौरान उस नई अनुमति को प्राप्त करने के लिए रन समय में अनुमतियों का अनुरोध करना चाहिए , जैसा कि नए एंड्रॉइड संस्करणों पर मानक है।

अनुमति प्राप्त करने के बाद, आपके ऐप को केवल TelecomManager की acceptRingingCall विधि को कॉल करना होगा । एक मूल आह्वान इस प्रकार दिखता है:

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();

विधि 1: TelephonyManager.answerRingingCall ()

जब आपके पास डिवाइस पर असीमित नियंत्रण होता है।

यह क्या है?

TelephonyManager.answerRingingCall () एक छिपी हुई, आंतरिक विधि है। यह ITelephony.answerRingingCall () के लिए एक सेतु के रूप में काम करता है जिसकी इंटरवेब पर चर्चा की गई है और यह शुरुआत में आशाजनक लगता है। यह 4.4.2_r1 पर उपलब्ध नहीं है क्योंकि यह केवल एंड्रॉइड 4.4 किटकैट ( 4.4.3_r1 पर लाइन 1537 ) के लिए कम से कम 83da75d में पेश किया गया था और बाद में Lipipop ( 5.0.0_r1 पर लाइन 3138 ) के लिए प्रतिबद्ध f1e1e77 में "reintroduced" कैसे के कारण गिट वृक्ष की संरचना की गई थी। इसका मतलब यह है कि जब तक आप केवल लॉलीपॉप वाले उपकरणों का समर्थन नहीं करते हैं, जो कि शायद अभी के रूप में इसके छोटे बाजार हिस्सेदारी के आधार पर एक बुरा निर्णय है, आपको अभी भी इस मार्ग से नीचे जाने पर फालबैक तरीके प्रदान करने की आवश्यकता है।

हम इसका उपयोग कैसे करेंगे?

जैसा कि प्रश्न में विधि एसडीके अनुप्रयोगों के उपयोग से छिपी हुई है, आपको रनटाइम के दौरान प्रतिबिंब का उपयोग गतिशील रूप से जांचने और विधि का उपयोग करने की आवश्यकता है। यदि आप प्रतिबिंब से परिचित नहीं हैं, तो आप जल्दी से पढ़ सकते हैं कि प्रतिबिंब क्या है, और यह क्यों उपयोगी है? । यदि आप ऐसा करने में रुचि रखते हैं, तो आप Trail: The Reflection API में विशेष रूप से खुदाई कर सकते हैं ।

और कोड में यह कैसे दिखता है?

// 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);
}

सच्चा बनने के लिए तो यह बहुत अच्छा है!

दरअसल, एक मामूली समस्या है। यह विधि पूरी तरह कार्यात्मक होनी चाहिए, लेकिन सुरक्षा प्रबंधक कॉल करने वालों को android.permission.MODIFY_PHONE_STATE पकड़ना चाहता है । यह अनुमति प्रणाली के केवल आंशिक रूप से प्रलेखित विशेषताओं के दायरे में है क्योंकि 3 पार्टियों को इसे छूने की उम्मीद नहीं है (जैसा कि आप इसके लिए प्रलेखन से देख सकते हैं)। आप <uses-permission>इसके लिए जोड़ने का प्रयास कर सकते हैं, लेकिन यह अच्छा नहीं होगा क्योंकि इस अनुमति के लिए सुरक्षा स्तर हस्ताक्षर हैं। सिस्टम ( कोर की 1201 लाइन देखें / 5.0.0_r1 पर AndroidManifest )।

आप समस्या 34785 पढ़ सकते हैं : अपडेट एंड्रॉइड: प्रोटेक्शनवेल डॉक्यूमेंट जो 2012 में बनाया गया था, यह देखने के लिए कि हम विशिष्ट "पाइप सिंटैक्स" के बारे में विवरण गायब हैं, लेकिन चारों ओर प्रयोग करने से, ऐसा प्रतीत होता है कि इसे 'और' के रूप में कार्य करना चाहिए। दी गई अनुमति के लिए निर्दिष्ट झंडे को पूरा करना होगा। उस धारणा के तहत काम करना, इसका मतलब होगा कि आपके पास आपका आवेदन होना चाहिए:

  1. सिस्टम एप्लिकेशन के रूप में इंस्टॉल किया गया।

    यह ठीक होना चाहिए और उपयोगकर्ताओं को पुनर्प्राप्ति में ज़िप का उपयोग करके इंस्टॉल करने के लिए कहकर पूरा किया जा सकता है, जैसे कि कस्टम रोम पर Google ऐप को रूट या इंस्टॉल करते समय, जिनके पास पहले से ही पैक नहीं है।

  2. एक ही हस्ताक्षर के साथ चौखटे / आधार उर्फ ​​सिस्टम, उर्फ ​​रॉम के रूप में हस्ताक्षर किए।

    यह वह जगह है जहाँ समस्याएं पॉप अप होती हैं। ऐसा करने के लिए, आपको रूपरेखा / आधार पर हस्ताक्षर करने के लिए उपयोग की जाने वाली चाबियों पर अपना हाथ होना चाहिए। आपको न केवल नेक्सस फ़ैक्टरी छवियों के लिए Google की कुंजियों तक पहुँच प्राप्त करनी होगी, बल्कि आपको अन्य सभी ओईएम और रोम डेवलपर्स की कुंजियों तक पहुँच प्राप्त करनी होगी। यह प्रशंसनीय प्रतीत नहीं होता है, इसलिए आप अपने एप्लिकेशन को सिस्टम कुंजियों के साथ एक कस्टम रॉम बनाकर हस्ताक्षर कर सकते हैं और अपने उपयोगकर्ताओं को इसे स्विच करने के लिए कह सकते हैं (जो कठिन हो सकता है) या एक ऐसा शोषण ढूंढकर जिसके साथ अनुमति संरक्षण स्तर को बायपास किया जा सकता है (जो कठिन भी हो सकता है)।

इसके अतिरिक्त, यह व्यवहार समस्या 34792 से संबंधित प्रतीत होता है : Android जेली बीन / 4.1: android.permission.READ_LOGS अब काम नहीं करता है जो एक अनजाने विकास झंडे के साथ-साथ समान सुरक्षा स्तर का उपयोग करता है।

टेलीफोनी मैनजर के साथ काम करना अच्छा लगता है, लेकिन जब तक आपको उपयुक्त अनुमति नहीं मिलती है, तब तक काम नहीं करेगा, जो अभ्यास में आसान नहीं है।

अन्य तरीकों से TelephonyManager का उपयोग करने के बारे में क्या?

अफसोस की बात यह है कि आपको android.permission.ODIFY_PHONE_STATE को उन कूल टूल्स का उपयोग करने की आवश्यकता है, जो बदले में आपको उन तरीकों तक पहुंचने में कठिन समय देने वाले हैं।


विधि 2: सेवा कॉल सेवा कोड

जब आप परीक्षण कर सकते हैं कि डिवाइस पर चल रहा निर्माण निर्दिष्ट कोड के साथ काम करेगा।

टेलीफोनी मैनजर के साथ बातचीत करने में सक्षम होने के बिना, serviceनिष्पादन योग्य के माध्यम से सेवा के साथ बातचीत करने की संभावना भी है ।

यह कैसे काम करता है?

यह काफी सरल है, लेकिन दूसरों की तुलना में इस मार्ग के बारे में कम प्रलेखन भी है। हमें पता है कि निष्पादन योग्य दो तर्कों में लगता है - सेवा का नाम और कोड।

  • जिस सेवा का नाम हम उपयोग करना चाहते हैं वह फोन है

    इसे चलाकर देखा जा सकता है service list

  • जिस कोड का हम उपयोग करना चाहते हैं वह 6 प्रतीत होता है, लेकिन लगता है अब 5 हो गया है

    ऐसा लगता है कि यह अब कई संस्करणों के लिए IBinder.FIRST_CALL_TRANSACTION + 5 पर आधारित है ( 1.5_r4 से 4.4.4_r1 तक ), लेकिन स्थानीय परीक्षण के दौरान कोड 5 ने एक इनकमिंग कॉल का जवाब देने के लिए काम किया। जैसा कि लॉलीपॉप चारों ओर एक बड़े पैमाने पर अद्यतन है, यह समझ में आता है कि यहां आंतरिक रूप से भी बदल गया है।

यह एक आदेश के साथ परिणाम है service call phone 5

हम इस प्रोग्राम को कैसे उपयोग करते हैं?

जावा

निम्नलिखित कोड अवधारणा के प्रमाण के रूप में कार्य करने के लिए बनाया गया एक मोटा कार्यान्वयन है। आप वास्तव में आगे जाना है और इस विधि का उपयोग करना चाहते हैं, तो आप शायद की जांच करना चाह समस्या से मुक्त सु उपयोग के लिए दिशा-निर्देश और संभवतः अधिक पूरी तरह से विकसित करने के लिए स्विच libsuperuser द्वारा 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();
}

प्रकट

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

क्या वास्तव में इसे रूट एक्सेस की आवश्यकता है?

अफसोस की बात है, ऐसा लगता है। आप उस पर Runtime.exec का उपयोग करने का प्रयास कर सकते हैं , लेकिन मैं उस मार्ग के साथ किसी भी भाग्य को प्राप्त करने में सक्षम नहीं था।

यह कितना स्थिर है?

मुझे खुशी है कि आपने पूछा। प्रलेखित नहीं होने के कारण, यह विभिन्न संस्करणों में टूट सकता है, जैसा कि ऊपर दिए गए कोड अंतर से सचित्र है। सेवा का नाम संभवतः विभिन्न बिल्ड में फोन रहना चाहिए , लेकिन हम सभी जानते हैं, कोड मान एक ही संस्करण के कई बिल्ड में बदल सकता है (आंतरिक संशोधनों के अनुसार, ओईएम की त्वचा) बदले में इस्तेमाल की गई विधि को तोड़ती है। इसलिए यह नेक्सस 4 (mako / occam) पर परीक्षण का उल्लेख करने योग्य है। मैं व्यक्तिगत रूप से आपको इस विधि का उपयोग करने के खिलाफ सलाह दूंगा, लेकिन जैसा कि मैं एक अधिक स्थिर विधि खोजने में सक्षम नहीं हूं, मेरा मानना ​​है कि यह सबसे अच्छा शॉट है।


मूल विधि: हेडसेट कीकोड इंटेंस

ऐसे समय के लिए जब आपको व्यवस्थित होना है।

निम्न अनुभाग दृढ़ता से प्रभावित था इस जवाब से रिले सी

मूल प्रश्न में पोस्ट किया गया सिम्युलेटेड हेडसेट आशय विधि को केवल उसी के अनुरूप प्रसारित किया जाना प्रतीत होता है, लेकिन यह कॉल का उत्तर देने के लक्ष्य को पूरा नहीं करता है। हालांकि ऐसा प्रतीत होता है कि कोड को उन इरादों को संभालना चाहिए, लेकिन वे केवल इस बात की परवाह नहीं कर रहे हैं, जिसका अर्थ है कि इस पद्धति के विरुद्ध किसी प्रकार के नए काउंटरमेशर होने चाहिए। लॉग में कोई दिलचस्पी नहीं दिखती है और मुझे व्यक्तिगत रूप से विश्वास नहीं है कि इसके लिए एंड्रॉइड स्रोत के माध्यम से खुदाई करना उचित होगा, क्योंकि Google द्वारा किसी भी तरह से आसानी से उपयोग किए जाने वाले तरीके को आसानी से तोड़ने के लिए Google द्वारा एक संभावित परिवर्तन शुरू करने की संभावना है।

क्या ऐसा कुछ है जो हम अभी कर सकते हैं?

इनपुट निष्पादन योग्य का उपयोग करके व्यवहार को लगातार पुन: पेश किया जा सकता है। यह एक कीकोड तर्क में लेता है, जिसके लिए हम बस KeyEvent.KEYCODE_HEADSETHOOK में पास होते हैं । विधि को आम जनता में आम उपयोग के मामलों के लिए उपयुक्त बनाने के लिए रूट एक्सेस की भी आवश्यकता नहीं है, लेकिन विधि में एक छोटी सी खामी है - हेडसेट बटन प्रेस घटना को अनुमति की आवश्यकता के लिए निर्दिष्ट नहीं किया जा सकता है, जिसका अर्थ है कि यह वास्तविक की तरह काम करता है बटन प्रेस और पूरी श्रृंखला के माध्यम से बुलबुले उठते हैं, जिसका अर्थ है कि आपको इस बारे में सतर्क रहना होगा कि बटन प्रेस का अनुकरण कब करना है, उदाहरण के लिए, प्लेबैक शुरू करने के लिए संगीत खिलाड़ी को ट्रिगर करें यदि कोई और उच्च प्राथमिकता को संभालने के लिए तैयार नहीं है। घटना।

कोड?

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; डॉ

एंड्रॉइड 8.0 ओरियो और बाद के लिए एक अच्छा सार्वजनिक एपीआई है।

Android 8.0 Oreo से पहले कोई सार्वजनिक API नहीं है। आंतरिक एपीआई बिना सीमा के या बिना प्रलेखन के होते हैं। आपको सावधानी से आगे बढ़ना चाहिए।


साथ हेडसेट कीकोड उद्देश्यों के संबंध में, आप गूगल स्रोत की जाँच की है यहाँ किसी भी कारण है कि वे पर काम किया जा रहा है बंद होगा? मजेदार बात यह है कि इन इरादों के साथ कॉल को आसानी से अस्वीकार किया जा सकता है (बस एक लंबी-प्रेस का अनुकरण करें), लेकिन जवाब देने के लिए कुछ भी काम नहीं करता है। मुझे अभी तक एक स्पष्ट अनुमति जांच या अन्य संभावित ब्लॉक की तलाश है और मुझे उम्मीद है कि आँखों के दूसरे सेट से कुछ उजागर हो सकता है।
रिले सी

थोड़ा व्यस्त था इसलिए देरी - यह पता लगाने में कुछ समय बिताने की कोशिश करेंगे। एक त्वरित नज़र के बाद, ऐसा लगता है कि CallsManager HeadsetMediaButton का निर्माण करती है। MediaSessionManager के कॉलबैक पर सत्र कॉलबैक को कॉल हैंडहेलसेटहूक (KeyEvent) कॉलिंग का ध्यान रखना चाहिए। लगता है कि सभी कोड मेल खा रहे हैं ... लेकिन मुझे आश्चर्य है कि क्या कोई व्यक्ति KeyEvent.ACTION_DOWN को परीक्षण के इरादे से हटा सकता है? (यही है, केवल KeyEvent.ACTION_UP को एक बार फायर करें।)
वाल्टर जैन्सन

दरअसल, HeadsetMediaButton MediaSession के साथ काम करता है और सीधे MediaSessionManager से बातचीत नहीं करता है ...
Valter Jansons

1
आप में से उन लोगों के लिए जो एक ऐसी स्थिति खोजने में कामयाब रहे जहां मूल विधि काम नहीं करती है (जैसे लॉलीपॉप के मुद्दे हैं), मेरे पास अच्छी और बुरी खबर है: मैं 100% काम करने में कामयाब रहा, अपने कोड में, एक के साथ FULL_WAKE_LOCK। यह एक PARTIAL_WAKE_LOCK के साथ काम नहीं करेगा। क्यों इस बारे में कोई प्रलेखन बिल्कुल नहीं है। मैं इसे भविष्य के उत्तर में विस्तार से बताऊंगा जब मैं अपने प्रयोग कोड को अधिक बड़े पैमाने पर परखता हूं। बुरी खबर, निश्चित रूप से, FULL_WAKE_LOCK पदावनत है, इसलिए यह एक फिक्स है जो केवल तब तक चलेगा जब तक Google इसे एपीआई में रखता है।
leRobot

1
मूल उत्तर से पकड़ को कई मामलों में नहीं कहा जाता है। मैंने पाया है कि पहले केवल निष्पादन को कॉल करना बेहतर है और उसके बाद बटन को ठीक वैसे ही कॉल करें।
वॉर्पज़िट

36

पूरी तरह से काम कर रहे समाधान @Valter स्ट्रोक कोड पर आधारित है।

इसे काम करने के लिए, आपको लॉक स्क्रीन पर एक (अदृश्य) गतिविधि प्रदर्शित करनी होगी जहां कोड निष्पादित किया गया है।

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>

कॉल स्वीकार गतिविधि

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 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>

अंत में जादू को बुलाओ!

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);

1
mi mi "अंत में मैजिक कॉल करें" के तहत कोड जोड़ने के लिए। क्या यह एंड्रॉइड 6.0
अक्षय शाह

मैं यहां यह कहने के लिए आया था कि ब्रॉडकास्टहेससेट कनेक्टेड (बूलियन जुड़ा हुआ) एक सैमसंग ए 3 2016 डिवाइस में एक समस्या का हल है। उसके बिना, एक बहुत ही समान विधि (अलग-अलग, पारदर्शी गतिविधि और इनवोक और व्हाट्सएप के लिए थ्रेड्स) लगभग 20 परीक्षण किए गए उपकरणों के लिए पूरी तरह से काम कर रही थी, फिर यह ए 3 साथ आया और मुझे नए उत्तरों के लिए इस प्रश्न को फिर से जांचने के लिए मजबूर किया। मेरे कोड के साथ तुलना करने के बाद, यह महत्वपूर्ण अंतर था!
leRobot

1
मैं कॉल को कैसे अस्वीकार कर सकता हूं? क्या आप इसे दिखाने के लिए उत्तर अपडेट कर सकते हैं?
अमन्नी

@leRobot यह उत्तर जाँचता है कि क्या यह HTC डिवाइस को प्रसारित करना है वैसे यह वास्तव में एक अच्छा जवाब है, मेरा ऐप फोन कॉल का जवाब दे सकता है यहां तक ​​कि स्क्रीन भी लॉक है।
इप्टी

@eepty आप बिल्ड डेटा के लिए आधिकारिक डिवाइस संदर्भ का उपयोग कर सकते हैं। ( support.google.com/googleplay/answer/1727131?hl=hi )। मैं A3 2016 के लिए Build.MODEL.startsWith ("SM-A310") का उपयोग करता हूं। लेकिन! मैं पुष्टि कर सकता हूँ कि A3 2016 हेडसेट से जुड़े प्रसारणों का समर्थन नहीं करता है! क्या वास्तव में मेरी समस्या हल हो गई थी ताकि ऑर्डर बदल रहा था ताकि Runtime.getRuntime () निष्पादित हो ... (... इन उपकरणों के लिए ट्रिगर होता है। यह उस डिवाइस के लिए हर बार काम करता है और अपवाद पर वापस नहीं आता है।
LeRobit

14

निम्नलिखित एक वैकल्पिक दृष्टिकोण है जो मेरे लिए काम करता है। यह मीडियाकंट्रोलर एपीआई का उपयोग करके सीधे टेलीकॉम सर्वर को प्रमुख घटना भेजता है। इसके लिए यह आवश्यक है कि ऐप में BIND_NOTIFICATION_LISTENER_SERVICE अनुमति हो और उसे उपयोगकर्ता से अधिसूचना का स्पष्ट अनुदान दिया जाए:

@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 उपरोक्त कोड में सिर्फ एक खाली वर्ग हो सकता है।

import android.service.notification.NotificationListenerService;

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

मेनिफ़ेस्ट में संबंधित अनुभाग के साथ:

    <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>

चूंकि घटना का लक्ष्य स्पष्ट है, इसलिए इसे मीडिया प्लेयर को ट्रिगर करने के किसी भी दुष्प्रभाव से बचना चाहिए।

नोट: टेलिकॉम सर्वर रिंगिंग इवेंट के तुरंत बाद सक्रिय नहीं हो सकता है। इसके लिए मज़बूती से काम करने के लिए, यह अनुप्रयोग के लिए उपयोगी हो सकता है कि MediaSessionManager.OnActiveSessionsChangedListener को यह देखने के लिए कि टेलीकॉम सर्वर सक्रिय हो जाता है, घटना भेजने से पहले।

अपडेट करें:

में एंड्रॉयड हे , अनुकरण करने के लिए एक की जरूरत ACTION_DOWNसे पहले ACTION_UP, अन्यथा ऊपर कोई प्रभाव नहीं है। अर्थात निम्नलिखित की आवश्यकता है:

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

लेकिन जब से Android O (शीर्ष उत्तर देखें) के बाद से उत्तर देने के लिए एक आधिकारिक कॉल उपलब्ध है, तब तक इस हैक की कोई आवश्यकता नहीं हो सकती है, जब तक कि एंड्रॉइड O से पहले कोई पुराने कंपाइल एपीआई स्तर के साथ अटक न जाए।


यह मेरे लिए सही नहीं रहा। इसने अनुमति त्रुटि दी। एप्लिकेशन को सूचना नहीं दी गई है। मैं
Jame

2
इसके लिए उपयोगकर्ता द्वारा अनुमति देने के अतिरिक्त चरण की आवश्यकता होती है, कहीं-कहीं सिस्टम के आधार पर सेटिंग्स मेनू में, अनुमति के अलावा प्रकट में स्वीकार करना।
हेडेक

यह रिपोर्टिंग एक संकीर्ण मामले में काम करने लगती है: मार्शमैलो के साथ गैलेक्सी ए 3 2016। मैं A3 उपकरणों के एक समूह में इसका परीक्षण करूंगा जो कि FATAL EXCEPTION: java.lang.SecurityException: के कारण इनपुट कीवेंट विधि के साथ काम नहीं कर रहे हैं: किसी अन्य अनुप्रयोग के लिए INJECT_EFFENTS अनुमति की आवश्यकता है। ऑफ़ेंडिंग डिवाइस मेरे उपयोगकर्ता-बेस का लगभग 2% है और मैं उनके अपवाद की नकल नहीं कर रहा हूं, लेकिन यह देखने का प्रयास करेगा कि क्या वे कॉल लेने का प्रबंधन करते हैं। सौभाग्य से मेरा ऐप पहले से ही स्पष्ट नोटिफ़ का अनुरोध कर रहा है। अन्य उद्देश्यों के लिए उपयोग।
leRobot

व्यापक परीक्षण के बाद, मुझे यह सूचित करते हुए खुशी हो रही है कि A3 2016 डिवाइस जो "इनपुट कीवेंट" के साथ विफल हो रहे थे, MediaController # dispatchMediaButtonEvent (<हुक KeryEvent>) विधि के साथ काम करने में कामयाब रहे। यह स्पष्ट रूप से उपयोगकर्ता द्वारा स्पष्ट अधिसूचना एक्सेस की अनुमति देने के बाद ही काम करता है, इसलिए आपको इसके लिए एंड्रॉइड सेटिंग्स पर एक स्क्रीन निर्देशन जोड़ना होगा, और आपको मूल रूप से उपयोगकर्ता को इसके लिए अतिरिक्त कार्रवाई करने की आवश्यकता है, जैसा कि उत्तर पर विस्तृत है। मेरे ऐप में हमने यह पूछने के लिए अतिरिक्त कदम उठाए हैं कि क्या उपयोगकर्ता उस स्क्रीन पर जाता है लेकिन नोटिफ़ को जोड़ता नहीं है। अभिगम
leRobot

यह एंड्रॉयड नूगट पर काम करता है। एंड्रॉइड पर @ नॉटज़ का समाधान बहुत अच्छा काम करता है, लेकिन शिकायत करता है "केवल सिस्टम एंड्रॉइड 7 पर" वैश्विक प्राथमिकता सत्र के लिए मीडिया की महत्वपूर्ण घटना को भेज सकता है "
पेंग बाई

9

@Muzikant द्वारा उत्तर पर एक छोटे से बिट को विस्तृत करने के लिए, और मेरे डिवाइस पर थोड़ा क्लीनर काम करने के लिए इसे संशोधित करने के input keyevent 79लिए, KeyEvent.KEYCODE_HEADSETHOOK के लिए निरंतर प्रयास करें । बहुत मोटे तौर पर:

    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();

काफी खराब कोडिंग सम्मेलनों को माफ कर दो, मैं Runtime.exec () कॉल में बहुत अच्छी तरह से वाकिफ नहीं हूं। ध्यान दें कि मेरा डिवाइस रूट नहीं है, न ही मैं रूट विशेषाधिकारों का अनुरोध कर रहा हूं।

इस दृष्टिकोण के साथ परेशानी यह है कि यह केवल कुछ शर्तों (मेरे लिए) के तहत काम करता है। यही है, अगर मैं उपर्युक्त थ्रेड को एक मेनू विकल्प से चलाता हूं जो उपयोगकर्ता कॉल करते समय चयन करता है, तो कॉल ठीक जवाब देता है। यदि मैं इसे एक रिसीवर से चलाता हूं जो आने वाली कॉल की स्थिति की निगरानी करता है, तो यह पूरी तरह से अनदेखा हो जाता है।

तो मेरे नेक्सस 5 पर यह उपयोगकर्ता द्वारा संचालित जवाब देने के लिए अच्छी तरह से काम करता है और एक कस्टम कॉल स्क्रीन के उद्देश्य के अनुरूप होना चाहिए। यह किसी भी प्रकार के स्वचालित कॉल-कंट्रोल-टाइप एप्लिकेशन के लिए काम नहीं करेगा।

नोट के अलावा सभी संभावित कैविएट हैं, जिसमें यह भी शामिल है, शायद एक अपडेट या दो में काम करना बंद कर देगा।


input keyevent 79सोनी एक्सपीरिया 5.0 पर ठीक काम करता है। किसी गतिविधि से या प्रसारण रिसीवर से कॉल करने पर काम करता है।
निकोलस

0

adb कमांड के माध्यम से adb के द्वारा कॉल कैसे उठाएं

ध्यान रखें कि एंड्रॉइड लिनक्स एक विशाल JVM के साथ सामने के छोर पर है। आप एक कमांड लाइन ऐप डाउनलोड कर सकते हैं और फोन को रूट कर सकते हैं और अब आपके पास एक नियमित लिनक्स कंप्यूटर और कमांड लाइन है जो सभी सामान्य चीजें करता है। स्क्रिप्ट चलाएँ, आप इसे भी कर सकते हैं (OpenVPN ट्रिक)


0

धन्यवाद @ नोट का जवाब मेरे लिए लोलिलॉप पर काम कर रहा है। इस कोड को पुराने Android SDK के साथ काम करने के लिए, आप इस कोड को कर सकते हैं:

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

कॉल का स्वचालित रूप से जवाब देने के बाद स्पीकर फोन को कैसे चालू करें।

मैंने अपनी समस्या को सेट अपस्कर्टफोनऑन के साथ हल किया। मुझे लगता है कि इसकी कीमत यहां पोस्ट करने के बाद से है, क्योंकि ऑटो कॉल के लिए फोन का उपयोग करने के मामले में अक्सर स्पीकरफोन की आवश्यकता होती है। इस थ्रेड पर फिर से सभी को धन्यवाद, क्या कमाल का काम है।

यह एंड्रॉइड 5.1.1 पर मेरे Nexus 4 पर बिना ROOT के मेरे लिए काम करता है। ;)

अनुमति आवश्यक:

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

जावा कोड:

// 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
}

1
दिलचस्प। मैं वास्तव में कॉल का जवाब देने और स्पीकर को एक साथ चालू करने की कोशिश कर रहा हूं ताकि यह दृष्टिकोण दोनों हल हो जाए :)। हालांकि अन्य उत्तरों में कुछ टिप्पणियों के समान मेरा प्रश्न है: यह कोड कहां जाता है?
फैंगमोबाइल

-1

निम्नलिखित कमांड को रूट के रूप में चलाएँ:

input keyevent 5

यहाँ keyevents अनुकरण पर अधिक जानकारी ।

आप अपने ऐप से रूट के रूप में कमांड चलाने के लिए बनाए गए इस बेस क्लास का उपयोग कर सकते हैं ।


1
एक नियमित उपयोगकर्ता प्रोफ़ाइल के साथ परीक्षण करते समय, इसने मेरे लिए इन-कॉल यूआई लाया, जो मुझे त्वरित कार्रवाई / प्रतिक्रिया को अस्वीकार / जवाब देने या उपयोग करने के लिए बाएं / दाएं स्वाइप करने के लिए कह रहा था। यदि ओपी एक कस्टम इनकमिंग कॉल स्क्रीन बना रहा है , तो यह वास्तव में किसी भी तरह की मदद नहीं है जब तक कि यह रूट के तहत अलग तरह से व्यवहार नहीं करता है, जो मुझे संदेह है जैसे कि यह एक नियमित उपयोगकर्ता के लिए अच्छा व्यवहार नहीं करता है, कॉल शायद विफल हो जाएगी और नहीं एक अलग कार्रवाई ट्रिगर।
वाल्टर जानसन

-2

इस का परीक्षण करें: सबसे पहले अनुमतियों को जोड़ें फिर कॉल का जवाब देने के लिए किलकॉल (हैंगअप का उपयोग करें) उत्तर () का उपयोग करें

<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();
    }
}

-2

FYI करें यदि आप एंड्रॉइड O पर चल रहे कॉल को कैसे करना चाहते हैं, तो Valter Method 1: TelephonyManager.answerRingingCall()काम करता है यदि आप अपने द्वारा भेजे जाने वाले तरीके को बदलते हैंendCall

इसके लिए केवल android.permission.CALL_PHONEअनुमति की आवश्यकता होती है ।

यहाँ कोड है:

// 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);
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.