package com.example.appopschecksample; import android.app.AlertDialog; import android.app.Dialog; import android.content.DialogInterface; import android.content.Intent; import android.database.Cursor; import android.os.Bundle; import android.provider.ContactsContract; import android.support.v4.app.DialogFragment; import android.support.v4.app.FragmentActivity; import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; import android.support.v4.widget.SimpleCursorAdapter; import android.view.Menu; import android.view.MenuItem; import android.widget.ListView; import com.example.appopscheck.AppOpsCheck; /** * 電話帳にある名前と電話番号を表示するサンプルアプリ。 * * AppOps状態を確認し、無効化されている場合はダイアログを表示して設定を * 変更してもらうように誘導します。 * */ public class MainActivity extends FragmentActivity implements LoaderManager.LoaderCallbacks<Cursor> { // 電話帳から検索する項目 private static final String[] CONTACT_PROJECTION = { ContactsContract.CommonDataKinds.Phone._ID, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone.NUMBER, }; // 表示する項目 (表示名と電話番号) private static final String[] ADAPTER_FROM = { ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone.NUMBER, }; // 表示項目を表示する部品のID private static final int[] ADAPTER_TO = { android.R.id.text1, android.R.id.text2, }; // CursorLoaderで使用するアダプタ private SimpleCursorAdapter mAdapter = null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // アダプタを生成する mAdapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_2, null, ADAPTER_FROM, ADAPTER_TO, 0); // ListViewにアダプタを設定する ListView listView = (ListView) findViewById(R.id.listview); listView.setAdapter(mAdapter); // LoaderManagerを初期化する getSupportLoaderManager().initLoader(0, null, this); } @Override protected void onResume() { super.onResume(); // AppOps設定変更画面から戻った時に最新の設定状態を反映するため、 // リセットする。 getSupportLoaderManager().restartLoader(0, null, this); // AppOpsCheckを用いて、 // 電話帳読み出し(OP_READ_CONTACTS)のAppOps設定を取得する AppOpsCheck appOpsCheck = new AppOpsCheck(this); int appOpsMode = appOpsCheck.checkOpNoThrow(AppOpsCheck.OP_READ_CONTACTS, this.getApplicationContext().getPackageName()); // AppOps設定が許可状態でなければ、エラーダイアログ表示 if (appOpsMode != AppOpsCheck.MODE_ALLOWED) { showErrorDialog(appOpsMode); } } // メニュー設定 @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); return true; } // OSバージョンが4.2以下ならメニューを無効化 @Override public boolean onPrepareOptionsMenu(Menu menu) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) { menu.findItem(R.id.action_settings).setEnabled(false); } return super.onPrepareOptionsMenu(menu); } // メニューから設定が選択されたら、AppOps設定画面を呼び出す。 @Override public boolean onOptionsItemSelected(MenuItem item) { Intent intent = new Intent(); intent.setClassName("com.android.settings", "com.android.settings.Settings$AppOpsSummaryActivity"); startActivity(intent); return true; } // 電話帳表示のためのCursorLoaderのための処理群 @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { return new CursorLoader(this, ContactsContract.CommonDataKinds.Phone.CONTENT_URI, CONTACT_PROJECTION, null, null, null); } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) { mAdapter.swapCursor(data); } @Override public void onLoaderReset(Loader<Cursor> loader) { mAdapter.swapCursor(null); } // エラーダイアログ表示用ユーティリティメソッド private void showErrorDialog(int mode) { ErrorDialogFragment fragment = ErrorDialogFragment.newInstance(mode); if (fragment != null) { fragment.show(getSupportFragmentManager(), "error dialog"); } } // エラーダイアログを表示するフラグメント public static class ErrorDialogFragment extends DialogFragment { private static final String APP_OPS_MODE = "APP_OPS_MODE"; public static ErrorDialogFragment newInstance(int mode) { ErrorDialogFragment fragment = new ErrorDialogFragment(); // 画面回転などでのフラグメント再生成を考慮して、必要な引数はsetArguments()で保持する Bundle args = fragment.getArguments(); if (args == null) { args = new Bundle(); } args.putInt(APP_OPS_MODE, mode); fragment.setArguments(args); return fragment; } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { // getArguments()でエラーダイアログ生成に必要な引数を修得する int mode = getArguments().getInt(APP_OPS_MODE); Dialog dialog; if (mode == AppOpsCheck.MODE_IGNORED) { // AppOps状態が無効なら、ダイアログにその旨を表示する。 // また、ダイアログに設定画面を呼び出すボタンも表示する。 dialog = new AlertDialog.Builder(getActivity()) .setTitle(R.string.error_dialog_title) .setMessage(R.string.appops_ignored) .setPositiveButton(R.string.appops_invoke, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Intent intent = new Intent(); intent.setClassName("com.android.settings", "com.android.settings.Settings$AppOpsSummaryActivity"); startActivity(intent); } }) .setNegativeButton(R.string.cancel,null) .create(); } else { // AppOps状態がエラーになった場合は、アプリを終了する。 dialog = new AlertDialog.Builder(getActivity()) .setTitle(R.string.error_dialog_title) .setMessage(R.string.appops_errored) .setPositiveButton(R.string.close, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { getActivity().finish(); } }) .setNegativeButton(R.string.cancel,null) .create(); } dialog.setCanceledOnTouchOutside(false); return dialog; } } }