Example #1
0
  /**
   * Get a new modhash by scraping and return it
   *
   * @param client
   * @return
   */
  public static String doUpdateModhash(HttpClient client) {
    final Pattern MODHASH_PATTERN = Pattern.compile("modhash: '(.*?)'");
    String modhash;
    HttpEntity entity = null;
    // The pattern to find modhash from HTML javascript area
    try {
      HttpGet httpget = new HttpGet(Constants.MODHASH_URL);
      HttpResponse response = client.execute(httpget);

      // For modhash, we don't care about the status, since the 404 page has the info we want.
      //    		status = response.getStatusLine().toString();
      //        	if (!status.contains("OK"))
      //        		throw new HttpException(status);

      entity = response.getEntity();

      BufferedReader in = new BufferedReader(new InputStreamReader(entity.getContent()));
      // modhash should appear within first 1200 chars
      char[] buffer = new char[1200];
      in.read(buffer, 0, 1200);
      in.close();
      String line = String.valueOf(buffer);
      entity.consumeContent();

      if (StringUtils.isEmpty(line)) {
        throw new HttpException(
            "No content returned from doUpdateModhash GET to " + Constants.MODHASH_URL);
      }
      if (line.contains("USER_REQUIRED")) {
        throw new Exception("User session error: USER_REQUIRED");
      }

      Matcher modhashMatcher = MODHASH_PATTERN.matcher(line);
      if (modhashMatcher.find()) {
        modhash = modhashMatcher.group(1);
        if (StringUtils.isEmpty(modhash)) {
          // Means user is not actually logged in.
          return null;
        }
      } else {
        throw new Exception("No modhash found at URL " + Constants.MODHASH_URL);
      }

      if (Constants.LOGGING) Common.logDLong(TAG, line);

      if (Constants.LOGGING) Log.d(TAG, "modhash: " + modhash);
      return modhash;

    } catch (Exception e) {
      if (entity != null) {
        try {
          entity.consumeContent();
        } catch (Exception e2) {
          if (Constants.LOGGING) Log.e(TAG, "entity.consumeContent()", e);
        }
      }
      if (Constants.LOGGING) Log.e(TAG, "doUpdateModhash()", e);
      return null;
    }
  }
/**
 * 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);
  }
}
  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();
  }
Example #4
0
  public static String checkResponseErrors(HttpResponse response, HttpEntity entity) {
    String status = response.getStatusLine().toString();
    String line;

    if (!status.contains("OK")) {
      return "HTTP error. Status = " + status;
    }

    try {
      BufferedReader in = new BufferedReader(new InputStreamReader(entity.getContent()));
      line = in.readLine();
      if (Constants.LOGGING) Common.logDLong(TAG, line);
      in.close();
    } catch (IOException e) {
      if (Constants.LOGGING) Log.e(TAG, "IOException", e);
      return "Error reading retrieved data.";
    }

    if (StringUtils.isEmpty(line)) {
      return "API returned empty data.";
    }
    if (line.contains("WRONG_PASSWORD")) {
      return "Wrong password.";
    }
    if (line.contains("USER_REQUIRED")) {
      // The modhash probably expired
      return "Login expired.";
    }
    if (line.contains("SUBREDDIT_NOEXIST")) {
      return "That subreddit does not exist.";
    }
    if (line.contains("SUBREDDIT_NOTALLOWED")) {
      return "You are not allowed to post to that subreddit.";
    }

    return null;
  }
Example #5
0
  public static String checkIDResponse(HttpResponse response, HttpEntity entity)
      throws CaptchaException, Exception {
    // Group 1: fullname. Group 2: kind. Group 3: id36.
    final Pattern NEW_ID_PATTERN = Pattern.compile("\"id\": \"((.+?)_(.+?))\"");
    // Group 1: whole error. Group 2: the time part
    final Pattern RATELIMIT_RETRY_PATTERN =
        Pattern.compile("(you are trying to submit too fast. try again in (.+?)\\.)");

    String status = response.getStatusLine().toString();
    String line;

    if (!status.contains("OK")) {
      throw new Exception("HTTP error. Status = " + status);
    }

    try {
      BufferedReader in = new BufferedReader(new InputStreamReader(entity.getContent()));
      line = in.readLine();
      if (Constants.LOGGING) Common.logDLong(TAG, line);
      in.close();
    } catch (IOException e) {
      if (Constants.LOGGING) Log.e(TAG, "IOException", e);
      throw new Exception("Error reading retrieved data.");
    }

    if (StringUtils.isEmpty(line)) {
      throw new Exception("API returned empty data.");
    }
    if (line.contains("WRONG_PASSWORD")) {
      throw new Exception("Wrong password.");
    }
    if (line.contains("USER_REQUIRED")) {
      // The modhash probably expired
      throw new Exception("Login expired.");
    }
    if (line.contains("SUBREDDIT_NOEXIST")) {
      throw new Exception("That subreddit does not exist.");
    }
    if (line.contains("SUBREDDIT_NOTALLOWED")) {
      throw new Exception("You are not allowed to post to that subreddit.");
    }

    String newId;
    Matcher idMatcher = NEW_ID_PATTERN.matcher(line);
    if (idMatcher.find()) {
      newId = idMatcher.group(3);
    } else {
      if (line.contains("RATELIMIT")) {
        // Try to find the # of minutes using regex
        Matcher rateMatcher = RATELIMIT_RETRY_PATTERN.matcher(line);
        if (rateMatcher.find()) throw new Exception(rateMatcher.group(1));
        else throw new Exception("you are trying to submit too fast. try again in a few minutes.");
      }
      if (line.contains("DELETED_LINK")) {
        throw new Exception("the link you are commenting on has been deleted");
      }
      if (line.contains("BAD_CAPTCHA")) {
        throw new CaptchaException("Bad CAPTCHA. Try again.");
      }
      // No id returned by reply POST.
      return null;
    }

    // Getting here means success.
    return newId;
  }
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;
  }
}