/** * Android ownCloud News * * @author David Luhmer * @copyright 2013 David Luhmer david-dev@live.de * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE * License as published by the Free Software Foundation; either * version 3 of the License, or any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU AFFERO GENERAL PUBLIC LICENSE for more details. * * You should have received a copy of the GNU Affero General Public * License along with this library. If not, see <http://www.gnu.org/licenses/>. * */ package de.luhmer.owncloudnewsreader; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.net.Uri; import android.os.Bundle; import android.preference.PreferenceManager; import android.support.customtabs.CustomTabsCallback; import android.support.customtabs.CustomTabsClient; import android.support.customtabs.CustomTabsIntent; import android.support.customtabs.CustomTabsServiceConnection; import android.support.customtabs.CustomTabsSession; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; import android.support.v4.app.FragmentStatePagerAdapter; import android.support.v4.content.ContextCompat; import android.support.v4.view.ViewPager; import android.support.v4.view.ViewPager.OnPageChangeListener; import android.support.v7.widget.Toolbar; import android.text.Html; import android.util.Log; import android.util.SparseArray; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; import android.view.ViewGroup; import android.widget.ProgressBar; import java.lang.ref.WeakReference; import java.util.HashSet; import java.util.List; import java.util.Set; import butterknife.Bind; import butterknife.ButterKnife; import de.luhmer.owncloudnewsreader.chrometabs.CustomTabActivityManager; import de.luhmer.owncloudnewsreader.database.DatabaseConnectionOrm; import de.luhmer.owncloudnewsreader.database.DatabaseConnectionOrm.SORT_DIRECTION; import de.luhmer.owncloudnewsreader.database.model.RssItem; import de.luhmer.owncloudnewsreader.helper.PostDelayHandler; import de.luhmer.owncloudnewsreader.helper.ThemeChooser; import de.luhmer.owncloudnewsreader.model.PodcastItem; import de.luhmer.owncloudnewsreader.model.TTSItem; import de.luhmer.owncloudnewsreader.widget.WidgetProvider; public class NewsDetailActivity extends PodcastFragmentActivity { private static final String TAG = NewsDetailActivity.class.getCanonicalName(); /** * The {@link android.support.v4.view.PagerAdapter} that will provide * fragments for each of the sections. We use a * {@link android.support.v4.app.FragmentPagerAdapter} derivative, which * will keep every loaded fragment in memory. If this becomes too memory * intensive, it may be best to switch to a * {@link android.support.v4.app.FragmentStatePagerAdapter}. */ SectionsPagerAdapter mSectionsPagerAdapter; @Bind(R.id.toolbar) Toolbar toolbar; @Bind(R.id.progressIndicator) ProgressBar progressIndicator; /** * The {@link ViewPager} that will host the section contents. */ public ViewPager mViewPager; private int currentPosition; private PostDelayHandler pDelayHandler; private MenuItem menuItem_PlayPodcast; private MenuItem menuItem_Starred; private MenuItem menuItem_Read; private DatabaseConnectionOrm dbConn; public List<RssItem> rssItems; private CustomTabsSession mCustomTabsSession; private CustomTabsClient mCustomTabsClient; private CustomTabsServiceConnection mCustomTabsConnection; private boolean mCustomTabsSupported; //public static final String DATABASE_IDS_OF_ITEMS = "DATABASE_IDS_OF_ITEMS"; @Override protected void onCreate(Bundle savedInstanceState) { ThemeChooser.chooseTheme(this); super.onCreate(savedInstanceState); setContentView(R.layout.activity_news_detail); ButterKnife.bind(this); if (toolbar != null) { setSupportActionBar(toolbar); } pDelayHandler = new PostDelayHandler(this); dbConn = new DatabaseConnectionOrm(this); Intent intent = getIntent(); //long subsciption_id = -1; //long folder_id = -1; int item_id = 0; //if(intent.hasExtra(NewsReaderDetailActivity.SUBSCRIPTION_ID)) // subsciption_id = intent.getExtras().getLong(NewsReaderDetailActivity.SUBSCRIPTION_ID); //if(intent.hasExtra(NewsReaderDetailActivity.FOLDER_ID)) // folder_id = intent.getExtras().getLong(NewsReaderDetailActivity.FOLDER_ID); if(intent.hasExtra(NewsReaderListActivity.ITEM_ID)) item_id = intent.getExtras().getInt(NewsReaderListActivity.ITEM_ID); if(intent.hasExtra(NewsReaderListActivity.TITEL)) getSupportActionBar().setTitle(intent.getExtras().getString(NewsReaderListActivity.TITEL)); //getActionBar().setTitle(intent.getExtras().getString(NewsReaderDetailActivity.TITEL)); getSupportActionBar().setDisplayHomeAsUpEnabled(true); //if(intent.hasExtra(DATABASE_IDS_OF_ITEMS)) // databaseItemIds = intent.getIntegerArrayListExtra(DATABASE_IDS_OF_ITEMS); rssItems = dbConn.getCurrentRssItemView(-1); //If the Activity gets started from the Widget, read the item id and get the selected index in the cursor. if(intent.hasExtra(WidgetProvider.RSS_ITEM_ID)) { long rss_item_id = intent.getExtras().getLong(WidgetProvider.RSS_ITEM_ID); for(RssItem rssItem : rssItems) { if(rss_item_id == rssItem.getId()) { getSupportActionBar().setTitle(rssItem.getTitle()); break; } else item_id++; } } // Create the adapter that will return a fragment for each of the three // primary sections of the app. mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager()); progressIndicator.setMax(mSectionsPagerAdapter.getCount()); // Set up the ViewPager with the sections adapter. mViewPager = (ViewPager) findViewById(R.id.pager); mViewPager.setAdapter(mSectionsPagerAdapter); try { mViewPager.setCurrentItem(item_id, true); PageChanged(item_id); } catch(Exception ex) { ex.printStackTrace(); } mViewPager.addOnPageChangeListener(onPageChangeListener); //Init ChromeCustomTabs mCustomTabsSupported = bindCustomTabsService(); } @Override protected void onDestroy() { super.onDestroy(); unbindCustomTabsService(); } private OnPageChangeListener onPageChangeListener = new OnPageChangeListener() { @Override public void onPageSelected(int pos) { PageChanged(pos); } @Override public void onPageScrolled(int arg0, float arg1, int arg2) { } @Override public void onPageScrollStateChanged(int arg0) { } }; public static SORT_DIRECTION getSortDirectionFromSettings(Context context) { SORT_DIRECTION sDirection = SORT_DIRECTION.asc; SharedPreferences mPrefs = PreferenceManager.getDefaultSharedPreferences(context); String sortDirection = mPrefs.getString(SettingsActivity.SP_SORT_ORDER, "1"); if (sortDirection.equals("1")) sDirection = SORT_DIRECTION.desc; return sDirection; } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { SharedPreferences mPrefs = PreferenceManager.getDefaultSharedPreferences(this); if(mPrefs.getBoolean(SettingsActivity.CB_NAVIGATE_WITH_VOLUME_BUTTONS_STRING, false)) { if ((keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) { if(currentPosition < rssItems.size()-1) { mViewPager.setCurrentItem(currentPosition + 1, true); } // capture event to avoid volume change at end of feed return true; } else if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP)) { if(currentPosition > 0) { mViewPager.setCurrentItem(currentPosition - 1, true); } // capture event to avoid volume change at beginning of feed return true; } } if(keyCode == KeyEvent.KEYCODE_BACK) { NewsDetailFragment ndf = getNewsDetailFragmentAtPosition(currentPosition);//(NewsDetailFragment) getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.pager + ":" + currentPosition); if(ndf != null && ndf.mWebView != null) { if (ndf.urls.size() > 1) { ndf.urls.remove(0); ndf.mWebView.loadUrl(ndf.urls.get(0)); return true; } else if(ndf.urls.size() == 1) { ndf.urls.remove(0); ndf.startLoadRssItemToWebViewTask(); Log.v(TAG, "Load rssitem to webview again"); return true; } } } return super.onKeyDown(keyCode, event); } @Override public boolean onKeyUp(int keyCode, KeyEvent event) { if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP) || (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) { // capture event to suppress android system sound return true; } return super.onKeyUp(keyCode, event); } private void PageChanged(int position) { StopVideoOnCurrentPage(); currentPosition = position; ResumeVideoPlayersOnCurrentPage(); progressIndicator.setProgress(position + 1); getSupportActionBar().setTitle(rssItems.get(position).getTitle()); if(!rssItems.get(position).getRead_temp()) { markItemAsReadUnread(rssItems.get(position), true); pDelayHandler.DelayTimer(); Log.v("PAGE CHANGED", "PAGE: " + position + " - IDFEED: " + rssItems.get(position).getId()); } else { //Only in else because the function markItemAsReas updates the ActionBar items as well UpdateActionBarIcons(); } } private NewsDetailFragment getNewsDetailFragmentAtPosition(int position) { if(mSectionsPagerAdapter.items.get(position) != null) return mSectionsPagerAdapter.items.get(position).get(); return null; } private void ResumeVideoPlayersOnCurrentPage() { NewsDetailFragment fragment = getNewsDetailFragmentAtPosition(currentPosition); if(fragment != null) // could be null if not instantiated yet fragment.ResumeCurrentPage(); } private void StopVideoOnCurrentPage() { NewsDetailFragment fragment = getNewsDetailFragmentAtPosition(currentPosition); if(fragment != null) // could be null if not instantiated yet fragment.PauseCurrentPage(); } public void UpdateActionBarIcons() { RssItem rssItem = rssItems.get(currentPosition); boolean isStarred = rssItem.getStarred_temp(); boolean isRead = rssItem.getRead_temp(); PodcastItem podcastItem = DatabaseConnectionOrm.ParsePodcastItemFromRssItem(this, rssItem); boolean podcastAvailable = !podcastItem.link.equals(""); if(menuItem_PlayPodcast != null) menuItem_PlayPodcast.setVisible(podcastAvailable); if(isStarred && menuItem_Starred != null) menuItem_Starred.setIcon(R.drawable.ic_action_star_dark); else if(menuItem_Starred != null) menuItem_Starred.setIcon(R.drawable.ic_action_star_border_dark); if(isRead && menuItem_Read != null) { menuItem_Read.setIcon(R.drawable.ic_check_box_white); menuItem_Read.setChecked(true); } else if(menuItem_Read != null) { menuItem_Read.setIcon(R.drawable.ic_check_box_outline_blank_white); menuItem_Read.setChecked(false); } } @Override public void onBackPressed() { if(!handlePodcastBackPressed()) super.onBackPressed(); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.news_detail, menu); menuItem_Starred = menu.findItem(R.id.action_starred); menuItem_Read = menu.findItem(R.id.action_read); menuItem_PlayPodcast = menu.findItem(R.id.action_playPodcast); SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getBaseContext()); Set<String> selections = preferences.getStringSet("sp_news_detail_actionbar_icons", new HashSet<String>()); String[] selected = selections.toArray(new String[] {}); for(String selection : selected) { switch(selection) { case "open_in_browser": menu.findItem(R.id.action_openInBrowser).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); break; case "share": menu.findItem(R.id.action_ShareItem).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); break; case "podcast": menu.findItem(R.id.action_playPodcast).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); break; //case "tts": // menu.findItem(R.id.action_tts).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); // break; } } UpdateActionBarIcons(); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { RssItem rssItem = rssItems.get(currentPosition); switch (item.getItemId()) { case android.R.id.home: onBackPressed(); return true; case R.id.action_read: markItemAsReadUnread(rssItem, !menuItem_Read.isChecked()); UpdateActionBarIcons(); pDelayHandler.DelayTimer(); break; case R.id.action_starred: Boolean curState = rssItem.getStarred_temp(); rssItem.setStarred_temp(!curState); dbConn.updateRssItem(rssItem); UpdateActionBarIcons(); pDelayHandler.DelayTimer(); break; case R.id.action_openInBrowser: NewsDetailFragment newsDetailFragment = getNewsDetailFragmentAtPosition(currentPosition); String link = newsDetailFragment.mWebView.getUrl(); if(link.equals("about:blank")) link = rssItem.getLink(); if(link.length() > 0) { if(isChromeDefaultBrowser() && mCustomTabsSupported) { mCustomTabsSession = getSession(); CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder(mCustomTabsSession); builder.setToolbarColor(ContextCompat.getColor(this, R.color.colorPrimaryDarkTheme)); builder.setShowTitle(true); builder.setStartAnimations(this, R.anim.slide_in_right, R.anim.slide_out_left); builder.setExitAnimations(this, R.anim.slide_in_left, R.anim.slide_out_right); builder.build().launchUrl(this, Uri.parse(link)); } else { Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(link)); startActivity(browserIntent); } } break; /* case R.id.action_sendSourceCode: String description = ""; if(cursor != null) { cursor.moveToFirst(); description = cursor.getString(cursor.getColumnIndex(DatabaseConnection.RSS_ITEM_BODY)); cursor.close(); } Intent i = new Intent(Intent.ACTION_SEND); i.setType("message/rfc822"); i.putExtra(Intent.EXTRA_EMAIL , new String[]{"david-dev@live.de"}); i.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.email_sourceCode)); //i.putExtra(Intent.EXTRA_TEXT , rssFiles.get(currentPosition).getDescription()); i.putExtra(Intent.EXTRA_TEXT , description); try { startActivity(Intent.createChooser(i, getString(R.string.email_sendMail))); } catch (android.content.ActivityNotFoundException ex) { Toast.makeText(NewsDetailActivity.this, "There are no email clients installed.", Toast.LENGTH_SHORT).show(); } break; */ case R.id.action_playPodcast: openPodcast(rssItem); break; case R.id.action_tts: TTSItem ttsItem = new TTSItem(rssItem.getId(), rssItem.getTitle(), rssItem.getTitle() + "\n\n " + Html.fromHtml(rssItem.getBody()).toString(), rssItem.getFeed().getFaviconUrl()); openMediaItem(ttsItem); break; case R.id.action_ShareItem: String title = rssItem.getTitle(); String content = rssItem.getLink(); NewsDetailFragment fragment = getNewsDetailFragmentAtPosition(currentPosition); if(fragment != null) { // could be null if not instantiated yet if(!fragment.mWebView.getUrl().equals("about:blank") && !fragment.mWebView.getUrl().trim().equals("")) { content = fragment.mWebView.getUrl(); title = fragment.mWebView.getTitle(); } } Intent share = new Intent(Intent.ACTION_SEND); share.setType("text/plain"); //share.putExtra(Intent.EXTRA_SUBJECT, rssFiles.get(currentPosition).getTitle()); //share.putExtra(Intent.EXTRA_TEXT, rssFiles.get(currentPosition).getLink()); share.putExtra(Intent.EXTRA_SUBJECT, title); share.putExtra(Intent.EXTRA_TEXT, content); startActivity(Intent.createChooser(share, "Share Item")); break; } return super.onOptionsItemSelected(item); } private boolean isChromeDefaultBrowser() { Intent browserIntent = new Intent("android.intent.action.VIEW", Uri.parse("http://")); ResolveInfo resolveInfo = getPackageManager().resolveActivity(browserIntent, PackageManager.MATCH_DEFAULT_ONLY); Log.v(TAG, "Default Browser is: " + resolveInfo.loadLabel(getPackageManager()).toString()); return (resolveInfo.loadLabel(getPackageManager()).toString().contains("Chrome")); } private boolean bindCustomTabsService() { if (mCustomTabsClient != null) return true; String packageName = CustomTabActivityManager.getInstance().getPackageNameToUse(this); if (packageName == null) return false; mCustomTabsConnection = new CustomTabsServiceConnection() { @Override public void onCustomTabsServiceConnected(ComponentName name, CustomTabsClient client) { mCustomTabsClient = client; } @Override public void onServiceDisconnected(ComponentName name) { mCustomTabsClient = null; } }; return CustomTabsClient.bindCustomTabsService(this, packageName, mCustomTabsConnection); } private void unbindCustomTabsService() { if (mCustomTabsConnection == null) return; unbindService(mCustomTabsConnection); mCustomTabsConnection = null; mCustomTabsClient = null; mCustomTabsSession = null; } private CustomTabsSession getSession() { if (mCustomTabsClient == null) { mCustomTabsSession = null; } else if (mCustomTabsSession == null) { mCustomTabsSession = mCustomTabsClient.newSession(new CustomTabsCallback()); } return mCustomTabsSession; } private void markItemAsReadUnread(RssItem item, boolean read) { item.setRead_temp(read); dbConn.updateRssItem(item); UpdateActionBarIcons(); } @Override public void finish() { Intent intent = new Intent(); intent.putExtra("POS", mViewPager.getCurrentItem()); setResult(RESULT_OK, intent); super.finish(); } /** * A {@link FragmentPagerAdapter} that returns a fragment corresponding to * one of the sections/tabs/pages. */ //public class SectionsPagerAdapter extends FragmentPagerAdapter { public class SectionsPagerAdapter extends FragmentStatePagerAdapter { SparseArray<WeakReference<NewsDetailFragment>> items = new SparseArray<>(); public SectionsPagerAdapter(FragmentManager fm) { super(fm); if(fm.getFragments() != null) { for (Fragment fragment : fm.getFragments()) { if (fragment instanceof NewsDetailFragment) { int id = ((NewsDetailFragment) fragment).getSectionNumber(); items.put(id, new WeakReference<>((NewsDetailFragment) fragment)); } } } } @Override public Fragment getItem(int position) { NewsDetailFragment fragment = null; if(items.get(position) != null) { fragment = items.get(position).get(); } if(fragment == null) { fragment = new NewsDetailFragment(); Bundle args = new Bundle(); args.putInt(NewsDetailFragment.ARG_SECTION_NUMBER, position); fragment.setArguments(args); items.put(position, new WeakReference<>(fragment)); } return fragment; } @Override public void destroyItem(ViewGroup container, int position, Object object) { items.remove(position); super.destroyItem(container, position, object); } @Override public int getCount() { //return cursor.getCount(); return rssItems.size(); } @Override public CharSequence getPageTitle(int position) { return null; } } }