/* * Copyright 2010 John R. Hicks * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.determinato.feeddroid.activity; import android.app.AlarmManager; import android.app.AlertDialog; import android.app.Dialog; import android.app.ListActivity; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.SearchManager; import android.content.ContentResolver; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.SystemClock; import android.util.Log; import android.view.ContextMenu; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ContextMenu.ContextMenuInfo; import android.widget.ImageView; import android.widget.ListView; import android.widget.SimpleCursorAdapter; import android.widget.Spinner; import android.widget.AdapterView.AdapterContextMenuInfo; import com.determinato.feeddroid.R; import com.determinato.feeddroid.adapters.FolderListCursorAdapter; import com.determinato.feeddroid.ads.FeedDroidAdConstants; import com.determinato.feeddroid.ads.FeedDroidAdSenseSpec; import com.determinato.feeddroid.dao.ChannelDao; import com.determinato.feeddroid.dao.FolderDao; import com.determinato.feeddroid.dao.FolderItemDao; import com.determinato.feeddroid.provider.FeedDroid; import com.determinato.feeddroid.service.FeedDroidUpdateService; import com.determinato.feeddroid.service.RssParserService; import com.determinato.feeddroid.view.FolderListRow; import com.google.ads.AdSenseSpec; import com.google.ads.GoogleAdView; import com.google.ads.AdSenseSpec.AdType; import com.google.ads.AdSenseSpec.ExpandDirection; /** * Main application activity. * * @author John R. Hicks <john@determinato.com> * */ public class HomeScreenActivity extends ListActivity { private static final String TAG = "HomeScreenActivity"; private static final String[] FOLDER_PROJECTION = {FeedDroid.Folders._ID, FeedDroid.Folders.NAME, FeedDroid.Folders.PARENT_ID}; private static final String[] CHANNEL_PROJECTION = { FeedDroid.Channels._ID, FeedDroid.Channels.TITLE, FeedDroid.Channels.URL, FeedDroid.Channels.ICON, FeedDroid.Channels.FOLDER_ID }; /** Preferences tag */ public static final String TAG_PREFS = "FeedDroid"; /** Add channel menu identifier */ public static final int ADD_CHANNEL_ID = R.id.menu_new_channel; /** Add folder menu identifier */ public static final int ADD_FOLDER_ID = R.id.menu_new_folder; /** Delete menu identifier */ public static final int DELETE_ID = R.id.remove_channel; /** Refresh menu identifier */ public static final int REFRESH_ID = Menu.FIRST + 2; /** Refresh All menu identifier */ public static final int REFRESH_ALL_ID = Menu.FIRST + 3; /** Edit menu identifier */ public static final int EDIT_ID = R.id.edit_channel; /** Move menu identifier */ public static final int MOVE_ID = R.id.move_channel; /** Preferences menu identifier */ public static final int PREFS_ID = Menu.FIRST; /** Search menu identifier */ public static final int SEARCH_ID = R.id.menu_search; /** New folder menu identifier */ public static final int FOLDER_ID = R.id.menu_new_folder; private static final int SHOW_PREFERENCES = 1; private static final int SHOW_MOVE = 2; private static final int STOP_SPLASH = 0; private static final long SPLASH_TIME = 2000; private static boolean mSplashShown = false; private Cursor mFolderCursor; private Cursor mChannelCursor; private FolderListCursorAdapter adapter; private NotificationManager mNotificationManager; private SharedPreferences mPreferences; private ContentResolver mResolver; private FeedDroidUpdateService mServiceBinder; private int mSelectedRow; private ImageView mSplash; /** * {@inheritDoc} */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.folder_list); mSplash = (ImageView) findViewById(R.id.splash); if (!mSplashShown) { mSplash.setVisibility(View.VISIBLE); Message msg = new Message(); msg.what = STOP_SPLASH; splashHandler.sendMessageDelayed(msg, SPLASH_TIME); mSplashShown = true; } mFolderCursor = managedQuery( FeedDroid.Folders.CONTENT_URI, FOLDER_PROJECTION, "parent_id=1", null, null); mChannelCursor = managedQuery( FeedDroid.Channels.CONTENT_URI, CHANNEL_PROJECTION, "folder_id=1", null, null); registerForContextMenu(getListView()); mResolver = getContentResolver(); mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); mPreferences = getSharedPreferences(PreferencesActivity.USER_PREFERENCE, MODE_PRIVATE); if (mPreferences.getBoolean(PreferencesActivity.PREF_AUTO_UPDATE, false)) { Log.d(TAG, "autoupdate set to true"); AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE); Intent i = new Intent("com.determinato.feeddroid.ACTION_REFRESH_RSS_ALARM"); PendingIntent pending = PendingIntent.getBroadcast(this, 0, i, 0); long time = mPreferences.getInt(PreferencesActivity.PREF_UPDATE_FREQ, 0) * 60 * 1000; Log.d(TAG, "update in " + time / 1000 + " seconds."); alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + time, time, pending); } // Set up ads AdSenseSpec spec = new FeedDroidAdSenseSpec(FeedDroidAdConstants.CLIENT_ID) .setCompanyName(FeedDroidAdConstants.COMPANY_NAME) .setAppName(getString(R.string.app_name)) .setChannel(FeedDroidAdConstants.CHANNEL_ID) .setAdType(AdType.TEXT_IMAGE) .setExpandDirection(ExpandDirection.TOP) .setAdTestEnabled(FeedDroidAdConstants.AD_TEST_ENABLED); GoogleAdView adView = (GoogleAdView) findViewById(R.id.adview); adView.showAds(spec); } /** * {@inheritDoc} */ @Override public void onResume() { super.onResume(); mNotificationManager.cancel(1); mFolderCursor.requery(); mChannelCursor.requery(); initAdapter(); } /** * Initializes/Refreshes adapter */ private void initAdapter() { getListView().setAdapter(new FolderListCursorAdapter(this, mFolderCursor, mChannelCursor)); } /** * {@inheritDoc} */ @Override protected void onListItemClick(ListView l, View v, int position, long id) { Intent intent = null; FolderItemDao data = (FolderItemDao) getListView().getItemAtPosition(position); if (data instanceof ChannelDao) { Long channelId = ((ChannelDao)data).getId(); Uri uri = Uri.withAppendedPath(FeedDroid.Posts.CONTENT_URI_LIST, channelId.toString()); intent = new Intent(Intent.ACTION_VIEW, uri); startActivity(intent); } else if (data instanceof FolderDao) { Long folderId = ((FolderDao)data).getId(); Uri uri = Uri.withAppendedPath(FeedDroid.Folders.CONTENT_URI, folderId.toString()); intent = new Intent(Intent.ACTION_VIEW, uri); startActivity(intent); } } /** * {@inheritDoc} */ @Override public boolean onPrepareOptionsMenu(Menu menu) { super.onPrepareOptionsMenu(menu); menu.removeGroup(Menu.CATEGORY_ALTERNATIVE); menu.add(Menu.CATEGORY_ALTERNATIVE, REFRESH_ALL_ID, Menu.NONE, "Refresh All") .setIcon(getResources().getDrawable(android.R.drawable.ic_menu_share)); return true; } /** * {@inheritDoc} */ @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.channel_list_menu, menu); menu.add(0, SHOW_PREFERENCES, Menu.NONE, getString(R.string.prefs)) .setIcon(android.R.drawable.ic_menu_preferences); MenuItem searchItem = menu.findItem(R.id.menu_search); searchItem.setAlphabeticShortcut(SearchManager.MENU_KEY); return true; } /** * {@inheritDoc} */ @Override public boolean onOptionsItemSelected(MenuItem item) { Intent i; switch(item.getItemId()) { case ADD_FOLDER_ID: i = new Intent(this, FolderAddActivity.class); i.setAction(Intent.ACTION_INSERT); i.setData(Uri.parse("vnd.android.cursor.dir/vnd.feeddroid.folder")); startActivityForResult(i, RESULT_OK); return true; case ADD_CHANNEL_ID: i = new Intent(this, ChannelAddActivity.class); i.setAction(Intent.ACTION_INSERT); i.setData(Uri.parse("vnd.android.cursor.dir/vnd.feeddroid.channel")); i.putExtra("folderId", 1L); startActivityForResult(i, RESULT_OK); return true; case REFRESH_ALL_ID: updateAllChannels(); return true; case PREFS_ID: i = new Intent(this, PreferencesActivity.class); startActivityForResult(i, SHOW_PREFERENCES); return true; case SEARCH_ID: onSearchRequested(); return true; } return super.onOptionsItemSelected(item); } /** * {@inheritDoc} */ @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo); MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.channel_list_context_menu, menu); AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo; FolderItemDao backing = ((FolderListRow) info.targetView).getBacking(); if (backing instanceof FolderDao) { MenuItem item = menu.findItem(EDIT_ID); item.setVisible(false); } } /** * {@inheritDoc} */ @Override public boolean onContextItemSelected(MenuItem item) { AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo(); FolderItemDao backing = ((FolderListRow)info.targetView).getBacking(); long id = backing.getId(); switch (item.getItemId()) { case EDIT_ID: if (((FolderListRow)info.targetView).getItemType() == FolderListRow.CHANNEL_VIEW) { startActivity(new Intent(Intent.ACTION_EDIT, ContentUris.withAppendedId(FeedDroid.Channels.CONTENT_URI, id))); } return true; case DELETE_ID: if (((FolderListRow)info.targetView).getItemType() == FolderListRow.CHANNEL_VIEW) { removeChannel(id); } else if (((FolderListRow)info.targetView).getItemType() == FolderListRow.FOLDER_VIEW) { removeFolder(id); } return true; case MOVE_ID: mSelectedRow = info.position; showDialog(SHOW_MOVE); return true; default: return false; } } /** * {@inheritDoc} */ @Override public Dialog onCreateDialog(int id) { switch(id) { case SHOW_MOVE: LayoutInflater inflater = LayoutInflater.from(this); View moveDialogView = inflater.inflate(R.layout.move_item, null); AlertDialog.Builder moveDialog = new AlertDialog.Builder(this); moveDialog.setTitle(getString(R.string.move_channel)); moveDialog.setView(moveDialogView); final Spinner folderSpinner = (Spinner) moveDialogView.findViewById(R.id.move_channel_spinner); Cursor c = managedQuery(FeedDroid.Folders.CONTENT_URI, new String[] {FeedDroid.Folders._ID, FeedDroid.Folders.NAME}, null, null, null); String[] columns = new String[] {FeedDroid.Folders.NAME}; int[] to = new int[] {android.R.id.text1}; SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, android.R.layout.simple_spinner_item, c, columns, to); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); folderSpinner.setAdapter(adapter); moveDialog.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { ContentValues values = new ContentValues(); long folderId = folderSpinner.getSelectedItemId(); Log.d(TAG, "selected row: " + mSelectedRow); FolderItemDao dao = (FolderItemDao) getListView().getItemAtPosition(mSelectedRow); long id = dao.getId(); Log.d(TAG, "id: " + id); if (dao instanceof FolderDao) { values.put(FeedDroid.Folders.PARENT_ID, folderId); getContentResolver().update(FeedDroid.Folders.CONTENT_URI, values, "_id=" + id, null); } else { values.put(FeedDroid.Channels.FOLDER_ID, folderId); getContentResolver().update(FeedDroid.Channels.CONTENT_URI, values, "_id" + id, null); } } }); moveDialog.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { } }); return moveDialog.create(); } initAdapter(); return null; } /** * {@inheritDoc} */ @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (data != null) { switch(requestCode) { case SHOW_PREFERENCES: mFolderCursor.requery(); mChannelCursor.requery(); adapter = new FolderListCursorAdapter(this, mFolderCursor, mChannelCursor); setListAdapter(adapter); break; } } } private void removeFolder(final long folderId) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setMessage("Are you sure you want to remove this folder?") .setCancelable(false) .setPositiveButton("Yes", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { ContentResolver resolver = getContentResolver(); resolver.delete(FeedDroid.Folders.CONTENT_URI, "_id=" + folderId, null); // Move all channels in this folder to the home folder. ContentValues values = new ContentValues(); values.put("folder_id", 1); resolver.update(FeedDroid.Channels.CONTENT_URI, values, "folder_id=" + folderId, null); onResume(); } }) .setNegativeButton("No", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { dialog.cancel(); } }); builder.create().show(); } /** * Removes a RSS channel from the database. * @param channelId ID of channel to remove */ private void removeChannel(final long channelId) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setMessage("Are you sure you want to remove this channel?") .setCancelable(false) .setPositiveButton("Yes", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { ContentResolver resolver = getContentResolver(); resolver.delete(FeedDroid.Posts.CONTENT_URI, "channel_id=" + channelId, null); resolver.delete(FeedDroid.Channels.CONTENT_URI, "_id=" + channelId, null); onResume(); } }) .setNegativeButton("No", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { dialog.cancel(); } }); builder.create().show(); } /** * Forces update of all channels. * * <p>This method calls the {@link com.determinato.feeddroid.service.FeedDroidUpdateService} * and requests it to update all RSS feeds in the background.</p> */ private void updateAllChannels() { //mServiceBinder.updateAllChannels(); ContentResolver resolver = getContentResolver(); String[] projection = new String[] {FeedDroid.Channels._ID, FeedDroid.Channels.URL}; Cursor c = resolver.query(FeedDroid.Channels.CONTENT_URI, projection, null, null, null); if (c.getCount() == 0) return; c.moveToFirst(); do { long id = c.getLong(c.getColumnIndex(FeedDroid.Channels._ID)); String url = c.getString(c.getColumnIndex(FeedDroid.Channels.URL)); Intent service = new Intent(this, RssParserService.class); service.putExtra("id", id); service.putExtra("url", url); startService(service); } while (c.moveToNext()); c.close(); } /** * Handler to remove splash screen and display main interface. */ private Handler splashHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case STOP_SPLASH: mSplash.setVisibility(View.GONE); break; } super.handleMessage(msg); } }; }