Software/Android/アプリケーション開発テキスト
この章ではサービスを利用して簡易RSSリーダーを拡張していきます。サービスはAndroidが提供するアプリケーションコンポーネントの一つで、アクティビティのように画面表示を持たずバックグラウンドで動作します。
Chapter 4で作成した簡易RSSリーダーでは取得ボタンのタップでRSSフィードを取得していましたが、サービスを利用することでバックグラウンドで定期的にRSSフィードを取得することが出来ます。
まずはRSSReaderプロジェクトにバックグラウンドでRSSフィードを取得するサービスを追加します。
android.app.ServiceをベースクラスとしたFeedServiceを生成し、スレッドの処理をこのクラスで行うためRunnableのimplementsといくつかの定数、メンバ変数を追加します(リスト5-1)。
public class FeedService extends Service implements Runnable { private static final String SERVICE_ACTION = "com.beatcraft.rssreader.SERVICE_ACTION"; private boolean mWorking = false; private FeedList mListFG = null; private FeedList mListBG = null;
定数SERVICE_ACTIONはこのサービスで取り扱うアクションを定義しています。実際にサービスがこのアクションを取り扱い出来るようにするためにはマニフェストファイルに記述する必要があります(後述)。
バックグラウンドでのフィード取得中に外部からリストを参照する可能性があるため、FeedListを二つ用意し、これを切り替える際の保護用にmWorkingというフラグも追加しています。
続いてonStartメソッドを追加します(リスト5-2)。
@Override public void onStart(Intent intent, int startID) { super.onStart(intent, startID); Thread thread = new Thread(this); thread.run(); }
onStartはサービスが開始されると呼び出されるメソッドで、今回作成するサービスではこのタイミングでスレッドを作成し、このクラスをRunnableとして実行しています。
アクティビティなどのコンポーネントから直接サービスの機能を呼び出して利用するには、サービスをバインドする必要があります。サービスをバインドするとonBindメソッドが呼ばれます(リスト5-3)。
@Override public IBinder onBind(Intent arg0) { return mIFeedServiceBinder; }
IBinder型のメンバ変数mIFeedServiceBinderを返すことで、このサービスへの直接アクセスを提供しています。mIFeedServiceBinderは後ほど実装します。
前述の通り、このクラスにRunnableインターフェースを実装してスレッドを処理するためrunメソッドを追加します(リスト5-4)。
@Override public void run() { RSSReaderApplication app = (RSSReaderApplication) getApplication(); mListBG = new FeedList(); if (mListBG.get(app) > 0) { lock(); mListFG = mListBG; unlock(); Intent bcast = new Intent(); bcast.setAction("RECEIVE_FEED"); getBaseContext().sendBroadcast(bcast); } Intent ac = new Intent(); ac.setAction(SERVICE_ACTION); PendingIntent pi = PendingIntent.getService(this, 0, ac, 0); AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE); long at = System.currentTimeMillis() + 60000; am.set(AlarmManager.RTC, at, pi); }
runメソッドではまず、RSSReaderApplicationクラスのインスタンスを取得し、バックグラウンド用のFeedListに引渡してRSSフィードの取得を行います。フィードの取得に成功したらフォアグラウンド用FeedListを置き換えますが、他のコンポーネントがサービスバインダーを利用してRSSフィードの取得を行う際、参照中にリストが置き換わることのないよう、lock/unlockで排他制御を行なっています。
置き換えが終わったら"RECEIVE_FEED"という文字列をアクションに設定したインテントを生成し、sendBroadcastメソッドでブロードキャストを発行します。
続いて定数SERVICE_ACTIONをアクションとして設定したインテントを基にペンディングインテント(PendingIntent)を生成しています。ペンディングインテントとは、通常のインテントのように即時ではなく、指定したタイミングで発行するためのインテントです。
ペンディングインテントの発行先としてこのサービスを指定し、現時刻から60秒後に発行されるよう、アラームマネージャ(AlarmManager)にセットします。
これによりこのサービスが60秒毎に呼び出され、定期的なRSSフィードの受信を実現しています。
サービスバインダーを追加します(リスト5-5)。IFeedServiceはこのサービスが外部コンポーネントに提供するメソッドインターフェースで、後ほど作成するAIDLファイルで定義したメソッドの実装をこれに含めます。
// service binder private final IFeedService.Stub mIFeedServiceBinder = new IFeedService.Stub() { @Override public void lockList() { lock(); } @Override public void unlockList() { unlock(); } @Override public int getFeedCount() throws RemoteException { if (mListFG != null) { return mListFG.count(); } return 0; } @Override public int getItemType(int index) { if (mListFG != null) { ArrayList<FeedItem> list = mListFG.getList(); return list.get(index).itemType(); } return 0; } @Override public String getFeedTitle(int index) { if (mListFG != null) { ArrayList<FeedItem> list = mListFG.getList(); return list.get(index).feedTitle(); } return ""; } @Override public String getArticleTitle(int index) { if (mListFG != null) { ArrayList<FeedItem> list = mListFG.getList(); return list.get(index).articleTitle(); } return ""; } @Override public String getPubDate(int index) { if (mListFG != null) { ArrayList<FeedItem> list = mListFG.getList(); return list.get(index).pubDate(); } return ""; } @Override public String getDescription(int index) { if (mListFG != null) { ArrayList<FeedItem> list = mListFG.getList(); return list.get(index).description(); } return ""; } @Override public String getLink(int index) { if (mListFG != null) { ArrayList<FeedItem> list = mListFG.getList(); return list.get(index).link(); } return ""; } };
lockList/unlockListはRSSフィードリストを参照する際に排他制御を行うために使用します。getFeedCountはフォラグラウンドになっているRSSフィードリストに含まれるフィード数を返し、その他の各種getメソッドで指定されたインデックスのフィード情報を外部コンポーネントから取得出来るようにしています。
private synchronized void lock() { while (mWorking) { try { wait(); } catch (Exception e) { } } mWorking = true; } private synchronized void unlock() { mWorking = false; }
最後に排他制御に使用するlock/unlockメソッドを追加します(リスト5-6)。lockではmWorkingがtrueの間waitループを行い、他スレッドで参照している間は以後の処理を行わないようブロックします。
以上でサービスクラスの作成は完了です。引き続きAIDLファイルの作成とマニフェスト修正を行います。AIDLはAndroid Interface Definition Language(Androidインターフェース定義言語)の略で、プロセス間通信に使用するインターフェースを定義するものです。
Package ExplorerでRSSReaderのsrc/com.beatcraft.rssreaderを右クリックし、メニューから「New > File」を選択して下さい。"New File"ダイアログが表示されたら、File nameに「IFeedService.aidl」と入力して「Finish」ボタンをクリックします。
package com.beatcraft.rssreader; interface IFeedService { void lockList(); void unlockList(); int getFeedCount(); int getItemType(int index); String getFeedTitle(int index); String getArticleTitle(int index); String getPubDate(int index); String getDescription(int index); String getLink(int index); }
作成されたIFeedService.aidlファイルを開き、リスト5-7のように編集します。先ほど追加したmIFeedServiceBinderで実装しているメソッドの宣言をここで行なっています。
最後にRSSReaderのマニフェストファイル(AndroidManifest.xml)を開いて、application要素の子要素として今回作成したサービスを追加します(リスト5-8)。
<service android:name=".FeedService" > <intent-filter > <action android:name="com.beatcraft.rssreader.SERVICE_ACTION" /> </intent-filter> </service>
インテントフィルタとして、サービスクラス内で定義した定数SERVICE_ACTIONと同じ内容を保つアクションがリストされていることに注目して下さい。このフィルタを追加することで、AlarmMangerを利用して時間指定をしたペンディングインテントをこのサービスが受け取ることが可能になります。
続いて、サービスが発行したブロードキャストを受信するブロードキャストレシーバを作成していきます。
android.content.BroadcastReceiverをベースクラスとしてFeedReceiverを生成し、メンバ変数とコンストラクタ、onReceiveメソッドを追加します(リスト5-9)。
public class FeedReceiver extends BroadcastReceiver { IFeedReceiver mReceiver; public FeedReceiver(IFeedReceiver receiver) { mReceiver = receiver; } @Override public void onReceive(Context context, Intent intent) { mReceiver.feedReceived(); } }
IFeedReceiverは受信したブロードキャストを実際に処理するためのインターフェースクラスで、コンストラクタで受け取ったIFeedReceiverのfeedReceivedメソッドをonReceiveで呼び出すようにしています。
続いてIFeedReceiverを作成するため、Package ExplorerでRSSReaderのsrc/com.beatcraft.rssreaderを右クリックし、メニューから「New > Interface」を選択して下さい。"New Java Interface"ダイアログが表示されたら、Nameに「IFeedReceiver」と入力して「Finish」ボタンをクリックします。
package com.beatcraft.rssreader; public interface IFeedReceiver { void feedReceived(); }
作成されたIFeedReceiver.javaを開き、feedReceivedメソッドの宣言を追加します(リスト5-10)。
以上でブロードキャストレシーバは完成です。
サービスとブロードキャストレシーバが完成したので、ここからはRSSReaderActivityをサービスを利用する形に修正していきます。
public class RSSReaderActivity extends ListActivity implements View.OnClickListener, IFeedReceiver, ITaskEntity { private FeedList mList = null; private IntentFilter mIFilter; private IFeedService mIFeedServiceBind = null; private ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { // インターフェースを取得 mIFeedServiceBind = IFeedService.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { // } };
IFeedReceiverのimplementsを追加し、インテントフィルタとサービスバインダー、サービスコネクションをメンバ変数に追加しています。サービスコネクションはサービスとの接続を管理する際に使用され、接続時にインターフェースバインダーを取得しメンバ変数に設定しています。
引き続きITaskEntityがimplementsされていますが、これは従来のHttpAccessTaskではなく、今回新規に作成するListGenerateTaskで使用します。ListGenerateTaskについては後ほど解説します。
ボタンによるフィードの手動取得を廃止するため、onCreateでボタンにリスナーを設定している箇所(リスト5-12)、onClickで取得ボタンを処理している箇所(リスト5-13)を削除します。これにあわせ、レイアウトファイルmain.xmlの「取得」ボタン、strings.xmlの文字列リソースget_feedも削除して下さい。
button = (Button) findViewById(R.id.Get); button.setOnClickListener(this);
case R.id.Get: mList = null; HttpAccessTask task = new HttpAccessTask(this); task.execute(this); break;
続いてonCreateメソッドの最後に、ブロードキャストレシーバの生成と登録、サービスの開始とバインド処理を追加します(リスト5-14)。
FeedReceiver recv = new FeedReceiver(this); mIFilter = new IntentFilter(); mIFilter.addAction("RECEIVE_FEED"); registerReceiver(recv, mIFilter); Intent intent = new Intent(getBaseContext(), FeedService.class); startService(intent); bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
先ほど実装したFeedReceiverを生成し、文字列"RECEIVE_FEED"をアクションとして設定したインテントフィルタとともにregistarReceiverメソッドでブロードキャストレシーバとして登録します。"RECEIVE_FEED"アクションはサービスがブロードキャストを発行する際にも使用していたことを思い出してください。これにより、このブロードキャストレシーバがFeedServiceの発行するブロードキャストを受け取ることが可能になります。
FeedServiceを明示的に指定したインテントを生成し、startServiceメソッドでサービスの開始を行い、bindServiceでこのアクティビティとサービスをバインドしています。
インターフェースIFeedReceiver唯一のメソッドであるfeedReceivedを追加します。
@Override public void feedReceived() { ListGenerateTask task = new ListGenerateTask(); task.execute(this); }
サービスがRSSフィードの取得を完了し、ブロードキャストを発行するとブロードキャストレシーバを通じてこのメソッドが呼び出されます。サービスが取得したRSSフィードを読み取ってリスト化するため、ListGenerateTaskを実行します。
ListGenerateTaskは後ほど作成しますが、HttpAccessTask同様にバックグラウンド処理としてbackgroundProc、後処理としてpostProcメソッドを呼び出します。HttpAccessTaskから呼び出される両メソッドを置き換える形で、ListGenerateTask用に修正を加えていきます。
修正後のbackgroundProcメソッドがリスト5-16になります。
@Override public void backgroundProc() { int count = 0; mList = new FeedList(); if (mIFeedServiceBind != null) { try { mIFeedServiceBind.lockList(); count = mIFeedServiceBind.getFeedCount(); FeedItem item; for (int i = 0; i < count; ++i) { int type = mIFeedServiceBind.getItemType(i); item = new FeedItem(type); item.setFeedTitle(mIFeedServiceBind.getFeedTitle(i)); item.setArticleTitle(mIFeedServiceBind.getArticleTitle(i)); item.setPubDate(mIFeedServiceBind.getPubDate(i)); item.setDescription(mIFeedServiceBind.getDescription(i)); item.setLink(mIFeedServiceBind.getLink(i)); mList.getList().add(item); } mIFeedServiceBind.unlockList(); } catch (RemoteException e) { e.printStackTrace(); } } }
空のFeedListを生成したら、サービスバインダーのlockListによりサービスが保持するRSSフィードリストをロックします。これにより、リストの取得中にリストの差し替えがおきることを防いでいます。
ロックに成功したらフィードの数を取得し、フィード数分のフィード情報をサービスから取得してFeedItemを生成、先程作成したFeedListに追加します。全てのフィードを追加し終えたら、unlockListでサービスのRSSフィードリストをアンロックします。
@Override public void postProc() { if (mList.count() > 0) { ArrayList<FeedItem> list = mList.getList(); if (list != null) { FeedAdapter adapter = new FeedAdapter(this, list); setListAdapter(adapter); } } }
postProc(リスト5-17)ではバックグラウンドで生成したリストをチェックし、フィード数が0でなければFeedAdapterを生成し、アクティビティのリストに設定します。これによりサービスがバックグラウンドで取得したRSSフィードがリストに表示されます。
最後にListGenerateTaskを作成します。ベースクラスをAsyncTask、テンプレートをITaskEntity, Integer, VoidとしてListGenerateTaskを生成し、リスト5-18のように修正します。
package com.beatcraft.rssreader; import android.os.AsyncTask; public class ListGenerateTask extends AsyncTask<ITaskEntity, Integer, Void> { private ITaskEntity mITaskEntity; @Override protected Void doInBackground(ITaskEntity... params) { mITaskEntity = params[0]; mITaskEntity.backgroundProc(); return null; } @Override protected void onPostExecute(Void v) { mITaskEntity.postProc(); } }
実際の処理はアクティビティに実装されたbackgroundProc/postProcが行うため、HttpAccessTaskとの違いはプログレスダイアログの有無のみとなっています。
以上で全ての実装は完了です。実行して動作を確認してみて下さい。
package com.beatcraft.rssreader; import android.app.Application; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; public class RSSReaderApplication extends Application { private static final String CONF_NAME = "rssreader.conf"; public static final int DEFAULT_NUM_OF_GET = 3; public static final int NUM_OF_FEED = 10; private int mNumOfGet = DEFAULT_NUM_OF_GET; private String mFeedURL[]; @Override public void onCreate() { mFeedURL = new String[NUM_OF_FEED]; loadConfig(); } public int numberOfGet() { return mNumOfGet; } public void setNumberOfGet(int numOfGet) { mNumOfGet = numOfGet; } public String feedURL(int index) { if ((index < 0) || (10 <= index)) { return null; } return mFeedURL[index]; } public void setFeedURL(int index, String url) { if ((index < 0) || (NUM_OF_FEED <= index)) { return; } mFeedURL[index] = url; } public void clearFeedURL() { for (int i = 0; i < NUM_OF_FEED; ++i) { mFeedURL[i] = ""; } } public void loadConfig() { SharedPreferences pref = getSharedPreferences(CONF_NAME, MODE_PRIVATE); mNumOfGet = pref.getInt("NumOfGet", DEFAULT_NUM_OF_GET); for (int i = 0; i < NUM_OF_FEED; ++i) { String key = String.format("FeedURL%02d", (i + 1)); mFeedURL[i] = pref.getString(key, ""); } } public void saveConfig() { SharedPreferences pref = getSharedPreferences(CONF_NAME, MODE_PRIVATE); Editor pe = pref.edit(); pe.putInt("NumOfGet", mNumOfGet); for (int i = 0; i < NUM_OF_FEED; ++i) { String key = String.format("FeedURL%02d", (i + 1)); pe.putString(key, mFeedURL[i]); } pe.commit(); } }
package com.beatcraft.rssreader; import java.util.ArrayList; import android.app.ListActivity; import android.content.ComponentName; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.view.View; import android.view.Window; import android.widget.AdapterView; import android.widget.Button; public class RSSReaderActivity extends ListActivity implements View.OnClickListener, IFeedReceiver, ITaskEntity { private FeedList mList = null; private IntentFilter mIFilter; private IFeedService mIFeedServiceBind = null; private ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { // インターフェースを取得 mIFeedServiceBind = IFeedService.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { // } }; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.main); Button button; button = (Button) findViewById(R.id.Config); button.setOnClickListener(this); getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int pos, long id) { FeedItem item = (FeedItem) getListView().getItemAtPosition(pos); Intent intent = new Intent(getApplicationContext(), com.beatcraft.rssreader.DescActivity.class); intent.putExtra("FeedTitle", item.feedTitle()); intent.putExtra("ArticleTitle", item.articleTitle()); intent.putExtra("PubDate", item.pubDate()); intent.putExtra("Description", item.description()); intent.putExtra("Link", item.link()); startActivity(intent); } }); FeedReceiver recv = new FeedReceiver(this); mIFilter = new IntentFilter(); mIFilter.addAction("RECEIVE_FEED"); registerReceiver(recv, mIFilter); Intent intent = new Intent(getBaseContext(), FeedService.class); startService(intent); bindService(intent, mServiceConnection, BIND_AUTO_CREATE); } @Override public void onClick(View view) { int id = view.getId(); switch (id) { case R.id.Config: showConfig(); break; } } @Override public void feedReceived() { ListGenerateTask task = new ListGenerateTask(); task.execute(this); } @Override public void backgroundProc() { int count = 0; mList = new FeedList(); if (mIFeedServiceBind != null) { try { mIFeedServiceBind.lockList(); count = mIFeedServiceBind.getFeedCount(); FeedItem item; for (int i = 0; i < count; ++i) { int type = mIFeedServiceBind.getItemType(i); item = new FeedItem(type); item.setFeedTitle(mIFeedServiceBind.getFeedTitle(i)); item.setArticleTitle(mIFeedServiceBind.getArticleTitle(i)); item.setPubDate(mIFeedServiceBind.getPubDate(i)); item.setDescription(mIFeedServiceBind.getDescription(i)); item.setLink(mIFeedServiceBind.getLink(i)); mList.getList().add(item); } mIFeedServiceBind.unlockList(); } catch (RemoteException e) { e.printStackTrace(); } } } @Override public void postProc() { if (mList.count() > 0) { ArrayList<FeedItem> list = mList.getList(); if (list != null) { FeedAdapter adapter = new FeedAdapter(this, list); setListAdapter(adapter); } } } private void showConfig() { Intent intent = new Intent(getApplicationContext(), com.beatcraft.rssreader.ConfigActivity.class); startActivity(intent); } }
package com.beatcraft.rssreader; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.view.Window; import android.webkit.WebView; import android.widget.Button; import android.widget.TextView; public class DescActivity extends Activity implements View.OnClickListener { private static final String WEBVIEW_BEGIN = "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" /></head><body bgcolor=\"#f6f6f6\">"; private static final String WEBVIEW_LINK = "<p><a href=\"%s\">%s</a></p>"; private static final String WEBVIEW_END = "</body></html>"; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.description); Button button = (Button) findViewById(R.id.Back); button.setOnClickListener(this); Intent intent = getIntent(); if (intent != null) { String tmp = ""; String desc = ""; TextView tv; tv = (TextView) findViewById(R.id.FeedTitle); tmp = intent.getStringExtra("FeedTitle"); tv.setText(tmp); tv = (TextView) findViewById(R.id.ArticleTitle); tmp = intent.getStringExtra("ArticleTitle"); tv.setText(tmp); tv = (TextView) findViewById(R.id.PubDate); tmp = intent.getStringExtra("PubDate"); tv.setText(tmp); WebView wv = (WebView) findViewById(R.id.Description); desc = WEBVIEW_BEGIN; tmp = intent.getStringExtra("Link"); desc += String.format(WEBVIEW_LINK, tmp, tmp); tmp = intent.getStringExtra("Description"); desc += tmp + WEBVIEW_END; wv.loadDataWithBaseURL("about:blank", desc, "text/html", "utf-8", null); } } @Override public void onClick(View view) { if (view.getId() == R.id.Back) { finish(); } } }
package com.beatcraft.rssreader; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.view.Window; import android.view.WindowManager.LayoutParams; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.EditText; import android.widget.Spinner; public class ConfigActivity extends Activity implements View.OnClickListener { public static final int REQUEST_RECOMMEND = 1234; private static final int NUM_OF_GET_LIST[] = {1, 3, 5}; private Spinner mNumOfGet; private EditText mFeedURL[]; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); this.getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); setContentView(R.layout.config); RSSReaderApplication app = (RSSReaderApplication) getApplication(); ArrayAdapter<CharSequence> adapter; adapter = ArrayAdapter.createFromResource(this, R.array.number_of_get, android.R.layout.simple_spinner_item); adapter.setDropDownViewResource( android.R.layout.simple_spinner_dropdown_item); mNumOfGet = (Spinner) findViewById(R.id.NumOfGet); mNumOfGet.setAdapter(adapter); for (int i = 0; i < 3; ++i) { if (NUM_OF_GET_LIST[i] == app.numberOfGet()) { mNumOfGet.setSelection(i); break; } } mFeedURL = new EditText[RSSReaderApplication.NUM_OF_FEED]; mFeedURL[0] = (EditText) findViewById(R.id.FeedURL01); mFeedURL[1] = (EditText) findViewById(R.id.FeedURL02); mFeedURL[2] = (EditText) findViewById(R.id.FeedURL03); mFeedURL[3] = (EditText) findViewById(R.id.FeedURL04); mFeedURL[4] = (EditText) findViewById(R.id.FeedURL05); mFeedURL[5] = (EditText) findViewById(R.id.FeedURL06); mFeedURL[6] = (EditText) findViewById(R.id.FeedURL07); mFeedURL[7] = (EditText) findViewById(R.id.FeedURL08); mFeedURL[8] = (EditText) findViewById(R.id.FeedURL09); mFeedURL[9] = (EditText) findViewById(R.id.FeedURL10); for (int i = 0; i < RSSReaderApplication.NUM_OF_FEED; ++i) { mFeedURL[i].setText(app.feedURL(i)); } Button button; button = (Button) findViewById(R.id.Recommend); button.setOnClickListener(this); button = (Button) findViewById(R.id.Cancel); button.setOnClickListener(this); button = (Button) findViewById(R.id.Regist); button.setOnClickListener(this); } @Override public void onClick(View view) { int id = view.getId(); switch (id) { case R.id.Recommend: showRecommend(); break; case R.id.Cancel: finish(); break; case R.id.Regist: regist(); break; } } protected void onActivityResult(int reqCode, int result, Intent data) { super.onActivityResult(reqCode, result, data); if (result == RESULT_OK) { if (reqCode == REQUEST_RECOMMEND) { int count = 0; count = data.getIntExtra("RecommendCount", 0); for (int i = 0; i < count; ++i) { String key = ""; String url = ""; key = String.format("Recommend%02d", (i + 1)); url = data.getStringExtra(key); if (url.equals("") == false) { for (int j = 0; j < RSSReaderApplication.NUM_OF_FEED; ++j) { String tmp = mFeedURL[j].getText().toString().trim(); if (tmp.equals("") == true) { mFeedURL[j].setText(url); break; } } } } } } } private void showRecommend() { Intent intent = new Intent(getApplicationContext(), com.beatcraft.rssreader.RecommendActivity.class); startActivityForResult(intent, REQUEST_RECOMMEND); } private void regist() { RSSReaderApplication app = (RSSReaderApplication) getApplication(); app.setNumberOfGet(NUM_OF_GET_LIST[mNumOfGet.getSelectedItemPosition()]); app.clearFeedURL(); for (int i = 0, j = 0; i < RSSReaderApplication.NUM_OF_FEED; ++i) { String url = mFeedURL[i].getText().toString().trim(); if (url.equals("") == false) { app.setFeedURL(j++, url); } } app.saveConfig(); finish(); } }
package com.beatcraft.rssreader; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.NameValuePair; import org.apache.http.auth.AuthScope; import org.apache.http.auth.Credentials; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.message.BasicNameValuePair; import org.apache.http.protocol.HTTP; import org.xmlpull.v1.XmlPullParser; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.util.Xml; import android.view.View; import android.view.Window; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.CheckBox; import android.widget.Spinner; public class RecommendActivity extends Activity implements View.OnClickListener, ITaskEntity { private static final String GENRE[] = {"news", "music", "movie"}; private static final String POST_DOMAIN = "labs.beatcraft.com"; private static final String POST_PATH = "/ja/androidtext/recommend.php"; private static final String POST_USER = "beatandroid"; private static final String POST_PASS = "sample"; private static final int NUM_OF_RECOMMEND = 3; private Spinner mGenre; private CheckBox mFeedCheck[]; private String mRecommendTitle[]; private String mRecommendURL[]; private Button mAddFeed; private int mStat = -1; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.recommend); ArrayAdapter<CharSequence> adapter; adapter = ArrayAdapter.createFromResource(this, R.array.genre, android.R.layout.simple_spinner_item); adapter.setDropDownViewResource( android.R.layout.simple_spinner_dropdown_item); mGenre = (Spinner) findViewById(R.id.Genre); mGenre.setAdapter(adapter); Button button; button = (Button) findViewById(R.id.Search); button.setOnClickListener(this); button = (Button) findViewById(R.id.Back); button.setOnClickListener(this); mAddFeed = (Button) findViewById(R.id.AddFeed); mAddFeed.setOnClickListener(this); mFeedCheck = new CheckBox[NUM_OF_RECOMMEND]; mFeedCheck[0] = (CheckBox) findViewById(R.id.Feed01); mFeedCheck[1] = (CheckBox) findViewById(R.id.Feed02); mFeedCheck[2] = (CheckBox) findViewById(R.id.Feed03); mRecommendTitle = new String[NUM_OF_RECOMMEND]; mRecommendURL = new String[NUM_OF_RECOMMEND]; } @Override public void onClick(View view) { int id = view.getId(); switch (id) { case R.id.Search: HttpAccessTask task = new HttpAccessTask(this); task.execute(this); break; case R.id.Back: finish(); break; case R.id.AddFeed: addFeed(); break; } } @Override public void backgroundProc() { for (int i = 0; i < NUM_OF_RECOMMEND; ++i) { mRecommendTitle[i] = ""; mRecommendURL[i] = ""; } mStat = get(GENRE[mGenre.getSelectedItemPosition()]); } @Override public void postProc() { if (mStat == 0) { for (int i = 0; i < NUM_OF_RECOMMEND; ++i) { mFeedCheck[i].setText(mRecommendTitle[i]); mFeedCheck[i].setVisibility(View.VISIBLE); mFeedCheck[i].setChecked(false); } mAddFeed.setVisibility(View.VISIBLE); } else { for (int i = 0; i < NUM_OF_RECOMMEND; ++i) { mFeedCheck[i].setText(""); mFeedCheck[i].setVisibility(View.INVISIBLE); mFeedCheck[i].setChecked(false); } mAddFeed.setVisibility(View.INVISIBLE); } } private int get(String genre) { String url = "http://" + POST_DOMAIN + POST_PATH; DefaultHttpClient client = new DefaultHttpClient(); if (client != null) { client.getParams().setParameter("http.socket.timeout", new Integer(15000)); HttpPost method = null; try { method = new HttpPost(url); } catch (Exception e) { e.printStackTrace(); } if (method == null) { return -1; } HttpResponse response = null; try { List<NameValuePair> pair = new ArrayList<NameValuePair>(); pair.add(new BasicNameValuePair("genre", genre)); method.setEntity(new UrlEncodedFormEntity(pair, HTTP.UTF_8)); Credentials cred = new UsernamePasswordCredentials(POST_USER, POST_PASS); client.getCredentialsProvider().setCredentials(new AuthScope(POST_DOMAIN, 80), cred); response = client.execute(method); int ret = response.getStatusLine().getStatusCode(); if (ret == HttpStatus.SC_OK) { InputStream is = response.getEntity().getContent(); return parse(is); } } catch (Exception e) { e.printStackTrace(); } finally { client.getConnectionManager().shutdown(); } } return -1; } private int parse(InputStream is) { int count = 0; boolean inFeed = false; XmlPullParser p = Xml.newPullParser(); try { p.setInput(is, null); int event = p.getEventType(); while (event != XmlPullParser.END_DOCUMENT) { String elem = null; String tmp = null; switch (event) { case XmlPullParser.START_TAG: elem = p.getName(); if (elem.equals("feed") == true) { if (inFeed == true) { count++; if (count >= NUM_OF_RECOMMEND) { return 0; } } inFeed = true; } else if (elem.equals("title") == true) { tmp = p.nextText(); if (tmp != null) { mRecommendTitle[count] = tmp; } } else if (elem.equals("url") == true) { tmp = p.nextText(); if (tmp != null) { mRecommendURL[count] = tmp; } } break; case XmlPullParser.END_TAG: elem = p.getName(); if (elem.equals("feed") == true) { count++; if (count >= NUM_OF_RECOMMEND) { return 0; } inFeed = false; } break; } event = p.next(); } } catch (Exception e) { e.printStackTrace(); return -1; } return 0; } private void addFeed() { int count = 0; Intent result = new Intent(); for (int i = 0; i < NUM_OF_RECOMMEND; ++i) { if (mFeedCheck[i].isChecked() == true) { String key = ""; key = String.format("Recommend%02d", (count + 1)); result.putExtra(key, mRecommendURL[i]); count++; } } result.putExtra("RecommendCount", count); setResult(RESULT_OK, result); finish(); } }
package com.beatcraft.rssreader; public class FeedItem { public static final int ITEMTYPE_FEEDCHANNEL = 0; public static final int ITEMTYPE_FEEDITEM = 1; private int mItemType; private String mFeedTitle = ""; private String mArticleTitle = ""; private String mPubDate = ""; private String mDescription = ""; private String mLink = ""; public FeedItem(int itemType) { mItemType = itemType; } public int itemType() { return mItemType; } public String feedTitle() { return mFeedTitle; } public void setFeedTitle(String title) { mFeedTitle = title; } public String articleTitle() { return mArticleTitle; } public void setArticleTitle(String title) { mArticleTitle = title; } public String pubDate() { return mPubDate; } public void setPubDate(String pubDate) { mPubDate = pubDate; } public String description() { return mDescription; } public void setDescription(String description) { mDescription = description; } public String link() { return mLink; } public void setLink(String link) { mLink = link; } }
package com.beatcraft.rssreader; import java.io.InputStream; import java.util.ArrayList; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import org.xmlpull.v1.XmlPullParser; import android.util.Xml; public class FeedList { private ArrayList<FeedItem> mList = null; public FeedList() { mList = new ArrayList<FeedItem>(); } public ArrayList<FeedItem> getList() { return mList; } public int count() { if (mList != null) { return mList.size(); } return 0; } public int get(RSSReaderApplication app) { int success = 0; for (int i = 0; i < RSSReaderApplication.NUM_OF_FEED; ++i) { String url = app.feedURL(i); if (url.equals("") == true) { continue; } DefaultHttpClient client = new DefaultHttpClient(); if (client != null) { client.getParams().setParameter("http.socket.timeout", new Integer(15000)); HttpGet method = null; try { method = new HttpGet(url); } catch (Exception e) { e.printStackTrace(); } if (method == null) { continue; } HttpResponse response = null; try { response = client.execute(method); int ret = response.getStatusLine().getStatusCode(); if (ret == HttpStatus.SC_OK) { InputStream is = response.getEntity().getContent(); if (parse(is, app.numberOfGet()) > 0) { success++; } is.close(); } } catch (Exception e) { e.printStackTrace(); } finally { client.getConnectionManager().shutdown(); } } } return success; } private int parse(InputStream is, int max) { int count = 0; boolean inChannel = false; boolean inItem = false; FeedItem item = null; String feedTitle = ""; XmlPullParser p = Xml.newPullParser(); try { p.setInput(is, null); int event = p.getEventType(); while (event != XmlPullParser.END_DOCUMENT) { String elem = null; String tmp = null; switch (event) { case XmlPullParser.START_TAG: elem = p.getName(); if (elem.equals("channel") == true) { inChannel = true; item = new FeedItem(FeedItem.ITEMTYPE_FEEDCHANNEL); } else if (elem.equals("item") == true) { if (inChannel == true) { if (item != null) { mList.add(item); item = null; count++; } inChannel = false; } inItem = true; item = new FeedItem(FeedItem.ITEMTYPE_FEEDITEM); item.setFeedTitle(feedTitle); } else if (elem.equals("title") == true) { tmp = p.nextText(); if ((tmp != null) && (item != null)) { if (inChannel == true) { feedTitle = tmp; item.setFeedTitle(tmp); } else if (inItem == true) { item.setArticleTitle(tmp); } } } else if ((elem.equals("pubDate") == true) || (elem.equals("date") == true)) { if (inItem == true) { tmp = p.nextText(); if ((tmp != null) && (item != null)) { item.setPubDate(tmp); } } } else if (elem.equals("description") == true) { if (inItem == true) { tmp = p.nextText(); if ((tmp != null) && (item != null)) { item.setDescription(tmp); } } } else if (elem.equals("link") == true) { if (inItem == true) { tmp = p.nextText(); if ((tmp != null) && (item != null)) { item.setLink(tmp); } } } break; case XmlPullParser.END_TAG: elem = p.getName(); if (elem.equals("channel") == true) { if (inChannel == true) { if (item != null) { mList.add(item); item = null; count++; } inChannel = false; } } else if (elem.equals("item") == true) { if (inItem == true) { if (item != null) { mList.add(item); item = null; count++; max--; if (max == 0) { return count; } } inItem = false; } } } event = p.next(); } } catch (Exception e) { e.printStackTrace(); return 0; } return count; } }
package com.beatcraft.rssreader; import java.util.List; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.TextView; public class FeedAdapter extends ArrayAdapter<FeedItem> { private LayoutInflater mInflate; public FeedAdapter(Context context, List<FeedItem> obj) { super(context, 0, obj); mInflate = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); } @Override public boolean isEnabled(int pos) { FeedItem item = getItem(pos); if (item.itemType() == FeedItem.ITEMTYPE_FEEDCHANNEL) { return false; } return true; } public View getView(final int pos, View convView, ViewGroup parent) { View view = convView; FeedItem item = getItem(pos); switch (item.itemType()) { case FeedItem.ITEMTYPE_FEEDCHANNEL: view = buildChannel(item); break; case FeedItem.ITEMTYPE_FEEDITEM: view = buildItem(item); break; } return view; } private View buildChannel(FeedItem item) { View view = null; view = mInflate.inflate(R.layout.item_channel, null); TextView tv; tv = (TextView) view.findViewById(R.id.FeedTitle); tv.setText(item.feedTitle()); return view; } private View buildItem(FeedItem item) { View view = null; view = mInflate.inflate(R.layout.item_item, null); TextView tv; tv = (TextView) view.findViewById(R.id.ArticleTitle); tv.setText(item.articleTitle()); tv = (TextView) view.findViewById(R.id.PubDate); tv.setText(item.pubDate()); return view; } }
package com.beatcraft.rssreader; public interface ITaskEntity { void backgroundProc(); void postProc(); }
package com.beatcraft.rssreader; import android.app.Activity; import android.app.ProgressDialog; import android.os.AsyncTask; public class HttpAccessTask extends AsyncTask<ITaskEntity, Integer, Void> { private Activity mActivity; private ProgressDialog mDialog; private ITaskEntity mITaskEntity; public HttpAccessTask(Activity activity) { mActivity = activity; } @Override protected void onPreExecute() { mDialog = new ProgressDialog(mActivity); mDialog.setMessage(mActivity.getString(R.string.loading)); mDialog.show(); } @Override protected Void doInBackground(ITaskEntity... params) { mITaskEntity = params[0]; mITaskEntity.backgroundProc(); return null; } @Override protected void onPostExecute(Void v) { mITaskEntity.postProc(); mDialog.dismiss(); mDialog = null; } }
package com.beatcraft.rssreader; import android.os.AsyncTask; public class ListGenerateTask extends AsyncTask<ITaskEntity, Integer, Void> { private ITaskEntity mITaskEntity; @Override protected Void doInBackground(ITaskEntity... params) { mITaskEntity = params[0]; mITaskEntity.backgroundProc(); return null; } @Override protected void onPostExecute(Void v) { mITaskEntity.postProc(); } }
package com.beatcraft.rssreader; import java.util.ArrayList; import android.app.AlarmManager; import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; public class FeedService extends Service implements Runnable { private static final String SERVICE_ACTION = "com.beatcraft.rssreader.SERVICE_ACTION"; private boolean mWorking = false; private FeedList mListFG = null; private FeedList mListBG = null; @Override public void onStart(Intent intent, int startID) { super.onStart(intent, startID); Thread thread = new Thread(this); thread.run(); } @Override public IBinder onBind(Intent arg0) { return mIFeedServiceBinder; } @Override public void run() { RSSReaderApplication app = (RSSReaderApplication) getApplication(); mListBG = new FeedList(); if (mListBG.get(app) > 0) { lock(); mListFG = mListBG; unlock(); Intent bcast = new Intent(); bcast.setAction("RECEIVE_FEED"); getBaseContext().sendBroadcast(bcast); } Intent ac = new Intent(); ac.setAction(SERVICE_ACTION); PendingIntent pi = PendingIntent.getService(this, 0, ac, 0); AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE); long at = System.currentTimeMillis() + 60000; am.set(AlarmManager.RTC, at, pi); } // service binder private final IFeedService.Stub mIFeedServiceBinder = new IFeedService.Stub() { @Override public void lockList() { lock(); } @Override public void unlockList() { unlock(); } @Override public int getFeedCount() throws RemoteException { if (mListFG != null) { return mListFG.count(); } return 0; } @Override public int getItemType(int index) { if (mListFG != null) { ArrayList<FeedItem> list = mListFG.getList(); return list.get(index).itemType(); } return 0; } @Override public String getFeedTitle(int index) { if (mListFG != null) { ArrayList<FeedItem> list = mListFG.getList(); return list.get(index).feedTitle(); } return ""; } @Override public String getArticleTitle(int index) { if (mListFG != null) { ArrayList<FeedItem> list = mListFG.getList(); return list.get(index).articleTitle(); } return ""; } @Override public String getPubDate(int index) { if (mListFG != null) { ArrayList<FeedItem> list = mListFG.getList(); return list.get(index).pubDate(); } return ""; } @Override public String getDescription(int index) { if (mListFG != null) { ArrayList<FeedItem> list = mListFG.getList(); return list.get(index).description(); } return ""; } @Override public String getLink(int index) { if (mListFG != null) { ArrayList<FeedItem> list = mListFG.getList(); return list.get(index).link(); } return ""; } }; private synchronized void lock() { while (mWorking) { try { wait(); } catch (Exception e) { } } mWorking = true; } private synchronized void unlock() { mWorking = false; } }
package com.beatcraft.rssreader; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; public class FeedReceiver extends BroadcastReceiver { IFeedReceiver mReceiver; public FeedReceiver(IFeedReceiver receiver) { mReceiver = receiver; } @Override public void onReceive(Context context, Intent intent) { mReceiver.feedReceived(); } }
package com.beatcraft.rssreader; public interface IFeedReceiver { void feedReceived(); }
package com.beatcraft.rssreader; interface IFeedService { void lockList(); void unlockList(); int getFeedCount(); int getItemType(int index); String getFeedTitle(int index); String getArticleTitle(int index); String getPubDate(int index); String getDescription(int index); String getLink(int index); }
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <LinearLayout android:id="@+id/linearLayout1" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_margin="10dip" > <Button android:id="@+id/Config" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="5dip" android:layout_marginRight="5dip" android:layout_weight="1" android:text="@string/config" /> </LinearLayout> <ListView android:id="@+id/android:list" android:layout_width="fill_parent" android:layout_height="wrap_content" android:fastScrollEnabled="true" > </ListView> </LinearLayout>
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#cccccc" android:orientation="vertical" > <TextView android:id="@+id/FeedTitle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="5dip" android:text="FeedTitle" android:textColor="#000000" android:textSize="16dip" android:textStyle="bold" /> </LinearLayout>
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <TextView android:id="@+id/ArticleTitle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="5dip" android:text="ArticleTitle" /> <TextView android:id="@+id/PubDate" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" android:layout_margin="5dip" android:text="pubDate" /> </LinearLayout>
<?xml version="1.0" encoding="utf-8"?> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/ScrollView" android:layout_width="fill_parent" android:layout_height="fill_parent" > <LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <Button android:id="@+id/Back" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="5dip" android:text="@string/back" /> <TextView android:id="@+id/FeedTitle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dip" android:layout_marginRight="10dip" android:text="FeedTitle" android:textSize="16dip" android:textStyle="bold" /> <TextView android:id="@+id/ArticleTitle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dip" android:layout_marginRight="10dip" android:text="ArticleTitle" /> <TextView android:id="@+id/PubDate" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" android:layout_marginRight="10dip" android:text="pubDate" /> <android.webkit.WebView android:id="@+id/Description" android:layout_width="fill_parent" android:layout_height="0dip" android:layout_margin="5dip" android:layout_weight="1" > </android.webkit.WebView> </LinearLayout> </ScrollView>
<?xml version="1.0" encoding="utf-8"?> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/ScrollView" android:layout_width="fill_parent" android:layout_height="fill_parent" > <LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <LinearLayout android:id="@+id/linearLayout1" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_margin="10dip" > <TextView android:id="@+id/LabelNumOfGet" android:layout_width="wrap_content" android:layout_height="fill_parent" android:gravity="center_vertical" android:text="@string/label_number_of_get" /> <Spinner android:id="@+id/NumOfGet" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <Button android:id="@+id/Recommend" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dip" android:layout_weight="1" android:text="@string/recommend" /> </LinearLayout> <EditText android:id="@+id/FeedURL01" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginLeft="10dip" android:layout_marginRight="10dip" android:inputType="textUri" > <requestFocus /> </EditText> <EditText android:id="@+id/FeedURL02" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginLeft="10dip" android:layout_marginRight="10dip" android:inputType="textUri" /> <EditText android:id="@+id/FeedURL03" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginLeft="10dip" android:layout_marginRight="10dip" android:inputType="textUri" /> <EditText android:id="@+id/FeedURL04" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginLeft="10dip" android:layout_marginRight="10dip" android:inputType="textUri" /> <EditText android:id="@+id/FeedURL05" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginLeft="10dip" android:layout_marginRight="10dip" android:inputType="textUri" /> <EditText android:id="@+id/FeedURL06" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginLeft="10dip" android:layout_marginRight="10dip" android:inputType="textUri" /> <EditText android:id="@+id/FeedURL07" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginLeft="10dip" android:layout_marginRight="10dip" android:inputType="textUri" /> <EditText android:id="@+id/FeedURL08" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginLeft="10dip" android:layout_marginRight="10dip" android:inputType="textUri" /> <EditText android:id="@+id/FeedURL09" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginLeft="10dip" android:layout_marginRight="10dip" android:inputType="textUri" /> <EditText android:id="@+id/FeedURL10" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginLeft="10dip" android:layout_marginRight="10dip" android:inputType="textUri" /> <LinearLayout android:id="@+id/linearLayout2" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_margin="10dip" > <Button android:id="@+id/Cancel" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="5dip" android:layout_marginRight="5dip" android:layout_weight="1" android:text="@string/cancel" /> <Button android:id="@+id/Regist" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="5dip" android:layout_marginRight="5dip" android:layout_weight="1" android:text="@string/regist" /> </LinearLayout> </LinearLayout> </ScrollView>
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <LinearLayout android:id="@+id/linearLayout1" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_margin="10dip" > <TextView android:id="@+id/LabelGenre" android:layout_width="wrap_content" android:layout_height="fill_parent" android:gravity="center_vertical" android:text="@string/label_genre" /> <Spinner android:id="@+id/Genre" android:layout_width="120dip" android:layout_height="wrap_content" /> <Button android:id="@+id/Search" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dip" android:layout_weight="1" android:text="@string/search" /> </LinearLayout> <Button android:id="@+id/Back" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="5dip" android:text="@string/back" /> <CheckBox android:id="@+id/Feed01" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_margin="5dip" android:text="CheckBox" android:visibility="invisible" /> <CheckBox android:id="@+id/Feed02" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_margin="5dip" android:text="CheckBox" android:visibility="invisible" /> <CheckBox android:id="@+id/Feed03" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_margin="5dip" android:text="CheckBox" android:visibility="invisible" /> <Button android:id="@+id/AddFeed" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_margin="5dip" android:text="@string/addfeed" android:visibility="invisible" /> </LinearLayout>
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.beatcraft.rssreader" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="7" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:name="com.beatcraft.rssreader.RSSReaderApplication" > <activity android:label="@string/app_name" android:name=".RSSReaderActivity" > <intent-filter > <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:label="@string/app_name" android:name=".ConfigActivity" /> <activity android:label="@string/app_name" android:name=".DescActivity" /> <activity android:label="@string/app_name" android:name=".RecommendActivity" /> <service android:name=".FeedService" > <intent-filter > <action android:name="com.beatcraft.rssreader.SERVICE_ACTION" /> </intent-filter> </service> </application> <uses-permission android:name="android.permission.INTERNET" > </uses-permission> </manifest>