@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); CookieSyncManager.createInstance(getApplicationContext()); mSettings.loadRedditPreferences(this, mClient); setRequestedOrientation(mSettings.getRotation()); setTheme(mSettings.getTheme()); requestWindowFeature(Window.FEATURE_PROGRESS); requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); setContentView(R.layout.saved_comments); sdbh = new SavedDBHandler(this); savedContent = sdbh.getSavedContent(mSettings.getUsername()); Adapter lAdapter = new Adapter(this, R.layout.saved_comments_item, savedContent); listview = (ListView) findViewById(R.id.savedcommentslv); listview.setAdapter(lAdapter); listview.setOnItemClickListener( new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { currentSavedContent = savedContent.get(position); showDialog(Constants.DIALOG_SAVED_COMMENTS); } }); }
private void parseCommentsJSON(InputStream in) throws IOException, JsonParseException { int insertedCommentIndex; String genericListingError = "Not a comments listing"; try { Listing[] listings = mObjectMapper.readValue(in, Listing[].class); // listings[0] is a thread Listing for the OP. // process same as a thread listing more or less Assert.assertEquals(Constants.JSON_LISTING, listings[0].getKind(), genericListingError); // Save modhash, ignore "after" and "before" which are meaningless in this context (and // probably null) ListingData threadListingData = listings[0].getData(); if (StringUtils.isEmpty(threadListingData.getModhash())) mSettings.setModhash(null); else mSettings.setModhash(threadListingData.getModhash()); if (Constants.LOGGING) Log.d(TAG, "Successfully got OP listing[0]: modhash " + mSettings.getModhash()); ThingListing threadThingListing = threadListingData.getChildren()[0]; Assert.assertEquals(Constants.THREAD_KIND, threadThingListing.getKind(), genericListingError); // listings[1] is a comment Listing for the comments ListingData commentListingData = listings[1].getData(); if (isInsertingEntireThread()) { parseOP( threadThingListing.getData(), commentListingData.getChildren().length == 0 ? null : commentListingData.getChildren()[0].getData()); insertedCommentIndex = 0; // we just inserted the OP into position 0 if (!StringUtils.isEmpty(mJumpToCommentId) && mJumpToCommentContext > 0) { // If viewing context, then the 'first' item will be the context-viewing warning. insertedCommentIndex++; } // at this point we've started displaying comments, so disable the loading screen disableLoadingScreenKeepProgress(); } else { insertedCommentIndex = mPositionOffset - 1; // -1 because we +1 for the first comment } // Go through the children and get the ThingInfos for (ThingListing commentThingListing : commentListingData.getChildren()) { // insert the comment and its replies, prefix traversal order insertedCommentIndex = insertNestedComment(commentThingListing, 0, insertedCommentIndex + 1); } mProcessCommentsTask.mergeLowPriorityListToMainList(); } catch (Exception ex) { if (Constants.LOGGING) Log.e(TAG, "parseCommentsJSON", ex); } }
@Override public boolean onPrepareOptionsMenu(Menu menu) { super.onPrepareOptionsMenu(menu); if (mSettings.isLoggedIn()) { menu.findItem(R.id.user_profile_menu_id).setVisible(true); menu.findItem(R.id.user_profile_menu_id) .setTitle( String.format( getResources().getString(R.string.user_profile), mSettings.getUsername())); } else { menu.findItem(R.id.user_profile_menu_id).setVisible(false); } MenuItem src, dest; src = Util.isLightTheme(mSettings.getTheme()) ? menu.findItem(R.id.dark_menu_id) : menu.findItem(R.id.light_menu_id); dest = menu.findItem(R.id.light_dark_menu_id); dest.setTitle(src.getTitle()); return true; }
@Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.user_profile_menu_id: Intent profileIntent = new Intent(getApplicationContext(), ProfileActivity.class); startActivity(profileIntent); break; case R.id.preferences_menu_id: Intent prefsIntent = new Intent(getApplicationContext(), RedditPreferencesPage.class); startActivity(prefsIntent); break; case R.id.light_dark_menu_id: mSettings.setTheme(Util.getInvertedTheme(mSettings.getTheme())); relaunchActivity(); break; case android.R.id.home: Common.goHome(this); break; default: throw new IllegalArgumentException("Unexpected action value " + item.getItemId()); } return true; }
@Override protected void onResume() { super.onResume(); int prevTheme = mSettings.getTheme(); mSettings.loadRedditPreferences(this, mClient); if (mSettings.getTheme() != prevTheme) { relaunchActivity(); } else { CookieSyncManager.getInstance().startSync(); setRequestedOrientation(mSettings.getRotation()); if (mSettings.isLoggedIn()) new PeekEnvelopeTask(this, mClient, mSettings.getMailNotificationStyle()).execute(); } }
/** * Recursive method to insert comment tree into the mCommentsList, with proper list order and * indentation */ int insertNestedComment( ThingListing commentThingListing, int indentLevel, int insertedCommentIndex) { ThingInfo ci = commentThingListing.getData(); // First test for moderator distinguished if (Constants.DISTINGUISHED_MODERATOR.equalsIgnoreCase(ci.getDistinguished())) { SpannableString distSS = new SpannableString(ci.getAuthor() + " [M]"); distSS.setSpan( Util.getModeratorSpan(mActivity.getApplicationContext()), 0, distSS.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); ci.setSSAuthor(distSS); } else if (Constants.DISTINGUISHED_ADMIN.equalsIgnoreCase(ci.getDistinguished())) { SpannableString distSS = new SpannableString(ci.getAuthor() + " [A]"); distSS.setSpan( Util.getAdminSpan(mActivity.getApplicationContext()), 0, distSS.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); ci.setSSAuthor(distSS); } else if (mOpThingInfo != null && mOpThingInfo.getAuthor().equalsIgnoreCase(ci.getAuthor())) { if (m_OPSpan == null) { m_OPSpan = new SpannableString(mOpThingInfo.getAuthor() + " [S]"); m_OPSpan.setSpan( Util.getOPSpan(mActivity.getApplicationContext(), mSettings.getTheme()), 0, m_OPSpan.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } ci.setSSAuthor(m_OPSpan); } // Add comment to deferred append/replace list if (isInsertingEntireThread()) deferCommentAppend(ci); else deferCommentReplacement(ci); // Keep track of jump target if (isHasJumpTarget()) { if (!isFoundJumpTargetComment() && mJumpToCommentId.equals(ci.getId())) processJumpTarget(ci, insertedCommentIndex); } if (isHasJumpTarget()) { // if we have found the jump target, then we did the messy stuff already. just append to main // processing list. if (isFoundJumpTargetComment()) { mProcessCommentsTask.addDeferred(new DeferredCommentProcessing(ci, insertedCommentIndex)); } // try to handle the context search, if we want context else if (mJumpToCommentContext > 0) { // any comment could be in the context; we don't know yet. so append to the high-priority // "context" list mProcessCommentsTask.addDeferredHighPriority( new DeferredCommentProcessing(ci, insertedCommentIndex)); // we push overflow onto the low priority list, since overflow will end up above the jump // target, off the top of the screen. // TODO don't use LinkedList.size() mProcessCommentsTask.moveHighPriorityOverflowToLowPriority(mJumpToCommentContext); } // if no context search, then push comments to low priority list until we find the jump target // comment else { mProcessCommentsTask.addDeferredLowPriority( new DeferredCommentProcessing(ci, insertedCommentIndex)); } } // if there is no jump target, there's just a single deferred-processing list to worry about. else { mProcessCommentsTask.addDeferred(new DeferredCommentProcessing(ci, insertedCommentIndex)); } // Formatting that applies to all items, both real comments and "more" entries ci.setIndent(mIndentation + indentLevel); // Handle "more" entry if (Constants.MORE_KIND.equals(commentThingListing.getKind())) { ci.setLoadMoreCommentsPlaceholder(true); if (Constants.LOGGING) Log.v(TAG, "new more position at " + (insertedCommentIndex)); return insertedCommentIndex; } // Regular comment // Skip things that are not comments, which shouldn't happen if (!Constants.COMMENT_KIND.equals(commentThingListing.getKind())) { if (Constants.LOGGING) Log.e( TAG, "comment whose kind is \"" + commentThingListing.getKind() + "\" (expected " + Constants.COMMENT_KIND + ")"); return insertedCommentIndex; } // handle the replies Listing repliesListing = ci.getReplies(); if (repliesListing == null) return insertedCommentIndex; ListingData repliesListingData = repliesListing.getData(); if (repliesListingData == null) return insertedCommentIndex; ThingListing[] replyThingListings = repliesListingData.getChildren(); if (replyThingListings == null) return insertedCommentIndex; for (ThingListing replyThingListing : replyThingListings) { insertedCommentIndex = insertNestedComment(replyThingListing, indentLevel + 1, insertedCommentIndex + 1); } return insertedCommentIndex; }
// XXX: maxComments is unused for now public Boolean doInBackground(Integer... maxComments) { HttpEntity entity = null; try { StringBuilder sb = new StringBuilder(Constants.REDDIT_BASE_URL); if (mSubreddit != null) { sb.append("/r/").append(mSubreddit.trim()); } if (mMoreChildrenId != null && mMoreChildrenId.length() > 0) { sb.append("/comments/") .append(mThreadId) .append("/z/") .append(mMoreChildrenId) .append("/.json?") .append(mSettings.getCommentsSortByUrl()); // Loading more with context makes no sense } else { sb.append("/comments/") .append(mThreadId) .append("/.json?") .append(mSettings.getCommentsSortByUrl()); if (!StringUtils.isEmpty(mJumpToCommentId) && mJumpToCommentContext > 0) { sb.append("&comment=") .append(mJumpToCommentId) .append("&context=") .append(mJumpToCommentContext); } } String url = sb.toString(); if (Constants.LOGGING) Log.d(TAG, "Loading comments from URL: " + url); InputStream in = null; boolean currentlyUsingCache = false; if (Constants.USE_COMMENTS_CACHE) { try { if (CacheInfo.checkFreshThreadCache(mActivity.getApplicationContext()) && url.equals(CacheInfo.getCachedThreadUrl(mActivity.getApplicationContext()))) { in = mActivity.openFileInput(Constants.FILENAME_THREAD_CACHE); mContentLength = mActivity.getFileStreamPath(Constants.FILENAME_THREAD_CACHE).length(); currentlyUsingCache = true; if (Constants.LOGGING) Log.d(TAG, "Using cached thread JSON, length=" + mContentLength); } } catch (Exception cacheEx) { if (Constants.LOGGING) Log.w(TAG, "skip cache", cacheEx); } } // If we couldn't use the cache, then do HTTP request if (!currentlyUsingCache) { HttpGet request = new HttpGet(url); HttpResponse response = mClient.execute(request); // Read the header to get Content-Length since entity.getContentLength() returns -1 Header contentLengthHeader = response.getFirstHeader("Content-Length"); if (contentLengthHeader != null) { mContentLength = Long.valueOf(contentLengthHeader.getValue()); if (Constants.LOGGING) Log.d(TAG, "Content length: " + mContentLength); } else { mContentLength = -1; if (Constants.LOGGING) Log.d(TAG, "Content length: UNAVAILABLE"); } entity = response.getEntity(); in = entity.getContent(); if (Constants.USE_COMMENTS_CACHE) { in = CacheInfo.writeThenRead( mActivity.getApplicationContext(), in, Constants.FILENAME_THREAD_CACHE); try { CacheInfo.setCachedThreadUrl(mActivity.getApplicationContext(), url); } catch (IOException e) { if (Constants.LOGGING) Log.e(TAG, "error on setCachedThreadId", e); } } } // setup a special InputStream to report progress ProgressInputStream pin = new ProgressInputStream(in, mContentLength); pin.addPropertyChangeListener(this); parseCommentsJSON(pin); if (Constants.LOGGING) Log.d(TAG, "parseCommentsJSON completed"); pin.close(); in.close(); return true; } catch (Exception e) { if (Constants.LOGGING) Log.e(TAG, "DownloadCommentsTask", e); } finally { if (entity != null) { try { entity.consumeContent(); } catch (Exception e2) { if (Constants.LOGGING) Log.e(TAG, "entity.consumeContent()", e2); } } } return false; }
@Override protected void onPause() { super.onPause(); CookieSyncManager.getInstance().stopSync(); mSettings.saveRedditPreferences(this); }