2011년 3월 11일 금요일

안드로이드 Setting 응용의 항목 관련

안드로이드로 플랫폼에서 추가한 하드웨어 등의 설정을 Settings 어플에 추가하기 위해서는 어떻게 해야 할까?

당연한 말이지만 이를 위해서는 java 코드를 주로 수정해야 한다. Settings 관련 코드는 다음에 위치한다.

packages/apps/Setting/

화면 처리와 관련된 내용을 수정한다고 가정하고 Display Setting을 보면 되는데 일단 가속도계의 입력에 따른 회전을 관리하는 Orientation을 찾아 보자.  이미 안드로이드 어플리케이션을 만들어 본 경우라면 당연한 이해하겠지만 기본적으로 3개의 파일이 관련되어 있다.

packages/apps/Setting/res/values/strings.xml
packages/apps/Setting/res/xml/sound_and_display_settings.xml
src/com/android/settings/SoundAndDisplaySettings.java

순서대로 보면 다음과 같다.

Sound & Display Setting 화면에서 Orientation에 대한 것은 on/off 만을 설정하도록 checkbox 형태로 구현되어 있다. 실제 화면을 보면 "Orientation" 이란 타이틀 문자열과, 타이틀 문자열 아래에서 현재 상태에 따라 나타나는 "Switch orientation automatically when rotating phone"에 대한 문자열이 필요하다.

    <!-- Sound & display settings screen, accelerometer-based rotation check box label -->
    <string name="accelerometer_title">Orientation</string>
    <!-- Sound & display settings screen, accelerometer-based rotation summary text when check box is selected -->
    <string name="accelerometer_summary_on">Switch orientation automatically when rotating phone</string>
    <!-- Sound & display settings screen, accelerometer-based rotation summary text when check box is clear -->
    <string name="accelerometer_summary_off">Switch orientation automatically when rotating phone</string>

실제 코드를 보면 accelerometer_title과 accelerometer_summary_on, accelerometer_summary_off 를 정의하고 있다. 여기서 뒤에 2개는 같은 내용으로 되어 있는데 상태에 따라 다른 내용을 보여주고 싶으면 다르게 정의하면 된다.

일단 문자열을 정의하면 checkbox 형태를 사용하므로 이에 대한 내용을 sound_and_display_settings.xml에 정의한다.

    <PreferenceCategory
            android:title="@string/display_settings">

        <CheckBoxPreference
            android:key="accelerometer"
            android:title="@string/accelerometer_title"
            android:summaryOn="@string/accelerometer_summary_on"
            android:summaryOff="@string/accelerometer_summary_off"/>


        <ListPreference
            android:key="animations"
            android:title="@string/animations_title"
            android:persistent="false"
            android:entries="@array/animations_entries"
            android:entryValues="@array/animations_values" />

CheckBox를 위한 key 이름을 정의하고 와 title, summayOn, summeryOff에 사용할 문자열을 strings.xml에 정의한 내용으로 정의(연결)해 둔다.

실제 코드 구현은 SoundAndDisplaySettings.java에 구현되어 있다.

먼저 사용할 KEY 문자열을 정의한다. sound_and_display_settings.xml에 정의한 key 문자열과 같은 값으로 한다. 여기에서 final은 JAVA에서 주로 사용하는 것으로 문자열이 수정하지 않는 고정된 값임을 나타낸다.

    private static final String KEY_ACCELEROMETER = "accelerometer";

실제 화면 구성에서 사용할 CheckBoxPreference 클래스의 accelerometer 인스턴스를 선언한다.

    private CheckBoxPreference mVibrate;
    private CheckBoxPreference mDtmfTone;
    private CheckBoxPreference mSoundEffects;
    private CheckBoxPreference mHapticFeedback;
    private ListPreference mAnimations;
    private CheckBoxPreference mAccelerometer;

Settings 어플 화면에서 관련 설정을 보여주고, 동작하도록 하기 위해서는 OnCreate(), updateState(), OnPreferenceTreeClick() 메소드를 수정하면 된다.

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


        mAnimations = (ListPreference) findPreference(KEY_ANIMATIONS);
        mAnimations.setOnPreferenceChangeListener(this);
        mAccelerometer = (CheckBoxPreference) findPreference(KEY_ACCELEROMETER);
        mAccelerometer.setPersistent(false);

        ......

    }

당연한 얘기지만 key 값을 통해서 xml 파일로부터 필요한 리소스 정보를 참조할 수 있다.

   private void updateState(boolean force) {
          ......

        mAnimations.setValueIndex(idx);
        updateAnimationsSummary(mAnimations.getValue());
        mAccelerometer.setChecked(Settings.System.getInt(
                getContentResolver(),
                Settings.System.ACCELEROMETER_ROTATION, 0) != 0);

         ......
   } 

위의 내용을 보면 화면에 해당 값을 보여줘야 할 경우에 Settings.System.ACCELEROMETER_ROTATION 의 값을 얻어서 현재 mAccelerometer의 값을 어떤 상태로 설정할지를 결정한다.

실제 사용자가 항목을 클릭했을 때의 동작을 정의하는 것은 OnPreferenceTreeClick() 메소드에 정의한다.

    public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
        if (preference == mSilent || preference == mVibrate) {
            setRingerMode(mSilent.isChecked(), mVibrate.isChecked());
            if (preference == mSilent) updateState(false);
        } else if (preference == mPlayMediaNotificationSounds) {
          ......
        } else if (preference == mAccelerometer) {
            Settings.System.putInt(getContentResolver(),
                    Settings.System.ACCELEROMETER_ROTATION,
                    mAccelerometer.isChecked() ? 1 : 0);

        }else if (preference == mNotificationPulse) {
         ......
    } 

여기는 반대로 현재의 상태를 확인하여 Settings.System.ACCELEROMETER_ROTATION 의 값을 변경하고 있다.

여기까지가 사용자의 입력을 받아서 화면상의 처리를 위한 코드이다.
실제 사용자의 입력은 어떻게 관리될까? 이를 위한 코드는 framework 코드를 참조해야 한다.

위에서 정의한 ACCELEROMETER_ROTATION은 다음 파일에서 정의한다.

frameworks/base/core/java/android/provider/Settings.java

        /**
         * Control whether the accelerometer will be used to change screen
         * orientation.  If 0, it will not be used unless explicitly requested
         * by the application; if 1, it will be used by default unless explicitly
         * disabled by the application.
         */
        public static final String ACCELEROMETER_ROTATION = "accelerometer_rotation";

정의된 값은 백업을 위해서 SETTINGS_TO_BACKUP 배열에 등록해 둔다. 실제 0과 1의 값이 어떤 의미인지를 설명하고 문자열로 정의해둔다.

실제 이렇게 정의된 값을 이용하는 것은 ACCELEROMETER_ROTATION은 frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java에서 값을 로드해 두는데 사용한다.

설정 값에 따라 응용 방법은 다양하게 변경될 수 있다.

 다른 예로 ADB를 들 수 있다. ADB의 경우에는 ADB_ENABLED 설정을 위와 유사하게 구현할 수 있다. 실제 ADB_ENABLED가 활성화되어도 동작 시점은 USB 케이블이 연결되어 있는 경우에만 동작하여야 한다. 이를 위해서 위와는 다른 코드 구현이 필요하다.

frameworks/base/service/java/com/android/server/SystemServer.java

여기에서는 ADB를 위하여 AdbSettingsObserver 클래스를 정의해 두고 ADB_ENABLED가 설정되면 persist.service.adb.enable 이라는 SystemProperties를 설정하도록 하고 있다.

    private class AdbSettingsObserver extends ContentObserver {
        public AdbSettingsObserver() {
            super(null);
        }
        @Override
        public void onChange(boolean selfChange) {
            boolean enableAdb = (Settings.Secure.getInt(mContentResolver,
                Settings.Secure.ADB_ENABLED, 0) > 0);
            // setting this secure property will start or stop adbd
           SystemProperties.set("persist.service.adb.enable", enableAdb ? "1" : "0");        }
    }

AdbSettingsObserver 클래스의 인스턴스 run() 메소드에서 생성된다.

        // register observer to listen for settings changes
        mContentResolver.registerContentObserver(
               Settings.Secure.getUriFor(Settings.Secure.ADB_ENABLED),
               false, new AdbSettingsObserver());

 이 코드는 Setting 어플에서의 설정 변화를 감시하고 있다가, 설정이 바뀌면 바로 persist.service.adb.enable를 바꾸도록 하고 있다.

댓글 2개: