2016-07-19 4 views
0

헤드폰 잭을 버튼으로 사용해야하는 앱을 개발 중입니다.헤드폰이 연결되어있을 때 기본 오디오를 이어폰으로 라우팅하는 방법은 무엇입니까?

요구 사항 : 헤드셋이

라우팅에 대한 스피커와 헤드폰과 블루투스 헤드셋하지만 아무것도를 통해 오디오 라우팅의 많은 예를있다 (헤드폰을 통해 오디오의 필요)가 연결되지 않은 경우 이어폰을 통해 기본 오디오 (전화를) 플레이 헤드셋이 연결되어있는 경우 장치의 이어 스피커를 통한 오디오. 나는 많은 시도와 일부 링크는

입니다

Android : Force audio routing (내 시나리오에서 작동하지 않습니다)

나는 된 SOUNDabout ( https://play.google.com/store/apps/details?id=com.woodslink.android.wiredheadphoneroutingfix&hl=en) 응용 프로그램을 확인하고이 헤드셋, 스피커 및 이어폰 같은 다양한 포트에 오디오 라우팅됩니다

. 헤드셋이 연결되어있는 경우

나는 스피커에 오디오 가지고있다 : 다음은 이어폰은 안드로이드의 미디어 사용되지 않습니다

if (Build.VERSION.SDK_INT >= 21) { 
      ForegroundService.audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION); 
      ForegroundService.audioManager.setSpeakerphoneOn(true); 
      SplashScreen.preferences.edit().putBoolean("isKey", true).commit(); 
     } else { 
      Class audioSystemClass = null; 
      try { 
       audioSystemClass = Class.forName("android.media.AudioSystem"); 
       Method setForceUse = audioSystemClass.getMethod("setForceUse", int.class, int.class); 
       setForceUse.invoke(null, FOR_MEDIA, FORCE_SPEAKER); 
      } catch (ClassNotFoundException e) { 
       e.printStackTrace(); 
      } catch (NoSuchMethodException e) { 
       e.printStackTrace(); 
      } catch (IllegalAccessException e) { 
       e.printStackTrace(); 
      } catch (InvocationTargetException e) { 
       e.printStackTrace(); 
      } 


      SplashScreen.preferences.edit().putBoolean("isKey", true).commit(); 
      ForegroundService.audioManager.setSpeakerphoneOn(true); 
     } 

답변

0

많이 연구 한 결과, 리플렉션을 사용하지 않고이 기능을 수행 할 수있는 방법이 없다는 것을 발견했습니다. 먼저 헤드셋 잭을 넣은 다음 적절한 매개 변수를 사용하여 setWiredDeviceConnectionState() 메서드를 호출하면 헤드폰이 끊긴 것처럼 동작하지만 클릭은 여전히 ​​작동합니다. 그래서 해킹이지만 내 요구 사항에 따라, 그것은 절대 안전한 해결책은 아니지만 지금 당장 노력하고 있습니다. 롤리팝 낮은 버전의

@TargetApi(Build.VERSION_CODES.M) 
public class HeadSetJackReciever extends AudioDeviceCallback { 
    public static boolean isAudioChecked; 

    public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) { 
     if (addedDevices.length != 0) { 
      for (int i = 0; i < addedDevices.length; i++) { 
       if (addedDevices[i].getType() == AudioDeviceInfo.TYPE_WIRED_HEADSET) { 
        AudioDeviceInfo audioDeviceInfo = addedDevices[i]; 
        int microphone = audioDeviceInfo.getType(); 
        String headsetName = "DCS"; 
        String headsetAddress = ""; 
        try { 
         Method method = audioDeviceInfo.getClass().getMethod("getAddress"); 
         method.setAccessible(true); 
         headsetAddress = (String) method.invoke(audioDeviceInfo); 
        } catch (NoSuchMethodException e) { 
         e.printStackTrace(); 
        } catch (InvocationTargetException e) { 
         e.printStackTrace(); 
        } catch (IllegalAccessException e) { 
         e.printStackTrace(); 
        } 
        Log.e("TEST", "microphone:"+microphone); 
        Log.e("TEST", "headsetName:"+headsetName); 
        Log.e("TEST", "headsetAddress:"+headsetAddress); 
        Intent intent = new Intent(ForegroundService.context, SelectAudioOutput.class); 
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 
        intent.putExtra("microphone",microphone); 
        intent.putExtra("headsetName",headsetName); 
        intent.putExtra("headsetAddress",headsetAddress); 
        ForegroundService.context.startActivity(intent); 
       } 
      } 
     } 
    } 

    public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) { 
     if (removedDevices.length != 0) { 
      Log.e("TEST", "Audio deinserted"); 
      if (SplashScreen.preferences.getBoolean("isKey", false)) { 
       Intent startIntent = new Intent(ForegroundService.context, ForegroundService.class); 
       startIntent.setAction(Constants.ACTION.STARTNOTIFICATION_ACTION); 
       ForegroundService.context.startService(startIntent); 
      } else { 
       Intent startIntent = new Intent(ForegroundService.context, ForegroundService.class); 
       startIntent.setAction(Constants.ACTION.STOPNOTIFICATION_ACTION); 
       ForegroundService.context.startService(startIntent); 
      } 
      ForegroundService.audioManager.setMode(AudioManager.MODE_IN_CALL); 
      ForegroundService.audioManager.setSpeakerphoneOn(false); 
     } 
    } 
} 

:

if (intent.getAction().equals(Intent.ACTION_HEADSET_PLUG)) { 
      headsetName = intent.getStringExtra("name"); 
      microphone = intent.getIntExtra("microphone", 0); 

      int state = intent.getIntExtra("state", -1); 
      switch (state) { 
       case 0: 
        Log.d("onReceive", "Headset unplugged"); 
        Log.e("TEST", "Audio deinserted"); 
        if (SplashScreen.preferences.getBoolean("isKey", false)) { 
         Intent startIntent = new Intent(ForegroundService.context, ForegroundService.class); 
         startIntent.setAction(Constants.ACTION.STARTNOTIFICATION_ACTION); 
         context.startService(startIntent); 
        } else { 
         Intent startIntent = new Intent(ForegroundService.context, ForegroundService.class); 
         startIntent.setAction(Constants.ACTION.STOPNOTIFICATION_ACTION); 
         context.startService(startIntent); 
        } 
        ForegroundService.audioManager.setMode(AudioManager.MODE_IN_CALL); 
        ForegroundService.audioManager.setSpeakerphoneOn(false); 
        break; 
       case 1: 

        Log.d("onReceive", "Headset plugged"); 
        Log.e("TEST", "microphone:"+microphone); 
        Log.e("TEST", "headsetName:"+headsetName); 
        Log.e("TEST", "headsetAddress:"+headsetAddress); 
        Intent intentone = new Intent(ForegroundService.context, SelectAudioOutput.class); 
        intentone.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 
        intentone.putExtra("microphone",microphone); 
        intentone.putExtra("headsetName",headsetName); 
        intentone.putExtra("headsetAddress",headsetAddress); 
        context.startActivity(intentone); 
        break; 
      } 
     } 

내가 뭔가를 그리워 알려줘 여기

private void sendIntent(Intent i) { 
     Method m; 
     Log.i(TAG, "Device sdk = " + Build.VERSION.SDK_INT); 
     try { 
      if (Build.VERSION.SDK_INT < 16) { 
       Class<?> clazz = Class.forName("android.app.ActivityManagerNative"); 
       m = clazz.getMethod("broadcastStickyIntent", Intent.class, String.class); 
       m.setAccessible(true); 
       m.invoke(clazz, i, null); 
       return; 
      } else if (Build.VERSION.SDK_INT < 23) { 
       //int type, int state, String address, String name 
       m = am.getClass().getMethod("setWiredDeviceConnectionState", Integer.TYPE, Integer.TYPE, String.class); 
       m.setAccessible(true); 
       Object[] objArr = new Object[3]; 
       objArr[0] = (i.getIntExtra("microphone", 0) == 0) ? 8 : 4; 
       objArr[1] = i.getIntExtra("state", 0); 
       objArr[2] = i.getStringExtra("name"); 
       m.invoke(am, objArr); 
      } else { 
       //int type, int state, String address, String name 
       m = am.getClass().getMethod("setWiredDeviceConnectionState", Integer.TYPE, Integer.TYPE, String.class, String.class); 
       m.setAccessible(true); 
       Object[] objArr = new Object[4]; 
       objArr[0] = (i.getIntExtra("microphone", 0) == 0) ? 8 : 4; 
       objArr[1] = i.getIntExtra("state", 0); 
       objArr[2] = i.getStringExtra("address"); 
       objArr[3] = i.getStringExtra("name"); 
       m.invoke(am, objArr); 
      } 
     } catch (ClassNotFoundException e) { 
      e.printStackTrace(); 
     } catch (NoSuchMethodException e) { 
      e.printStackTrace(); 
     } catch (InvocationTargetException e) { 
      e.printStackTrace(); 
     } catch (IllegalAccessException e) { 
      e.printStackTrace(); 
     } 
    } 

의도

을 보내,이 작업을 수행하려면 코드입니다. 감사합니다. .

+0

in HeadsetJackReceiver addedDevices [] array 나는 2 개의 헤드셋과 1 개의 헤드폰을 얻고 있습니다. 그래서 코드는 두 개의 인 텐트를 보내고 오디오는 라우트되지 않습니다. Android M (23)을 사용하고 있습니다. 도울 수 있니? –

1

내 번호 및 전화가 "전화 인 경우에만 사용할 수 있습니다 "또는"통신 "(VoIP) 상태를 나타냅니다.

"FORCE_EARPIECE"상수가 없다는 것을 알고 있으므로 setForceUse에 대한 호출에서는 지정할 수 없습니다.

또한, 해당 장치가 선택 될 것입니다, 이어폰 통화에 대한 출력 장치 선택에서 가장 낮은 우선 순위를 가지고, 그래서 전화가 연결된 아무것도이있는 경우 (그리고 귀하의 경우 가짜 헤드셋가) (https://android.googlesource.com/platform/frameworks/av/+/322b4d2/services/audiopolicy/enginedefault/src/Engine.cpp#381 참조).

죄송합니다. 의도하는 바를 달성 할 수없는 것 같습니다.

UPDATE

된 SOUNDabout은 미디어 이어폰의 사용을 강제하는 동안 media.audio_policy 상태를 검사 한 후, 나는이 응용 프로그램이 사용하는 다음과 같은 트릭을 발견했다 :

  1. 그것은 시행에 대한 AudioSystem.setPhoneState(MODE_IN_COMMUNICATION)를 호출을 " 통신 "전화 상태 (일반적으로 VoIP 통화에 사용됨).

  2. 헤드셋 (또는 헤드폰)이 연결된 경우 우선 순위가 높아져서 소리가 라우트되지 않도록하려면 AudioSystem.setDeviceConnectionState(DEVICE_OUT_WIRED_HEADSET, DEVICE_STATE_UNAVAILABLE, ...)으로 전화하여 헤드셋이 없다고 Audio Manager를 속이십시오.

이들은 모두 해킹이며 앱이 휴대 전화 상태를 자세히 모니터링해야합니다. 그것은 또한 항상 작동하지 않습니다.

또 다른 단점은 이어 피스를 사용하여 온 - 칩 오디오 압축 풀기가 비활성화되어 배터리 사용량이 높아진다는 것입니다.

일반적으로 이러한 기술을 사용하지 않는 것이 좋습니다.

+0

@Mikhail에게 감사 인사를 전합니다. 나는 그것이 불가능하다는 것을 알고있다. Google은 개발자를위한 직접 버튼을 제공 할 방법을 제공하지 않으려 고합니다. 나는 이것에 관해 많은 연구를 해왔고, 그것에 접근 할 수있는 모든 메소드가 제거되거나 숨겨 졌거나 모욕적 인 것으로 나타났다. 하지만 mSwitch, Keycut, iKey 등의 앱을 플레이 스토어에서 사용하는 일부 앱은 가능하지만 앱에서이 기능이 필요한 이유는 모르지만 어떻게해야할까요? 저는 리플렉션을 사용하여 프로그래밍 방식으로 헤드폰을 연결 해제한다고 생각합니다. 오디오는 평소와 같이 우선 순위를 사용하여 이어 피스로 라우팅됩니다. – DCS

+0

적어도 mSwitch (KeyCut)는 휴대 전화를 응원해야합니다. 루트 액세스를 사용하면 필요한 모든 작업을 오디오 정책 구성을 변경할 수 있습니다. –

+0

루트 액세스를 원하지 않으며 테스트 한 장치가 루팅되지 않았습니다. Playstore에서 SoundAbout 및 Kclick 앱을 확인하십시오. 수화기를 라우트하려면 선택하고 헤드폰이 아닌 수화기로 오디오를들을 수 있습니다. – DCS