2011年11月9日水曜日

画像と文字列の一覧表を表示する

画像と文字列の一覧表を表示する

画像と文字列を含むViewを一覧にして表示する方法は、次に説明しました。
android.R.drawableのアイコンを一覧表示する
android.R.drawableに含まれる全ての画像を一覧表示させる
これらの方法は、画像の全てがresource IDとして取り扱える場合に使用できます。
SimpleAdapterは、デフォルトでは、resource IDの画像を取り扱えるのです。

しかし、事情により、画像が、resource IDとして取り扱うことができず、Drawableである場合、SimpleAdapterの振る舞いを修正しなければなりません。
この修正には、SimpleAdapter.ViewBinderを使います。

sample codeを下記のとおり掲載しておきます。共有により起動可能なアプリの一覧を、アプリの名称とアイコンを対にして表示します。
この例では、Drawable型の画像とresource IDによる画像を一覧表の中に混在させて表示します。
学習・実験を目的とするため、冗長に書いてあります。
参考:共有を事前に登録する

2011年11月14日 アイコンが重複して表示されるバグを修正しました。

Resources r;
Intent it;
PackageManager pm;
String sFrom[] = {"Image", "String"};
int iTo[] =  new int[] {R.id.Image, R.id.TextAppName};
final List<Map<String, Object>> data;
Map<String, Object> m;
SimpleAdapter sa;
SimpleAdapter.ViewBinder savb;

savb = new SimpleAdapter.ViewBinder(){
    @Override
    public boolean setViewValue(View view, Object data, String textRepresentation){
        if(view.getId()==R.id.Image){
            if(data instanceof Integer){
                Integer ig;
                ig = (Integer)data;
                view.setBackgroundResource(ig.intValue());
            }
            if(data instanceof Drawable){
                view.setBackgroundDrawable((Drawable)data);
            }
        }
        return false;
    }
};

r = getResources();
it = new Intent();
//起動するアプリを限定させて頂きます。
it.setAction(Intent.ACTION_SEND);
it.setType("text/plain");
pm = getPackageManager();
lri = pm.queryIntentActivities(it, PackageManager.MATCH_DEFAULT_ONLY);
Collections.sort(lri, new ResolveInfo.DisplayNameComparator(pm));

data =new ArrayList<Map<String, Object>>();
if(lri.size()==0){
    m = new HashMap<String, Object>();
    m.put("String", r.getString(R.string.noData));
    m.put("Image", 0);
    data.add(m);
}
else{
    int i;
    for(i=0; i<lri.size(); i++){
        m = new HashMap<String, Object>();
        m.put("String", lri.get(i).loadLabel(pm).toString());
        
        //Drawabledeで取り扱う。
        m.put("Image", lri.get(i).loadIcon(pm));
        
        //リソースで取得すると、表示できない。
        //m.put("Image", lri.get(i).getIconResource());
        data.add(m);
    }
    //最後に、resource IDによるアイコンを表示する。
    m = new HashMap<String, Object>();
    m.put("Image", android.R.drawable.ic_menu_revert);
    m.put("String", r.getString(R.string.noShare));
    data.add(m);
}

sa = new SimpleAdapter(this, data, R.layout.listitem, sFrom, iTo);
sa.setViewBinder(savb);
lv = (ListView)findViewById(R.id.ListView);
lv.setAdapter(sa);//一覧の表示を提供する。
lv.setOnItemClickListener(this);

2011年11月8日火曜日

「本体設定」の値の一覧取得プログラム

「本体設定」の値の一覧取得プログラム

「本体設定」の値の一覧を取得するプログラムを作成しました。下記のとおり掲載しておきます。

【このプログラムの特徴】「本体設定」の全ての値を取得できる
例えば、音関係の本体設定情報を取得する場合には、AudioManagerクラスを使います。これはこれで使い易いですが、このような設定情報取得用のクラスが準備されていない設定情報も存在します。例えば、端末メーカー依存の設定情報です。端末メーカー依存の設定情報等は、その存在すら不明です。
下記に掲載したプログラムを使えば、「本体設定」の全ての値を取得できます。
この情報を活用すれば、端末メーカー依存のアプリを開発できます。

【使用対象者等】android programmerが実機で使う
【制約】当然のことながら、利用・活用は自己責任です。
【動作内容】....実は、大したことはやってない。が、実用性はあると思う。
Settings.System及びSettings.Secureを使って取得します。
Settings.Secureは、読み込みはできますが、書き込みはできません。
【参考】
「本体設定」の「音の設定」画面を表示する
ContentResolverで本体設定情報を取得する

public class SettingsActivity extends Activity

    implements
    OnClickListener
    {
    boolean bFlag;
    Button bu;
    @Override
    public void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        bu = (Button)findViewById(R.id.Button);
        bu.setOnClickListener(this);
        bFlag = true;
    }
    
    @Override
    public void onClick(View v) {
        if(v==bu) Show();
    }
    
    void Show(){
        String sTV;
        ArrayAdapter<String> aa;
        ListView lv;
        ContentResolver cr;
        Cursor c;
        Uri u;
        String p[];
        TextView tv;
        
        aa = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1);
        cr = getContentResolver();
        tv = (TextView)findViewById(R.id.Text);
        if(bFlag==true){
            u =  Settings.System.CONTENT_URI;
            sTV = "Settings.System : ";
            bFlag = false;
        }
        else{
            u =  Settings.Secure.CONTENT_URI;
            sTV = "Settings.Secure : ";
            bFlag = true;
        }
        p = new String[]{
                Settings.NameValueTable.NAME,
                Settings.NameValueTable.VALUE
        };
        c = cr.query(u, p, null, null, null);
        if(c.moveToFirst()==true){
            do{
                String s;
                s = c.getString(c.getColumnIndex(Settings.NameValueTable.NAME));
                s = s + " : " + c.getString(c.getColumnIndex(Settings.NameValueTable.VALUE));
                aa.add(s);
            }while(c.moveToNext()==true);
        }
        aa.sort(new MyComparator());
        sTV = sTV + String.valueOf(c.getCount());
        c.close();
        tv.setText(sTV);
        lv = (ListView)findViewById(R.id.ListView);
        lv.setAdapter(aa);
    }
    
    public class MyComparator implements Comparator<String>{
        @Override
        public int compare(String s1, String s2){
            return s1.compareTo(s2);
        }
    }
}


main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <Button
        android:id="@+id/Button"
        android:text="@string/Button"
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" 
        >
    </Button>
    <TextView
        android:id="@+id/Text"
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" 
        >
    </TextView>
    <ListView
        android:id="@+id/ListView"
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" 
        >
    </ListView>
</LinearLayout>

2011年11月7日月曜日

ContentResolverで本体設定情報を取得する

ContentResolverで本体設定情報を取得する

ContentResolverの振る舞いを調べるため、その学習・実験材料として、代表的なコマンドであるマナーモードに関する情報を取得します。

マナーモードを取得する方法は2種類あります。
一方はAudioManagerを使う方法であり、他方はContentResolverを使う方法です。
本当にマナーモードを取得したいのであれば、AudioManagerを使えばよろしいです。
両方の方法を実装した、非実用的なサンプルプログラムを掲載しておきます。
マナーモードは、Settings.Secureに属するのではなく、Settings.Systemに属し、mode_ringerという名前です。

public class Resolver2Activity extends Activity


    implements
    OnClickListener
    {
    boolean bFlag;
    Button bu;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        bu = (Button)findViewById(R.id.Button);
        bu.setOnClickListener(this);
        bFlag = true;
        
        int iRet;
        String s;
        AudioManager am;
        am = (AudioManager)getSystemService(AUDIO_SERVICE);
        iRet = am.getRingerMode();
        switch(iRet){
        case AudioManager.RINGER_MODE_SILENT:
            s = "RINGER_MODE_SILENT";
            s = s + " : " + String.valueOf(AudioManager.RINGER_MODE_SILENT);
            break;
        case AudioManager.RINGER_MODE_NORMAL:
            s = "RINGER_MODE_NORMAL";
            s = s + " : " + String.valueOf(AudioManager.RINGER_MODE_NORMAL);
            break;
        case AudioManager.RINGER_MODE_VIBRATE:
            s = "RINGER_MODE_VIBRATE";
            s = s + " : " + String.valueOf(AudioManager.RINGER_MODE_VIBRATE);
            break;
        default: s = ""; break;
        }
        TextView tv;
        tv = (TextView)findViewById(R.id.TextAudioManager);
        tv.setText(s);
    }
    
    @Override
    public void onClick(View v) {
        if(v==bu) UsingContentResolver();
    }
    
    void UsingContentResolver(){
        ArrayAdapter<String> aa;
        ListView lv;
        ContentResolver cr;
        Cursor c;
        Uri u;
        String p[];
        TextView tv;
        
        aa = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1);
        cr = getContentResolver();
        tv = (TextView)findViewById(R.id.Text);
        if(bFlag==true){
            u =  Settings.System.CONTENT_URI;
            tv.setText("Settings.System");
            bFlag = false;
        }
        else{
            u =  Settings.Secure.CONTENT_URI;
            tv.setText("Settings.Secure");
            bFlag = true;
        }
        p = new String[]{
                Settings.NameValueTable.NAME,
                Settings.NameValueTable.VALUE
        };
        c = cr.query(u, p, Settings.NameValueTable.NAME + " = 'mode_ringer'", null, null);
        if(c.moveToFirst()==true){
            do{
                String s;
                s = c.getString(c.getColumnIndex(Settings.NameValueTable.NAME));
                s = s + " : " + c.getString(c.getColumnIndex(Settings.NameValueTable.VALUE));
                aa.add(s);
            }while(c.moveToNext()==true);
        }
        c.close();
        lv = (ListView)findViewById(R.id.ListView);
        lv.setAdapter(aa);
    }
}

2011年11月4日金曜日

本体設定の画面を表示する

本体設定の画面を表示する

本体設定の画面を表示するには、Settingsを使う。SettingsをIntentで使えば、「本体設定」コマンドをクリックした時に表示される画面を表示させることができる。

Settingsの基本的な使い方
    Intent it;
    it = new Intent(Settings.ACTION_APPLICATION_SETTINGS);
    try{
        startActivity(it);
    }
    catch(ActivityNotFoundException e){
        Log.e(TAG, "ActivityNotFoundException");
    }

様々なACTION_*について
上記の例ではSettings.ACTION_APPLICATION_SETTINGSを使ったが、他にも様々なACTION_がある。このACTION_*に関する留意点は次のとおりである。
  1. platformによっては実行できないものがある。
  2. platformを満たしていても、実行できない場合がある。
このため、Intentの発行に際しては、例外処理を施しておく必要がある。

Settingsの記事内容について
実機を操作してみると分かるが、実機では表示される画面があるにも関わらず、その画面を呼び出すACTIONがSettingsに掲載されていない場合がある。
例えば、端末のメーカーが独自に実装した画面などである。また、Settingsには動かないACTIONが記載されていることもある。
Settingsに掲載されていないACTION情報、又は正確なACTION情報を取得する方法は次のとおりである。
なお、この方法は、ddmsのLog欄でわかるAndroid端末の動作を参考にした。
  1. ACTION情報調査用のプログラムを作成し、デバッグモードで実行させる。
  2. DDMSを使い、端末利用者が画面を切り替えた時点における、各種Intentの発行のログを確認する。
  3. そのログに書かれてあるとおり、本番用のプログラムに書く。
この手順の詳細は下記のとおりである。

正確なACTION情報で本体設定画面を呼び出す
1 調査用プログラム作成
次のプログラムを作成しデバッグモードで実行する。すると、「設定」画面が表示される。この「設定」画面を操作して、調べたい画面を表示させる。
public class SettingsActivity extends Activity{
    final String TAG = "me";
    @Override
    public void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        Intent it;
        it = new Intent(Settings.ACTION_SETTINGS);
        try{
            startActivity(it);
        }
        catch(ActivityNotFoundException e){
            Log.e(TAG, "ActivityNotFoundException");
        }
    }
}

2 DDMSでログを見る
端末操作者が、例えば、「音声入出力」>「テキスト読み上げの設定」をクリックすると、ログには次のような趣旨の内容が表示される。
Starting: Intent { act=android.intent.action.MAIN cmp=com.android.settings/.VoiceInputOutputSettings }
Starting: Intent { act=android.intent.action.MAIN cmp=com.android.settings/.TextToSpeechSettings }
この情報を基に、プログラミングをする。

3 本番用プログラム作成
上記のログ情報を基に、下記のようにExplicit Intentを発行する。VoiceInputOutputSettings画面を経由せず、いきなりTextToSpeechSettings画面を表示するため、端末利用者からすれば便利である。
public class SettingsActivity extends Activity{
    final String TAG = "me";
    @Override
    public void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        Intent it;
        it = new Intent();
        it.setClassName("com.android.settings", "com.android.settings.TextToSpeechSettings");
        try{
            Log.i(TAG, "startActivity : TextToSpeechSettings");
            startActivity(it);
        }
        catch(ActivityNotFoundException e){
            Log.e(TAG, "ActivityNotFoundException");
        }
    }
}

2011年11月2日水曜日

「本体設定」の「音の設定」画面を表示する

「本体設定」の「音の設定」画面を表示する

下記のとおりです。Platformによって、コマンドが異なるようです。

なお、startActivityForResultメソッドを使った場合、設定画面が閉じられた時に、onActivityResultメソッドで「閉じられた」という通知を受けとることができます。ただし、第三番目の引数Intentの値はnullです。

AudioManagerのgetRingerModeメソッドにより、「マナーモード」の状態を取得できます。RINGER_MODE_SILENT及びRINGER_MODE_VIBRATEがマナーモードであることを示します。

下記のプログラムは動作原理を理解するための、学習・実験用ですので、実用性はありません。

稼働中の端末のPlatform(API level・バージョン) を取得するに関連記事を書いておきました。


public class SettingsActivity extends Activity
    implements
    OnClickListener
    {
    final String TAG = "me";
    Button bu;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        bu = (Button)findViewById(R.id.Button);
        bu.setOnClickListener(this);
    }


    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data){
        if(requestCode==0){
            int iRet;
            String s;
            AudioManager am;
            am = (AudioManager)getSystemService(AUDIO_SERVICE);
            iRet = am.getRingerMode();
            switch(iRet){
            case AudioManager.RINGER_MODE_SILENT:
                s = "RINGER_MODE_SILENT";
                break;
            case AudioManager.RINGER_MODE_NORMAL:
                s = "RINGER_MODE_NORMAL";
                break;
            case AudioManager.RINGER_MODE_VIBRATE:
                s = "RINGER_MODE_VIBRATE";
                break;
            default: s = ""; break;
            }
            TextView tv;
            tv = (TextView)findViewById(R.id.Text);
            tv.setText(s);
        }
        super.onActivityResult(requestCode, resultCode, data);
    }


    @Override
    public void onClick(View v){
        if(v==bu){
            Intent it;
            
            //Platform 2.2の場合
            it = new Intent(Settings.ACTION_SOUND_SETTINGS);
            try{
                startActivityForResult(it, 0);
            }
            catch(ActivityNotFoundException e){
                Log.e(TAG, "ActivityNotFoundException 1");
                // Platform 1.6の場合
                it = new Intent(Settings.ACTION_DISPLAY_SETTINGS);
                try{
                    startActivityForResult(it, 0);
                }
                catch(ActivityNotFoundException e2){
                    Log.e(TAG, "ActivityNotFoundException 2");
                }
            }
        }
    }
}

2011年10月30日日曜日

startActivityForResultメソッドのsample code

startActivityForResultメソッドのsample code

処理順序
  1. startActivityForResultメソッドを使い、他のActivityを呼び出す。
  2. 呼び出された側のActivityが必要な処理を行い、処理が終わると、このActivityは終了する。
  3. 呼出元ActivityのonActivityResultメソッドが呼ばれる。
ここに掲載したプログラムは、動作原理を理解するために、学習・実験用に作ったものです。
startActivityForResultメソッドは、例えば、「本体設定」画面から入力された値を取得する場合に有用かもしれません。
参考:「本体設定」の「音の設定」画面を表示する

呼び出す側
public class ForResultActivity extends Activity
implements
OnClickListener
{
final String TAG = "My";
static final int PICK_CONTACT_REQUEST = 0;
Button bu;
@Override
public void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    bu = (Button)findViewById(R.id.Button);
    bu.setOnClickListener(this);
}

@Override
public void onClick(View v){
    if(v==bu){
        Intent it;
        it = new Intent(this, WorkerActivity.class);
        try{
            startActivityForResult(it, PICK_CONTACT_REQUEST);
        }
        catch(ActivityNotFoundException e){
            Log.e(TAG, "ActivityNotFoundException");
        }
    }
}


@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
    super.onActivityResult(requestCode, resultCode, data);
    if(requestCode==PICK_CONTACT_REQUEST){
        //「戻る」キーの押し下げでも、ここに来る。

        if(resultCode==RESULT_OK){
            //「戻る」キーの押し下げでは、ここには来ない。
            Log.i(TAG, "RESULT_OK");
            Log.i(TAG, data.getStringExtra("What are these?"));
            Log.i(TAG, String.valueOf(data.getIntExtra("How many pens?", -1)));
        }
    }
}
}

呼び出される側
public class WorkerActivity extends Activity{
    final String TAG = "My";
    @Override
    public void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.work);
        Log.i(TAG, "WorkerActivity");
        Intent data;
        data = new Intent();
        data.putExtra("What are these?", "These are pens.");
        data.putExtra("How many pens?", 3);
        setResult(RESULT_OK, data);
        finish();
    }
}

2011年10月24日月曜日

共有アプリ一覧ダイアログをカスタマイズする

共有アプリ一覧ダイアログをカスタマイズする

他のアプリを「共有」機能を使って起動させる場合、共有が可能なアプリの一覧を表示させて、その一覧の中から、利用者が、お好みのアプリを選択することになります。

Implicit Intentを使って共有コマンドを発行する場合、指定した条件に合致する全てのアプリがアプリ一覧に表示されてしまいます。この時表示される「アプリ一覧表示ダイアログ」は、androidシステムが準備したものです。

自分のアプリが他のアプリから「共有」機能により起動可能な場合であって、かつ自分のアプリで「共有」機能を使う場合、「共有一覧ダイアログ」には自分のアプリも表示されてしまいます。
もし、そこで、自分のアプリを選択した場合、自分のアプリで自分のアプリを共有したことになります。
このような些末な件は、多くのアプリにおいては、どちらでも良いことです。
しかし、アプリの性質上、これは困る場合もあります。

このような問題の場合、共有アプリ一覧表示プログラムをカスタマイズすることになります。

仕掛けの流れは次のようになります。
  1. PackageManagerを使って、指定した条件に合致する、端末内の全てのアプリ情報を取得する。
  2. 取得した情報の中から、任意のアプリを削除する。
  3. 一覧をAlertDialogに表示する。
  4. 利用者が任意のアプリを選択する。
  5. 選択されたアプリをExplicit Intentで実行する。
つまり、Implicit Intentを使わないようにするのです。
PackageManagerのコーディングは次のとおりです。
    PackageManager pm = getPackageManager();
このような書き方だと、自分のアプリだけの情報を取得しただけかと思ってしまいますが、端末の全てのアプリの情報を取り扱う仕掛けになっているので注意しましょう。

参考

下に、上記1~3までを実行するsourceを掲載しておきます。
int i;
Intent it;
PackageManager pm;
List<ResolveInfo> lri;
ArrayList<ResolveInfoList> al;
ArrayAdapter<ResolveInfoList> aa;
AlertDialog.Builder b;
TextView tv;
ResolveInfoList ril;
Resources r;

r = getResources();
al = new ArrayList<ResolveInfoList>();
it = new Intent();
//起動するアプリを限定させて頂きます。
it.setAction(Intent.ACTION_SEND);
it.setType("text/plain");
pm = getPackageManager();
lri = pm.queryIntentActivities(it, PackageManager.MATCH_DEFAULT_ONLY);

switch(lri.size()){
case 0:
    tv = (TextView)findViewById(R.id.TextRegister);
    tv.setText(R.string.noData);
    return;
default: break;
}

//自分自身を含めないようにする。
ApplicationInfo ai = null;
try{
    ai = pm.getApplicationInfo(getPackageName(), 0);
}
catch(PackageManager.NameNotFoundException e){
}
String sLabel;
sLabel = ai.loadLabel(pm).toString();

//AlertDialogに表示させる一覧を作成する。
for(i=0; i<lri.size(); i++){
    String s;
    ResolveInfo ri;
    ri = lri.get(i);
    s = ri.loadLabel(pm).toString();
    if(s.equals(sLabel)==true);
    else{
        ril = new ResolveInfoList();
        ril.sResolveInfo = s ;
        ril.ri = ri;
        al.add(ril);//リストに追加する。
    }
}
ril = new ResolveInfoList();
ril.sResolveInfo = r.getString(R.string.noShare);
al.add(ril);
aa = new ArrayAdapter<ResolveInfoList>(this, 
android.R.layout.select_dialog_singlechoice, al);
aa.sort(new MyComparator());//文字列をキーにして並べ替える。
b = new AlertDialog.Builder(this);
b.setTitle(R.string.DialogTitle);
b.setSingleChoiceItems(aa, 0, this);
b.setNegativeButton(R.string.Close,
    new DialogInterface.OnClickListener(){
        public void onClick(DialogInterface dialog, int id) {
            dialog.cancel();//「閉じる」ボタンを押しても何もしない。
        }
    }
);
adServiceDialog = b.show();//見せる。