/** * This is an example of implementing an application service that will run in response to an alarm, * allowing us to move long duration work out of an intent receiver. * * @see AlarmService * @see AlarmService_Alarm */ public class EnvelopeService extends Service { NotificationManager mNM; private RedditSettings mSettings = new RedditSettings(); private HttpClient mClient = Common.getGzipHttpClient(); @Override public void onCreate() { mNM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); mSettings.loadRedditPreferences(this, mClient); new PeekEnvelopeServiceTask(this, mClient, mSettings.getMailNotificationStyle()).execute(); } private class PeekEnvelopeServiceTask extends PeekEnvelopeTask { public PeekEnvelopeServiceTask( Context context, HttpClient client, String mailNotificationStyle) { super(context, client, mailNotificationStyle); } @Override public void onPostExecute(Object count) { super.onPostExecute(count); EnvelopeService.this.stopSelf(); } } @Override public IBinder onBind(Intent intent) { return mBinder; } /** * This is the object that receives interactions from clients. See RemoteService for a more * complete example. */ private final IBinder mBinder = new Binder() { @Override protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { return super.onTransact(code, data, reply, flags); } }; public static void resetAlarm(Context context, long interval) { // Create an IntentSender that will launch our service, to be scheduled // with the alarm manager. PendingIntent alarmSender = PendingIntent.getService(context, 0, new Intent(context, EnvelopeService.class), 0); AlarmManager am = (AlarmManager) context.getSystemService(ALARM_SERVICE); am.cancel(alarmSender); if (interval != 0) am.setRepeating( AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), interval, alarmSender); } }
public final class PickSubredditActivity extends ListActivity { private static final String TAG = "PickSubredditActivity"; // Group 1: inner private final Pattern MY_SUBREDDITS_OUTER = Pattern.compile("your front page reddits.*?<ul>(.*?)</ul>", Pattern.CASE_INSENSITIVE); // Group 3: subreddit name. Repeat the matcher.find() until it fails. private final Pattern MY_SUBREDDITS_INNER = Pattern.compile("<a(.*?)/r/(.*?)>(.+?)</a>"); private RedditSettings mSettings = new RedditSettings(); private HttpClient mClient = Common.getGzipHttpClient(); private PickSubredditAdapter mSubredditsAdapter; private ArrayList<String> mSubredditsList; private static final Object ADAPTER_LOCK = new Object(); private EditText mEt; private AsyncTask<?, ?, ?> mCurrentTask = null; private final Object mCurrentTaskLock = new Object(); public static final String[] DEFAULT_SUBREDDITS = { "reddit.com", "pics", "politics", "wtf", "funny", "technology", "askreddit", "science", "programming", "gaming", "worldnews", "comics", "offbeat", "videos", "environment", "iama", "business", "entertainment", "bestof", "economics", "marijuana", "todayilearned", "linux", "android" }; // A list of special subreddits that can be viewed, but cannot be used for submissions. They // inherit from the FakeSubreddit class // in the redditdev source, so we use the same naming here. Note: Should we add r/Random and // r/Friends? public static final String[] FAKE_SUBREDDITS = {Constants.FRONTPAGE_STRING, "all"}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); CookieSyncManager.createInstance(getApplicationContext()); mSettings.loadRedditPreferences(this, mClient); setRequestedOrientation(mSettings.getRotation()); requestWindowFeature(Window.FEATURE_PROGRESS); requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); resetUI(null); mSubredditsList = cacheSubredditsList(mSubredditsList); if (CollectionUtils.isEmpty(mSubredditsList)) restoreLastNonConfigurationInstance(); if (CollectionUtils.isEmpty(mSubredditsList)) { new DownloadRedditsTask().execute(); } else { addFakeSubredditsUnlessSuppressed(); resetUI(new PickSubredditAdapter(this, mSubredditsList)); } } @Override public void onResume() { super.onResume(); CookieSyncManager.getInstance().startSync(); } @Override public void onPause() { super.onPause(); CookieSyncManager.getInstance().stopSync(); } @Override public Object onRetainNonConfigurationInstance() { // Avoid having to re-download and re-parse the subreddits list // when rotating or opening keyboard. return mSubredditsList; } @SuppressWarnings("unchecked") private void restoreLastNonConfigurationInstance() { mSubredditsList = (ArrayList<String>) getLastNonConfigurationInstance(); } void resetUI(PickSubredditAdapter adapter) { setTheme(mSettings.getTheme()); setContentView(R.layout.pick_subreddit_view); registerForContextMenu(getListView()); synchronized (ADAPTER_LOCK) { if (adapter == null) { // Reset the list to be empty. mSubredditsList = new ArrayList<String>(); mSubredditsAdapter = new PickSubredditAdapter(this, mSubredditsList); } else { mSubredditsAdapter = adapter; } setListAdapter(mSubredditsAdapter); mSubredditsAdapter.mLoading = false; mSubredditsAdapter.notifyDataSetChanged(); // Just in case } Common.updateListDrawables(this, mSettings.getTheme()); // Set the EditText to do same thing as onListItemClick mEt = (EditText) findViewById(R.id.pick_subreddit_input); if (mEt != null) { mEt.setOnKeyListener( new OnKeyListener() { public boolean onKey(View v, int keyCode, KeyEvent event) { if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) { returnSubreddit(mEt.getText().toString().trim()); return true; } return false; } }); mEt.setFocusableInTouchMode(true); } Button goButton = (Button) findViewById(R.id.pick_subreddit_button); if (goButton != null) { goButton.setOnClickListener( new OnClickListener() { public void onClick(View v) { returnSubreddit(mEt.getText().toString().trim()); } }); } getListView().requestFocus(); } @Override protected void onListItemClick(ListView l, View v, int position, long id) { String item = mSubredditsAdapter.getItem(position); returnSubreddit(item); } private void returnSubreddit(String subreddit) { Intent intent = new Intent(); intent.setData(Util.createSubredditUri(subreddit)); setResult(RESULT_OK, intent); finish(); } private void enableLoadingScreen() { if (Util.isLightTheme(mSettings.getTheme())) { setContentView(R.layout.loading_light); } else { setContentView(R.layout.loading_dark); } synchronized (ADAPTER_LOCK) { if (mSubredditsAdapter != null) mSubredditsAdapter.mLoading = true; } getWindow().setFeatureInt(Window.FEATURE_PROGRESS, 0); } private void disableLoadingScreen() { resetUI(mSubredditsAdapter); getWindow().setFeatureInt(Window.FEATURE_PROGRESS, 10000); } class DownloadRedditsTask extends AsyncTask<Void, Void, ArrayList<String>> { @Override public ArrayList<String> doInBackground(Void... voidz) { ArrayList<String> reddits = null; HttpEntity entity = null; try { reddits = cacheSubredditsList(reddits); if (reddits == null) { reddits = new ArrayList<String>(); HttpGet request = new HttpGet(Constants.REDDIT_BASE_URL + "/reddits"); // Set timeout to 15 seconds HttpParams params = request.getParams(); HttpConnectionParams.setConnectionTimeout(params, 15000); HttpConnectionParams.setSoTimeout(params, 15000); HttpResponse response = mClient.execute(request); entity = response.getEntity(); BufferedReader in = new BufferedReader(new InputStreamReader(entity.getContent())); String line = in.readLine(); in.close(); entity.consumeContent(); Matcher outer = MY_SUBREDDITS_OUTER.matcher(line); if (outer.find()) { Matcher inner = MY_SUBREDDITS_INNER.matcher(outer.group(1)); while (inner.find()) { reddits.add(inner.group(3)); } } else { return null; } if (Constants.LOGGING) Log.d(TAG, "new subreddit list size: " + reddits.size()); if (Constants.USE_SUBREDDITS_CACHE) { try { CacheInfo.setCachedSubredditList(getApplicationContext(), reddits); if (Constants.LOGGING) Log.d(TAG, "wrote subreddit list to cache:" + reddits); } catch (IOException e) { if (Constants.LOGGING) Log.e(TAG, "error on setCachedSubredditList", e); } } } return reddits; } catch (Exception e) { if (Constants.LOGGING) Log.e(TAG, "failed", e); if (entity != null) { try { entity.consumeContent(); } catch (Exception e2) { // Ignore. } } } return null; } @Override public void onPreExecute() { synchronized (mCurrentTaskLock) { if (mCurrentTask != null) { this.cancel(true); return; } mCurrentTask = this; } resetUI(null); enableLoadingScreen(); } @Override public void onPostExecute(ArrayList<String> reddits) { synchronized (mCurrentTaskLock) { mCurrentTask = null; } disableLoadingScreen(); if (reddits == null || reddits.size() == 0) { // Need to make a copy because Arrays.asList returns List backed by original array mSubredditsList = new ArrayList<String>(); mSubredditsList.addAll(Arrays.asList(DEFAULT_SUBREDDITS)); } else { mSubredditsList = reddits; } addFakeSubredditsUnlessSuppressed(); resetUI(new PickSubredditAdapter(PickSubredditActivity.this, mSubredditsList)); } } private void addFakeSubredditsUnlessSuppressed() { // Insert special reddits (front page, all) into subreddits list, unless suppressed by Intent // extras Bundle extras = getIntent().getExtras(); boolean addFakeSubreddits = false; if (extras != null) { boolean shouldHideFakeSubreddits = extras.getBoolean(Constants.EXTRA_HIDE_FAKE_SUBREDDITS_STRING, false); if (!shouldHideFakeSubreddits) { addFakeSubreddits = true; } } else { addFakeSubreddits = true; } if (addFakeSubreddits) { mSubredditsList.addAll(0, Arrays.asList(FAKE_SUBREDDITS)); } } private final class PickSubredditAdapter extends ArrayAdapter<String> { private LayoutInflater mInflater; private boolean mLoading = true; private int mFrequentSeparatorPos = ListView.INVALID_POSITION; public PickSubredditAdapter(Context context, List<String> objects) { super(context, 0, objects); mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); } @Override public boolean isEmpty() { if (mLoading) { // We don't want the empty state to show when loading. return false; } else { return super.isEmpty(); } } @Override public int getItemViewType(int position) { if (position == mFrequentSeparatorPos) { // We don't want the separator view to be recycled. return IGNORE_ITEM_VIEW_TYPE; } return super.getItemViewType(position); } @Override public View getView(int position, View convertView, ViewGroup parent) { View view; // Here view may be passed in for re-use, or we make a new one. if (convertView == null) { view = mInflater.inflate(android.R.layout.simple_list_item_1, null); } else { view = convertView; } TextView text = (TextView) view.findViewById(android.R.id.text1); text.setText(mSubredditsAdapter.getItem(position)); return view; } } protected Dialog onCreateDialog(int id) { Dialog dialog; ProgressDialog pdialog; switch (id) { // "Please wait" case Constants.DIALOG_LOADING_REDDITS_LIST: pdialog = new ProgressDialog(this); pdialog.setMessage("Loading your reddits..."); pdialog.setIndeterminate(true); pdialog.setCancelable(false); dialog = pdialog; break; default: throw new IllegalArgumentException("Unexpected dialog id " + id); } return dialog; } @Override protected void onRestoreInstanceState(Bundle state) { super.onRestoreInstanceState(state); final int[] myDialogs = { Constants.DIALOG_LOADING_REDDITS_LIST, }; for (int dialog : myDialogs) { try { dismissDialog(dialog); } catch (IllegalArgumentException e) { // Ignore. } } } protected ArrayList<String> cacheSubredditsList(ArrayList<String> reddits) { if (Constants.USE_SUBREDDITS_CACHE) { if (CacheInfo.checkFreshSubredditListCache(getApplicationContext())) { reddits = CacheInfo.getCachedSubredditList(getApplicationContext()); if (Constants.LOGGING) Log.d(TAG, "cached subreddit list:" + reddits); } } return reddits; } }