コンテンツ作成 > アプリ > 設定の制御

設定の制御


コンテンツ


概要

  • OreSettingsを作成した際の技術メモです
  • 主にAndroidの輝度、音量、各種モードなどの設定方法について記載します
    • 対象OSはAndroid 2.2以降です
  • このページに記載されているすべてのソースコードはパブリックドメインとします
    • 内容は無保証です

画面の輝度

取得
  • パーミッションは不要
// 輝度を取得(brightnessは0~255)
int brightness = System.getInt(getContentResolver(), System.SCREEN_BRIGHTNESS);

// パーセント表記(0~100)にする場合
int percent = brightness * 100 / 255;
設定
  • android.permission.WRITE_SETTINGSのパーミッションが必要
  • システムの輝度を変更後に画面の輝度を変更→Activity終了で反映される仕様(バグ?)を利用
// 輝度を設定(brightnessは0~255)
System.putInt(getContentResolver(), System.SCREEN_BRIGHTNESS, brightness);

// 輝度を0.0~1.0に変換
float brf = (float)brightness  / 255.0f;

// android.view.WindowManager.LayoutParamsを取得
LayoutParams lp = getWindow().getAttributes();

// 輝度を設定
lp.screenBrightness = brf;

// 輝度を反映(この時点で画面の輝度が変わる)
// 本来この方法は現在のActivityの輝度設定にしか使えない
getWindow().setAttributes(lp);

// この後にActivityを閉じると設定値が永続的に反映される(されてしまう)
備考
  • 100段階で設定する場合、実際の指定は0~255なのでうまく変換する必要あり
    • 適当に実装すると設定と取得で値が異なる事態が起こりうる
  • 機種によっては輝度を特定の値以下にすると即ブラックアウトするので注意
    • エミュレータでも再現可能
    • ロック画面解除後も即ブラックアウトするため危険

GPS(取得のみ)

取得
  • android.permission.ACCESS_FINE_LOCATIONのパーミッションが必要
// 位置情報マネージャを取得
LocationManager manager = (LocationManager)getSystemService(LOCATION_SERVICE);

// GPSの設定を取得
boolean state = manager.isProviderEnabled(LocationManager.GPS_PROVIDER);
備考

ネットワーク位置情報(取得のみ)

取得
  • android.permission.ACCESS_COARSE_LOCATIONのパーミッションが必要
  • android.permission.ACCESS_FINE_LOCATIONが設定済みの場合は不要(上記を兼ねる)
// 位置情報マネージャを取得
LocationManager manager = (LocationManager)getSystemService(LOCATION_SERVICE);

// ネットワーク位置情報の設定を取得
boolean state = manager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
備考
  • 特になし

Wi-Fi

取得
  • android.permission.ACCESS_WIFI_STATEのパーミッションが必要
// Wi-Fiマネージャを取得
WifiManager manager = (WifiManager)getSystemService(WIFI_SERVICE);

// Wi-Fiが有効かを取得
boolean state = manager.isWifiEnabled();
設定
  • android.permission.CHANGE_WIFI_STATEのパーミッションが必要
// Wi-Fiマネージャを取得
WifiManager manager = (WifiManager)getSystemService(WIFI_SERVICE);

// Wi-Fiの有効/無効を設定
manager.setWifiEnabled(state);
通知
  • android.permission.ACCESS_WIFI_STATEのパーミッションが必要
  • 次のブロードキャストを受信する
WifiManager.WIFI_STATE_CHANGED_ACTION
備考
  • 特になし

画面の自動回転

取得
  • パーミッションは不要
// 自動回転かを取得(0は回転なし、1は回転あり)
int value = System.getInt(getContentResolver(), System.ACCELEROMETER_ROTATION);
設定
  • android.permission.WRITE_SETTINGSのパーミッションが必要
// 自動回転かを設定(0は回転なし、1は回転あり)
System.putInt(getContentResolver(), System.ACCELEROMETER_ROTATION, value);
備考
  • 特になし

Bluetooth

取得
  • android.permission.BLUETOOTHのパーミッションが必要
// Bluetoothアダプタの取得
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();

// アダプタが使用可能な場合
if(adapter != null)
{
    // Bluetoothが有効かを取得
    boolean state = adapter.isEnabled();
}
設定
  • android.permission.BLUETOOTH_ADMINのパーミッションが必要
// Bluetoothアダプタの取得
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();

// アダプタが使用可能な場合
if(adapter != null)
{
    // 有効にする場合
    adapter.enable();

    // 無効にする場合
    // adapter.disable();
}
通知
  • android.permission.BLUETOOTHのパーミッションが必要
  • 次のブロードキャストを受信する
BluetoothAdapter.ACTION_STATE_CHANGED
備考
  • Bluetoothが使用できない端末ではBluetoothAdapter.getDefaultAdapter()はnullを返す

機内モード

取得
  • パーミッションは不要
// 機内モードかを取得(0はNO、1はYES)
int value = System.getInt(getContentResolver(), System.AIRPLANE_MODE_ON);
設定
  • android.permission.WRITE_SETTINGSのパーミッションが必要
// 設定する場合:value = 1; state = true;
// 解除する場合:value = 0; state = false;

// 機内モードかを設定(0はNO、1はYES)
System.putInt(getContentResolver(), System.AIRPLANE_MODE_ON, value);

// ACTION_AIRPLANE_MODE_CHANGEDをブロードキャスト
Intent intent = new Intent();
intent.setAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
intent.putExtra("state", state);
sendBroadcast(intent);
備考
  • 設定時はSystem.putInt()だけでなくブロードキャストも必要
  • Intent.ACTION_AIRPLANE_MODE_CHANGEDはprotected intentのはずだが何故か送出可能
  • API Level 17(JELLY_BEAN_MR1)以降では設定が動作しないため以下の分岐を使うことを推奨
if(Build.VERSION.SDK_INT < 17)
{
    // 設定処理
}

電池の残量(取得のみ)

取得
  • パーミッションは不要
// ACTION_BATTERY_CHANGEDを受け取るためのフィルタを生成
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);

// 同期的に電池の残量を取得
Intent intent = getApplicationContext().registerReceiver(null, filter);

// 最大レベルを取得
int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100); 

// 現在のレベルを取得
int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
通知
  • パーミッションは不要
  • 次のブロードキャストを受信する
Intent.ACTION_BATTERY_CHANGED
備考
  • ApplicationContextのregisterReceiver()を使うことで同期的に電池の残量を取得可能
    • 第一引数をnullにするとレシーバを登録せずに済む
    • stickyなブロードキャストでのみ有効

端末の音量

取得
  • パーミッションは不要
// typeは以下のいずれか
// AudioManager.STREAM_RING
// AudioManager.STREAM_VOICE_CALL
// AudioManager.STREAM_MUSIC
// AudioManager.STREAM_ALARM
// AudioManager.STREAM_NOTIFICATION
// AudioManager.STREAM_SYSTEM
// AudioManager.STREAM_DTMF

// オーディオマネージャの取得
AudioManager manager = (AudioManager)getSystemService(AUDIO_SERVICE);

// 最大音量を取得
int max = mAudioManager.getStreamMaxVolume(type);

// 現在の音量を取得
int cur = mAudioManager.getStreamVolume(type);
設定
  • パーミッションは不要
// typeは以下のいずれか
// AudioManager.STREAM_RING
// AudioManager.STREAM_VOICE_CALL
// AudioManager.STREAM_MUSIC
// AudioManager.STREAM_ALARM
// AudioManager.STREAM_NOTIFICATION
// AudioManager.STREAM_SYSTEM
// AudioManager.STREAM_DTMF

// curは設定する音量

// オーディオマネージャの取得
AudioManager manager = (AudioManager)getSystemService(AUDIO_SERVICE);

// 音量を設定
manager.setStreamVolume(type, cur, 0);
備考
  • 音量が連動するものやモードによって変更できない場合がある
    • 複数の音量を一度に変更できるUIの場合は1つ変更したら他の音量の表示を更新すること

システムの言語(取得のみ)

取得
  • パーミッションは不要
// 現在のロケールを取得
Locale locale = Locale.getDefault();

// 言語名を取得
String language = locale.getDisplayLanguage();
通知
  • アプリ実行中に言語を切り替えるとアプリが再起動するため不要
  • 再起動前に必要なデータは保存すること
備考
  • 特になし

アカウントの同期

取得
  • android.permission.READ_SYNC_SETTINGSのパーミッションが必要
// 同期のマスター設定を取得
boolean state = ContentResolver.getMasterSyncAutomatically();
設定
  • android.permission.WRITE_SYNC_SETTINGSのパーミッションが必要
// 同期のマスター設定を変更
ContentResolver.setMasterSyncAutomatically(state);
備考
  • 他の設定と異なり取得にも設定にもパーミッションが必要
  • マスター設定なので全同期処理が対象になる

呼び出しモード

取得
  • パーミッションは不要
// オーディオマネージャを取得
AudioManager manager = (AudioManager)getSystemService(AUDIO_SERVICE);

// モードで分岐
switch(manager.getRingerMode())
{
    case AudioManager.RINGER_MODE_NORMAL:
        if(manager.shouldVibrate(AudioManager.VIBRATE_TYPE_RINGER))
        {
            // 音声&振動の場合
        }
        else
        {
            // 音声のみの場合
        }
        break;

    case AudioManager.RINGER_MODE_VIBRATE:
        // 振動のみの場合
        break;

    case AudioManager.RINGER_MODE_SILENT:
        // なしの場合
        break;
}
設定
  • パーミッションは不要
// 音声&振動に設定
mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
mAudioManager.setVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER, AudioManager.VIBRATE_SETTING_ON);

// 音声のみに設定
//mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
//mAudioManager.setVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER, AudioManager.VIBRATE_SETTING_OFF);

// 振動のみに設定
//mAudioManager.setRingerMode(AudioManager.RINGER_MODE_VIBRATE);

// なしに設定
//mAudioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
通知
  • パーミッションは不要
  • 次のブロードキャストを受信する
AudioManager.RINGER_MODE_CHANGED_ACTION
備考
  • 音声と振動は別制御になっていることに注意

画面ロックの種別(取得のみ)

取得
  • パーミッションは不要
// システムディレクトリを取得
String dataSystemDirectory = Environment.getDataDirectory().getAbsolutePath() + "/system/";

// 「パターン」の時にパターンが格納されるファイル
// 「パターン」以外の場合にサイズが0になる
File patternFile = new File(dataSystemDirectory + "gesture.key");

// 「PIN」または「パスワード」の時に文字が格納されるファイル
// 上記以外の場合にサイズが0になる
File passwordFile = new File(dataSystemDirectory + "password.key");

if(patternFile.length() > 0)
{
    // 「パターン」の場合
}
else if(passwordFile.length() > 0)
{
    // 「PIN」または「パスワード」の場合

    // 使われた文字種を取得する
    int type = (int)Secure.getLong(getContentResolver(), "lockscreen.password_type", DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
    switch(type)
    {
        case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
        case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
            // 「パスワード」の場合
            break;

        case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
            // 「PIN」の場合
            break;

        default:
            // 「PIN」か「パスワード」か分からない場合
            break;
    }
}
else
{
    // 「なし」か「スライド」の場合
}
備考
  • ソースを解析して発見した強引な方法であり、動作するかは保障できない

画面の消灯時間

取得
  • パーミッションは不要
  • 「-1」の場合は常灯扱い(消灯しない)
// 消灯時間を取得(単位はミリ秒)
int time = System.getInt(getContentResolver(), System.SCREEN_OFF_TIMEOUT, -1);
設定
  • android.permission.WRITE_SETTINGSのパーミッションが必要
  • 「-1」の場合は常灯扱い(消灯しない)
// 消灯時間を設定(単位はミリ秒)
System.putInt(getContentResolver(), System.SCREEN_OFF_TIMEOUT, time);
備考
  • Androidのバージョンによって「画面設定」から選択できる値は異なる
  • アプリ側からも好きに値を設定できるため取得値の扱いに注意が必要

ストレージの容量(取得のみ)

取得
  • パーミッションは不要
// pathは外部ストレージまたは内部ストレージのルートディレクトリ
// 外部ストレージ:Environment.getExternalStorageDirectory().getAbsolutePath()
// 内部ストレージ:Environment.getDataDirectory().getAbsolutePath()

// ブロックサイズなどを取得し計算
StatFs fs = new StatFs(path);
long size = fs.getBlockSize();
long total = fs.getBlockCount();
long avail = fs.getAvailableBlocks();
avail = total - avail;

// ストレージの全容量
long capacityMax = size * total;

// ストレージの使用中容量
long capacityUsed = size * avail;
備考
  • android.os.StatFsでディレクトリ配下の容量を取得する
    • メディアが無いなど取得できない場合を考慮すること
    • StatFs#getBlockSize()など各種メソッドがintを返すことに注意
      • longにキャストしてから乗算しないと2GBを超える場合に結果がおかしくなる
      • Javaはオーバーフローのチェックが無いため気付きにくい
      • サンプルではあらかじめlongに入れることで回避している
  • 容量の文字列化にはjava.text.NumberFormatやandroid.text.format.Formatterが便利

アニメーション速度

取得
  • パッケージandroid.osを作成して、その中に次のファイルを作成する
package android.os;

public class ServiceManager
{
    public static IBinder getService(String serviceName)
    {
        return null;
    }
}
  • パッケージandroid.viewを作成して、その中に次のファイルを作成する
package android.view;

import android.os.IBinder;

public interface IWindowManager
{
    public static class Stub
    {
        public static IWindowManager asInterface(IBinder binder)
        {
            return null;
        }
    }

    public float getAnimationScale(int which);

    public void setAnimationScale(int which, float scale);
}
  • android.permission.SET_ANIMATION_SCALEのパーミッションが必要
// typeは以下のいずれか
// ウィンドウ  :0
// トランジション:1
// speedは通常のアニメーション速度に対する倍率

// IWindowManagerの取得
IWindowManager manager = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));

// アニメーション速度の取得
float speed = manager.getAnimationScale(type);
設定
  • 取得で使ったServiceManagerとIWindowManagerが必要
  • android.permission.SET_ANIMATION_SCALEのパーミッションが必要
// typeは以下のいずれか
// ウィンドウ  :0
// トランジション:1
// speedは通常のアニメーション速度に対する倍率

// IWindowManagerの取得
IWindowManager manager = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));

// アニメーション速度の設定
manager.setAnimationScale(type, speed);
備考
  • 非公開クラスを強引に使っているため、今後利用できなくなる可能性がある
    • 例外処理を忘れずに
  • 速度は通常のアニメーション速度に対する倍率で取得/設定する
    • 0.5なら2倍速(速くなる)、2.0なら1/2倍速(遅くなる)
    • 0.0のときはアニメーションしない
  • 極端に大きな値を設定するとアニメーションが終わらず操作不能になる
    • 設定アプリを作る場合は最大値に制限を掛けること
  • 取得/設定に関係なくSET_~と付くパーミッションが必要

ファンシーなIMEアニメーション

取得
  • パーミッションは不要
// リフレクションでSystem.FANCY_IME_ANIMATIONSの値を取得
Field field = System.class.getField("FANCY_IME_ANIMATIONS");
String imeFieldValue = (String)field.get(null);

// IMEアニメーションが有効かを取得(0は無効、1は有効)
int value = System.getInt(getContentResolver(), imeFieldValue);
設定
  • android.permission.WRITE_SETTINGSのパーミッションが必要
// リフレクションでSystem.FANCY_IME_ANIMATIONSの値を取得
Field field = System.class.getField("FANCY_IME_ANIMATIONS");
String imeFieldValue = (String)field.get(null);

// IMEアニメーションが有効かを設定(0は無効、1は有効)
System.putInt(getContentResolver(), mFancyImeFieldValue, value);
備考
  • IME起動/終了時のアニメーションが派手になる設定
    • 反映するには電源オンオフかホームアプリ再起動(未検証、言語変更でOK?)が必要
  • 非公開のSystem.FANCY_IME_ANIMATIONSを使っているため、今後利用できなくなる可能性がある
    • 例外処理を忘れずに

データ通信

取得
  • android.permission.ACCESS_NETWORK_STATEのパーミッションが必要
// リフレクションでConnectivityManager#getMobileDataEnabled()を取得する
ConnectivityManager manager = (ConnectivityManager)getSystemService(CONNECTIVITY_SERVICE);
Method method = manager.getClass().getMethod("getMobileDataEnabled");

// データ通信が有効か取得
boolean state = ((Boolean)method.invoke(manager)).booleanValue();
設定
  • android.permission.CHANGE_NETWORK_STATEのパーミッションが必要
// リフレクションでConnectivityManager#setMobileDataEnabled()を取得する
ConnectivityManager manager = (ConnectivityManager)getSystemService(CONNECTIVITY_SERVICE);
Method method = manager.getClass().getMethod("setMobileDataEnabled", boolean.class);

// データ通信が有効か設定
method.invoke(manager, state);
通知
  • android.permission.ACCESS_NETWORK_STATEのパーミッションが必要(かどうか未検証)
  • 次のブロードキャストを受信する
ConnectivityManager.CONNECTIVITY_ACTION
備考
  • Android 2.2には対応していない
    • 別途方法があるようだがエミュレータで動作しなかったので今回は見送り
  • 非公開メソッドをリフレクションで呼び出しているため、今後利用できなくなる可能性がある
    • 例外処理を忘れずに

キャッシュのクリア

実行
  • パッケージpackage android.content.pmを作成して、その中にIPackageDataObserver.aidlを作成する
package android.content.pm;

oneway interface IPackageDataObserver {
    void onRemoveCompleted(in String packageName, boolean succeeded);
}
  • android.permission.CLEAR_APP_CACHEのパーミッションが必要
// リフレクションでPackageManager#freeStorageAndNotify(long, IPackageDataObserver)を呼び出す
// このメソッドは非同期実行なので終了時にToastを出す場合はHandlerを使う必要がある
try
{
    final Handler handler = new Handler();
    PackageManager pm = getPackageManager();
    Method method = pm.getClass().getMethod("freeStorageAndNotify", long.class, IPackageDataObserver.class);
    method.invoke(pm, Long.MAX_VALUE, new IPackageDataObserver.Stub()
    {
        @Override
        public void onRemoveCompleted(String packageName, boolean succeeded) throws RemoteException
        {
            handler.post(new Runnable()
            {
                @Override
                public void run()
                {
                    // ここで終了メッセージを表示
                }
            });
        }
    });
}
catch(Exception e)
{
}

コメント

名前:
コメント: