Merge branch 'develop'

This commit is contained in:
daniel oeh 2015-02-01 14:12:40 +01:00
commit c9e8af52c2
125 changed files with 3512 additions and 1780 deletions

View File

@ -1,6 +1,14 @@
Change Log
==========
Version 1.0
-----------
* The queue can now be sorted
* Added option to delete episode after playback
* Fixed a bug that caused chapters to be displayed multiple times
* Several other improvements and bugfixes
Version 0.9.9.6
---------------
* Fixed problems related to variable playback speed plugins

View File

@ -1,3 +1,7 @@
```
Please note that AntennaPod is no longer maintained until further notice.
```
How to report a bug
-------------------
- Use the search function to see if someone else has already reported the bug. If an issue has already been opened, see if you can add any useful information to it.

View File

@ -9,6 +9,7 @@ wseemann
hzulla
andrewgaul
peschmae0
TomHennen
Translations:

View File

@ -1,5 +1,10 @@
# AntennaPod
```
Please note that AntennaPod will not receive any future updates until further notice.
```
This is the official repository of AntennaPod, a podcast manager for Android.
[![Download from Google Play](http://www.android.com/images/brand/android_app_on_play_large.png "Download from Google Play")](https://play.google.com/store/apps/details?id=de.danoeh.antennapod)

View File

@ -4,12 +4,10 @@ repositories {
mavenCentral()
}
dependencies {
compile 'com.android.support:support-v4:21.0.2'
compile 'com.android.support:appcompat-v7:21.0.2'
compile 'com.android.support:support-v4:21.0.3'
compile 'com.android.support:appcompat-v7:21.0.3'
compile 'org.apache.commons:commons-lang3:3.3.2'
compile('org.shredzone.flattr4j:flattr4j-core:2.11') {
exclude group: 'org.apache.httpcomponents', module: 'httpcore'
exclude group: 'org.apache.httpcomponents', module: 'httpclient'
compile('org.shredzone.flattr4j:flattr4j-core:2.12') {
exclude group: 'org.json', module: 'json'
}
compile 'commons-io:commons-io:2.4'
@ -17,15 +15,15 @@ dependencies {
compile 'com.jayway.android.robotium:robotium-solo:5.2.1'
compile 'org.jsoup:jsoup:1.7.3'
compile 'com.squareup.picasso:picasso:2.4.0'
compile 'com.squareup.okhttp:okhttp:2.1.0'
compile 'com.squareup.okhttp:okhttp-urlconnection:2.1.0'
compile 'com.squareup.okio:okio:1.0.1'
compile 'com.squareup.okhttp:okhttp:2.2.0'
compile 'com.squareup.okhttp:okhttp-urlconnection:2.2.0'
compile 'com.squareup.okio:okio:1.2.0'
compile project(':core')
}
android {
compileSdkVersion 21
buildToolsVersion "21.1.1"
buildToolsVersion "21.1.2"
defaultConfig {
minSdkVersion 10

View File

@ -41,6 +41,7 @@ public class PlaybackTest extends ActivityInstrumentationTestCase2<MainActivity>
adapter.open();
adapter.close();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getInstrumentation().getTargetContext());
prefs.edit().putBoolean(UserPreferences.PREF_UNPAUSE_ON_HEADSET_RECONNECT, false).commit();
prefs.edit().putBoolean(UserPreferences.PREF_PAUSE_ON_HEADSET_DISCONNECT, false).commit();
}

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="de.danoeh.antennapod"
android:versionCode="43"
android:versionName="0.9.9.6">
android:versionCode="44"
android:versionName="1.0">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
@ -304,6 +304,13 @@
</intent-filter>
</receiver>
<receiver android:name=".receiver.PowerConnectionReceiver">
<intent-filter>
<action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
</intent-filter>
</receiver>
<receiver android:name=".receiver.SPAReceiver">
<intent-filter>
<action android:name="de.danoeh.antennapdsp.intent.SP_APPS_QUERY_FEEDS_RESPONSE"/>

View File

@ -41,7 +41,7 @@
<div id="header" align="center">
<img src="logo.png" alt="Logo" width="100px" height="100px"/>
<p>AntennaPod, Version 0.9.9.6</p>
<p>AntennaPod, Version 1.0</p>
<p>Copyright © 2014 Daniel Oeh</p>

View File

@ -11,6 +11,7 @@ import android.support.v4.app.FragmentTransaction;
import android.support.v4.app.ListFragment;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
@ -21,7 +22,6 @@ import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView.ScaleType;
import android.widget.ListView;
import android.widget.TextView;
@ -49,6 +49,7 @@ import de.danoeh.antennapod.fragment.CoverFragment;
import de.danoeh.antennapod.fragment.ItemDescriptionFragment;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
import de.danoeh.antennapod.menuhandler.NavDrawerActivity;
import de.danoeh.antennapod.preferences.PreferenceController;
/**
* Activity for playing audio files.
@ -59,6 +60,7 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
private static final int POS_DESCR = 1;
private static final int POS_CHAPTERS = 2;
private static final int NUM_CONTENT_FRAGMENTS = 3;
private static final int POS_NONE = -1;
final String TAG = "AudioplayerActivity";
private static final String PREFS = "AudioPlayerActivityPreferences";
@ -68,6 +70,7 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
private DrawerLayout drawerLayout;
private NavListAdapter navAdapter;
private ListView navList;
private View navDrawer;
private ActionBarDrawerToggle drawerToggle;
private Fragment[] detachedFragments;
@ -78,6 +81,7 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
private Fragment currentlyShownFragment;
private int currentlyShownPosition = -1;
private int lastShownPosition = POS_NONE;
/**
* Used if onResume was called without loadMediaInfo.
*/
@ -85,8 +89,8 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
private TextView txtvTitle;
private Button butPlaybackSpeed;
private ImageButton butNavLeft;
private ImageButton butNavRight;
private ImageButton butNavChaptersShownotes;
private ImageButton butShowCover;
private void resetFragmentView() {
FragmentTransaction fT = getSupportFragmentManager().beginTransaction();
@ -138,10 +142,14 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
}
@Override
protected void chooseTheme() {
setTheme(UserPreferences.getNoTitleTheme());
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getSupportActionBar().setDisplayShowTitleEnabled(false);
detachedFragments = new Fragment[NUM_CONTENT_FRAGMENTS];
}
@ -320,24 +328,32 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
chapterFragment = new ListFragment() {
@Override
public void onListItemClick(ListView l, View v,
int position, long id) {
super.onListItemClick(l, v, position, id);
Chapter chapter = (Chapter) this
.getListAdapter().getItem(position);
controller.seekToChapter(chapter);
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
// add padding
final ListView lv = getListView();
lv.setClipToPadding(false);
final int vertPadding = getResources().getDimensionPixelSize(R.dimen.list_vertical_padding);
lv.setPadding(0, vertPadding, 0, vertPadding);
}
};
chapterFragment.setListAdapter(new ChapterListAdapter(
AudioplayerActivity.this, 0, media
.getChapters(), media
.getChapters(), media, new ChapterListAdapter.Callback() {
@Override
public void onPlayChapterButtonClicked(int position) {
Chapter chapter = (Chapter)
chapterFragment.getListAdapter().getItem(position);
controller.seekToChapter(chapter);
}
}
));
}
currentlyShownFragment = chapterFragment;
break;
}
if (currentlyShownFragment != null) {
lastShownPosition = currentlyShownPosition;
currentlyShownPosition = pos;
if (detachedFragments[pos] != null) {
if (BuildConfig.DEBUG)
@ -355,78 +371,70 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
}
}
/**
* Switches to the fragment that was displayed before the current one or the description fragment
* if no fragment was previously displayed.
*/
public void switchToLastFragment() {
if (lastShownPosition != POS_NONE) {
switchToFragment(lastShownPosition);
} else {
switchToFragment(POS_DESCR);
}
}
private void updateNavButtonDrawable() {
final int[] buttonTexts = new int[]{R.string.show_shownotes_label,
R.string.show_chapters_label, R.string.show_cover_label};
R.string.show_chapters_label};
final TypedArray drawables = obtainStyledAttributes(new int[]{
R.attr.navigation_shownotes, R.attr.navigation_chapters});
final Playable media = controller.getMedia();
if (butNavLeft != null && butNavRight != null && media != null) {
butNavRight.setTag(R.id.imageloader_key, null);
butNavLeft.setTag(R.id.imageloader_key, null);
if (butNavChaptersShownotes != null && butShowCover != null && media != null) {
butNavChaptersShownotes.setTag(R.id.imageloader_key, null);
setNavButtonVisibility();
switch (currentlyShownPosition) {
case POS_COVER:
butNavLeft.setScaleType(ScaleType.CENTER);
butNavLeft.setImageDrawable(drawables.getDrawable(0));
butNavLeft.setContentDescription(getString(buttonTexts[0]));
butNavRight.setImageDrawable(drawables.getDrawable(1));
butNavRight.setContentDescription(getString(buttonTexts[1]));
butShowCover.setVisibility(View.GONE);
if (lastShownPosition == POS_CHAPTERS) {
butNavChaptersShownotes.setImageDrawable(drawables.getDrawable(1));
butNavChaptersShownotes.setContentDescription(getString(buttonTexts[1]));
} else {
butNavChaptersShownotes.setImageDrawable(drawables.getDrawable(0));
butNavChaptersShownotes.setContentDescription(getString(buttonTexts[0]));
}
break;
case POS_DESCR:
butNavLeft.setScaleType(ScaleType.CENTER_CROP);
butNavLeft.post(new Runnable() {
@Override
public void run() {
Picasso.with(AudioplayerActivity.this)
.load(media.getImageUri())
.fit()
.into(butNavLeft);
}
});
butNavLeft.setContentDescription(getString(buttonTexts[2]));
butNavRight.setImageDrawable(drawables.getDrawable(1));
butNavRight.setContentDescription(getString(buttonTexts[1]));
butShowCover.setVisibility(View.VISIBLE);
butNavChaptersShownotes.setImageDrawable(drawables.getDrawable(1));
butNavChaptersShownotes.setContentDescription(getString(buttonTexts[1]));
break;
case POS_CHAPTERS:
butNavLeft.setScaleType(ScaleType.CENTER_CROP);
butNavLeft.post(new Runnable() {
@Override
public void run() {
Picasso.with(AudioplayerActivity.this)
.load(media.getImageUri())
.fit()
.into(butNavLeft);
}
});
butNavLeft.setContentDescription(getString(buttonTexts[2]));
butNavRight.setImageDrawable(drawables.getDrawable(0));
butNavRight.setContentDescription(getString(buttonTexts[0]));
butShowCover.setVisibility(View.VISIBLE);
butNavChaptersShownotes.setImageDrawable(drawables.getDrawable(0));
butNavChaptersShownotes.setContentDescription(getString(buttonTexts[0]));
break;
}
}
drawables.recycle();
}
@Override
protected void setupGUI() {
super.setupGUI();
resetFragmentView();
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
navList = (ListView) findViewById(R.id.nav_list);
txtvTitle = (TextView) findViewById(R.id.txtvTitle);
butNavLeft = (ImageButton) findViewById(R.id.butNavLeft);
butNavRight = (ImageButton) findViewById(R.id.butNavRight);
navDrawer = findViewById(R.id.nav_layout);
butPlaybackSpeed = (Button) findViewById(R.id.butPlaybackSpeed);
butNavChaptersShownotes = (ImageButton) findViewById(R.id.butNavChaptersShownotes);
butShowCover = (ImageButton) findViewById(R.id.butCover);
txtvTitle = (TextView) findViewById(R.id.txtvTitle);
drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, R.string.drawer_open, R.string.drawer_close) {
CharSequence currentTitle = getSupportActionBar().getTitle();
@ -463,38 +471,39 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
intent.putExtra(MainActivity.EXTRA_NAV_INDEX, relPos);
startActivity(intent);
}
drawerLayout.closeDrawer(navList);
drawerLayout.closeDrawer(navDrawer);
}
});
drawerToggle.syncState();
butNavLeft.setOnClickListener(new OnClickListener() {
findViewById(R.id.nav_settings).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (currentlyShownFragment == null
|| currentlyShownPosition == POS_DESCR) {
switchToFragment(POS_COVER);
} else if (currentlyShownPosition == POS_COVER) {
switchToFragment(POS_DESCR);
} else if (currentlyShownPosition == POS_CHAPTERS) {
switchToFragment(POS_COVER);
}
drawerLayout.closeDrawer(navDrawer);
startActivity(new Intent(AudioplayerActivity.this, PreferenceController.getPreferenceActivity()));
}
});
butNavRight.setOnClickListener(new OnClickListener() {
butNavChaptersShownotes.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (currentlyShownPosition == POS_CHAPTERS) {
switchToFragment(POS_DESCR);
} else {
} else if (currentlyShownPosition == POS_DESCR) {
switchToFragment(POS_CHAPTERS);
} else if (currentlyShownPosition == POS_COVER) {
switchToLastFragment();
}
}
});
butShowCover.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
switchToFragment(POS_COVER);
}
});
butPlaybackSpeed.setOnClickListener(new OnClickListener() {
@Override
@ -539,6 +548,22 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
});
}
private void setNavButtonVisibility() {
if (butNavChaptersShownotes != null) {
if (controller != null) {
Playable media = controller.getMedia();
if (media != null) {
if (media.getChapters() != null || currentlyShownPosition == POS_COVER) {
butNavChaptersShownotes.setVisibility(View.VISIBLE);
return;
}
}
}
butNavChaptersShownotes.setVisibility(View.GONE);
}
}
@Override
protected void onPlaybackSpeedChange() {
super.onPlaybackSpeedChange();
@ -567,12 +592,13 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
return false;
}
txtvTitle.setText(media.getEpisodeTitle());
if (media.getChapters() != null) {
butNavRight.setVisibility(View.VISIBLE);
} else {
butNavRight.setVisibility(View.INVISIBLE);
}
getSupportActionBar().setTitle("");
Picasso.with(this)
.load(media.getImageUri())
.fit()
.into(butShowCover);
setNavButtonVisibility();
if (currentlyShownPosition == -1) {
if (!restoreFromPreferences()) {
@ -632,7 +658,7 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
@Override
public boolean isDrawerOpen() {
return drawerLayout != null && navList != null && drawerLayout.isDrawerOpen(navList);
return drawerLayout != null && navDrawer != null && drawerLayout.isDrawerOpen(navDrawer);
}
@Override

View File

@ -14,9 +14,8 @@ import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
@ -62,15 +61,22 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
public static final String EXTRA_NAV_TYPE = "nav_type";
public static final String EXTRA_FRAGMENT_ARGS = "fragment_args";
public static final String SAVE_BACKSTACK_COUNT = "backstackCount";
public static final String SAVE_SELECTED_NAV_INDEX = "selectedNavIndex";
public static final String SAVE_TITLE = "title";
public static final int POS_NEW = 0,
POS_QUEUE = 1,
POS_DOWNLOADS = 2,
POS_HISTORY = 3,
POS_ADD = 4;
private Toolbar toolbar;
private ExternalPlayerFragment externalPlayerFragment;
private DrawerLayout drawerLayout;
private View navDrawer;
private ListView navList;
private NavListAdapter navAdapter;
@ -82,17 +88,22 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
@Override
public void onCreate(Bundle savedInstanceState) {
setTheme(UserPreferences.getTheme());
setTheme(UserPreferences.getNoTitleTheme());
super.onCreate(savedInstanceState);
StorageUtils.checkStorageAvailability(this);
setContentView(R.layout.main);
setVolumeControlStream(AudioManager.STREAM_MUSIC);
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setElevation(3.0f);
drawerTitle = currentTitle = getTitle();
drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
navList = (ListView) findViewById(R.id.nav_list);
navDrawer = findViewById(R.id.nav_layout);
Log.i(TAG, "");
drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, R.string.drawer_open, R.string.drawer_close) {
@Override
public void onDrawerOpened(View drawerView) {
@ -111,8 +122,21 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
}
};
if (savedInstanceState != null) {
int backstackCount = savedInstanceState.getInt(SAVE_BACKSTACK_COUNT, 0);
drawerToggle.setDrawerIndicatorEnabled(backstackCount == 0);
}
drawerLayout.setDrawerListener(drawerToggle);
FragmentManager fm = getSupportFragmentManager();
final FragmentManager fm = getSupportFragmentManager();
fm.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
@Override
public void onBackStackChanged() {
drawerToggle.setDrawerIndicatorEnabled(fm.getBackStackEntryCount() == 0);
}
});
FragmentTransaction transaction = fm.beginTransaction();
@ -120,7 +144,7 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
if (mainFragment != null) {
transaction.replace(R.id.main_view, mainFragment);
} else {
loadFragment(NavListAdapter.VIEW_TYPE_NAV, POS_NEW, null);
loadFragment(NavListAdapter.VIEW_TYPE_NAV, POS_QUEUE, null);
}
externalPlayerFragment = new ExternalPlayerFragment();
@ -134,6 +158,14 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
navList.setAdapter(navAdapter);
navList.setOnItemClickListener(navListClickListener);
findViewById(R.id.nav_settings).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
drawerLayout.closeDrawer(navDrawer);
startActivity(new Intent(MainActivity.this, PreferenceController.getPreferenceActivity()));
}
});
checkFirstLaunch();
}
@ -143,7 +175,7 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
drawerLayout.openDrawer(navList);
drawerLayout.openDrawer(navDrawer);
}
}, 1500);
@ -158,7 +190,7 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
}
public boolean isDrawerOpen() {
return drawerLayout != null && navList != null && drawerLayout.isDrawerOpen(navList);
return drawerLayout != null && navDrawer != null && drawerLayout.isDrawerOpen(navDrawer);
}
public List<Feed> getFeeds() {
@ -241,6 +273,14 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
.commit();
}
public void dismissChildFragment() {
getSupportFragmentManager().popBackStack();
}
public Toolbar getToolbar() {
return toolbar;
}
private AdapterView.OnItemClickListener navListClickListener = new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
@ -251,7 +291,7 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
selectedNavListIndex = position;
navAdapter.notifyDataSetChanged();
}
drawerLayout.closeDrawer(navList);
drawerLayout.closeDrawer(navDrawer);
}
};
@ -260,11 +300,11 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
super.onPostCreate(savedInstanceState);
drawerToggle.syncState();
if (savedInstanceState != null) {
currentTitle = savedInstanceState.getString("title");
if (!drawerLayout.isDrawerOpen(navList)) {
currentTitle = savedInstanceState.getString(SAVE_TITLE);
if (!drawerLayout.isDrawerOpen(navDrawer)) {
getSupportActionBar().setTitle(currentTitle);
}
selectedNavListIndex = savedInstanceState.getInt("selectedNavIndex");
selectedNavListIndex = savedInstanceState.getInt(SAVE_SELECTED_NAV_INDEX);
}
}
@ -277,8 +317,9 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("title", getSupportActionBar().getTitle().toString());
outState.putInt("selectedNavIndex", selectedNavListIndex);
outState.putString(SAVE_TITLE, getSupportActionBar().getTitle().toString());
outState.putInt(SAVE_SELECTED_NAV_INDEX, selectedNavListIndex);
outState.putInt(SAVE_BACKSTACK_COUNT, getSupportFragmentManager().getBackStackEntryCount());
}
@ -312,29 +353,16 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
public boolean onOptionsItemSelected(MenuItem item) {
if (drawerToggle.onOptionsItemSelected(item)) {
return true;
}
switch (item.getItemId()) {
case R.id.show_preferences:
startActivity(new Intent(this, PreferenceController.getPreferenceActivity()));
return true;
default:
return super.onOptionsItemSelected(item);
} else if (item.getItemId() == android.R.id.home) {
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
dismissChildFragment();
}
return true;
} else {
return super.onOptionsItemSelected(item);
}
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
return true;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main, menu);
return true;
}
private DBReader.NavDrawerData navDrawerData;
private AsyncTask<Void, Void, DBReader.NavDrawerData> loadTask;

View File

@ -174,7 +174,6 @@ public abstract class MediaplayerActivity extends ActionBarActivity
orientation = getResources().getConfiguration().orientation;
getWindow().setFormat(PixelFormat.TRANSPARENT);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
@Override

View File

@ -72,15 +72,6 @@ public class PreferenceActivity extends ActionBarActivity {
preferenceController.onActivityResult(requestCode, resultCode, data);
}
@Override
public void onBackPressed() {
// The default back button behavior has to be overwritten because changing the theme clears the back stack
Intent destIntent = new Intent(this, MainActivity.class);
destIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(destIntent);
finish();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
@ -91,9 +82,6 @@ public class PreferenceActivity extends ActionBarActivity {
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
Intent destIntent = new Intent(this, MainActivity.class);
destIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(destIntent);
finish();
return true;
default:

View File

@ -84,13 +84,4 @@ public class PreferenceActivityGingerbread extends android.preference.Preference
);
return false;
}
@Override
public void onBackPressed() {
// The default back button behavior has to be overwritten because changing the theme clears the back stack
Intent destIntent = new Intent(this, MainActivity.class);
destIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(destIntent);
finish();
}
}

View File

@ -111,6 +111,7 @@ public class VideoplayerActivity extends MediaplayerActivity {
@Override
protected void setupGUI() {
super.setupGUI();
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
videoOverlay = (LinearLayout) findViewById(R.id.overlay);
videoview = (AspectRatioVideoView) findViewById(R.id.videoview);
progressIndicator = (ProgressBar) findViewById(R.id.progressIndicator);

View File

@ -14,6 +14,7 @@ import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageButton;
import android.widget.TextView;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.Chapter;
@ -31,16 +32,18 @@ public class ChapterListAdapter extends ArrayAdapter<Chapter> {
private Playable media;
private int defaultTextColor;
private final Callback callback;
public ChapterListAdapter(Context context, int textViewResourceId,
List<Chapter> objects, Playable media) {
List<Chapter> objects, Playable media, Callback callback) {
super(context, textViewResourceId, objects);
this.chapters = objects;
this.media = media;
this.callback = callback;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
public View getView(final int position, View convertView, ViewGroup parent) {
Holder holder;
Chapter sc = getItem(position);
@ -56,6 +59,7 @@ public class ChapterListAdapter extends ArrayAdapter<Chapter> {
defaultTextColor = holder.title.getTextColors().getDefaultColor();
holder.start = (TextView) convertView.findViewById(R.id.txtvStart);
holder.link = (TextView) convertView.findViewById(R.id.txtvLink);
holder.butPlayChapter = (ImageButton) convertView.findViewById(R.id.butPlayChapter);
convertView.setTag(holder);
} else {
holder = (Holder) convertView.getTag();
@ -122,6 +126,14 @@ public class ChapterListAdapter extends ArrayAdapter<Chapter> {
}
});
holder.butPlayChapter.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (callback != null) {
callback.onPlayChapterButtonClicked(position);
}
}
});
Chapter current = ChapterUtils.getCurrentChapter(media);
if (current != null) {
if (current == sc) {
@ -144,6 +156,7 @@ public class ChapterListAdapter extends ArrayAdapter<Chapter> {
TextView title;
TextView start;
TextView link;
ImageButton butPlayChapter;
}
@Override
@ -177,4 +190,8 @@ public class ChapterListAdapter extends ArrayAdapter<Chapter> {
return super.getItem(position);
}
public static interface Callback {
public void onPlayChapterButtonClicked(int position);
}
}

View File

@ -1,5 +1,6 @@
package de.danoeh.antennapod.config;
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.core.ClientConfig;
/**
@ -8,12 +9,13 @@ import de.danoeh.antennapod.core.ClientConfig;
public class ClientConfigurator {
static {
ClientConfig.USER_AGENT = "AntennaPod/0.9.9.6";
ClientConfig.USER_AGENT = "AntennaPod/" + BuildConfig.VERSION_NAME;
ClientConfig.applicationCallbacks = new ApplicationCallbacksImpl();
ClientConfig.downloadServiceCallbacks = new DownloadServiceCallbacksImpl();
ClientConfig.gpodnetCallbacks = new GpodnetCallbacksImpl();
ClientConfig.playbackServiceCallbacks = new PlaybackServiceCallbacksImpl();
ClientConfig.storageCallbacks = new StorageCallbacksImpl();
ClientConfig.flattrCallbacks = new FlattrCallbacksImpl();
ClientConfig.dbTasksCallbacks = new DBTasksCallbacksImpl();
}
}

View File

@ -0,0 +1,20 @@
package de.danoeh.antennapod.config;
import de.danoeh.antennapod.core.DBTasksCallbacks;
import de.danoeh.antennapod.core.storage.APCleanupAlgorithm;
import de.danoeh.antennapod.core.storage.APDownloadAlgorithm;
import de.danoeh.antennapod.core.storage.AutomaticDownloadAlgorithm;
import de.danoeh.antennapod.core.storage.EpisodeCleanupAlgorithm;
public class DBTasksCallbacksImpl implements DBTasksCallbacks {
@Override
public AutomaticDownloadAlgorithm getAutomaticDownloadAlgorithm() {
return new APDownloadAlgorithm();
}
@Override
public EpisodeCleanupAlgorithm getEpisodeCacheCleanupAlgorithm() {
return new APCleanupAlgorithm();
}
}

View File

@ -13,7 +13,7 @@ public class StorageCallbacksImpl implements StorageCallbacks {
@Override
public int getDatabaseVersion() {
return 13;
return 14;
}
@Override
@ -105,9 +105,24 @@ public class StorageCallbacksImpl implements StorageCallbacks {
}
if (oldVersion <= 12) {
db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ " ADD COLUMN " + PodDBAdapter.KEY_IS_PAGED + " INTEGER DEFAULT 0");
+ " ADD COLUMN " + PodDBAdapter.KEY_IS_PAGED + " INTEGER DEFAULT 0");
db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ " ADD COLUMN " + PodDBAdapter.KEY_NEXT_PAGE_LINK + " TEXT");
}
if (oldVersion <= 13) {
// remove duplicate rows in "Chapters" table that were created because of a bug.
db.execSQL(String.format("DELETE FROM %s WHERE %s NOT IN " +
"(SELECT MIN(%s) as %s FROM %s GROUP BY %s,%s,%s,%s,%s)",
PodDBAdapter.TABLE_NAME_SIMPLECHAPTERS,
PodDBAdapter.KEY_ID,
PodDBAdapter.KEY_ID,
PodDBAdapter.KEY_ID,
PodDBAdapter.TABLE_NAME_SIMPLECHAPTERS,
PodDBAdapter.KEY_TITLE,
PodDBAdapter.KEY_START,
PodDBAdapter.KEY_FEEDITEM,
PodDBAdapter.KEY_LINK,
PodDBAdapter.KEY_CHAPTER_TYPE));
}
}
}

View File

@ -1,445 +0,0 @@
package de.danoeh.antennapod.dialog;
import android.annotation.TargetApi;
import android.app.Dialog;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.widget.PopupMenu;
import android.text.TextUtils;
import android.util.Log;
import android.util.TypedValue;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ImageButton;
import android.widget.TextView;
import android.widget.Toast;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.Validate;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Callable;
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.QueueAccess;
import de.danoeh.antennapod.core.util.ShownotesProvider;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
/**
* Shows information about a specific FeedItem and provides actions like playing, downloading, etc.
*/
public class FeedItemDialog extends Dialog {
private static final String TAG = "FeedItemDialog";
private FeedItem item;
private QueueAccess queue;
private ViewGroup contentContainer;
private View header;
private TextView txtvTitle;
private WebView webvDescription;
private ImageButton butAction1;
private ImageButton butAction2;
private ImageButton butMore;
private PopupMenu popupMenu;
public static FeedItemDialog newInstance(Context context, FeedItemDialogSavedInstance savedInstance) {
Validate.notNull(savedInstance);
FeedItemDialog dialog = newInstance(context, savedInstance.item, savedInstance.queueAccess);
if (savedInstance.isShowing) {
dialog.show();
}
return dialog;
}
public static FeedItemDialog newInstance(Context context, FeedItem item, QueueAccess queue) {
if (useDarkThemeWorkAround()) {
return new FeedItemDialog(context, R.style.Theme_AntennaPod_Dark, item, queue);
} else {
return new FeedItemDialog(context, item, queue);
}
}
public FeedItemDialog(Context context, int theme, FeedItem item, QueueAccess queue) {
super(context, theme);
Validate.notNull(item);
Validate.notNull(queue);
this.item = item;
this.queue = queue;
}
private FeedItemDialog(Context context, FeedItem item, QueueAccess queue) {
this(context, 0, item, queue);
}
/**
* Returns true if the dialog should use a dark theme. This has to be done on Gingerbread devices
* because dialogs are only available in a dark theme.
*/
private static boolean useDarkThemeWorkAround() {
return Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1
&& UserPreferences.getTheme() != R.style.Theme_AntennaPod_Dark;
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.feeditem_dialog);
contentContainer = (ViewGroup) findViewById(R.id.contentContainer);
txtvTitle = (TextView) findViewById(R.id.txtvTitle);
header = findViewById(R.id.header);
webvDescription = (WebView) findViewById(R.id.webview);
butAction1 = (ImageButton) findViewById(R.id.butAction1);
butAction2 = (ImageButton) findViewById(R.id.butAction2);
butMore = (ImageButton) findViewById(R.id.butMoreActions);
popupMenu = new PopupMenu(getContext(), butMore);
webvDescription.setWebViewClient(new WebViewClient());
if (Build.VERSION.SDK_INT >= 14) { // ellipsize is causing problems on old versions, see #448
txtvTitle.setEllipsize(TextUtils.TruncateAt.END);
}
txtvTitle.setText(item.getTitle());
if (UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) {
if (Build.VERSION.SDK_INT >= 11
&& Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
webvDescription.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
webvDescription.setBackgroundColor(getContext().getResources().getColor(
R.color.black));
}
webvDescription.getSettings().setUseWideViewPort(false);
webvDescription.getSettings().setLayoutAlgorithm(
WebSettings.LayoutAlgorithm.NARROW_COLUMNS);
webvDescription.getSettings().setLoadWithOverviewMode(true);
webvDescription.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
try {
getContext().startActivity(intent);
} catch (ActivityNotFoundException e) {
e.printStackTrace();
return false;
}
return true;
}
});
loadDescriptionWebview(item);
butAction1.setOnClickListener(new View.OnClickListener() {
DefaultActionButtonCallback actionButtonCallback = new DefaultActionButtonCallback(getContext());
@Override
public void onClick(View v) {
actionButtonCallback.onActionButtonPressed(item);
FeedMedia media = item.getMedia();
if (media != null && media.isDownloaded()) {
// playback was started, dialog should close itself
dismiss();
}
}
}
);
butAction2.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v) {
if (item.hasMedia()) {
FeedMedia media = item.getMedia();
if (!media.isDownloaded()) {
DBTasks.playMedia(getContext(), media, true, true, true);
dismiss();
} else {
DBWriter.deleteFeedMediaOfItem(getContext(), media.getId());
}
} else if (item.getLink() != null) {
Uri uri = Uri.parse(item.getLink());
getContext().startActivity(new Intent(Intent.ACTION_VIEW, uri));
}
}
}
);
butMore.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
popupMenu.getMenu().clear();
popupMenu.inflate(R.menu.feeditem_dialog);
if (item.hasMedia()) {
FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item, true, queue);
} else {
// these are already available via button1 and button2
FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item, true, queue,
R.id.mark_read_item, R.id.visit_website_item);
}
popupMenu.show();
}
}
);
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
try {
return FeedItemMenuHandler.onMenuItemClicked(getContext(), menuItem.getItemId(), item);
} catch (DownloadRequestException e) {
e.printStackTrace();
Toast.makeText(getContext(), e.getMessage(), Toast.LENGTH_LONG).show();
return true;
}
}
}
);
updateMenuAppearance();
}
@Override
public void dismiss() {
super.dismiss();
if (contentContainer != null && webvDescription != null) {
contentContainer.removeAllViews();
webvDescription.destroy();
}
}
private final FeedItemMenuHandler.MenuInterface popupMenuInterface = new FeedItemMenuHandler.MenuInterface() {
@Override
public void setItemVisibility(int id, boolean visible) {
MenuItem item = popupMenu.getMenu().findItem(id);
if (item != null) {
item.setVisible(visible);
}
}
};
public void updateMenuAppearance() {
if (item == null || queue == null) {
Log.w(TAG, "UpdateMenuAppearance called while item or queue was null");
return;
}
FeedMedia media = item.getMedia();
if (media == null) {
TypedArray drawables = getContext().obtainStyledAttributes(new int[]{R.attr.navigation_accept,
R.attr.location_web_site});
if (!item.isRead()) {
butAction1.setImageDrawable(drawables.getDrawable(0));
butAction1.setContentDescription(getContext().getString(R.string.mark_read_label));
butAction1.setVisibility(View.VISIBLE);
} else {
butAction1.setVisibility(View.INVISIBLE);
}
if (item.getLink() != null) {
butAction2.setImageDrawable(drawables.getDrawable(1));
butAction2.setContentDescription(getContext().getString(R.string.visit_website_label));
} else {
butAction2.setEnabled(false);
}
drawables.recycle();
} else {
boolean isDownloading = DownloadRequester.getInstance().isDownloadingFile(media);
TypedArray drawables = getContext().obtainStyledAttributes(new int[]{R.attr.av_play,
R.attr.av_download, R.attr.action_stream, R.attr.content_discard, R.attr.navigation_cancel});
if (!media.isDownloaded()) {
butAction2.setImageDrawable(drawables.getDrawable(2));
butAction2.setContentDescription(getContext().getString(R.string.stream_label));
} else {
butAction2.setImageDrawable(drawables.getDrawable(3));
butAction2.setContentDescription(getContext().getString(R.string.remove_episode_lable));
}
if (isDownloading) {
butAction1.setImageDrawable(drawables.getDrawable(4));
butAction1.setContentDescription(getContext().getString(R.string.cancel_download_label));
} else if (media.isDownloaded()) {
butAction1.setImageDrawable(drawables.getDrawable(0));
butAction1.setContentDescription(getContext().getString(R.string.play_label));
} else {
butAction1.setImageDrawable(drawables.getDrawable(1));
butAction1.setContentDescription(getContext().getString(R.string.download_label));
}
drawables.recycle();
}
}
private void loadDescriptionWebview(final ShownotesProvider shownotesProvider) {
AsyncTask<Void, Void, Void> loadTask = new AsyncTask<Void, Void, Void>() {
String data;
private String applyWebviewStyle(String textColor, String data) {
final String WEBVIEW_STYLE = "<html><head><style type=\"text/css\"> @font-face { font-family: 'Roboto-Light'; src: url('file:///android_asset/Roboto-Light.ttf'); } * { color: %s; font-family: roboto-Light; font-size: 11pt; } a { font-style: normal; text-decoration: none; font-weight: normal; color: #00A8DF; } img { display: block; margin: 10 auto; max-width: %s; height: auto; } body { margin: %dpx %dpx %dpx %dpx; }</style></head><body>%s</body></html>";
final int pageMargin = (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 8, getContext().getResources()
.getDisplayMetrics()
);
return String.format(WEBVIEW_STYLE, textColor, "100%", pageMargin,
pageMargin, pageMargin, pageMargin, data);
}
@Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
// /webvDescription.loadData(url, "text/html", "utf-8");
if (FeedItemDialog.this.isShowing() && webvDescription != null) {
webvDescription.loadDataWithBaseURL(null, data, "text/html",
"utf-8", "about:blank");
if (BuildConfig.DEBUG)
Log.d(TAG, "Webview loaded");
}
}
@Override
protected Void doInBackground(Void... params) {
if (BuildConfig.DEBUG)
Log.d(TAG, "Loading Webview");
try {
Callable<String> shownotesLoadTask = shownotesProvider.loadShownotes();
final String shownotes = shownotesLoadTask.call();
data = StringEscapeUtils.unescapeHtml4(shownotes);
TypedArray res = getContext()
.getTheme()
.obtainStyledAttributes(
new int[]{android.R.attr.textColorPrimary});
int colorResource;
if (useDarkThemeWorkAround()) {
colorResource = getContext().getResources().getColor(R.color.black);
} else {
colorResource = res.getColor(0, 0);
}
String colorString = String.format("#%06X",
0xFFFFFF & colorResource);
Log.i(TAG, "text color: " + colorString);
res.recycle();
data = applyWebviewStyle(colorString, data);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
};
loadTask.execute();
}
/**
* Convenience method that calls setQueue() and setItemFromCollection() with
* the given arguments.
*
* @return true if one of the calls to setItemFromCollection returned true,
* false otherwise.
*/
public boolean updateContent(QueueAccess queue, List<FeedItem>... collections) {
setQueue(queue);
boolean setItemFromCollectionResult = false;
if (collections != null) {
for (List<FeedItem> list : collections) {
setItemFromCollectionResult |= setItemFromCollection(list);
}
}
if (isShowing()) {
updateMenuAppearance();
}
return setItemFromCollectionResult;
}
public void setItem(FeedItem item) {
Validate.notNull(item);
this.item = item;
}
/**
* Finds the FeedItem of this dialog in a collection and updates its state from that
* collection.
*
* @return true if the FeedItem was found, false otherwise.
*/
public boolean setItemFromCollection(Collection<FeedItem> items) {
for (FeedItem item : items) {
if (item.getId() == this.item.getId()) {
setItem(item);
return true;
}
}
return false;
}
public void setQueue(QueueAccess queue) {
Validate.notNull(queue);
this.queue = queue;
}
public FeedItem getItem() {
return item;
}
public QueueAccess getQueue() {
return queue;
}
public FeedItemDialogSavedInstance save() {
return new FeedItemDialogSavedInstance(item, queue, isShowing());
}
/**
* Used to save the FeedItemDialog's state across configuration changes
*/
public static class FeedItemDialogSavedInstance {
final FeedItem item;
final QueueAccess queueAccess;
final boolean isShowing;
private FeedItemDialogSavedInstance(FeedItem item, QueueAccess queueAccess, boolean isShowing) {
this.item = item;
this.queueAccess = queueAccess;
this.isShowing = isShowing;
}
}
}

View File

@ -8,17 +8,17 @@ import android.support.v4.app.ListFragment;
import android.view.View;
import android.widget.ListView;
import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.DownloadedEpisodesListAdapter;
import de.danoeh.antennapod.dialog.FeedItemDialog;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.QueueAccess;
import java.util.List;
/**
* Displays all running downloads and provides a button to delete them
*/
@ -36,8 +36,6 @@ public class CompletedDownloadsFragment extends ListFragment {
private boolean viewCreated = false;
private boolean itemsLoaded = false;
private FeedItemDialog feedItemDialog;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -69,7 +67,6 @@ public class CompletedDownloadsFragment extends ListFragment {
super.onDestroyView();
listAdapter = null;
viewCreated = false;
feedItemDialog = null;
stopItemLoader();
}
@ -102,8 +99,7 @@ public class CompletedDownloadsFragment extends ListFragment {
super.onListItemClick(l, v, position, id);
FeedItem item = listAdapter.getItem(position - l.getHeaderViewsCount());
if (item != null) {
feedItemDialog = FeedItemDialog.newInstance(getActivity(), item, queue);
feedItemDialog.show();
((MainActivity) getActivity()).loadChildFragment(ItemFragment.newInstance(item.getId()));
}
}
@ -115,12 +111,6 @@ public class CompletedDownloadsFragment extends ListFragment {
}
setListShown(true);
listAdapter.notifyDataSetChanged();
if (feedItemDialog != null) {
boolean res = feedItemDialog.updateContent(queue, items);
if (!res && feedItemDialog.isShowing()) {
feedItemDialog.dismiss();
}
}
}
private DownloadedEpisodesListAdapter.ItemAccess itemAccess = new DownloadedEpisodesListAdapter.ItemAccess() {
@ -143,11 +133,7 @@ public class CompletedDownloadsFragment extends ListFragment {
private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
@Override
public void update(EventDistributor eventDistributor, Integer arg) {
if ((arg & EventDistributor.DOWNLOAD_QUEUED) != 0) {
if (feedItemDialog != null && feedItemDialog.isShowing()) {
feedItemDialog.updateMenuAppearance();
}
} else if ((arg & EVENTS) != 0) {
if ((arg & EVENTS) != 0) {
startItemLoader();
}
}

View File

@ -1,5 +1,6 @@
package de.danoeh.antennapod.fragment;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
@ -13,6 +14,7 @@ import com.squareup.picasso.Picasso;
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.AudioplayerActivity;
import de.danoeh.antennapod.activity.AudioplayerActivity.AudioplayerContentFragment;
import de.danoeh.antennapod.core.util.playback.Playable;
@ -57,6 +59,15 @@ public class CoverFragment extends Fragment implements
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.cover_fragment, container, false);
imgvCover = (ImageView) root.findViewById(R.id.imgvCover);
imgvCover.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Activity activity = getActivity();
if (activity != null && activity instanceof AudioplayerActivity) {
((AudioplayerActivity)activity).switchToLastFragment();
}
}
});
viewCreated = true;
return root;
}

View File

@ -1,19 +1,16 @@
package de.danoeh.antennapod.fragment;
import android.app.Activity;
import android.content.res.Resources;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBar;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
/**
* Shows the CompletedDownloadsFragment and the RunningDownloadsFragment
@ -27,7 +24,6 @@ public class DownloadsFragment extends Fragment {
public static final int POS_LOG = 2;
private ViewPager pager;
private MainActivity activity;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
@ -36,42 +32,6 @@ public class DownloadsFragment extends Fragment {
pager = (ViewPager) root.findViewById(R.id.pager);
DownloadsPagerAdapter pagerAdapter = new DownloadsPagerAdapter(getChildFragmentManager(), getResources());
pager.setAdapter(pagerAdapter);
final ActionBar actionBar = activity.getMainActivtyActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
ActionBar.TabListener tabListener = new ActionBar.TabListener() {
@Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
pager.setCurrentItem(tab.getPosition());
}
@Override
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
}
@Override
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
}
};
actionBar.removeAllTabs();
actionBar.addTab(actionBar.newTab()
.setText(R.string.downloads_running_label)
.setTabListener(tabListener));
actionBar.addTab(actionBar.newTab()
.setText(R.string.downloads_completed_label)
.setTabListener(tabListener));
actionBar.addTab(actionBar.newTab()
.setText(R.string.downloads_log_label)
.setTabListener(tabListener));
pager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageSelected(int position) {
super.onPageSelected(position);
actionBar.setSelectedNavigationItem(position);
}
});
return root;
}
@ -84,24 +44,8 @@ public class DownloadsFragment extends Fragment {
}
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
this.activity = (MainActivity) activity;
}
@Override
public void onDetach() {
super.onDetach();
activity.getMainActivtyActionBar().removeAllTabs();
activity.getMainActivtyActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
}
public class DownloadsPagerAdapter extends FragmentPagerAdapter {
Resources resources;
public DownloadsPagerAdapter(FragmentManager fm, Resources resources) {

View File

@ -12,7 +12,6 @@ import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;

View File

@ -0,0 +1,442 @@
package de.danoeh.antennapod.fragment;
import android.annotation.TargetApi;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.content.res.TypedArray;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.support.v4.util.Pair;
import android.support.v7.widget.PopupMenu;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.squareup.picasso.Picasso;
import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
import de.danoeh.antennapod.core.asynctask.DBTaskLoader;
import de.danoeh.antennapod.core.asynctask.DownloadObserver;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.Downloader;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.QueueAccess;
import de.danoeh.antennapod.core.util.playback.Timeline;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
/**
* Displays information about a FeedItem and actions.
*/
public class ItemFragment extends Fragment implements LoaderManager.LoaderCallbacks<Pair<FeedItem, QueueAccess>> {
private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED |
EventDistributor.DOWNLOAD_QUEUED |
EventDistributor.QUEUE_UPDATE |
EventDistributor.UNREAD_ITEMS_UPDATE;
private static final String ARG_FEEDITEM = "feeditem";
/**
* Creates a new instance of an ItemFragment
*
* @param feeditem The ID of the FeedItem that should be displayed.
* @return The ItemFragment instance
*/
public static ItemFragment newInstance(long feeditem) {
ItemFragment fragment = new ItemFragment();
Bundle args = new Bundle();
args.putLong(ARG_FEEDITEM, feeditem);
fragment.setArguments(args);
return fragment;
}
private boolean itemsLoaded = false;
private long itemID;
private FeedItem item;
private QueueAccess queue;
private String webviewData;
private DownloadObserver downloadObserver;
private List<Downloader> downloaderList;
private ViewGroup root;
private View header;
private WebView webvDescription;
private TextView txtvTitle;
private ImageView imgvCover;
private ProgressBar progbarDownload;
private ProgressBar progbarLoading;
private Button butAction1;
private Button butAction2;
private ImageButton butMore;
private PopupMenu popupMenu;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
setHasOptionsMenu(false);
itemID = getArguments().getLong(ARG_FEEDITEM, -1);
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
getLoaderManager().initLoader(0, null, this);
Toolbar toolbar = ((MainActivity) getActivity()).getToolbar();
toolbar.addView(header);
}
@Override
public void onStart() {
super.onStart();
EventDistributor.getInstance().register(contentUpdate);
if (downloadObserver != null) {
downloadObserver.setActivity(getActivity());
downloadObserver.onResume();
}
if (itemsLoaded) {
onFragmentLoaded();
}
}
@Override
public void onStop() {
super.onStop();
EventDistributor.getInstance().unregister(contentUpdate);
}
private void resetViewState() {
if (downloadObserver != null) {
downloadObserver.onPause();
}
Toolbar toolbar = ((MainActivity) getActivity()).getToolbar();
toolbar.removeView(header);
}
@Override
public void onDestroyView() {
super.onDestroyView();
resetViewState();
if (webvDescription != null && root != null) {
root.removeView(webvDescription);
webvDescription.destroy();
}
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
((MainActivity) getActivity()).getSupportActionBar().setTitle("");
Toolbar toolbar = ((MainActivity) getActivity()).getToolbar();
View layout = inflater.inflate(R.layout.feeditem_fragment, container, false);
header = inflater.inflate(R.layout.feeditem_fragment_header, toolbar, false);
root = (ViewGroup) layout.findViewById(R.id.content_root);
txtvTitle = (TextView) header.findViewById(R.id.txtvTitle);
if (Build.VERSION.SDK_INT >= 14) { // ellipsize is causing problems on old versions, see #448
txtvTitle.setEllipsize(TextUtils.TruncateAt.END);
}
webvDescription = (WebView) layout.findViewById(R.id.webvDescription);
if (UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) {
if (Build.VERSION.SDK_INT >= 11
&& Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
webvDescription.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
webvDescription.setBackgroundColor(getResources().getColor(
R.color.black));
}
webvDescription.getSettings().setUseWideViewPort(false);
webvDescription.getSettings().setLayoutAlgorithm(
WebSettings.LayoutAlgorithm.NARROW_COLUMNS);
webvDescription.getSettings().setLoadWithOverviewMode(true);
webvDescription.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
try {
startActivity(intent);
} catch (ActivityNotFoundException e) {
e.printStackTrace();
return true;
}
return true;
}
});
imgvCover = (ImageView) header.findViewById(R.id.imgvCover);
progbarDownload = (ProgressBar) header.findViewById(R.id.progbarDownload);
progbarLoading = (ProgressBar) layout.findViewById(R.id.progbarLoading);
butAction1 = (Button) header.findViewById(R.id.butAction1);
butAction2 = (Button) header.findViewById(R.id.butAction2);
butMore = (ImageButton) header.findViewById(R.id.butMoreActions);
popupMenu = new PopupMenu(getActivity(), butMore);
butAction1.setOnClickListener(new View.OnClickListener() {
DefaultActionButtonCallback actionButtonCallback = new DefaultActionButtonCallback(getActivity());
@Override
public void onClick(View v) {
if (item == null) {
return;
}
actionButtonCallback.onActionButtonPressed(item);
FeedMedia media = item.getMedia();
if (media != null && media.isDownloaded()) {
// playback was started, dialog should close itself
((MainActivity) getActivity()).dismissChildFragment();
}
}
}
);
butAction2.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v) {
if (item == null) {
return;
}
if (item.hasMedia()) {
FeedMedia media = item.getMedia();
if (!media.isDownloaded()) {
DBTasks.playMedia(getActivity(), media, true, true, true);
((MainActivity) getActivity()).dismissChildFragment();
} else {
DBWriter.deleteFeedMediaOfItem(getActivity(), media.getId());
}
} else if (item.getLink() != null) {
Uri uri = Uri.parse(item.getLink());
getActivity().startActivity(new Intent(Intent.ACTION_VIEW, uri));
}
}
}
);
butMore.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (item == null) {
return;
}
popupMenu.getMenu().clear();
popupMenu.inflate(R.menu.feeditem_dialog);
if (item.hasMedia()) {
FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item, true, queue);
} else {
// these are already available via button1 and button2
FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item, true, queue,
R.id.mark_read_item, R.id.visit_website_item);
}
popupMenu.show();
}
}
);
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
try {
return FeedItemMenuHandler.onMenuItemClicked(getActivity(), menuItem.getItemId(), item);
} catch (DownloadRequestException e) {
e.printStackTrace();
Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_LONG).show();
return true;
}
}
}
);
return layout;
}
private final FeedItemMenuHandler.MenuInterface popupMenuInterface = new FeedItemMenuHandler.MenuInterface() {
@Override
public void setItemVisibility(int id, boolean visible) {
MenuItem item = popupMenu.getMenu().findItem(id);
if (item != null) {
item.setVisible(visible);
}
}
};
private void onFragmentLoaded() {
progbarLoading.setVisibility(View.GONE);
if (webviewData != null) {
webvDescription.loadDataWithBaseURL(null, webviewData, "text/html",
"utf-8", "about:blank");
}
updateAppearance();
downloadObserver = new DownloadObserver(getActivity(), new Handler(), downloadObserverCallback);
downloadObserver.onResume();
}
private void updateAppearance() {
txtvTitle.setText(item.getTitle());
Picasso.with(getActivity()).load(item.getImageUri())
.fit()
.into(imgvCover);
progbarDownload.setVisibility(View.GONE);
if (item.hasMedia() && downloaderList != null) {
for (Downloader downloader : downloaderList) {
if (downloader.getDownloadRequest().getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA
&& downloader.getDownloadRequest().getFeedfileId() == item.getMedia().getId()) {
progbarDownload.setVisibility(View.VISIBLE);
progbarDownload.setProgress(downloader.getDownloadRequest().getProgressPercent());
}
}
}
FeedMedia media = item.getMedia();
if (media == null) {
TypedArray drawables = getActivity().obtainStyledAttributes(new int[]{R.attr.navigation_accept,
R.attr.location_web_site});
if (!item.isRead()) {
butAction1.setCompoundDrawablesWithIntrinsicBounds(drawables.getDrawable(0), null, null, null);
butAction1.setText(getActivity().getString(R.string.mark_read_label));
butAction1.setVisibility(View.VISIBLE);
} else {
butAction1.setVisibility(View.INVISIBLE);
}
if (item.getLink() != null) {
butAction2.setCompoundDrawablesWithIntrinsicBounds(drawables.getDrawable(1), null, null, null);
butAction2.setText(getActivity().getString(R.string.visit_website_label));
} else {
butAction2.setEnabled(false);
}
drawables.recycle();
} else {
boolean isDownloading = DownloadRequester.getInstance().isDownloadingFile(media);
TypedArray drawables = getActivity().obtainStyledAttributes(new int[]{R.attr.av_play,
R.attr.av_download, R.attr.action_stream, R.attr.content_discard, R.attr.navigation_cancel});
if (!media.isDownloaded()) {
butAction2.setCompoundDrawablesWithIntrinsicBounds(drawables.getDrawable(2), null, null, null);
butAction2.setText(getActivity().getString(R.string.stream_label));
} else {
butAction2.setCompoundDrawablesWithIntrinsicBounds(drawables.getDrawable(3), null, null, null);
butAction2.setText(getActivity().getString(R.string.remove_episode_lable));
}
if (isDownloading) {
butAction1.setCompoundDrawablesWithIntrinsicBounds(drawables.getDrawable(4), null, null, null);
butAction1.setText(getActivity().getString(R.string.cancel_download_label));
} else if (media.isDownloaded()) {
butAction1.setCompoundDrawablesWithIntrinsicBounds(drawables.getDrawable(0), null, null, null);
butAction1.setText(getActivity().getString(R.string.play_label));
} else {
butAction1.setCompoundDrawablesWithIntrinsicBounds(drawables.getDrawable(1), null, null, null);
butAction1.setText(getActivity().getString(R.string.download_label));
}
drawables.recycle();
}
}
@Override
public Loader<Pair<FeedItem, QueueAccess>> onCreateLoader(int id, Bundle args) {
return new DBTaskLoader<Pair<FeedItem, QueueAccess>>(getActivity()) {
@Override
public Pair<FeedItem, QueueAccess> loadInBackground() {
FeedItem data1 = DBReader.getFeedItem(getContext(), itemID);
if (data1 != null) {
Timeline t = new Timeline(getActivity(), data1);
webviewData = t.processShownotes(false);
}
QueueAccess data2 = QueueAccess.IDListAccess(DBReader.getQueueIDList(getContext()));
return Pair.create(data1, data2);
}
};
}
@Override
public void onLoadFinished(Loader<Pair<FeedItem, QueueAccess>> loader, Pair<FeedItem, QueueAccess> data) {
if (data != null) {
item = data.first;
queue = data.second;
if (!itemsLoaded) {
itemsLoaded = true;
onFragmentLoaded();
} else {
updateAppearance();
}
}
}
@Override
public void onLoaderReset(Loader<Pair<FeedItem, QueueAccess>> loader) {
}
private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
@Override
public void update(EventDistributor eventDistributor, Integer arg) {
if ((arg & EVENTS) != 0) {
getLoaderManager().restartLoader(0, null, ItemFragment.this);
}
}
};
private final DownloadObserver.Callback downloadObserverCallback = new DownloadObserver.Callback() {
@Override
public void onContentChanged() {
if (itemsLoaded && getActivity() != null) {
updateAppearance();
}
}
@Override
public void onDownloadDataAvailable(List<Downloader> downloaderList) {
ItemFragment.this.downloaderList = downloaderList;
if (itemsLoaded && getActivity() != null) {
updateAppearance();
}
}
};
}

View File

@ -5,6 +5,7 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.ListFragment;
@ -18,6 +19,7 @@ import android.view.MenuItem;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.TextView;
@ -35,6 +37,7 @@ import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
import de.danoeh.antennapod.adapter.FeedItemlistAdapter;
import de.danoeh.antennapod.core.asynctask.DownloadObserver;
import de.danoeh.antennapod.core.asynctask.FeedRemover;
import de.danoeh.antennapod.core.asynctask.PicassoProvider;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.core.feed.EventDistributor;
@ -49,7 +52,6 @@ import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.QueueAccess;
import de.danoeh.antennapod.core.util.gui.MoreContentListFooterUtil;
import de.danoeh.antennapod.dialog.FeedItemDialog;
import de.danoeh.antennapod.menuhandler.FeedMenuHandler;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
import de.danoeh.antennapod.menuhandler.NavDrawerActivity;
@ -81,9 +83,6 @@ public class ItemlistFragment extends ListFragment {
private DownloadObserver downloadObserver;
private List<Downloader> downloaderList;
private FeedItemDialog feedItemDialog;
private FeedItemDialog.FeedItemDialogSavedInstance feedItemDialogSavedInstance;
private MoreContentListFooterUtil listFooter;
private boolean isUpdatingFeed;
@ -156,13 +155,10 @@ public class ItemlistFragment extends ListFragment {
private void resetViewState() {
adapter = null;
viewsCreated = false;
listFooter = null;
if (downloadObserver != null) {
downloadObserver.onPause();
}
if (feedItemDialog != null) {
feedItemDialogSavedInstance = feedItemDialog.save();
}
feedItemDialog = null;
}
private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker = new MenuItemUtils.UpdateRefreshMenuItemChecker() {
@ -258,6 +254,15 @@ public class ItemlistFragment extends ListFragment {
}
@Override
public void setListAdapter(ListAdapter adapter) {
// This workaround prevents the ListFragment from setting a list adapter when its state is restored.
// This is only necessary on API 10 because addFooterView throws an internal exception in this case.
if (Build.VERSION.SDK_INT > 10 || insideOnFragmentLoaded) {
super.setListAdapter(adapter);
}
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
@ -272,8 +277,9 @@ public class ItemlistFragment extends ListFragment {
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
FeedItem selection = adapter.getItem(position - l.getHeaderViewsCount());
feedItemDialog = FeedItemDialog.newInstance(getActivity(), selection, queue);
feedItemDialog.show();
if (selection != null) {
((MainActivity) getActivity()).loadChildFragment(ItemFragment.newInstance(selection.getId()));
}
}
private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
@ -303,9 +309,12 @@ public class ItemlistFragment extends ListFragment {
}
private boolean insideOnFragmentLoaded = false;
private void onFragmentLoaded() {
insideOnFragmentLoaded = true;
if (adapter == null) {
getListView().setAdapter(null);
setListAdapter(null);
setupHeaderView();
setupFooterView();
adapter = new FeedItemlistAdapter(getActivity(), itemAccess, new DefaultActionButtonCallback(getActivity()), false);
@ -316,17 +325,14 @@ public class ItemlistFragment extends ListFragment {
setListShown(true);
adapter.notifyDataSetChanged();
if (feedItemDialog != null) {
feedItemDialog.updateContent(queue, feed.getItems());
} else if (feedItemDialogSavedInstance != null) {
feedItemDialog = FeedItemDialog.newInstance(getActivity(), feedItemDialogSavedInstance);
}
getActivity().supportInvalidateOptionsMenu();
if (feed != null && feed.getNextPageLink() == null && listFooter != null) {
getListView().removeFooterView(listFooter.getRoot());
}
insideOnFragmentLoaded = false;
}
private DownloadObserver.Callback downloadObserverCallback = new DownloadObserver.Callback() {
@ -335,9 +341,6 @@ public class ItemlistFragment extends ListFragment {
if (adapter != null) {
adapter.notifyDataSetChanged();
}
if (feedItemDialog != null && feedItemDialog.isShowing()) {
feedItemDialog.updateMenuAppearance();
}
}
@Override
@ -362,12 +365,21 @@ public class ItemlistFragment extends ListFragment {
TextView txtvTitle = (TextView) header.findViewById(R.id.txtvTitle);
TextView txtvAuthor = (TextView) header.findViewById(R.id.txtvAuthor);
ImageView imgvBackground = (ImageView) header.findViewById(R.id.imgvBackground);
ImageView imgvCover = (ImageView) header.findViewById(R.id.imgvCover);
ImageButton butShowInfo = (ImageButton) header.findViewById(R.id.butShowInfo);
txtvTitle.setText(feed.getTitle());
txtvAuthor.setText(feed.getAuthor());
Picasso.with(getActivity())
.load(feed.getImageUri())
.placeholder(R.color.image_readability_tint)
.error(R.color.image_readability_tint)
.transform(PicassoProvider.blurTransformation)
.resize(PicassoProvider.BLUR_IMAGE_SIZE, PicassoProvider.BLUR_IMAGE_SIZE)
.into(imgvBackground);
Picasso.with(getActivity())
.load(feed.getImageUri())
.fit()

View File

@ -7,21 +7,28 @@ import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.Fragment;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.SearchView;
import android.view.*;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.mobeta.android.dslv.DragSortListView;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
import de.danoeh.antennapod.adapter.NewEpisodesListAdapter;
import de.danoeh.antennapod.core.asynctask.DownloadObserver;
import de.danoeh.antennapod.dialog.FeedItemDialog;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
@ -37,9 +44,6 @@ import de.danoeh.antennapod.core.util.QueueAccess;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
import de.danoeh.antennapod.menuhandler.NavDrawerActivity;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
/**
* Shows unread or recently published episodes
*/
@ -73,9 +77,6 @@ public class NewEpisodesFragment extends Fragment {
private DownloadObserver downloadObserver = null;
private FeedItemDialog feedItemDialog;
private FeedItemDialog.FeedItemDialogSavedInstance feedItemDialogSavedInstance;
private boolean isUpdatingFeeds;
@Override
@ -133,10 +134,6 @@ public class NewEpisodesFragment extends Fragment {
if (downloadObserver != null) {
downloadObserver.onPause();
}
if (feedItemDialog != null) {
feedItemDialogSavedInstance = feedItemDialog.save();
}
feedItemDialog = null;
}
@ -226,8 +223,7 @@ public class NewEpisodesFragment extends Fragment {
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
FeedItem item = (FeedItem) listAdapter.getItem(position - listView.getHeaderViewsCount());
if (item != null) {
feedItemDialog = FeedItemDialog.newInstance(activity.get(), item, queueAccess);
feedItemDialog.show();
((MainActivity) getActivity()).loadChildFragment(ItemFragment.newInstance(item.getId()));
}
}
@ -257,11 +253,6 @@ public class NewEpisodesFragment extends Fragment {
downloadObserver = new DownloadObserver(activity.get(), new Handler(), downloadObserverCallback);
downloadObserver.onResume();
}
if (feedItemDialog != null) {
feedItemDialog.updateContent(queueAccess, unreadItems, recentItems);
} else if (feedItemDialogSavedInstance != null) {
feedItemDialog = FeedItemDialog.newInstance(activity.get(), feedItemDialogSavedInstance);
}
listAdapter.notifyDataSetChanged();
getActivity().supportInvalidateOptionsMenu();
updateShowOnlyEpisodesListViewState();
@ -273,9 +264,6 @@ public class NewEpisodesFragment extends Fragment {
if (listAdapter != null) {
listAdapter.notifyDataSetChanged();
}
if (feedItemDialog != null && feedItemDialog.isShowing()) {
feedItemDialog.updateMenuAppearance();
}
}
@Override

View File

@ -13,11 +13,15 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.ListView;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
import de.danoeh.antennapod.adapter.FeedItemlistAdapter;
import de.danoeh.antennapod.core.asynctask.DownloadObserver;
import de.danoeh.antennapod.dialog.FeedItemDialog;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
@ -28,9 +32,6 @@ import de.danoeh.antennapod.core.util.QueueAccess;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
import de.danoeh.antennapod.menuhandler.NavDrawerActivity;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
public class PlaybackHistoryFragment extends ListFragment {
private static final String TAG = "PlaybackHistoryFragment";
@ -46,9 +47,6 @@ public class PlaybackHistoryFragment extends ListFragment {
private DownloadObserver downloadObserver;
private List<Downloader> downloaderList;
private FeedItemDialog feedItemDialog;
private FeedItemDialog.FeedItemDialogSavedInstance feedItemDialogSavedInstance;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -103,10 +101,6 @@ public class PlaybackHistoryFragment extends ListFragment {
if (downloadObserver != null) {
downloadObserver.onPause();
}
if (feedItemDialog != null) {
feedItemDialogSavedInstance = feedItemDialog.save();
}
feedItemDialog = null;
}
@Override
@ -130,8 +124,7 @@ public class PlaybackHistoryFragment extends ListFragment {
super.onListItemClick(l, v, position, id);
FeedItem item = adapter.getItem(position - l.getHeaderViewsCount());
if (item != null) {
feedItemDialog = FeedItemDialog.newInstance(activity.get(), item, queue);
feedItemDialog.show();
((MainActivity) getActivity()).loadChildFragment(ItemFragment.newInstance(item.getId()));
}
}
@ -158,7 +151,7 @@ public class PlaybackHistoryFragment extends ListFragment {
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (!super.onOptionsItemSelected(item)) {
switch(item.getItemId()) {
switch (item.getItemId()) {
case R.id.clear_history_item:
DBWriter.clearPlaybackHistory(getActivity());
return true;
@ -190,11 +183,6 @@ public class PlaybackHistoryFragment extends ListFragment {
}
setListShown(true);
adapter.notifyDataSetChanged();
if (feedItemDialog != null && feedItemDialog.isShowing()) {
feedItemDialog.updateContent(queue, playbackHistory);
} else if (feedItemDialogSavedInstance != null) {
feedItemDialog = FeedItemDialog.newInstance(activity.get(), feedItemDialogSavedInstance);
}
getActivity().supportInvalidateOptionsMenu();
}
@ -204,9 +192,6 @@ public class PlaybackHistoryFragment extends ListFragment {
if (adapter != null) {
adapter.notifyDataSetChanged();
}
if (feedItemDialog != null && feedItemDialog.isShowing()) {
feedItemDialog.updateMenuAppearance();
}
}
@Override

View File

@ -3,6 +3,7 @@ package de.danoeh.antennapod.fragment;
import android.app.Activity;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.Fragment;
@ -29,14 +30,17 @@ import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
import de.danoeh.antennapod.adapter.QueueListAdapter;
import de.danoeh.antennapod.core.asynctask.DownloadObserver;
import de.danoeh.antennapod.dialog.FeedItemDialog;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.service.download.DownloadService;
import de.danoeh.antennapod.core.service.download.Downloader;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.QueueAccess;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.QueueSorter;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
import de.danoeh.antennapod.menuhandler.NavDrawerActivity;
@ -59,14 +63,12 @@ public class QueueFragment extends Fragment {
private boolean itemsLoaded = false;
private boolean viewsCreated = false;
private boolean isUpdatingFeeds = false;
private AtomicReference<Activity> activity = new AtomicReference<Activity>();
private DownloadObserver downloadObserver = null;
private FeedItemDialog feedItemDialog;
private FeedItemDialog.FeedItemDialogSavedInstance feedItemDialogSavedInstance;
/**
* Download observer updates won't result in an upate of the list adapter if this is true.
*/
@ -122,10 +124,6 @@ public class QueueFragment extends Fragment {
if (downloadObserver != null) {
downloadObserver.onPause();
}
if (feedItemDialog != null) {
feedItemDialogSavedInstance = feedItemDialog.save();
}
feedItemDialog = null;
}
@Override
@ -134,10 +132,19 @@ public class QueueFragment extends Fragment {
resetViewState();
}
private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker = new MenuItemUtils.UpdateRefreshMenuItemChecker() {
@Override
public boolean isRefreshing() {
return DownloadService.isRunning && DownloadRequester.getInstance().isDownloadingFeeds();
}
};
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
if (itemsLoaded && !MenuItemUtils.isActivityDrawerOpen((NavDrawerActivity) getActivity())) {
inflater.inflate(R.menu.queue, menu);
final SearchView sv = new SearchView(getActivity());
MenuItemUtils.addSearchItem(menu, sv);
sv.setQueryHint(getString(R.string.search_hint));
@ -154,9 +161,47 @@ public class QueueFragment extends Fragment {
return false;
}
});
isUpdatingFeeds = MenuItemUtils.updateRefreshMenuItem(menu, R.id.refresh_item, updateRefreshMenuItemChecker);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (!super.onOptionsItemSelected(item)) {
switch (item.getItemId()) {
case R.id.refresh_item:
List<Feed> feeds = ((MainActivity) getActivity()).getFeeds();
if (feeds != null) {
DBTasks.refreshAllFeeds(getActivity(), feeds);
}
return true;
case R.id.queue_sort_alpha_asc:
QueueSorter.sort(getActivity(), QueueSorter.Rule.ALPHA_ASC, true);
return true;
case R.id.queue_sort_alpha_desc:
QueueSorter.sort(getActivity(), QueueSorter.Rule.ALPHA_DESC, true);
return true;
case R.id.queue_sort_date_asc:
QueueSorter.sort(getActivity(), QueueSorter.Rule.DATE_ASC, true);
return true;
case R.id.queue_sort_date_desc:
QueueSorter.sort(getActivity(), QueueSorter.Rule.DATE_DESC, true);
return true;
case R.id.queue_sort_duration_asc:
QueueSorter.sort(getActivity(), QueueSorter.Rule.DURATION_ASC, true);
return true;
case R.id.queue_sort_duration_desc:
QueueSorter.sort(getActivity(), QueueSorter.Rule.DURATION_DESC, true);
return true;
default:
return false;
}
} else {
return true;
}
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
@ -215,8 +260,7 @@ public class QueueFragment extends Fragment {
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
FeedItem item = (FeedItem) listAdapter.getItem(position - listView.getHeaderViewsCount());
if (item != null) {
feedItemDialog = FeedItemDialog.newInstance(activity.get(), item, QueueAccess.ItemListAccess(queue));
feedItemDialog.show();
((MainActivity) getActivity()).loadChildFragment(ItemFragment.newInstance(item.getId()));
}
}
});
@ -268,11 +312,10 @@ public class QueueFragment extends Fragment {
downloadObserver.onResume();
}
listAdapter.notifyDataSetChanged();
if (feedItemDialog != null) {
feedItemDialog.updateContent(QueueAccess.ItemListAccess(queue), queue);
} else if (feedItemDialogSavedInstance != null) {
feedItemDialog = FeedItemDialog.newInstance(activity.get(), feedItemDialogSavedInstance);
}
// we need to refresh the options menu because it sometimes
// needs data that may have just been loaded.
getActivity().supportInvalidateOptionsMenu();
}
private DownloadObserver.Callback downloadObserverCallback = new DownloadObserver.Callback() {
@ -281,9 +324,6 @@ public class QueueFragment extends Fragment {
if (listAdapter != null && !blockDownloadObserverUpdate) {
listAdapter.notifyDataSetChanged();
}
if (feedItemDialog != null && feedItemDialog.isShowing()) {
feedItemDialog.updateMenuAppearance();
}
}
@Override
@ -325,6 +365,9 @@ public class QueueFragment extends Fragment {
public void update(EventDistributor eventDistributor, Integer arg) {
if ((arg & EVENTS) != 0) {
startItemLoader();
if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) {
getActivity().supportInvalidateOptionsMenu();
}
}
}
};

View File

@ -12,19 +12,23 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.ListView;
import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.SearchlistAdapter;
import de.danoeh.antennapod.dialog.FeedItemDialog;
import de.danoeh.antennapod.core.feed.*;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedComponent;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.SearchResult;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.FeedSearcher;
import de.danoeh.antennapod.core.util.QueueAccess;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
import de.danoeh.antennapod.menuhandler.NavDrawerActivity;
import java.util.List;
/**
* Performs a search operation on all feeds or one specific feed and displays the search result.
*/
@ -42,9 +46,6 @@ public class SearchFragment extends ListFragment {
private QueueAccess queue;
private FeedItemDialog feedItemDialog;
private FeedItemDialog.FeedItemDialogSavedInstance feedItemDialogSavedInstance;
/**
* Create a new SearchFragment that searches all feeds.
*/
@ -99,10 +100,6 @@ public class SearchFragment extends ListFragment {
super.onDestroyView();
searchAdapter = null;
viewCreated = false;
if (feedItemDialog != null) {
feedItemDialogSavedInstance = feedItemDialog.save();
}
feedItemDialog = null;
}
@Override
@ -128,11 +125,11 @@ public class SearchFragment extends ListFragment {
SearchResult result = (SearchResult) l.getAdapter().getItem(position);
FeedComponent comp = result.getComponent();
if (comp.getClass() == Feed.class) {
((MainActivity)getActivity()).loadFeedFragment(comp.getId());
((MainActivity) getActivity()).loadFeedFragment(comp.getId());
} else {
if (comp.getClass() == FeedItem.class) {
feedItemDialog = FeedItemDialog.newInstance(getActivity(), (FeedItem) comp, queue);
feedItemDialog.show();
FeedItem item = (FeedItem) comp;
((MainActivity) getActivity()).loadChildFragment(ItemFragment.newInstance(item.getId()));
}
}
}
@ -167,9 +164,6 @@ public class SearchFragment extends ListFragment {
private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
@Override
public void update(EventDistributor eventDistributor, Integer arg) {
if ((arg & (EventDistributor.DOWNLOAD_QUEUED)) != 0 && feedItemDialog != null) {
feedItemDialog.updateMenuAppearance();
}
if ((arg & (EventDistributor.UNREAD_ITEMS_UPDATE
| EventDistributor.DOWNLOAD_HANDLED
| EventDistributor.QUEUE_UPDATE)) != 0) {
@ -185,18 +179,6 @@ public class SearchFragment extends ListFragment {
}
searchAdapter.notifyDataSetChanged();
setListShown(true);
if (feedItemDialog != null && feedItemDialog.isShowing()) {
feedItemDialog.setQueue(queue);
for (SearchResult result : searchResults) {
FeedComponent comp = result.getComponent();
if (comp.getClass() == FeedItem.class && ((FeedItem) comp).getId() == feedItemDialog.getItem().getId()) {
feedItemDialog.setItem((FeedItem) comp);
}
}
feedItemDialog.updateMenuAppearance();
} else if (feedItemDialogSavedInstance != null) {
feedItemDialog = FeedItemDialog.newInstance(getActivity(), feedItemDialogSavedInstance);
}
}
private final SearchlistAdapter.ItemAccess itemAccess = new SearchlistAdapter.ItemAccess() {

View File

@ -1,85 +1,32 @@
package de.danoeh.antennapod.fragment.gpodnet;
import android.app.Activity;
import android.content.res.Resources;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBar;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
/**
* Main navigation hub for gpodder.net podcast directory
*/
public class GpodnetMainFragment extends Fragment {
private ViewPager pager;
private MainActivity activity;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View root = inflater.inflate(R.layout.pager_fragment, container, false);
pager = (ViewPager) root.findViewById(R.id.pager);
ViewPager pager = (ViewPager) root.findViewById(R.id.pager);
GpodnetPagerAdapter pagerAdapter = new GpodnetPagerAdapter(getChildFragmentManager(), getResources());
pager.setAdapter(pagerAdapter);
final ActionBar actionBar = activity.getMainActivtyActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
ActionBar.TabListener tabListener = new ActionBar.TabListener() {
@Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
pager.setCurrentItem(tab.getPosition());
}
@Override
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
}
@Override
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
}
};
actionBar.removeAllTabs();
actionBar.addTab(actionBar.newTab()
.setText(R.string.gpodnet_taglist_header)
.setTabListener(tabListener));
actionBar.addTab(actionBar.newTab()
.setText(R.string.gpodnet_toplist_header)
.setTabListener(tabListener));
actionBar.setTitle(R.string.gpodnet_main_label);
pager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageSelected(int position) {
super.onPageSelected(position);
actionBar.setSelectedNavigationItem(position);
}
});
return root;
}
@Override
public void onDestroyView() {
super.onDestroyView();
activity.getMainActivtyActionBar().removeAllTabs();
activity.getMainActivtyActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
this.activity = (MainActivity) activity;
}
public class GpodnetPagerAdapter extends FragmentPagerAdapter {

View File

@ -10,7 +10,7 @@ import de.danoeh.antennapod.core.R;
/**
* Utilities for menu items
*/
public class MenuItemUtils {
public class MenuItemUtils extends de.danoeh.antennapod.core.menuhandler.MenuItemUtils {
public static MenuItem addSearchItem(Menu menu, SearchView searchView) {
MenuItem item = menu.add(Menu.NONE, R.id.search_item, Menu.NONE, R.string.search_label);
@ -28,29 +28,4 @@ public class MenuItemUtils {
public static boolean isActivityDrawerOpen(NavDrawerActivity activity) {
return activity != null && activity.isDrawerOpen();
}
/**
* Changes the appearance of a MenuItem depending on whether the given UpdateRefreshMenuItemChecker
* is refreshing or not. If it returns true, the menu item will be replaced by an indeterminate progress
* bar, otherwise nothing will happen.
*
* @param menu The menu that the MenuItem belongs to
* @param resId The id of the MenuItem
* @param checker Is used for checking whether to show the progress indicator or not.
* @return The returned value of the UpdateRefreshMenuItemChecker's isRefreshing() method.
*/
public static boolean updateRefreshMenuItem(Menu menu, int resId, UpdateRefreshMenuItemChecker checker) {
// expand actionview if feeds are being downloaded, collapse otherwise
if (checker.isRefreshing()) {
MenuItem refreshItem = menu.findItem(resId);
MenuItemCompat.setActionView(refreshItem, de.danoeh.antennapod.R.layout.refresh_action_view);
return true;
} else {
return false;
}
}
public static interface UpdateRefreshMenuItemChecker {
public boolean isRefreshing();
}
}

View File

@ -24,6 +24,7 @@ import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.AboutActivity;
import de.danoeh.antennapod.activity.DirectoryChooserActivity;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.activity.PreferenceActivity;
import de.danoeh.antennapod.activity.PreferenceActivityGingerbread;
import de.danoeh.antennapod.asynctask.OpmlExportWorker;
@ -178,7 +179,7 @@ public class PreferenceController {
@Override
public boolean onPreferenceChange(
Preference preference, Object newValue) {
Intent i = activity.getIntent();
Intent i = new Intent(activity, MainActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK
| Intent.FLAG_ACTIVITY_NEW_TASK);
activity.finish();
@ -194,6 +195,7 @@ public class PreferenceController {
if (newValue instanceof Boolean) {
ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL_WIFI_FILTER).setEnabled((Boolean) newValue);
setSelectedNetworksEnabled((Boolean) newValue && UserPreferences.isEnableAutodownloadWifiFilter());
ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL_ON_BATTERY).setEnabled((Boolean) newValue);
}
return true;
}
@ -375,6 +377,8 @@ public class PreferenceController {
setSelectedNetworksEnabled(UserPreferences.isEnableAutodownload()
&& UserPreferences.isEnableAutodownloadWifiFilter());
ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL_ON_BATTERY)
.setEnabled(UserPreferences.isEnableAutodownload());
}
private void setEpisodeCacheSizeText(int cacheSize) {

View File

@ -0,0 +1,45 @@
package de.danoeh.antennapod.receiver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.BatteryManager;
import android.util.Log;
import de.danoeh.antennapod.core.BuildConfig;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DownloadRequester;
// modified from http://developer.android.com/training/monitoring-device-state/battery-monitoring.html
// and ConnectivityActionReceiver.java
public class PowerConnectionReceiver extends BroadcastReceiver {
private static final String TAG = "PowerConnectionReceiver";
@Override
public void onReceive(Context context, Intent intent) {
int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
status == BatteryManager.BATTERY_STATUS_FULL;
if (isCharging) {
Log.d(TAG, "charging, starting auto-download");
// we're plugged in, this is a great time to auto-download if everything else is
// right. So, even if the user allows auto-dl on battery, let's still start
// downloading now. They shouldn't mind.
// autodownloadUndownloadedItems will make sure we're on the right wifi networks,
// etc... so we don't have to worry about it.
DBTasks.autodownloadUndownloadedItems(context);
} else {
// if we're not supposed to be auto-downloading when we're not charging, stop it
if (!UserPreferences.isEnableAutodownloadOnBattery()) {
Log.d(TAG, "not charging anymore, canceling auto-download");
DownloadRequester.getInstance().cancelAllDownloads(context);
} else {
Log.d(TAG, "not charging anymore, but the user allows auto-download " +
"when on battery so we'll keep going");
}
}
}
}

View File

@ -1,188 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<FrameLayout
android:id="@+id/contentView"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="0.5">
</FrameLayout>
<RelativeLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="0.5"
android:background="?attr/non_transparent_background"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/navBar"
android:layout_width="fill_parent"
android:layout_height="60dp"
android:layout_alignParentTop="true">
<ImageButton
android:id="@+id/butNavLeft"
android:contentDescription="@string/show_shownotes_label"
android:layout_width="60dp"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:background="?attr/selectableItemBackground"
android:padding="4dp"/>
<ImageButton
android:id="@+id/butNavRight"
android:contentDescription="@string/show_chapters_label"
android:layout_width="60dp"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:background="?attr/selectableItemBackground"
android:padding="4dp"/>
<TextView
android:id="@+id/txtvTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="8dp"
android:layout_toLeftOf="@id/butNavRight"
android:layout_toRightOf="@id/butNavLeft"
android:ellipsize="marquee"
android:marqueeRepeatLimit="marquee_forever"
android:maxLines="1"
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/text_size_medium"
android:textStyle="bold"/>
<TextView
android:id="@+id/txtvFeed"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/txtvTitle"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_toLeftOf="@id/butNavRight"
android:layout_toRightOf="@id/butNavLeft"
android:ellipsize="marquee"
android:marqueeRepeatLimit="marquee_forever"
android:maxLines="1"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/text_size_small"/>
</RelativeLayout>
<View
android:id="@+id/navBarDivider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_below="@id/navBar"
android:background="@color/bright_blue"/>
<RelativeLayout
android:id="@+id/player_control"
android:layout_width="match_parent"
android:layout_height="80dp"
android:layout_alignParentBottom="true"
android:background="?attr/overlay_background">
<ImageButton
android:id="@+id/butPlay"
android:contentDescription="@string/pause_label"
android:layout_width="80dp"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
android:background="?attr/selectableItemBackground"
android:src="?attr/av_pause"/>
<ImageButton
android:id="@+id/butRev"
android:contentDescription="@string/rewind_label"
android:layout_width="60dp"
android:layout_height="match_parent"
android:layout_toLeftOf="@id/butPlay"
android:background="?attr/selectableItemBackground"
android:src="?attr/av_rew_big"/>
<ImageButton
android:id="@+id/butFF"
android:contentDescription="@string/fast_forward_label"
android:layout_width="60dp"
android:layout_height="match_parent"
android:layout_toRightOf="@id/butPlay"
android:background="?attr/selectableItemBackground"
android:src="?attr/av_ff_big"/>
<Button
android:id="@+id/butPlaybackSpeed"
android:layout_width="60dp"
android:layout_height="match_parent"
android:layout_toRightOf="@id/butFF"
android:background="?attr/selectableItemBackground"
android:src="?attr/av_fast_forward"
android:textColor="@color/gray"
android:textSize="@dimen/text_size_medium"
android:visibility="gone"/>
</RelativeLayout>
<RelativeLayout
android:id="@+id/playtime_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@id/player_control"
android:layout_alignParentLeft="true"
android:background="?attr/overlay_drawable">
<TextView
android:id="@+id/txtvPosition"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_marginLeft="8dp"
android:layout_marginTop="16dp"
android:text="@string/position_default_label"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/text_size_micro"/>
<TextView
android:id="@+id/txtvLength"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_centerVertical="true"
android:layout_marginRight="8dp"
android:layout_marginTop="16dp"
android:text="@string/position_default_label"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/text_size_micro"/>
<SeekBar
android:id="@+id/sbPosition"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="16dp"
android:layout_toLeftOf="@id/txtvLength"
android:layout_toRightOf="@id/txtvPosition"
android:max="500"/>
</RelativeLayout>
</RelativeLayout>
</LinearLayout>
<include layout="@layout/nav_list"/>
</android.support.v4.widget.DrawerLayout>

View File

@ -1,7 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
android:layout_height="match_parent"
tools:background="@android:color/darker_gray">
<RelativeLayout
android:id="@+id/footer"
@ -62,7 +64,9 @@
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:background="?attr/selectableItemBackground"
android:src="?attr/navigation_up" />
android:src="?attr/navigation_up"
tools:src="@drawable/navigation_up"
tools:background="@android:color/holo_green_dark" />
<TextView
android:id="@+id/txtvSelectedFolderLabel"
@ -74,7 +78,8 @@
android:layout_marginTop="8dp"
android:layout_toRightOf="@id/butNavUp"
android:text="@string/selected_folder_label"
android:textStyle="bold" >
android:textStyle="bold"
tools:background="@android:color/holo_green_dark">
</TextView>
<TextView
@ -87,7 +92,9 @@
android:layout_toRightOf="@id/butNavUp"
android:ellipsize="start"
android:scrollHorizontally="true"
android:singleLine="true" />
android:singleLine="true"
tools:text="/path/to/selected/folder"
tools:background="@android:color/holo_green_dark"/>
<View
android:id="@+id/divider"

View File

@ -1,173 +1,198 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
tools:background="@android:color/holo_red_dark">
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/non_transparent_background"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/navBar"
android:layout_width="fill_parent"
android:layout_height="60dp"
android:layout_alignParentTop="true">
<ImageButton
android:id="@+id/butNavLeft"
android:contentDescription="@string/show_shownotes_label"
android:layout_width="60dp"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:background="?attr/selectableItemBackground"
android:padding="4dp"/>
<ImageButton
android:id="@+id/butNavRight"
android:contentDescription="@string/show_chapters_label"
android:layout_width="60dp"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:background="?attr/selectableItemBackground"
android:padding="4dp"/>
<TextView
android:id="@+id/txtvTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="8dp"
android:layout_toLeftOf="@id/butNavRight"
android:layout_toRightOf="@id/butNavLeft"
android:ellipsize="marquee"
android:marqueeRepeatLimit="marquee_forever"
android:maxLines="2"
android:textColor="?android:attr/textColorPrimary"
android:textSize="16sp"
android:fontFamily="sans-serif-light"
/>
</RelativeLayout>
<View
android:id="@+id/navBarDivider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_below="@id/navBar"
android:background="@color/bright_blue"/>
<RelativeLayout
android:id="@+id/player_control"
android:layout_width="match_parent"
android:layout_height="80dp"
android:layout_alignParentBottom="true"
android:background="?attr/overlay_background">
<ImageButton
android:id="@+id/butPlay"
android:contentDescription="@string/pause_label"
android:layout_width="80dp"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
android:background="?attr/selectableItemBackground"
android:src="?attr/av_pause"/>
<ImageButton
android:id="@+id/butRev"
android:contentDescription="@string/rewind_label"
android:layout_width="80dp"
android:layout_height="match_parent"
android:layout_toLeftOf="@id/butPlay"
android:background="?attr/selectableItemBackground"
android:src="?attr/av_rew_big"/>
<ImageButton
android:id="@+id/butFF"
android:contentDescription="@string/fast_forward_label"
android:layout_width="80dp"
android:layout_height="match_parent"
android:layout_toRightOf="@id/butPlay"
android:background="?attr/selectableItemBackground"
android:src="?attr/av_ff_big"/>
<Button
android:id="@+id/butPlaybackSpeed"
android:contentDescription="@string/set_playback_speed_label"
android:layout_width="80dp"
android:layout_height="match_parent"
android:layout_toRightOf="@id/butFF"
android:background="?attr/selectableItemBackground"
android:src="?attr/av_fast_forward"
android:textColor="@color/gray"
android:textSize="@dimen/text_size_medium"
android:visibility="gone"/>
</RelativeLayout>
<RelativeLayout
android:id="@+id/playtime_layout"
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@id/player_control"
android:layout_alignParentLeft="true"
android:background="?attr/overlay_drawable">
android:background="?attr/colorPrimary"
android:minHeight="?attr/actionBarSize"
tools:background="@android:color/darker_gray">
<TextView
android:id="@+id/txtvPosition"
android:layout_width="wrap_content"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:paddingLeft="8dp"
android:paddingRight="8dp">
<TextView
android:id="@+id/txtvTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:ellipsize="end"
android:gravity="left"
android:maxLines="2"
android:textColor="?android:attr/textColorPrimary"
android:textSize="16sp"
tools:text="Audio title"
tools:background="@android:color/holo_green_dark" />
<ImageButton
android:id="@+id/butCover"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="center_vertical"
android:layout_marginLeft="8dp"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/show_cover_label"
android:gravity="right"
tools:src="@drawable/ic_stat_antenna_default"
tools:background="@android:color/holo_green_dark" />
</LinearLayout>
</android.support.v7.widget.Toolbar>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/non_transparent_background"
android:foreground="?android:windowContentOverlay"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/player_control"
android:layout_width="match_parent"
android:layout_height="@dimen/audioplayer_playercontrols_length"
android:layout_alignParentBottom="true"
android:background="?attr/overlay_background"
tools:background="@android:color/holo_purple">
<ImageButton
android:id="@+id/butPlay"
android:layout_width="@dimen/audioplayer_playercontrols_length"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/pause_label"
android:src="?attr/av_pause"
tools:src="@drawable/ic_pause_white_36dp"
tools:background="@android:color/holo_green_dark" />
<ImageButton
android:id="@+id/butRev"
android:layout_width="@dimen/audioplayer_playercontrols_length"
android:layout_height="match_parent"
android:layout_toLeftOf="@id/butPlay"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/rewind_label"
android:src="?attr/av_rew_big"
tools:src="@drawable/ic_fast_rewind_white_36dp"
tools:background="@android:color/holo_blue_dark" />
<ImageButton
android:id="@+id/butFF"
android:layout_width="@dimen/audioplayer_playercontrols_length"
android:layout_height="match_parent"
android:layout_toRightOf="@id/butPlay"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/fast_forward_label"
android:src="?attr/av_ff_big"
tools:src="@drawable/ic_fast_forward_white_36dp"
tools:background="@android:color/holo_blue_dark" />
<Button
android:id="@+id/butPlaybackSpeed"
android:layout_width="@dimen/audioplayer_playercontrols_length"
android:layout_height="match_parent"
android:layout_toRightOf="@id/butFF"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/set_playback_speed_label"
android:src="?attr/av_fast_forward"
android:textColor="@color/gray"
android:textSize="@dimen/text_size_medium"
android:visibility="gone"
tools:background="@android:color/holo_green_dark" />
<ImageButton
android:id="@+id/butNavChaptersShownotes"
android:layout_width="@dimen/audioplayer_playercontrols_length"
android:layout_height="match_parent"
android:layout_toLeftOf="@id/butRev"
android:background="?attr/selectableItemBackground"
android:scaleType="centerInside"
android:src="@drawable/ic_toc_white_36dp"
tools:background="@android:color/holo_green_dark" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/playtime_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@id/player_control"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_marginLeft="8dp"
android:layout_marginTop="16dp"
android:text="@string/position_default_label"
android:textColor="?android:attr/textColorSecondary"
android:fontFamily="sans-serif-light"
android:textSize="@dimen/text_size_micro"/>
android:background="?attr/overlay_drawable">
<TextView
android:id="@+id/txtvLength"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
<TextView
android:id="@+id/txtvPosition"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_marginLeft="8dp"
android:layout_marginTop="16dp"
android:text="@string/position_default_label"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/text_size_micro"
tools:background="@android:color/holo_green_dark" />
<TextView
android:id="@+id/txtvLength"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_centerVertical="true"
android:layout_marginRight="8dp"
android:layout_marginTop="16dp"
android:text="@string/position_default_label"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/text_size_micro"
tools:background="@android:color/holo_green_dark" />
<SeekBar
android:id="@+id/sbPosition"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="16dp"
android:layout_toLeftOf="@id/txtvLength"
android:layout_toRightOf="@id/txtvPosition"
android:max="500"
tools:background="@android:color/holo_green_dark" />
</RelativeLayout>
<FrameLayout
android:id="@+id/contentView"
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_above="@id/playtime_layout"
android:layout_alignParentTop="true"
android:layout_centerVertical="true"
android:layout_marginRight="8dp"
android:layout_marginTop="16dp"
android:text="@string/position_default_label"
android:textColor="?android:attr/textColorSecondary"
android:fontFamily="sans-serif-light"
android:textSize="@dimen/text_size_micro"/>
android:foreground="?android:windowContentOverlay"
tools:background="@android:color/holo_orange_light" />
<SeekBar
android:id="@+id/sbPosition"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="16dp"
android:layout_toLeftOf="@id/txtvLength"
android:layout_toRightOf="@id/txtvPosition"
android:max="500"/>
</RelativeLayout>
<FrameLayout
android:id="@+id/contentView"
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_above="@id/playtime_layout"
android:layout_below="@id/navBarDivider">
</FrameLayout>
</RelativeLayout>
</LinearLayout>
<include layout="@layout/nav_list"/>
<include layout="@layout/nav_list" />
</android.support.v4.widget.DrawerLayout>

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/cover_fragment_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
@ -11,9 +12,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:adjustViewBounds="true"
android:scaleType="centerInside" />
android:scaleType="centerCrop"
tools:src="@android:drawable/sym_def_app_icon" />
</RelativeLayout>

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
@ -40,7 +41,9 @@
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:background="?attr/selectableItemBackground"
android:src="?attr/navigation_up" />
android:src="?attr/navigation_up"
tools:src="@drawable/navigation_up"
tools:background="@android:color/holo_green_dark" />
<TextView
android:id="@+id/txtvSelectedFolderLabel"
@ -52,7 +55,8 @@
android:layout_marginTop="8dp"
android:layout_toRightOf="@id/butNavUp"
android:text="@string/selected_folder_label"
android:textStyle="bold" >
android:textStyle="bold"
tools:background="@android:color/holo_blue_bright">
</TextView>
<TextView
@ -65,7 +69,9 @@
android:layout_toRightOf="@id/butNavUp"
android:ellipsize="start"
android:scrollHorizontally="true"
android:singleLine="true" />
android:singleLine="true"
tools:text="/path/to/selected/folder"
tools:background="@android:color/holo_blue_bright"/>
<View
android:id="@+id/divider"

View File

@ -1,9 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="@dimen/listitem_threeline_height"
android:orientation="horizontal">
android:orientation="horizontal"
tools:background="@android:color/darker_gray">
<ImageView
android:id="@+id/imgvImage"
@ -14,7 +16,9 @@
android:layout_marginLeft="@dimen/listitem_threeline_horizontalpadding"
android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
android:contentDescription="@string/cover_label"
android:scaleType="centerCrop" />
android:scaleType="centerCrop"
tools:src="@drawable/ic_stat_antenna_default"
tools:background="@android:color/holo_green_dark" />
<RelativeLayout
android:layout_width="0dp"
@ -22,7 +26,8 @@
android:layout_marginLeft="@dimen/listitem_threeline_textleftpadding"
android:layout_marginRight="@dimen/listitem_threeline_textrightpadding"
android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
android:layout_weight="1">
android:layout_weight="1"
tools:background="@android:color/holo_red_dark">
<TextView
android:id="@+id/txtvTitle"
@ -33,7 +38,9 @@
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_centerVertical="true"
android:layout_marginBottom="4dp" />
android:layout_marginBottom="4dp"
tools:text="Downloaded episode title"
tools:background="@android:color/holo_green_dark" />
<TextView
android:id="@+id/txtvSize"
@ -41,7 +48,9 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@id/txtvTitle" />
android:layout_below="@id/txtvTitle"
tools:text="23 MB"
tools:background="@android:color/holo_green_dark" />
<TextView
android:id="@+id/txtvPublished"
@ -49,7 +58,9 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_below="@id/txtvTitle" />
android:layout_below="@id/txtvTitle"
tools:text="Jan 23"
tools:background="@android:color/holo_green_dark" />
</RelativeLayout>
@ -64,6 +75,8 @@
android:contentDescription="@string/remove_episode_lable"
android:focusable="false"
android:focusableInTouchMode="false"
android:src="?attr/content_discard" />
android:src="?attr/content_discard"
tools:src="@drawable/ic_delete_white_24dp"
tools:background="@android:color/holo_green_dark" />
</LinearLayout>

View File

@ -1,8 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="@dimen/listitem_threeline_height"
android:orientation="horizontal">
android:orientation="horizontal"
tools:background="@android:color/darker_gray">
<LinearLayout
android:layout_width="0dp"
@ -19,7 +21,9 @@
android:ellipsize="end"
android:lines="1"
android:textColor="?android:attr/textColorPrimary"
android:textSize="16sp" />
android:textSize="16sp"
tools:text="Download item title"
tools:background="@android:color/holo_green_dark" />
<ProgressBar
android:id="@+id/progProgress"
@ -29,7 +33,8 @@
android:layout_marginBottom="4dp"
android:layout_marginLeft="@dimen/listitem_threeline_horizontalpadding"
android:layout_marginRight="@dimen/listitem_threeline_horizontalpadding"
android:layout_marginTop="4dp" />
android:layout_marginTop="4dp"
tools:background="@android:color/holo_blue_light" />
<RelativeLayout
android:layout_width="match_parent"
@ -46,7 +51,9 @@
android:ellipsize="end"
android:lines="1"
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/text_size_small" />
android:textSize="@dimen/text_size_small"
tools:text="21 MB / 42 MB"
tools:background="@android:color/holo_green_dark" />
<TextView
android:id="@+id/txtvPercent"
@ -56,7 +63,9 @@
android:ellipsize="end"
android:lines="1"
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/text_size_small" />
android:textSize="@dimen/text_size_small"
tools:text="50%"
tools:background="@android:color/holo_green_dark" />
</RelativeLayout>
</LinearLayout>
@ -72,5 +81,7 @@
android:contentDescription="@string/cancel_download_label"
android:focusable="false"
android:focusableInTouchMode="false"
android:src="?attr/navigation_cancel" />
</LinearLayout>
android:src="?attr/navigation_cancel"
tools:src="@drawable/ic_cancel_white_24dp"
tools:background="@android:color/holo_green_dark" />
</LinearLayout>

View File

@ -1,8 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
android:orientation="vertical"
tools:background="@android:color/darker_gray">
<RelativeLayout
android:layout_width="match_parent"
@ -17,7 +19,9 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginLeft="@dimen/listitem_threeline_textleftpadding" />
android:layout_marginLeft="@dimen/listitem_threeline_textleftpadding"
tools:text="Media file"
tools:background="@android:color/holo_green_dark" />
<TextView
android:id="@+id/txtvTitle"
@ -26,7 +30,9 @@
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_toLeftOf="@id/txtvType" />
android:layout_toLeftOf="@id/txtvType"
tools:text="Download item title"
tools:background="@android:color/holo_blue_light" />
</RelativeLayout>
<RelativeLayout
@ -42,14 +48,18 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_marginRight="8dp" />
android:layout_marginRight="8dp"
tools:text="January 23"
tools:background="@android:color/holo_green_dark" />
<TextView
android:id="@+id/txtvStatus"
style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true" />
android:layout_alignParentRight="true"
tools:text="successful"
tools:background="@android:color/holo_green_dark" />
</RelativeLayout>
@ -61,6 +71,8 @@
android:layout_marginLeft="@dimen/listitem_threeline_horizontalpadding"
android:layout_marginRight="@dimen/listitem_threeline_horizontalpadding"
android:textColor="?android:attr/textColorTertiary"
android:textSize="@dimen/text_size_micro" />
android:textSize="@dimen/text_size_micro"
tools:text="@string/design_time_downloaded_log_failure_reason"
tools:background="@android:color/holo_green_dark" />
</LinearLayout>

View File

@ -1,9 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
tools:background="@android:color/darker_gray">
<TextView
android:id="@+id/txtvTitle"
@ -14,6 +16,8 @@
android:lines="1"
android:singleLine="true"
android:layout_margin="16dp"
android:ellipsize="start"/>
android:ellipsize="start"
tools:text="List item title"
tools:background="@android:color/holo_green_dark"/>
</LinearLayout>

View File

@ -1,11 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/fragmentLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone"
android:background="?attr/colorPrimary">
android:background="?attr/colorPrimary"
tools:visibility="visible"
tools:background="@android:color/darker_gray">
<View
android:layout_width="match_parent"
@ -32,7 +35,9 @@
android:padding="4dp"
android:adjustViewBounds="true"
android:cropToPadding="true"
android:scaleType="fitXY"/>
android:scaleType="fitXY"
tools:src="@drawable/ic_stat_antenna_default"
tools:background="@android:color/holo_green_dark" />
<TextView
android:id="@+id/txtvTitle"
@ -46,7 +51,9 @@
android:ellipsize="end"
android:maxLines="2"
android:textSize="18sp"
android:fontFamily="sans-serif-light"/>
android:fontFamily="sans-serif-light"
tools:text="Playback item title"
tools:background="@android:color/holo_green_dark"/>
</RelativeLayout>
<ImageButton
@ -54,7 +61,9 @@
android:contentDescription="@string/pause_label"
android:layout_width="@dimen/external_player_height"
android:layout_height="@dimen/external_player_height"
android:background="?attr/selectableItemBackground"/>
android:background="?attr/selectableItemBackground"
tools:src="@drawable/ic_play_arrow_white_36dp"
tools:background="@android:color/holo_green_dark"/>
</LinearLayout>
</LinearLayout>

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
@ -19,7 +20,9 @@
android:layout_height="70dp"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_margin="4dp"/>
android:layout_margin="4dp"
tools:src="@drawable/ic_stat_antenna_default"
tools:background="@android:color/holo_green_dark" />
<TextView
android:id="@+id/txtvTitle"
@ -28,7 +31,9 @@
android:layout_centerVertical="true"
android:layout_margin="4dp"
android:layout_toRightOf="@id/imgvCover"
style="@style/AntennaPod.TextView.Heading"/>
style="@style/AntennaPod.TextView.Heading"
tools:text="Feed title"
tools:background="@android:color/holo_green_dark" />
<View
android:id="@+id/divider"
@ -67,7 +72,8 @@
android:layout_alignParentLeft="true"
android:layout_margin="8dp"
android:text="@string/author_label"
android:textColor="?android:attr/textColorPrimary"/>
android:textColor="?android:attr/textColorPrimary"
tools:background="@android:color/holo_red_light" />
<TextView
android:id="@+id/txtvAuthor"
@ -75,7 +81,9 @@
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_margin="8dp"
android:layout_toRightOf="@id/center_divider"/>
android:layout_toRightOf="@id/center_divider"
tools:text="Daniel Oeh"
tools:background="@android:color/holo_green_dark" />
<TextView
android:id="@+id/lblLanguage"
@ -85,7 +93,8 @@
android:layout_below="@id/txtvAuthor"
android:layout_margin="8dp"
android:text="@string/language_label"
android:textColor="?android:attr/textColorPrimary"/>
android:textColor="?android:attr/textColorPrimary"
tools:background="@android:color/holo_red_light" />
<TextView
android:id="@+id/txtvLanguage"
@ -94,7 +103,9 @@
android:layout_alignParentRight="true"
android:layout_below="@id/txtvAuthor"
android:layout_margin="8dp"
android:layout_toRightOf="@id/center_divider"/>
android:layout_toRightOf="@id/center_divider"
tools:text="English"
tools:background="@android:color/holo_green_dark" />
</RelativeLayout>
<TextView
@ -115,7 +126,8 @@
android:layout_margin="8dp"
android:text="@string/auto_download_label"
android:enabled="false"
android:textColor="?android:attr/textColorPrimary"/>
android:textColor="?android:attr/textColorPrimary"
tools:background="@android:color/holo_red_light" />
<TextView
android:id="@+id/txtvAuthentication"
@ -196,7 +208,9 @@
android:id="@+id/txtvDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"/>
android:layout_margin="8dp"
android:text="@string/design_time_lorem_ipsum"
tools:background="@android:color/holo_green_dark" />
</LinearLayout>
</ScrollView>

View File

@ -1,73 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/contentContainer"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/txtvTitle"
style="@style/AntennaPod.Dialog.Title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_margin="16dp"
android:ellipsize="none"
android:maxLines="5" />
<View
android:id="@+id/title_divider"
android:layout_width="match_parent"
android:layout_height="2dp"
android:layout_below="@id/txtvTitle"
android:background="@color/bright_blue" />
<LinearLayout
android:id="@+id/header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/title_divider"
android:orientation="horizontal">
<ImageButton
android:id="@+id/butAction1"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_weight="1"
android:background="?attr/selectableItemBackground"
tools:ignore="ContentDescription" />
<ImageButton
android:id="@+id/butAction2"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_weight="1"
android:background="?attr/selectableItemBackground"
tools:ignore="ContentDescription" />
<ImageButton
android:id="@+id/butMoreActions"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_weight="1"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/butAction_label"
android:src="?attr/ic_action_overflow" />
</LinearLayout>
<View
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="2dp"
android:layout_below="@id/header"
android:background="@color/bright_blue" />
<WebView
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_alignParentBottom="true"
android:layout_below="@id/divider" />
</RelativeLayout>

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/content_root"
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView
android:id="@+id/webvDescription"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foreground="?android:windowContentOverlay" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
android:id="@+id/progbarLoading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminate="true" />
</FrameLayout>
</FrameLayout>

View File

@ -0,0 +1,114 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:background="?attr/colorPrimary"
android:gravity="center_horizontal"
android:orientation="vertical"
tools:background="@android:color/darker_gray">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:orientation="horizontal"
android:paddingBottom="8dp">
<ImageView
android:id="@+id/imgvCover"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="center_vertical"
android:layout_marginBottom="8dp"
android:layout_marginTop="16dp"
android:contentDescription="@string/cover_label"
android:gravity="center_vertical"
tools:src="@drawable/ic_stat_antenna_default"
tools:background="@android:color/holo_green_dark" />
<ImageButton
android:id="@+id/butMoreActions"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/butAction_label"
android:paddingTop="4dp"
android:src="?attr/ic_action_overflow"
tools:src="@drawable/ic_info_white_24dp"
tools:background="@android:color/holo_green_dark" />
<TextView
android:id="@+id/txtvTitle"
style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginBottom="8dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="8dp"
android:layout_marginTop="16dp"
android:layout_toLeftOf="@id/butMoreActions"
android:layout_toRightOf="@id/imgvCover"
android:maxLines="5"
tools:text="Podcast title"
tools:background="@android:color/holo_green_dark" />
</RelativeLayout>
<ProgressBar
android:id="@+id/progbarDownload"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:visibility="gone" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:layout_marginRight="8dp"
android:orientation="horizontal"
tools:background="@android:color/holo_blue_bright">
<Button
android:id="@+id/butAction1"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_gravity="center_vertical"
android:layout_marginRight="8dp"
android:layout_weight="1"
android:background="?attr/selectableItemBackground"
android:ellipsize="end"
android:paddingTop="4dp"
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/text_size_small"
tools:text="Button 1"
tools:background="@android:color/holo_red_light" />
<Button
android:id="@+id/butAction2"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_gravity="center_vertical"
android:layout_marginLeft="8dp"
android:layout_weight="1"
android:background="?attr/selectableItemBackground"
android:ellipsize="end"
android:paddingTop="4dp"
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/text_size_small"
tools:text="Button 2"
tools:background="@android:color/holo_orange_dark" />
</LinearLayout>
</LinearLayout>

View File

@ -4,7 +4,14 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="@dimen/feeditemlist_header_height"
tools:context="de.danoeh.antennapod.activity.MainActivity">
tools:context="de.danoeh.antennapod.activity.MainActivity"
tools:background="@android:color/darker_gray">
<ImageView
android:id="@+id/imgvBackground"
style="@style/BigBlurryBackground"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ImageView
android:id="@+id/imgvCover"
@ -16,7 +23,9 @@
android:layout_marginBottom="16dp"
android:layout_marginLeft="16dp"
android:layout_marginTop="16dp"
android:contentDescription="@string/cover_label" />
android:contentDescription="@string/cover_label"
tools:src="@drawable/ic_stat_antenna_default"
tools:background="@android:color/holo_green_dark"/>
<ImageButton
android:id="@+id/butShowInfo"
@ -29,7 +38,8 @@
android:layout_marginTop="8dp"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/show_info_label"
android:src="?attr/action_about" />
android:src="@drawable/ic_info_white_24dp"
tools:background="@android:color/holo_green_dark"/>
<TextView
android:id="@+id/txtvTitle"
@ -43,7 +53,12 @@
android:layout_toLeftOf="@id/butShowInfo"
android:layout_toRightOf="@id/imgvCover"
android:ellipsize="end"
android:maxLines="2" />
android:maxLines="2"
android:shadowColor="@color/black"
android:shadowRadius="3"
android:textColor="@color/white"
tools:text="Podcast title"
tools:background="@android:color/holo_green_dark" />
<TextView
android:id="@+id/txtvAuthor"
@ -56,8 +71,12 @@
android:layout_toRightOf="@id/imgvCover"
android:ellipsize="end"
android:lines="1"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/text_size_small" />
android:shadowColor="@color/black"
android:shadowRadius="3"
android:textColor="@color/white"
android:textSize="@dimen/text_size_small"
tools:text="Podcast author"
tools:background="@android:color/holo_green_dark" />
</RelativeLayout>

View File

@ -4,14 +4,16 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="@dimen/listitem_threeline_height"
android:orientation="horizontal">
android:orientation="horizontal"
tools:background="@android:color/darker_gray">
<RelativeLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginLeft="@dimen/listitem_threeline_horizontalpadding"
android:layout_weight="1">
android:layout_weight="1"
tools:background="@android:color/holo_orange_dark">
<TextView
android:id="@+id/statusUnread"
@ -20,7 +22,9 @@
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_margin="16dp" />
android:layout_margin="16dp"
tools:text="Status unread"
tools:background="@android:color/white" />
<TextView
android:id="@+id/txtvItemname"
@ -31,7 +35,9 @@
android:layout_alignParentTop="true"
android:layout_marginBottom="8dp"
android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
android:layout_toLeftOf="@id/statusUnread" />
android:layout_toLeftOf="@id/statusUnread"
tools:text="Feed item name"
tools:background="@android:color/holo_green_dark" />
<ImageView
@ -43,7 +49,9 @@
android:layout_marginRight="4dp"
android:contentDescription="@string/in_queue_label"
android:src="?attr/stat_playlist"
android:visibility="visible" />
android:visibility="visible"
tools:src="@drawable/ic_list_white_24dp"
tools:background="@android:color/holo_red_light" />
<ImageView
android:id="@+id/imgvType"
@ -52,7 +60,9 @@
android:layout_below="@id/txtvItemname"
android:layout_marginRight="4dp"
android:layout_toLeftOf="@+id/imgvInPlaylist"
tools:ignore="ContentDescription" />
tools:ignore="ContentDescription"
tools:src="@drawable/ic_hearing_white_18dp"
tools:background="@android:color/holo_red_light" />
<TextView
android:id="@+id/txtvLenSize"
@ -60,7 +70,9 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@id/txtvItemname" />
android:layout_below="@id/txtvItemname"
tools:text="00:42:23"
tools:background="@android:color/holo_green_dark" />
<ProgressBar
android:id="@+id/pbar_episode_progress"
@ -71,7 +83,8 @@
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:layout_toLeftOf="@id/imgvType"
android:layout_toRightOf="@id/txtvLenSize" />
android:layout_toRightOf="@id/txtvLenSize"
tools:background="@android:color/holo_blue_light" />
<TextView
@ -81,7 +94,9 @@
android:layout_height="wrap_content"
android:layout_below="@id/txtvItemname"
android:layout_marginRight="4dp"
android:layout_toLeftOf="@id/imgvType" />
android:layout_toLeftOf="@id/imgvType"
tools:text="Jan 23"
tools:background="@android:color/holo_green_dark" />
</RelativeLayout>
<include layout="@layout/vertical_list_divider"/>

View File

@ -35,7 +35,10 @@
android:layout_margin="16dp"
android:textAlignment="center"
android:textSize="@dimen/text_size_small"
android:visibility="gone" />
android:visibility="gone"
tools:visibility="visible"
tools:text="Error message"
tools:background="@android:color/holo_red_light" />
<Button
android:id="@+id/butRetry"
@ -45,5 +48,7 @@
android:layout_centerHorizontal="true"
android:layout_margin="16dp"
android:text="@string/retry_label"
android:visibility="gone" />
</RelativeLayout>
android:visibility="gone"
tools:visibility="visible"
tools:background="@android:color/holo_red_light" />
</RelativeLayout>

View File

@ -1,8 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="@dimen/listitem_threeline_height">
android:layout_height="@dimen/listitem_threeline_height"
tools:background="@android:color/darker_gray">
<ImageView
android:id="@+id/imgvCover"
@ -17,7 +19,9 @@
android:adjustViewBounds="true"
android:contentDescription="@string/cover_label"
android:cropToPadding="true"
android:scaleType="fitXY" />
android:scaleType="fitXY"
tools:src="@drawable/ic_stat_antenna_default"
tools:background="@android:color/holo_green_dark" />
<TextView
android:id="@+id/txtvTitle"
@ -28,5 +32,7 @@
android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
android:layout_marginRight="@dimen/listitem_threeline_horizontalpadding"
android:layout_toRightOf="@id/imgvCover"
android:maxLines="1" />
</RelativeLayout>
android:maxLines="1"
tools:text="Podcast title"
tools:background="@android:color/holo_green_dark" />
</RelativeLayout>

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
@ -61,7 +62,9 @@
android:maxLines="2"
android:ellipsize="end"
android:gravity="center"
android:layout_margin="16dp"/>
android:layout_margin="16dp"
tools:text="Error message"
tools:background="@android:color/holo_green_dark" />
<ProgressBar
android:id="@+id/progBarLogin"

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
@ -70,7 +71,8 @@
android:textColor="@color/download_failed_red"
android:layout_margin="16dp"
android:textSize="@dimen/text_size_small"
/>
tools:text="Error message"
tools:background="@android:color/holo_green_dark" />
<ProgressBar
android:id="@+id/progbarCreateDevice"

View File

@ -1,16 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
android:orientation="vertical"
tools:background="@android:color/holo_orange_light">
<TextView
android:id="@+id/txtvTitle"
style="@style/AntennaPod.TextView.ListItemPrimaryTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp" />
android:layout_margin="16dp"
tools:text="Feed item title"
tools:background="@android:color/holo_green_dark" />
<TextView
android:id="@+id/txtvDescription"
@ -20,5 +24,7 @@
android:layout_marginBottom="16dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:lines="3" />
</LinearLayout>
android:lines="3"
tools:text="Feed item description"
tools:background="@android:color/holo_green_dark" />
</LinearLayout>

View File

@ -1,31 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/playerFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"/>
android:layout_alignParentBottom="true"
tools:layout_height="64dp"
tools:background="@android:color/holo_green_light" />
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:background="?attr/colorPrimary"
android:minHeight="?attr/actionBarSize"
tools:background="@android:color/holo_blue_dark" />
<FrameLayout
android:id="@+id/main_view"
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_alignParentTop="true"
android:layout_above="@id/playerFragment"/>
android:layout_above="@id/playerFragment"
android:layout_below="@id/toolbar"
android:foreground="?android:windowContentOverlay"
tools:background="@android:color/holo_red_dark" />
</RelativeLayout>
<include layout="@layout/nav_list"/>
<include layout="@layout/nav_list" />
</android.support.v4.widget.DrawerLayout>

View File

@ -1,9 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="@dimen/listitem_iconwithtext_height">
android:layout_height="@dimen/listitem_iconwithtext_height"
tools:background="@android:color/darker_gray">
<ImageView
@ -18,7 +20,9 @@
android:scaleType="fitXY"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:layout_marginLeft="@dimen/listitem_icon_leftpadding"/>
android:layout_marginLeft="@dimen/listitem_icon_leftpadding"
tools:src="@drawable/ic_stat_antenna_default"
tools:background="@android:color/holo_green_dark"/>
<TextView
@ -36,5 +40,7 @@
android:layout_marginBottom="@dimen/listitem_iconwithtext_textverticalpadding"
android:layout_marginRight="@dimen/listitem_icon_rightpadding"
android:layout_toRightOf="@id/imgvCover"
tools:text="Navigation feed item title"
tools:background="@android:color/holo_green_dark"
/>
</RelativeLayout>

View File

@ -1,14 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/nav_list"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nav_layout"
android:layout_width="@dimen/drawer_width"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="?attr/nav_drawer_background"
android:choiceMode="singleChoice"
android:clipToPadding="false"
android:divider="@android:color/transparent"
android:dividerHeight="0dp"
android:paddingBottom="@dimen/list_vertical_padding"
android:paddingTop="@dimen/list_vertical_padding"
android:scrollbarStyle="outsideOverlay" />
android:orientation="vertical">
<ListView
android:id="@+id/nav_list"
android:layout_width="@dimen/drawer_width"
android:layout_height="0dp"
android:layout_weight="1"
android:choiceMode="singleChoice"
android:clipToPadding="false"
android:divider="@android:color/transparent"
android:dividerHeight="0dp"
android:paddingBottom="@dimen/list_vertical_padding"
android:paddingTop="@dimen/list_vertical_padding"
android:scrollbarStyle="outsideOverlay"
tools:listitem="@layout/nav_listitem"
tools:background="@android:color/holo_purple" />
<View
android:layout_width="@dimen/drawer_width"
android:layout_height="1dp"
android:layout_centerVertical="true"
android:background="?android:attr/listDivider"
tools:background="@android:color/holo_red_dark" />
<LinearLayout
android:id="@+id/nav_settings"
android:layout_width="@dimen/drawer_width"
android:layout_height="@dimen/listitem_iconwithtext_height"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/settings_label"
android:orientation="horizontal">
<ImageView
android:id="@+id/imgvCover"
android:layout_width="@dimen/thumbnail_length_navlist"
android:layout_height="@dimen/thumbnail_length_navlist"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_marginBottom="8dp"
android:layout_marginLeft="@dimen/listitem_icon_leftpadding"
android:layout_marginTop="8dp"
android:adjustViewBounds="true"
android:contentDescription="@string/cover_label"
android:cropToPadding="true"
android:padding="8dp"
android:scaleType="centerCrop"
android:src="?attr/ic_settings"
tools:src="@android:drawable/sym_def_app_icon"
tools:background="@android:color/holo_orange_dark" />
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:layout_margin="16dp"
android:layout_weight="1"
android:gravity="center_vertical"
android:text="@string/settings_label"
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/text_size_navdrawer"
tools:background="@android:color/holo_green_light"/>
</LinearLayout>
</LinearLayout>

View File

@ -1,9 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="@dimen/listitem_iconwithtext_height">
android:layout_height="@dimen/listitem_iconwithtext_height"
tools:background="@android:color/darker_gray">
<ImageView
android:id="@+id/imgvCover"
@ -18,7 +20,9 @@
android:padding="8dp"
android:layout_marginLeft="@dimen/listitem_icon_leftpadding"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"/>
android:layout_marginBottom="8dp"
tools:src="@drawable/ic_new_releases_white_24dp"
tools:background="@android:color/holo_green_dark"/>
<TextView
@ -36,6 +40,8 @@
android:layout_marginBottom="@dimen/listitem_iconwithtext_textverticalpadding"
android:layout_marginRight="48dp"
android:layout_toRightOf="@id/imgvCover"
tools:text="Navigation item title"
tools:background="@android:color/holo_green_dark"
/>
<TextView
@ -50,5 +56,7 @@
android:layout_marginBottom="14dp"
android:layout_marginRight="@dimen/listitem_icon_rightpadding"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"/>
</RelativeLayout>
android:layout_centerVertical="true"
tools:text="23"
tools:background="@android:color/holo_green_dark"/>
</RelativeLayout>

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="24dp"
android:background="@android:color/transparent"
@ -10,5 +11,6 @@
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_centerVertical="true"
android:background="@color/gray" />
</RelativeLayout>
android:background="?android:attr/listDivider"
tools:background="@android:color/holo_red_dark"/>
</RelativeLayout>

View File

@ -2,6 +2,7 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:dslv="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
@ -25,7 +26,8 @@
dslv:sort_enabled="false"
dslv:track_drag_sort="false"
dslv:float_background_color="?attr/dragview_float_background"
dslv:use_default_controller="true"/>
dslv:use_default_controller="true"
tools:background="@android:color/holo_green_dark"/>
<TextView
android:id="@id/android:empty"
@ -41,6 +43,10 @@
android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminateOnly="true"
android:visibility="gone"/>
android:visibility="gone"
tools:visibility="visible"
tools:layout_width="match_parent"
tools:layout_height="64dp"
tools:background="@android:color/holo_red_light"/>
</FrameLayout>

View File

@ -1,9 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="@dimen/listitem_threeline_height"
android:orientation="horizontal">
android:orientation="horizontal"
tools:background="@android:color/darker_gray">
<ImageView
android:id="@+id/imgvImage"
@ -14,7 +16,9 @@
android:layout_marginLeft="@dimen/listitem_threeline_horizontalpadding"
android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
android:contentDescription="@string/cover_label"
android:scaleType="centerCrop" />
android:scaleType="centerCrop"
tools:src="@drawable/ic_stat_antenna_default"
tools:background="@android:color/holo_green_dark" />
<RelativeLayout
android:layout_width="0dp"
@ -23,7 +27,8 @@
android:layout_marginLeft="@dimen/listitem_threeline_textleftpadding"
android:layout_marginRight="@dimen/listitem_threeline_textrightpadding"
android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
android:layout_weight="1">
android:layout_weight="1"
tools:background="@android:color/white" >
<TextView
@ -41,7 +46,9 @@
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_toLeftOf="@id/statusUnread" />
android:layout_toLeftOf="@id/statusUnread"
tools:text="Episode title"
tools:background="@android:color/holo_green_dark" />
<RelativeLayout
android:id="@+id/bottom_bar"
@ -50,7 +57,8 @@
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_marginTop="16dp">
android:layout_marginTop="16dp"
tools:background="@android:color/holo_red_light" >
<ImageView
android:id="@id/imgvInPlaylist"
@ -60,7 +68,9 @@
android:layout_marginLeft="8dp"
android:layout_marginRight="4dp"
android:contentDescription="@string/in_queue_label"
android:src="?attr/stat_playlist" />
android:src="?attr/stat_playlist"
tools:src="@drawable/ic_list_grey600_24dp"
tools:background="@android:color/black" />
<ProgressBar
android:id="@+id/pbar_download_progress"
@ -78,7 +88,9 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_toLeftOf="@id/imgvInPlaylist" />
android:layout_toLeftOf="@id/imgvInPlaylist"
tools:text="00:42:23"
tools:background="@android:color/holo_blue_dark" />
<TextView
android:id="@+id/txtvPublished"
@ -87,7 +99,9 @@
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_toLeftOf="@id/imgvInPlaylist"
android:ellipsize="end" />
android:ellipsize="end"
tools:text="Jan 23"
tools:background="@android:color/holo_green_dark" />
</RelativeLayout>
</RelativeLayout>

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
@ -13,7 +14,9 @@
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="16dp"
android:contentDescription="@string/cover_label" />
android:contentDescription="@string/cover_label"
tools:src="@drawable/ic_stat_antenna_default"
tools:background="@android:color/holo_green_dark" />
<TextView
android:id="@+id/txtvTitle"
@ -27,7 +30,9 @@
android:layout_toRightOf="@id/imgvCover"
android:ellipsize="end"
android:gravity="center_vertical"
android:maxLines="2" />
android:maxLines="2"
tools:text="Online feed title"
tools:background="@android:color/holo_green_dark" />
<TextView
android:id="@+id/txtvAuthor"
@ -40,7 +45,9 @@
android:ellipsize="end"
android:lines="1"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/text_size_small" />
android:textSize="@dimen/text_size_small"
tools:text="Online feed author"
tools:background="@android:color/holo_green_dark" />
<LinearLayout
android:layout_width="match_parent"
@ -75,7 +82,9 @@
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/text_size_small" />
android:textSize="@dimen/text_size_small"
tools:text="@string/design_time_lorem_ipsum"
tools:background="@android:color/holo_green_dark"/>
</LinearLayout>
</RelativeLayout>

View File

@ -1,20 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
android:orientation="vertical"
tools:background="@android:color/darker_gray">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:text="@string/opml_import_explanation" />
android:text="@string/opml_import_explanation"
tools:background="@android:color/holo_green_dark" />
<TextView
android:id="@+id/txtvPath"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp" />
android:layout_margin="8dp"
tools:text="Path"
tools:background="@android:color/holo_green_dark" />
<Button
android:id="@+id/butStartImport"

View File

@ -1,12 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
android:layout_height="match_parent">
<android.support.v4.view.PagerTabStrip
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top" />
</android.support.v4.view.ViewPager>
</LinearLayout>

View File

@ -4,7 +4,8 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="@dimen/listitem_threeline_height"
android:orientation="horizontal">
android:orientation="horizontal"
tools:background="@android:color/darker_gray" >
<ImageView
android:id="@+id/drag_handle"
@ -13,7 +14,9 @@
android:layout_margin="8dp"
android:contentDescription="@string/drag_handle_content_description"
android:scaleType="center"
android:src="?attr/dragview_background" />
android:src="?attr/dragview_background"
tools:src="@drawable/ic_drag_handle"
tools:background="@android:color/holo_green_dark" />
<ImageView
android:id="@+id/imgvImage"
@ -23,7 +26,9 @@
android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
android:contentDescription="@string/cover_label"
android:scaleType="centerCrop" />
android:scaleType="centerCrop"
tools:src="@drawable/ic_stat_antenna_default"
tools:background="@android:color/holo_green_dark"/>
<RelativeLayout
android:layout_width="0dp"
@ -32,7 +37,8 @@
android:layout_marginLeft="@dimen/listitem_threeline_textleftpadding"
android:layout_marginRight="@dimen/listitem_threeline_textrightpadding"
android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
android:layout_weight="1">
android:layout_weight="1"
tools:background="@android:color/holo_red_dark">
<TextView
android:id="@+id/txtvTitle"
@ -41,7 +47,9 @@
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true" />
android:layout_alignParentTop="true"
android:text="Queue item title"
tools:background="@android:color/holo_blue_light" />
<RelativeLayout
android:id="@+id/bottom_bar"
@ -57,7 +65,9 @@
style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true" />
android:layout_alignParentLeft="true"
android:text="00:42:23"
tools:background="@android:color/holo_blue_light" />
<ProgressBar
android:id="@+id/pbar_download_progress"
@ -67,7 +77,8 @@
android:layout_alignParentRight="true"
android:layout_marginLeft="8dp"
android:layout_toRightOf="@id/txtvPosition"
android:max="100" />
android:max="100"
tools:background="@android:color/holo_blue_light" />
</RelativeLayout>
</RelativeLayout>

View File

@ -1,7 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/listitem_threeline_height">
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="@dimen/listitem_threeline_height"
tools:background="@android:color/darker_gray">
<ImageView
android:id="@+id/imgvFeedimage"
@ -13,7 +15,9 @@
android:layout_marginLeft="@dimen/listitem_threeline_horizontalpadding"
android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
android:contentDescription="@string/cover_label"
android:scaleType="centerCrop" />
android:scaleType="centerCrop"
tools:src="@drawable/ic_stat_antenna_default"
tools:background="@android:color/holo_green_dark"/>
<LinearLayout
android:layout_width="wrap_content"
@ -22,21 +26,26 @@
android:layout_marginRight="@dimen/listitem_threeline_verticalpadding"
android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
android:layout_toRightOf="@id/imgvFeedimage"
android:orientation="vertical">
android:orientation="vertical"
tools:background="@android:color/holo_red_dark">
<TextView
android:id="@+id/txtvTitle"
style="@style/AntennaPod.TextView.ListItemPrimaryTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp" />
android:layout_marginBottom="8dp"
tools:text="Search item title"
tools:background="@android:color/holo_green_dark" />
<TextView
android:id="@+id/txtvSubtitle"
style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:lines="1" />
android:lines="1"
tools:text="Search item subtitle"
tools:background="@android:color/holo_blue_light"/>
</LinearLayout>
</RelativeLayout>

View File

@ -8,4 +8,6 @@
android:clickable="false"
android:focusable="false"
android:focusableInTouchMode="false"
tools:ignore="ContentDescription" />
tools:ignore="ContentDescription"
tools:src="@drawable/ic_play_arrow_grey600_36dp"
tools:background="@android:color/holo_green_dark" />

View File

@ -1,43 +1,73 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="12dp"
android:paddingTop="12dp">
android:layout_height="@dimen/listitem_threeline_height"
android:orientation="horizontal"
tools:background="@android:color/darker_gray">
<TextView
android:id="@+id/txtvStart"
style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_margin="8dp"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/text_size_small"/>
android:layout_gravity="center_vertical"
android:layout_marginLeft="@dimen/listitem_threeline_horizontalpadding"
android:gravity="center_vertical"
tools:text="Start"
tools:background="@android:color/holo_green_dark" />
<TextView
android:id="@+id/txtvTitle"
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:padding="8dp"
android:layout_toLeftOf="@id/txtvStart"
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/text_size_small"/>
android:layout_height="match_parent"
android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
android:layout_marginLeft="@dimen/listitem_threeline_horizontalpadding"
android:layout_marginRight="@dimen/listitem_threeline_horizontalpadding"
android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
android:layout_weight="1"
android:gravity="center_vertical"
android:orientation="vertical"
tools:background="@android:color/holo_red_dark">
<TextView
android:id="@+id/txtvLink"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@id/txtvTitle"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_toLeftOf="@id/txtvStart"
<TextView
android:id="@+id/txtvTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:ellipsize="end"
android:maxLines="2"
android:textColor="?android:attr/textColorPrimary"
android:textSize="16sp"
tools:text="Chapter title"
tools:background="@android:color/holo_green_dark" />
<TextView
android:id="@+id/txtvLink"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="false"
android:focusableInTouchMode="false"
android:maxLines="1"
android:visibility="gone"
tools:visibility="visible"
tools:text="Link"
tools:background="@android:color/holo_green_dark" />
</LinearLayout>
<include layout="@layout/vertical_list_divider" />
<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/butPlayChapter"
android:layout_width="@dimen/listview_secondary_button_width"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackground"
android:clickable="false"
android:contentDescription="@string/chapters_label"
android:focusable="false"
android:focusableInTouchMode="false"
android:visibility="gone"
android:maxLines="2" />
android:src="?attr/av_play"
tools:src="@drawable/ic_play_arrow_white_36dp"
tools:background="@android:color/holo_green_dark" />
</RelativeLayout>
</LinearLayout>

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
@ -42,13 +43,15 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginRight="8dp"
android:layout_weight="1" />
android:layout_weight="1"
tools:text="Confirm" />
<Button
android:id="@+id/butCancel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
android:layout_weight="1"
tools:text="Cancel" />
</LinearLayout>
</LinearLayout>

View File

@ -1,7 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<View xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="1dp"
android:layout_height="match_parent"
android:layout_marginBottom="16dp"
android:layout_marginTop="16dp"
android:background="?android:attr/listDivider"/>
android:background="?android:attr/listDivider"
tools:background="@android:color/holo_red_dark" />

View File

@ -5,19 +5,19 @@
<item
android:id="@+id/download_item"
android:icon="?attr/av_download"
custom:showAsAction="ifRoom|collapseActionView"
custom:showAsAction="collapseActionView"
android:title="@string/download_label">
</item>
<item
android:id="@+id/stream_item"
android:icon="?attr/action_stream"
custom:showAsAction="ifRoom|collapseActionView"
custom:showAsAction="collapseActionView"
android:title="@string/stream_label">
</item>
<item
android:id="@+id/play_item"
android:icon="?attr/av_play"
custom:showAsAction="ifRoom|collapseActionView"
custom:showAsAction="collapseActionView"
android:title="@string/play_label">
</item>
<item
@ -65,7 +65,7 @@
<item
android:id="@+id/visit_website_item"
android:icon="?attr/location_web_site"
custom:showAsAction="ifRoom|collapseActionView"
custom:showAsAction="collapseActionView"
android:title="@string/visit_website_label">
</item>
<item

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/show_preferences"
android:title="@string/settings_label"
android:menuCategory="system"
custom:showAsAction="collapseActionView"/>
</menu>

View File

@ -21,7 +21,7 @@
<item
android:id="@+id/visit_website_item"
android:icon="?attr/location_web_site"
custom:showAsAction="ifRoom|collapseActionView"
custom:showAsAction="collapseActionView"
android:title="@string/visit_website_label"
android:visible="false">
</item>

View File

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/refresh_item"
android:title="@string/refresh_label"
android:menuCategory="container"
custom:showAsAction="ifRoom"
android:icon="?attr/navigation_refresh"/>
<item
android:id="@+id/queue_sort"
android:title="@string/sort">
<menu>
<item
android:id="@+id/queue_sort_alpha"
android:title="@string/alpha">
<menu>
<item
android:id="@+id/queue_sort_alpha_asc"
android:title="@string/ascending"/>
<item
android:id="@+id/queue_sort_alpha_desc"
android:title="@string/descending"/>
</menu>
</item>
<item
android:id="@+id/queue_sort_date"
android:title="@string/date">
<menu>
<item
android:id="@+id/queue_sort_date_asc"
android:title="@string/ascending"/>
<item
android:id="@+id/queue_sort_date_desc"
android:title="@string/descending"/>
</menu>
</item>
<item
android:id="@+id/queue_sort_duration"
android:title="@string/duration">
<menu>
<item
android:id="@+id/queue_sort_duration_asc"
android:title="@string/ascending"/>
<item
android:id="@+id/queue_sort_duration_desc"
android:title="@string/descending"/>
</menu>
</item>
</menu>
</item>
</menu>

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<resources
xmlns:tools="http://schemas.android.com/tools"
tools:ignore="MissingTranslation">
<!--
DESIGN TIME ATTRIBUTES
Put too long strings to embed in layout files here.
-->
<string name="design_time_lorem_ipsum">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus ullamcorper
tempor metus, at varius tellus dignissim et. Fusce quis justo sollicitudin,
feugiat mi in, accumsan erat. Fusce tempor, sapien ut luctus scelerisque,
lectus nisi hendrerit odio, eget mattis lorem massa id eros. Integer consequat
tellus eu rhoncus pellentesque. Quisque et leo faucibus, aliquam nisi ut,
feugiat lectus.
</string>
<string name="design_time_downloaded_log_failure_reason">
IO Error: failed to connect to feeds.example.com/100.100.100.100
(port 80) after 3000ms: isConnected failed: ECONNREFUSED (Connection refused)
</string>
</resources>

View File

@ -29,17 +29,30 @@
android:key="prefPauseOnHeadsetDisconnect"
android:summary="@string/pref_pauseOnHeadsetDisconnect_sum"
android:title="@string/pref_pauseOnHeadsetDisconnect_title"/>
<CheckBoxPreference
android:defaultValue="true"
android:enabled="true"
android:dependency="prefPauseOnHeadsetDisconnect"
android:key="prefUnpauseOnHeadsetReconnect"
android:summary="@string/pref_unpauseOnHeadsetReconnect_sum"
android:title="@string/pref_unpauseOnHeadsetReconnect_title"/>
<CheckBoxPreference
android:defaultValue="false"
android:enabled="true"
android:key="prefFollowQueue"
android:summary="@string/pref_followQueue_sum"
android:title="@string/pref_followQueue_title"/>
<CheckBoxPreference
android:defaultValue="false"
android:enabled="true"
android:key="prefAutoDelete"
android:summary="@string/pref_auto_delete_sum"
android:title="@string/pref_auto_delete_title"/>
<Preference
android:key="prefPlaybackSpeedLauncher"
android:summary="@string/pref_playback_speed_sum"
android:title="@string/pref_playback_speed_title" />
<CheckBoxPreference
android:defaultValue="false"
android:enabled="true"
@ -85,6 +98,11 @@
android:key="prefEnableAutoDl"
android:title="@string/pref_automatic_download_title"
android:defaultValue="false"/>
<CheckBoxPreference
android:key="prefEnableAutoDownloadOnBattery"
android:title="@string/pref_automatic_download_on_battery_title"
android:summary="@string/pref_automatic_download_on_battery_sum"
android:defaultValue="true"/>
<CheckBoxPreference
android:key="prefEnableAutoDownloadWifiFilter"
android:title="@string/pref_autodl_wifi_filter_title"

View File

@ -5,7 +5,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.0.0-rc4'
classpath 'com.android.tools.build:gradle:1.0.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files

View File

@ -2,7 +2,7 @@ apply plugin: 'com.android.library'
android {
compileSdkVersion 21
buildToolsVersion "21.1.1"
buildToolsVersion "21.1.2"
defaultConfig {
minSdkVersion 10
@ -31,19 +31,17 @@ android {
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:21.0.2'
compile 'com.android.support:support-v4:21.0.2'
compile 'com.android.support:appcompat-v7:21.0.3'
compile 'com.android.support:support-v4:21.0.3'
compile 'org.apache.commons:commons-lang3:3.3.2'
compile ('org.shredzone.flattr4j:flattr4j-core:2.11') {
exclude group: 'org.apache.httpcomponents', module: 'httpcore'
exclude group: 'org.apache.httpcomponents', module: 'httpclient'
compile ('org.shredzone.flattr4j:flattr4j-core:2.12') {
exclude group: 'org.json', module: 'json'
}
compile 'commons-io:commons-io:2.4'
compile 'com.jayway.android.robotium:robotium-solo:5.2.1'
compile 'org.jsoup:jsoup:1.7.3'
compile 'com.squareup.picasso:picasso:2.4.0'
compile 'com.squareup.okhttp:okhttp:2.1.0'
compile 'com.squareup.okhttp:okhttp-urlconnection:2.1.0'
compile 'com.squareup.okio:okio:1.0.1'
compile 'com.squareup.okhttp:okhttp:2.2.0'
compile 'com.squareup.okhttp:okhttp-urlconnection:2.2.0'
compile 'com.squareup.okio:okio:1.2.0'
}

View File

@ -22,4 +22,6 @@ public class ClientConfig {
public static FlattrCallbacks flattrCallbacks;
public static StorageCallbacks storageCallbacks;
public static DBTasksCallbacks dbTasksCallbacks;
}

View File

@ -0,0 +1,20 @@
package de.danoeh.antennapod.core;
import de.danoeh.antennapod.core.storage.AutomaticDownloadAlgorithm;
import de.danoeh.antennapod.core.storage.EpisodeCleanupAlgorithm;
/**
* Callbacks for the DBTasks class of the storage module.
*/
public interface DBTasksCallbacks {
/**
* Returns the client's implementation of the AutomaticDownloadAlgorithm interface.
*/
public AutomaticDownloadAlgorithm getAutomaticDownloadAlgorithm();
/**
* Returns the client's implementation of the EpisodeCacheCleanupAlgorithm interface.
*/
public EpisodeCleanupAlgorithm getEpisodeCacheCleanupAlgorithm();
}

View File

@ -0,0 +1,29 @@
package de.danoeh.antennapod.core.asynctask;
import android.content.Context;
import android.support.v4.content.AsyncTaskLoader;
/**
* Subclass of AsyncTaskLoader that is made for loading data with one of the DB*-classes.
* This class will provide a useful default implementation that would otherwise always be necessary when interacting
* with the DB*-classes with an AsyncTaskLoader.
*/
public abstract class DBTaskLoader<D> extends AsyncTaskLoader<D> {
public DBTaskLoader(Context context) {
super(context);
}
@Override
protected void onStopLoading() {
super.onStopLoading();
cancelLoad();
}
@Override
protected void onStartLoading() {
super.onStartLoading();
// according to https://code.google.com/p/android/issues/detail?id=14944, this has to be called manually
forceLoad();
}
}

View File

@ -14,6 +14,7 @@ import com.squareup.picasso.OkHttpDownloader;
import com.squareup.picasso.Picasso;
import com.squareup.picasso.Request;
import com.squareup.picasso.RequestHandler;
import com.squareup.picasso.Transformation;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
@ -209,4 +210,254 @@ public class PicassoProvider {
options.inJustDecodeBounds = false;
}
}
public static final int BLUR_RADIUS = 1;
public static final int BLUR_IMAGE_SIZE = 100;
public static final String BLUR_KEY = "blur";
public static final Transformation blurTransformation = new Transformation() {
@Override
public Bitmap transform(Bitmap source) {
Bitmap result = fastblur(source, BLUR_RADIUS);
source.recycle();
return result;
}
@Override
public String key() {
return BLUR_KEY;
}
};
public static Bitmap fastblur(Bitmap sentBitmap, int radius) {
// Stack Blur v1.0 from
// http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html
//
// Java Author: Mario Klingemann <mario at quasimondo.com>
// http://incubator.quasimondo.com
// created Feburary 29, 2004
// Android port : Yahel Bouaziz <yahel at kayenko.com>
// http://www.kayenko.com
// ported april 5th, 2012
// This is a compromise between Gaussian Blur and Box blur
// It creates much better looking blurs than Box Blur, but is
// 7x faster than my Gaussian Blur implementation.
//
// I called it Stack Blur because this describes best how this
// filter works internally: it creates a kind of moving stack
// of colors whilst scanning through the image. Thereby it
// just has to add one new block of color to the right side
// of the stack and remove the leftmost color. The remaining
// colors on the topmost layer of the stack are either added on
// or reduced by one, depending on if they are on the right or
// on the left side of the stack.
//
// If you are using this algorithm in your code please add
// the following line:
//
// Stack Blur Algorithm by Mario Klingemann <mario@quasimondo.com>
Bitmap bitmap = sentBitmap.copy(sentBitmap.getConfig(), true);
if (radius < 1) {
return (null);
}
int w = bitmap.getWidth();
int h = bitmap.getHeight();
int[] pix = new int[w * h];
Log.e("pix", w + " " + h + " " + pix.length);
bitmap.getPixels(pix, 0, w, 0, 0, w, h);
int wm = w - 1;
int hm = h - 1;
int wh = w * h;
int div = radius + radius + 1;
int r[] = new int[wh];
int g[] = new int[wh];
int b[] = new int[wh];
int rsum, gsum, bsum, x, y, i, p, yp, yi, yw;
int vmin[] = new int[Math.max(w, h)];
int divsum = (div + 1) >> 1;
divsum *= divsum;
int dv[] = new int[256 * divsum];
for (i = 0; i < 256 * divsum; i++) {
dv[i] = (i / divsum);
}
yw = yi = 0;
int[][] stack = new int[div][3];
int stackpointer;
int stackstart;
int[] sir;
int rbs;
int r1 = radius + 1;
int routsum, goutsum, boutsum;
int rinsum, ginsum, binsum;
for (y = 0; y < h; y++) {
rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
for (i = -radius; i <= radius; i++) {
p = pix[yi + Math.min(wm, Math.max(i, 0))];
sir = stack[i + radius];
sir[0] = (p & 0xff0000) >> 16;
sir[1] = (p & 0x00ff00) >> 8;
sir[2] = (p & 0x0000ff);
rbs = r1 - Math.abs(i);
rsum += sir[0] * rbs;
gsum += sir[1] * rbs;
bsum += sir[2] * rbs;
if (i > 0) {
rinsum += sir[0];
ginsum += sir[1];
binsum += sir[2];
} else {
routsum += sir[0];
goutsum += sir[1];
boutsum += sir[2];
}
}
stackpointer = radius;
for (x = 0; x < w; x++) {
r[yi] = dv[rsum];
g[yi] = dv[gsum];
b[yi] = dv[bsum];
rsum -= routsum;
gsum -= goutsum;
bsum -= boutsum;
stackstart = stackpointer - radius + div;
sir = stack[stackstart % div];
routsum -= sir[0];
goutsum -= sir[1];
boutsum -= sir[2];
if (y == 0) {
vmin[x] = Math.min(x + radius + 1, wm);
}
p = pix[yw + vmin[x]];
sir[0] = (p & 0xff0000) >> 16;
sir[1] = (p & 0x00ff00) >> 8;
sir[2] = (p & 0x0000ff);
rinsum += sir[0];
ginsum += sir[1];
binsum += sir[2];
rsum += rinsum;
gsum += ginsum;
bsum += binsum;
stackpointer = (stackpointer + 1) % div;
sir = stack[(stackpointer) % div];
routsum += sir[0];
goutsum += sir[1];
boutsum += sir[2];
rinsum -= sir[0];
ginsum -= sir[1];
binsum -= sir[2];
yi++;
}
yw += w;
}
for (x = 0; x < w; x++) {
rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
yp = -radius * w;
for (i = -radius; i <= radius; i++) {
yi = Math.max(0, yp) + x;
sir = stack[i + radius];
sir[0] = r[yi];
sir[1] = g[yi];
sir[2] = b[yi];
rbs = r1 - Math.abs(i);
rsum += r[yi] * rbs;
gsum += g[yi] * rbs;
bsum += b[yi] * rbs;
if (i > 0) {
rinsum += sir[0];
ginsum += sir[1];
binsum += sir[2];
} else {
routsum += sir[0];
goutsum += sir[1];
boutsum += sir[2];
}
if (i < hm) {
yp += w;
}
}
yi = x;
stackpointer = radius;
for (y = 0; y < h; y++) {
// Preserve alpha channel: ( 0xff000000 & pix[yi] )
pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum];
rsum -= routsum;
gsum -= goutsum;
bsum -= boutsum;
stackstart = stackpointer - radius + div;
sir = stack[stackstart % div];
routsum -= sir[0];
goutsum -= sir[1];
boutsum -= sir[2];
if (x == 0) {
vmin[y] = Math.min(y + r1, hm) * w;
}
p = x + vmin[y];
sir[0] = r[p];
sir[1] = g[p];
sir[2] = b[p];
rinsum += sir[0];
ginsum += sir[1];
binsum += sir[2];
rsum += rinsum;
gsum += ginsum;
bsum += binsum;
stackpointer = (stackpointer + 1) % div;
sir = stack[stackpointer];
routsum += sir[0];
goutsum += sir[1];
boutsum += sir[2];
rinsum -= sir[0];
ginsum -= sir[1];
binsum -= sir[2];
yi += w;
}
}
Log.e("pix", w + " " + h + " " + pix.length);
bitmap.setPixels(pix, 0, w, 0, 0, w, h);
return (bitmap);
}
}

View File

@ -143,7 +143,7 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr
paymentLink = other.paymentLink;
}
if (other.chapters != null) {
if (chapters == null) {
if (!hasChapters) {
chapters = other.chapters;
}
}

View File

@ -0,0 +1,38 @@
package de.danoeh.antennapod.core.menuhandler;
import android.support.v4.view.MenuItemCompat;
import android.view.Menu;
import android.view.MenuItem;
import de.danoeh.antennapod.core.R;
/**
* Utilities for menu items
*/
public class MenuItemUtils {
/**
* Changes the appearance of a MenuItem depending on whether the given UpdateRefreshMenuItemChecker
* is refreshing or not. If it returns true, the menu item will be replaced by an indeterminate progress
* bar, otherwise nothing will happen.
*
* @param menu The menu that the MenuItem belongs to
* @param resId The id of the MenuItem
* @param checker Is used for checking whether to show the progress indicator or not.
* @return The returned value of the UpdateRefreshMenuItemChecker's isRefreshing() method.
*/
public static boolean updateRefreshMenuItem(Menu menu, int resId, UpdateRefreshMenuItemChecker checker) {
// expand actionview if feeds are being downloaded, collapse otherwise
if (checker.isRefreshing()) {
MenuItem refreshItem = menu.findItem(resId);
MenuItemCompat.setActionView(refreshItem, R.layout.refresh_action_view);
return true;
} else {
return false;
}
}
public static interface UpdateRefreshMenuItemChecker {
public boolean isRefreshing();
}
}

View File

@ -38,6 +38,7 @@ public class UserPreferences implements
private static final String TAG = "UserPreferences";
public static final String PREF_PAUSE_ON_HEADSET_DISCONNECT = "prefPauseOnHeadsetDisconnect";
public static final String PREF_UNPAUSE_ON_HEADSET_RECONNECT = "prefUnpauseOnHeadsetReconnect";
public static final String PREF_FOLLOW_QUEUE = "prefFollowQueue";
public static final String PREF_DOWNLOAD_MEDIA_ON_WIFI_ONLY = "prefDownloadMediaOnWifiOnly";
public static final String PREF_UPDATE_INTERVAL = "prefAutoUpdateIntervall";
@ -50,6 +51,7 @@ public class UserPreferences implements
public static final String PREF_DATA_FOLDER = "prefDataFolder";
public static final String PREF_ENABLE_AUTODL = "prefEnableAutoDl";
public static final String PREF_ENABLE_AUTODL_WIFI_FILTER = "prefEnableAutoDownloadWifiFilter";
public static final String PREF_ENABLE_AUTODL_ON_BATTERY = "prefEnableAutoDownloadOnBattery";
private static final String PREF_AUTODL_SELECTED_NETWORKS = "prefAutodownloadSelectedNetworks";
public static final String PREF_EPISODE_CACHE_SIZE = "prefEpisodeCacheSize";
private static final String PREF_PLAYBACK_SPEED = "prefPlaybackSpeed";
@ -69,6 +71,7 @@ public class UserPreferences implements
// Preferences
private boolean pauseOnHeadsetDisconnect;
private boolean unpauseOnHeadsetReconnect;
private boolean followQueue;
private boolean downloadMediaOnWifiOnly;
private long updateInterval;
@ -80,6 +83,7 @@ public class UserPreferences implements
private int theme;
private boolean enableAutodownload;
private boolean enableAutodownloadWifiFilter;
private boolean enableAutodownloadOnBattery;
private String[] autodownloadSelectedNetworks;
private int episodeCacheSize;
private String playbackSpeed;
@ -121,6 +125,8 @@ public class UserPreferences implements
R.integer.episode_cache_size_unlimited);
pauseOnHeadsetDisconnect = sp.getBoolean(
PREF_PAUSE_ON_HEADSET_DISCONNECT, true);
unpauseOnHeadsetReconnect = sp.getBoolean(
PREF_UNPAUSE_ON_HEADSET_RECONNECT, true);
followQueue = sp.getBoolean(PREF_FOLLOW_QUEUE, false);
downloadMediaOnWifiOnly = sp.getBoolean(
PREF_DOWNLOAD_MEDIA_ON_WIFI_ONLY, true);
@ -140,6 +146,7 @@ public class UserPreferences implements
episodeCacheSize = readEpisodeCacheSizeInternal(sp.getString(
PREF_EPISODE_CACHE_SIZE, "20"));
enableAutodownload = sp.getBoolean(PREF_ENABLE_AUTODL, false);
enableAutodownloadOnBattery = sp.getBoolean(PREF_ENABLE_AUTODL_ON_BATTERY, true);
playbackSpeed = sp.getString(PREF_PLAYBACK_SPEED, "1.0");
playbackSpeedArray = readPlaybackSpeedArray(sp.getString(
PREF_PLAYBACK_SPEED_ARRAY, null));
@ -221,6 +228,11 @@ public class UserPreferences implements
return instance.pauseOnHeadsetDisconnect;
}
public static boolean isUnpauseOnHeadsetReconnect() {
instanceAvailable();
return instance.unpauseOnHeadsetReconnect;
}
public static boolean isFollowQueue() {
instanceAvailable();
return instance.followQueue;
@ -282,6 +294,15 @@ public class UserPreferences implements
return instance.theme;
}
public static int getNoTitleTheme() {
int theme = getTheme();
if (theme == R.style.Theme_AntennaPod_Dark) {
return R.style.Theme_AntennaPod_Dark_NoTitle;
} else {
return R.style.Theme_AntennaPod_Light_NoTitle;
}
}
public static boolean isEnableAutodownloadWifiFilter() {
instanceAvailable();
return instance.enableAutodownloadWifiFilter;
@ -326,6 +347,11 @@ public class UserPreferences implements
return instance.enableAutodownload;
}
public static boolean isEnableAutodownloadOnBattery() {
instanceAvailable();
return instance.enableAutodownloadOnBattery;
}
public static boolean shouldPauseForFocusLoss() {
instanceAvailable();
return instance.pauseForFocusLoss;
@ -377,6 +403,8 @@ public class UserPreferences implements
PREF_EPISODE_CACHE_SIZE, "20"));
} else if (key.equals(PREF_ENABLE_AUTODL)) {
enableAutodownload = sp.getBoolean(PREF_ENABLE_AUTODL, false);
} else if (key.equals(PREF_ENABLE_AUTODL_ON_BATTERY)) {
enableAutodownloadOnBattery = sp.getBoolean(PREF_ENABLE_AUTODL_ON_BATTERY, true);
} else if (key.equals(PREF_PLAYBACK_SPEED)) {
playbackSpeed = sp.getString(PREF_PLAYBACK_SPEED, "1.0");
} else if (key.equals(PREF_PLAYBACK_SPEED_ARRAY)) {
@ -388,6 +416,8 @@ public class UserPreferences implements
seekDeltaSecs = Integer.valueOf(sp.getString(PREF_SEEK_DELTA_SECS, "30"));
} else if (key.equals(PREF_PAUSE_ON_HEADSET_DISCONNECT)) {
pauseOnHeadsetDisconnect = sp.getBoolean(PREF_PAUSE_ON_HEADSET_DISCONNECT, true);
} else if (key.equals(PREF_UNPAUSE_ON_HEADSET_RECONNECT)) {
unpauseOnHeadsetReconnect = sp.getBoolean(PREF_UNPAUSE_ON_HEADSET_RECONNECT, true);
} else if (key.equals(PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD)) {
autoFlattrPlayedDurationThreshold = sp.getFloat(PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD,
PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD_DEFAULT);

View File

@ -144,6 +144,10 @@ public class PlaybackService extends Service {
* Is true if service has received a valid start command.
*/
public static boolean started = false;
/**
* Is true if the service was running, but paused due to headphone disconnect
*/
public static boolean transientPause = false;
private static final int NOTIFICATION_ID = 1;
@ -206,6 +210,8 @@ public class PlaybackService extends Service {
Intent.ACTION_HEADSET_PLUG));
registerReceiver(shutdownReceiver, new IntentFilter(
ACTION_SHUTDOWN_PLAYBACK_SERVICE));
registerReceiver(bluetoothStateUpdated, new IntentFilter(
AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED));
registerReceiver(audioBecomingNoisy, new IntentFilter(
AudioManager.ACTION_AUDIO_BECOMING_NOISY));
registerReceiver(skipCurrentEpisodeReceiver, new IntentFilter(
@ -228,6 +234,7 @@ public class PlaybackService extends Service {
unregisterReceiver(headsetDisconnected);
unregisterReceiver(shutdownReceiver);
unregisterReceiver(bluetoothStateUpdated);
unregisterReceiver(audioBecomingNoisy);
unregisterReceiver(skipCurrentEpisodeReceiver);
mediaPlayer.shutdown();
@ -284,7 +291,6 @@ public class PlaybackService extends Service {
private void handleKeycode(int keycode) {
if (BuildConfig.DEBUG)
Log.d(TAG, "Handling keycode: " + keycode);
final PlaybackServiceMediaPlayer.PSMPInfo info = mediaPlayer.getPSMPInfo();
final PlayerStatus status = info.playerStatus;
switch (keycode) {
@ -315,12 +321,14 @@ public class PlaybackService extends Service {
break;
case KeyEvent.KEYCODE_MEDIA_PAUSE:
if (status == PlayerStatus.PLAYING) {
if (UserPreferences.isPersistNotify()) {
mediaPlayer.pause(false, true);
} else {
mediaPlayer.pause(true, true);
}
mediaPlayer.pause(false, true);
}
if (UserPreferences.isPersistNotify()) {
mediaPlayer.pause(false, true);
} else {
mediaPlayer.pause(true, true);
}
break;
case KeyEvent.KEYCODE_MEDIA_NEXT:
case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
@ -333,7 +341,9 @@ public class PlaybackService extends Service {
case KeyEvent.KEYCODE_MEDIA_STOP:
if (status == PlayerStatus.PLAYING) {
mediaPlayer.pause(true, true);
started = false;
}
stopForeground(true); // gets rid of persistent notification
break;
default:
@ -411,10 +421,13 @@ public class PlaybackService extends Service {
taskManager.cancelWidgetUpdater();
if (UserPreferences.isPersistNotify() && android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
// do not remove notification on pause based on user pref and whether android version supports expanded notifications
} else {
// Change [Play] button to [Pause]
setupNotification(newInfo);
} else if (!UserPreferences.isPersistNotify()) {
// remove notifcation on pause
stopForeground(true);
}
break;
case STOPPED:
@ -431,6 +444,7 @@ public class PlaybackService extends Service {
taskManager.startPositionSaver();
taskManager.startWidgetUpdater();
setupNotification(newInfo);
started = true;
break;
case ERROR:
writePlaybackPreferencesNoMediaPlaying();
@ -539,6 +553,15 @@ public class PlaybackService extends Service {
if (isAutoFlattrable(media) && UserPreferences.getAutoFlattrPlayedDurationThreshold() == 1.0f) {
DBTasks.flattrItemIfLoggedIn(PlaybackService.this, item);
}
//Delete episode if enabled
if(UserPreferences.isAutoDelete()) {
DBWriter.deleteFeedMediaOfItem(PlaybackService.this, item.getMedia().getId());
if(BuildConfig.DEBUG)
Log.d(TAG, "Episode Deleted");
}
}
// Load next episode if previous episode was in the queue and if there
@ -734,8 +757,9 @@ public class PlaybackService extends Service {
PlaybackServiceMediaPlayer.PSMPInfo newInfo = mediaPlayer.getPSMPInfo();
final int smallIcon = ClientConfig.playbackServiceCallbacks.getNotificationIconResource(getApplicationContext());
if (!isCancelled() && info.playerStatus == PlayerStatus.PLAYING
&& info.playable != null) {
if (!isCancelled() &&
started &&
info.playable != null) {
String contentText = info.playable.getFeedTitle();
String contentTitle = info.playable.getEpisodeTitle();
Notification notification = null;
@ -775,16 +799,30 @@ public class PlaybackService extends Service {
.setContentIntent(pIntent)
.setLargeIcon(icon)
.setSmallIcon(smallIcon)
.setPriority(UserPreferences.getNotifyPriority()) // set notification priority
.addAction(android.R.drawable.ic_media_play, //play action
getString(R.string.play_label),
playButtonPendingIntent)
.addAction(android.R.drawable.ic_media_pause, //pause action
getString(R.string.pause_label),
pauseButtonPendingIntent)
.addAction(android.R.drawable.ic_menu_close_clear_cancel, // stop action
getString(R.string.stop_label),
stopButtonPendingIntent);
.setPriority(UserPreferences.getNotifyPriority()); // set notification priority
if (newInfo.playerStatus == PlayerStatus.PLAYING) {
notificationBuilder.addAction(android.R.drawable.ic_media_pause, //pause action
getString(R.string.pause_label),
pauseButtonPendingIntent);
} else {
notificationBuilder.addAction(android.R.drawable.ic_media_play, //play action
getString(R.string.play_label),
playButtonPendingIntent);
}
if (UserPreferences.isPersistNotify()) {
notificationBuilder.addAction(android.R.drawable.ic_menu_close_clear_cancel, // stop action
getString(R.string.stop_label),
stopButtonPendingIntent);
}
if (Build.VERSION.SDK_INT >= 21) {
notificationBuilder.setStyle(new Notification.MediaStyle()
.setMediaSession((android.media.session.MediaSession.Token) mediaPlayer.getSessionToken().getToken())
.setShowActionsInCompactView(0))
.setVisibility(Notification.VISIBILITY_PUBLIC)
.setColor(Notification.COLOR_DEFAULT);
}
notification = notificationBuilder.build();
} else {
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(
@ -793,11 +831,9 @@ public class PlaybackService extends Service {
.setContentText(contentText).setOngoing(true)
.setContentIntent(pIntent).setLargeIcon(icon)
.setSmallIcon(smallIcon);
notification = notificationBuilder.getNotification();
}
if (newInfo.playerStatus == PlayerStatus.PLAYING) {
startForeground(NOTIFICATION_ID, notification);
notification = notificationBuilder.build();
}
startForeground(NOTIFICATION_ID, notification);
if (BuildConfig.DEBUG)
Log.d(TAG, "Notification set up");
}
@ -966,6 +1002,7 @@ public class PlaybackService extends Service {
private BroadcastReceiver headsetDisconnected = new BroadcastReceiver() {
private static final String TAG = "headsetDisconnected";
private static final int UNPLUGGED = 0;
private static final int PLUGGED = 1;
@Override
public void onReceive(Context context, Intent intent) {
@ -978,6 +1015,10 @@ public class PlaybackService extends Service {
if (BuildConfig.DEBUG)
Log.d(TAG, "Headset was unplugged during playback.");
pauseIfPauseOnDisconnect();
} else if (state == PLUGGED) {
if (BuildConfig.DEBUG)
Log.d(TAG, "Headset was plugged in during playback.");
unpauseIfPauseOnDisconnect();
}
} else {
Log.e(TAG, "Received invalid ACTION_HEADSET_PLUG intent");
@ -986,6 +1027,21 @@ public class PlaybackService extends Service {
}
};
private BroadcastReceiver bluetoothStateUpdated = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (StringUtils.equals(intent.getAction(), AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED)) {
int state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, -1);
int prevState = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_PREVIOUS_STATE, -1);
if (state == AudioManager.SCO_AUDIO_STATE_CONNECTED) {
if (BuildConfig.DEBUG)
Log.d(TAG, "Received bluetooth connection intent");
unpauseIfPauseOnDisconnect();
}
}
}
};
private BroadcastReceiver audioBecomingNoisy = new BroadcastReceiver() {
@Override
@ -1003,6 +1059,9 @@ public class PlaybackService extends Service {
*/
private void pauseIfPauseOnDisconnect() {
if (UserPreferences.isPauseOnHeadsetDisconnect()) {
if (mediaPlayer.getPlayerStatus() == PlayerStatus.PLAYING) {
transientPause = true;
}
if (UserPreferences.isPersistNotify()) {
mediaPlayer.pause(false, true);
} else {
@ -1011,6 +1070,15 @@ public class PlaybackService extends Service {
}
}
private void unpauseIfPauseOnDisconnect() {
if (transientPause) {
transientPause = false;
if (UserPreferences.isPauseOnHeadsetDisconnect() && UserPreferences.isUnpauseOnHeadsetReconnect()) {
mediaPlayer.resume();
}
}
}
private BroadcastReceiver shutdownReceiver = new BroadcastReceiver() {
@Override

View File

@ -6,6 +6,9 @@ import android.media.AudioManager;
import android.media.RemoteControlClient;
import android.net.wifi.WifiManager;
import android.os.PowerManager;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.util.Pair;
@ -48,6 +51,10 @@ public class PlaybackServiceMediaPlayer {
private volatile PlayerStatus statusBeforeSeeking;
private volatile IPlayer mediaPlayer;
private volatile Playable media;
/**
* Only used for Lollipop notifications.
*/
private final MediaSessionCompat mediaSession;
private volatile boolean stream;
private volatile MediaType mediaType;
@ -89,6 +96,10 @@ public class PlaybackServiceMediaPlayer {
}
);
mediaSession = new MediaSessionCompat(context, TAG);
mediaSession.setCallback(sessionCallback);
mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
mediaPlayer = null;
statusBeforeSeeking = null;
pausedBecauseOfTransientAudiofocusLoss = false;
@ -181,6 +192,7 @@ public class PlaybackServiceMediaPlayer {
setPlayerStatus(PlayerStatus.INITIALIZING, media);
try {
media.loadMetadata();
mediaSession.setMetadata(getMediaSessionMetadata(media));
if (stream) {
mediaPlayer.setDataSource(media.getStreamUrl());
} else {
@ -211,6 +223,13 @@ public class PlaybackServiceMediaPlayer {
}
}
private MediaMetadataCompat getMediaSessionMetadata(Playable p) {
MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder();
builder.putString(MediaMetadataCompat.METADATA_KEY_TITLE, p.getEpisodeTitle());
builder.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, p.getFeedTitle());
return builder.build();
}
/**
* Resumes playback if the PSMP object is in PREPARED or PAUSED state. If the PSMP object is in an invalid state.
@ -586,6 +605,10 @@ public class PlaybackServiceMediaPlayer {
return mediaType;
}
public PlayerStatus getPlayerStatus() {
return playerStatus;
}
public boolean isStreaming() {
return stream;
}
@ -599,6 +622,9 @@ public class PlaybackServiceMediaPlayer {
if (mediaPlayer != null) {
mediaPlayer.release();
}
if (mediaSession != null) {
mediaSession.release();
}
releaseWifiLockIfNecessary();
}
@ -662,6 +688,16 @@ public class PlaybackServiceMediaPlayer {
return new PSMPInfo(playerStatus, media);
}
/**
* Returns a token to this object's MediaSession. The MediaSession should only be used for notifications
* at the moment.
*
* @return The MediaSessionCompat.Token object.
*/
public MediaSessionCompat.Token getSessionToken() {
return mediaSession.getSessionToken();
}
/**
* Sets the player status of the PSMP object. PlayerStatus and media attributes have to be set at the same time
* so that getPSMPInfo can't return an invalid state (e.g. status is PLAYING, but media is null).
@ -679,6 +715,45 @@ public class PlaybackServiceMediaPlayer {
this.playerStatus = newStatus;
this.media = newMedia;
PlaybackStateCompat.Builder sessionState = new PlaybackStateCompat.Builder();
int state;
if (playerStatus != null) {
switch (playerStatus) {
case PLAYING:
state = PlaybackStateCompat.STATE_PLAYING;
break;
case PREPARED:
case PAUSED:
state = PlaybackStateCompat.STATE_PAUSED;
break;
case STOPPED:
state = PlaybackStateCompat.STATE_STOPPED;
break;
case SEEKING:
state = PlaybackStateCompat.STATE_FAST_FORWARDING;
break;
case PREPARING:
case INITIALIZING:
state = PlaybackStateCompat.STATE_CONNECTING;
break;
case INITIALIZED:
case INDETERMINATE:
state = PlaybackStateCompat.STATE_NONE;
break;
case ERROR:
state = PlaybackStateCompat.STATE_ERROR;
break;
default:
state = PlaybackStateCompat.STATE_NONE;
break;
}
} else {
state = PlaybackStateCompat.STATE_NONE;
}
sessionState.setState(state, PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, getPlaybackSpeed());
callback.statusChanged(new PSMPInfo(playerStatus, media));
}
@ -976,4 +1051,54 @@ public class PlaybackServiceMediaPlayer {
}
});
}
private final MediaSessionCompat.Callback sessionCallback = new MediaSessionCompat.Callback() {
@Override
public void onPlay() {
if (playerStatus == PlayerStatus.PAUSED || playerStatus == PlayerStatus.PREPARED) {
resume();
} else if (playerStatus == PlayerStatus.INITIALIZED) {
setStartWhenPrepared(true);
prepare();
}
}
@Override
public void onPause() {
super.onPause();
if (playerStatus == PlayerStatus.PLAYING) {
pause(false, true);
}
if (UserPreferences.isPersistNotify()) {
pause(false, true);
} else {
pause(true, true);
}
}
@Override
public void onSkipToNext() {
super.onSkipToNext();
endPlayback();
}
@Override
public void onFastForward() {
super.onFastForward();
seekDelta(UserPreferences.getSeekDeltaMs());
}
@Override
public void onRewind() {
super.onRewind();
seekDelta(-UserPreferences.getSeekDeltaMs());
}
@Override
public void onSeekTo(long pos) {
super.onSeekTo(pos);
seekTo((int) pos);
}
};
}

View File

@ -0,0 +1,103 @@
package de.danoeh.antennapod.core.storage;
import android.content.Context;
import android.util.Log;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ExecutionException;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.QueueAccess;
/**
* Implementation of the EpisodeCleanupAlgorithm interface used by AntennaPod.
*/
public class APCleanupAlgorithm implements EpisodeCleanupAlgorithm<Integer> {
private static final String TAG = "APCleanupAlgorithm";
@Override
public int performCleanup(Context context, Integer episodeNumber) {
List<FeedItem> candidates = new ArrayList<FeedItem>();
List<FeedItem> downloadedItems = DBReader.getDownloadedItems(context);
QueueAccess queue = QueueAccess.IDListAccess(DBReader.getQueueIDList(context));
List<FeedItem> delete;
for (FeedItem item : downloadedItems) {
if (item.hasMedia() && item.getMedia().isDownloaded()
&& !queue.contains(item.getId()) && item.isRead()) {
candidates.add(item);
}
}
Collections.sort(candidates, new Comparator<FeedItem>() {
@Override
public int compare(FeedItem lhs, FeedItem rhs) {
Date l = lhs.getMedia().getPlaybackCompletionDate();
Date r = rhs.getMedia().getPlaybackCompletionDate();
if (l == null) {
l = new Date(0);
}
if (r == null) {
r = new Date(0);
}
return l.compareTo(r);
}
});
if (candidates.size() > episodeNumber) {
delete = candidates.subList(0, episodeNumber);
} else {
delete = candidates;
}
for (FeedItem item : delete) {
try {
DBWriter.deleteFeedMediaOfItem(context, item.getMedia().getId()).get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
int counter = delete.size();
Log.i(TAG, String.format(
"Auto-delete deleted %d episodes (%d requested)", counter,
episodeNumber));
return counter;
}
@Override
public Integer getDefaultCleanupParameter(Context context) {
return getPerformAutoCleanupArgs(context, 0);
}
@Override
public Integer getPerformCleanupParameter(Context context, List<FeedItem> items) {
return getPerformAutoCleanupArgs(context, items.size());
}
static int getPerformAutoCleanupArgs(Context context,
final int episodeNumber) {
if (episodeNumber >= 0
&& UserPreferences.getEpisodeCacheSize() != UserPreferences
.getEpisodeCacheSizeUnlimited()) {
int downloadedEpisodes = DBReader
.getNumberOfDownloadedEpisodes(context);
if (downloadedEpisodes + episodeNumber >= UserPreferences
.getEpisodeCacheSize()) {
return downloadedEpisodes + episodeNumber
- UserPreferences.getEpisodeCacheSize();
}
}
return 0;
}
}

View File

@ -0,0 +1,133 @@
package de.danoeh.antennapod.core.storage;
import android.content.Context;
import android.util.Log;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import de.danoeh.antennapod.core.BuildConfig;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.core.util.PowerUtils;
/**
* Implements the automatic download algorithm used by AntennaPod. This class assumes that
* the client uses the APEpisodeCleanupAlgorithm.
*/
public class APDownloadAlgorithm implements AutomaticDownloadAlgorithm {
private static final String TAG = "APDownloadAlgorithm";
private final APCleanupAlgorithm cleanupAlgorithm = new APCleanupAlgorithm();
/**
* Looks for undownloaded episodes in the queue or list of unread items and request a download if
* 1. Network is available
* 2. The device is charging or the user allows auto download on battery
* 3. There is free space in the episode cache
* This method is executed on an internal single thread executor.
*
* @param context Used for accessing the DB.
* @param mediaIds If this list is not empty, the method will only download a candidate for automatic downloading if
* its media ID is in the mediaIds list.
* @return A Runnable that will be submitted to an ExecutorService.
*/
@Override
public Runnable autoDownloadUndownloadedItems(final Context context, final long... mediaIds) {
return new Runnable() {
@Override
public void run() {
// true if we should auto download based on network status
boolean networkShouldAutoDl = NetworkUtils.autodownloadNetworkAvailable(context)
&& UserPreferences.isEnableAutodownload();
// true if we should auto download based on power status
boolean powerShouldAutoDl = PowerUtils.deviceCharging(context)
|| UserPreferences.isEnableAutodownloadOnBattery();
// we should only auto download if both network AND power are happy
if (networkShouldAutoDl && powerShouldAutoDl) {
Log.d(TAG, "Performing auto-dl of undownloaded episodes");
final List<FeedItem> queue = DBReader.getQueue(context);
final List<FeedItem> unreadItems = DBReader
.getUnreadItemsList(context);
int undownloadedEpisodes = DBTasks.getNumberOfUndownloadedEpisodes(queue,
unreadItems);
int downloadedEpisodes = DBReader
.getNumberOfDownloadedEpisodes(context);
int deletedEpisodes = cleanupAlgorithm.performCleanup(context,
APCleanupAlgorithm.getPerformAutoCleanupArgs(context, undownloadedEpisodes));
int episodeSpaceLeft = undownloadedEpisodes;
boolean cacheIsUnlimited = UserPreferences.getEpisodeCacheSize() == UserPreferences
.getEpisodeCacheSizeUnlimited();
if (!cacheIsUnlimited
&& UserPreferences.getEpisodeCacheSize() < downloadedEpisodes
+ undownloadedEpisodes) {
episodeSpaceLeft = UserPreferences.getEpisodeCacheSize()
- (downloadedEpisodes - deletedEpisodes);
}
Arrays.sort(mediaIds); // sort for binary search
final boolean ignoreMediaIds = mediaIds.length == 0;
List<FeedItem> itemsToDownload = new ArrayList<FeedItem>();
if (episodeSpaceLeft > 0 && undownloadedEpisodes > 0) {
for (int i = 0; i < queue.size(); i++) { // ignore playing item
FeedItem item = queue.get(i);
long mediaId = (item.hasMedia()) ? item.getMedia().getId() : -1;
if ((ignoreMediaIds || Arrays.binarySearch(mediaIds, mediaId) >= 0)
&& item.hasMedia()
&& !item.getMedia().isDownloaded()
&& !item.getMedia().isPlaying()
&& item.getFeed().getPreferences().getAutoDownload()) {
itemsToDownload.add(item);
episodeSpaceLeft--;
undownloadedEpisodes--;
if (episodeSpaceLeft == 0 || undownloadedEpisodes == 0) {
break;
}
}
}
}
if (episodeSpaceLeft > 0 && undownloadedEpisodes > 0) {
for (FeedItem item : unreadItems) {
long mediaId = (item.hasMedia()) ? item.getMedia().getId() : -1;
if ((ignoreMediaIds || Arrays.binarySearch(mediaIds, mediaId) >= 0)
&& item.hasMedia()
&& !item.getMedia().isDownloaded()
&& item.getFeed().getPreferences().getAutoDownload()) {
itemsToDownload.add(item);
episodeSpaceLeft--;
undownloadedEpisodes--;
if (episodeSpaceLeft == 0 || undownloadedEpisodes == 0) {
break;
}
}
}
}
if (BuildConfig.DEBUG)
Log.d(TAG, "Enqueueing " + itemsToDownload.size()
+ " items for download");
try {
DBTasks.downloadFeedItems(false, context,
itemsToDownload.toArray(new FeedItem[itemsToDownload
.size()])
);
} catch (DownloadRequestException e) {
e.printStackTrace();
}
}
}
};
}
}

View File

@ -0,0 +1,139 @@
package de.danoeh.antennapod.core.storage;
import android.content.Context;
import android.util.Log;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ExecutionException;
import de.danoeh.antennapod.core.feed.FeedItem;
/**
* Implementation of the EpisodeCleanupAlgorithm interface used by AntennaPodSP apps.
*/
public class APSPCleanupAlgorithm implements EpisodeCleanupAlgorithm<Integer> {
private static final String TAG = "APSPCleanupAlgorithm";
final int numberOfNewAutomaticallyDownloadedEpisodes;
public APSPCleanupAlgorithm(int numberOfNewAutomaticallyDownloadedEpisodes) {
this.numberOfNewAutomaticallyDownloadedEpisodes = numberOfNewAutomaticallyDownloadedEpisodes;
}
/**
* Performs an automatic cleanup. Episodes that have been downloaded first will also be deleted first.
* The episode that is currently playing as well as the n most recent episodes (the exact value is determined
* by AppPreferences.numberOfNewAutomaticallyDownloadedEpisodes) will never be deleted.
*
* @param context
* @param episodeSize The maximum amount of space that should be freed by this method
* @return The number of episodes that have been deleted
*/
@Override
public int performCleanup(Context context, Integer episodeSize) {
Log.i(TAG, String.format("performAutoCleanup(%d)", episodeSize));
if (episodeSize <= 0) {
return 0;
}
List<FeedItem> candidates = getAutoCleanupCandidates(context);
List<FeedItem> deleteList = new ArrayList<FeedItem>();
long deletedEpisodesSize = 0;
Collections.sort(candidates, new Comparator<FeedItem>() {
@Override
public int compare(FeedItem lhs, FeedItem rhs) {
File lFile = new File(lhs.getMedia().getFile_url());
File rFile = new File(rhs.getMedia().getFile_url());
if (!lFile.exists() || !rFile.exists()) {
return 0;
}
if (FileUtils.isFileOlder(lFile, rFile)) {
return -1;
} else {
return 1;
}
}
});
// listened episodes will be deleted first
Iterator<FeedItem> it = candidates.iterator();
if (it.hasNext()) {
for (FeedItem i = it.next(); it.hasNext() && deletedEpisodesSize <= episodeSize; i = it.next()) {
if (!i.getMedia().isPlaying() && i.getMedia().getPlaybackCompletionDate() != null) {
it.remove();
deleteList.add(i);
deletedEpisodesSize += i.getMedia().getSize();
}
}
}
// delete unlistened old episodes if necessary
it = candidates.iterator();
if (it.hasNext()) {
for (FeedItem i = it.next(); it.hasNext() && deletedEpisodesSize <= episodeSize; i = it.next()) {
if (!i.getMedia().isPlaying()) {
it.remove();
deleteList.add(i);
deletedEpisodesSize += i.getMedia().getSize();
}
}
}
for (FeedItem item : deleteList) {
try {
DBWriter.deleteFeedMediaOfItem(context, item.getMedia().getId()).get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
Log.i(TAG, String.format("performAutoCleanup(%d) deleted %d episodes and freed %d bytes of memory",
episodeSize, deleteList.size(), deletedEpisodesSize));
return deleteList.size();
}
@Override
public Integer getDefaultCleanupParameter(Context context) {
return 0;
}
@Override
public Integer getPerformCleanupParameter(Context context, List<FeedItem> items) {
int episodeSize = 0;
for (FeedItem item : items) {
if (item.hasMedia() && !item.getMedia().isDownloaded()) {
episodeSize += item.getMedia().getSize();
}
}
return episodeSize;
}
/**
* Returns list of FeedItems that have been downloaded, but are not one of the
* [numberOfNewAutomaticallyDownloadedEpisodes] most recent items.
*/
private List<FeedItem> getAutoCleanupCandidates(Context context) {
List<FeedItem> downloaded = new ArrayList<FeedItem>(DBReader.getDownloadedItems(context));
List<FeedItem> recent = new ArrayList<FeedItem>(DBReader.getRecentlyPublishedEpisodes(context,
numberOfNewAutomaticallyDownloadedEpisodes));
for (FeedItem r : recent) {
if (r.hasMedia() && r.getMedia().isDownloaded()) {
for (int i = 0; i < downloaded.size(); i++) {
if (downloaded.get(i).getId() == r.getId()) {
downloaded.remove(i);
break;
}
}
}
}
return downloaded;
}
}

View File

@ -0,0 +1,72 @@
package de.danoeh.antennapod.core.storage;
import android.content.Context;
import android.util.Log;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import de.danoeh.antennapod.core.BuildConfig;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.NetworkUtils;
/**
* Implements the automatic download algorithm used by AntennaPodSP apps.
*/
public class APSPDownloadAlgorithm implements AutomaticDownloadAlgorithm {
private static final String TAG = "APSPDownloadAlgorithm";
private final int numberOfNewAutomaticallyDownloadedEpisodes;
public APSPDownloadAlgorithm(int numberOfNewAutomaticallyDownloadedEpisodes) {
this.numberOfNewAutomaticallyDownloadedEpisodes = numberOfNewAutomaticallyDownloadedEpisodes;
}
/**
* Downloads the most recent episodes automatically. The exact number of
* episodes that will be downloaded can be set in the AppPreferences.
*
* @param context Used for accessing the DB.
* @return A Runnable that will be submitted to an ExecutorService.
*/
@Override
public Runnable autoDownloadUndownloadedItems(final Context context, final long... mediaIds) {
return new Runnable() {
@Override
public void run() {
if (BuildConfig.DEBUG)
Log.d(TAG, "Performing auto-dl of undownloaded episodes");
if (NetworkUtils.autodownloadNetworkAvailable(context)
&& UserPreferences.isEnableAutodownload()) {
Arrays.sort(mediaIds);
List<FeedItem> itemsToDownload = DBReader.getRecentlyPublishedEpisodes(context,
numberOfNewAutomaticallyDownloadedEpisodes);
Iterator<FeedItem> it = itemsToDownload.iterator();
for (FeedItem item = it.next(); it.hasNext(); item = it.next()) {
if (!item.hasMedia()
|| item.getMedia().isDownloaded()
|| Arrays.binarySearch(mediaIds, item.getMedia().getId()) < 0) {
it.remove();
}
}
if (BuildConfig.DEBUG)
Log.d(TAG, "Enqueueing " + itemsToDownload.size()
+ " items for automatic download");
if (!itemsToDownload.isEmpty()) {
try {
DBTasks.downloadFeedItems(false, context,
itemsToDownload.toArray(new FeedItem[itemsToDownload
.size()]));
} catch (DownloadRequestException e) {
e.printStackTrace();
}
}
}
}
};
}
}

View File

@ -0,0 +1,20 @@
package de.danoeh.antennapod.core.storage;
import android.content.Context;
public interface AutomaticDownloadAlgorithm {
/**
* Looks for undownloaded episodes and request a download if
* 1. Network is available
* 2. The device is charging or the user allows auto download on battery
* 3. There is free space in the episode cache
* This method is executed on an internal single thread executor.
*
* @param context Used for accessing the DB.
* @param mediaIds If this list is not empty, the method will only download a candidate for automatic downloading if
* its media ID is in the mediaIds list.
* @return A Runnable that will be submitted to an ExecutorService.
*/
public Runnable autoDownloadUndownloadedItems(Context context, long... mediaIds);
}

View File

@ -8,7 +8,6 @@ import android.util.Log;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
@ -35,7 +34,6 @@ import de.danoeh.antennapod.core.service.GpodnetSyncService;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.util.DownloadError;
import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.core.util.QueueAccess;
import de.danoeh.antennapod.core.util.comparator.FeedItemPubdateComparator;
import de.danoeh.antennapod.core.util.exception.MediaFileNotFoundException;
@ -386,8 +384,8 @@ public final class DBTasks {
downloadFeedItems(true, context, items);
}
private static void downloadFeedItems(boolean performAutoCleanup,
final Context context, final FeedItem... items)
static void downloadFeedItems(boolean performAutoCleanup,
final Context context, final FeedItem... items)
throws DownloadRequestException {
final DownloadRequester requester = DownloadRequester.getInstance();
@ -396,8 +394,10 @@ public final class DBTasks {
@Override
public void run() {
performAutoCleanup(context,
getPerformAutoCleanupArgs(context, items.length));
ClientConfig.dbTasksCallbacks.getEpisodeCacheCleanupAlgorithm()
.performCleanup(context,
ClientConfig.dbTasksCallbacks.getEpisodeCacheCleanupAlgorithm()
.getPerformCleanupParameter(context, Arrays.asList(items)));
}
}.start();
@ -427,7 +427,7 @@ public final class DBTasks {
}
}
private static int getNumberOfUndownloadedEpisodes(
static int getNumberOfUndownloadedEpisodes(
final List<FeedItem> queue, final List<FeedItem> unreadItems) {
int counter = 0;
for (FeedItem item : queue) {
@ -449,7 +449,8 @@ public final class DBTasks {
/**
* Looks for undownloaded episodes in the queue or list of unread items and request a download if
* 1. Network is available
* 2. There is free space in the episode cache
* 2. The device is charging or the user allows auto download on battery
* 3. There is free space in the episode cache
* This method is executed on an internal single thread executor.
*
* @param context Used for accessing the DB.
@ -458,107 +459,9 @@ public final class DBTasks {
* @return A Future that can be used for waiting for the methods completion.
*/
public static Future<?> autodownloadUndownloadedItems(final Context context, final long... mediaIds) {
return autodownloadExec.submit(new Runnable() {
@Override
public void run() {
if (BuildConfig.DEBUG)
Log.d(TAG, "Performing auto-dl of undownloaded episodes");
if (NetworkUtils.autodownloadNetworkAvailable(context)
&& UserPreferences.isEnableAutodownload()) {
final List<FeedItem> queue = DBReader.getQueue(context);
final List<FeedItem> unreadItems = DBReader
.getUnreadItemsList(context);
return autodownloadExec.submit(ClientConfig.dbTasksCallbacks.getAutomaticDownloadAlgorithm()
.autoDownloadUndownloadedItems(context, mediaIds));
int undownloadedEpisodes = getNumberOfUndownloadedEpisodes(queue,
unreadItems);
int downloadedEpisodes = DBReader
.getNumberOfDownloadedEpisodes(context);
int deletedEpisodes = performAutoCleanup(context,
getPerformAutoCleanupArgs(context, undownloadedEpisodes));
int episodeSpaceLeft = undownloadedEpisodes;
boolean cacheIsUnlimited = UserPreferences.getEpisodeCacheSize() == UserPreferences
.getEpisodeCacheSizeUnlimited();
if (!cacheIsUnlimited
&& UserPreferences.getEpisodeCacheSize() < downloadedEpisodes
+ undownloadedEpisodes) {
episodeSpaceLeft = UserPreferences.getEpisodeCacheSize()
- (downloadedEpisodes - deletedEpisodes);
}
Arrays.sort(mediaIds); // sort for binary search
final boolean ignoreMediaIds = mediaIds.length == 0;
List<FeedItem> itemsToDownload = new ArrayList<FeedItem>();
if (episodeSpaceLeft > 0 && undownloadedEpisodes > 0) {
for (int i = 0; i < queue.size(); i++) { // ignore playing item
FeedItem item = queue.get(i);
long mediaId = (item.hasMedia()) ? item.getMedia().getId() : -1;
if ((ignoreMediaIds || Arrays.binarySearch(mediaIds, mediaId) >= 0)
&& item.hasMedia()
&& !item.getMedia().isDownloaded()
&& !item.getMedia().isPlaying()
&& item.getFeed().getPreferences().getAutoDownload()) {
itemsToDownload.add(item);
episodeSpaceLeft--;
undownloadedEpisodes--;
if (episodeSpaceLeft == 0 || undownloadedEpisodes == 0) {
break;
}
}
}
}
if (episodeSpaceLeft > 0 && undownloadedEpisodes > 0) {
for (FeedItem item : unreadItems) {
long mediaId = (item.hasMedia()) ? item.getMedia().getId() : -1;
if ((ignoreMediaIds || Arrays.binarySearch(mediaIds, mediaId) >= 0)
&& item.hasMedia()
&& !item.getMedia().isDownloaded()
&& item.getFeed().getPreferences().getAutoDownload()) {
itemsToDownload.add(item);
episodeSpaceLeft--;
undownloadedEpisodes--;
if (episodeSpaceLeft == 0 || undownloadedEpisodes == 0) {
break;
}
}
}
}
if (BuildConfig.DEBUG)
Log.d(TAG, "Enqueueing " + itemsToDownload.size()
+ " items for download");
try {
downloadFeedItems(false, context,
itemsToDownload.toArray(new FeedItem[itemsToDownload
.size()])
);
} catch (DownloadRequestException e) {
e.printStackTrace();
}
}
}
});
}
private static int getPerformAutoCleanupArgs(Context context,
final int episodeNumber) {
if (episodeNumber >= 0
&& UserPreferences.getEpisodeCacheSize() != UserPreferences
.getEpisodeCacheSizeUnlimited()) {
int downloadedEpisodes = DBReader
.getNumberOfDownloadedEpisodes(context);
if (downloadedEpisodes + episodeNumber >= UserPreferences
.getEpisodeCacheSize()) {
return downloadedEpisodes + episodeNumber
- UserPreferences.getEpisodeCacheSize();
}
}
return 0;
}
/**
@ -570,63 +473,8 @@ public final class DBTasks {
* @param context Used for accessing the DB.
*/
public static void performAutoCleanup(final Context context) {
performAutoCleanup(context, getPerformAutoCleanupArgs(context, 0));
}
private static int performAutoCleanup(final Context context,
final int episodeNumber) {
List<FeedItem> candidates = new ArrayList<FeedItem>();
List<FeedItem> downloadedItems = DBReader.getDownloadedItems(context);
QueueAccess queue = QueueAccess.IDListAccess(DBReader.getQueueIDList(context));
List<FeedItem> delete;
for (FeedItem item : downloadedItems) {
if (item.hasMedia() && item.getMedia().isDownloaded()
&& !queue.contains(item.getId()) && item.isRead()) {
candidates.add(item);
}
}
Collections.sort(candidates, new Comparator<FeedItem>() {
@Override
public int compare(FeedItem lhs, FeedItem rhs) {
Date l = lhs.getMedia().getPlaybackCompletionDate();
Date r = rhs.getMedia().getPlaybackCompletionDate();
if (l == null) {
l = new Date(0);
}
if (r == null) {
r = new Date(0);
}
return l.compareTo(r);
}
});
if (candidates.size() > episodeNumber) {
delete = candidates.subList(0, episodeNumber);
} else {
delete = candidates;
}
for (FeedItem item : delete) {
try {
DBWriter.deleteFeedMediaOfItem(context, item.getMedia().getId()).get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
int counter = delete.size();
if (BuildConfig.DEBUG)
Log.d(TAG, String.format(
"Auto-delete deleted %d episodes (%d requested)", counter,
episodeNumber));
return counter;
ClientConfig.dbTasksCallbacks.getEpisodeCacheCleanupAlgorithm().performCleanup(context,
ClientConfig.dbTasksCallbacks.getEpisodeCacheCleanupAlgorithm().getDefaultCleanupParameter(context));
}
/**

View File

@ -7,23 +7,14 @@ import android.content.SharedPreferences;
import android.database.Cursor;
import android.preference.PreferenceManager;
import android.util.Log;
import de.danoeh.antennapod.core.BuildConfig;
import de.danoeh.antennapod.core.ClientConfig;
import de.danoeh.antennapod.core.asynctask.FlattrClickWorker;
import de.danoeh.antennapod.core.feed.*;
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.util.QueueAccess;
import de.danoeh.antennapod.core.util.flattr.FlattrStatus;
import de.danoeh.antennapod.core.util.flattr.FlattrThing;
import de.danoeh.antennapod.core.util.flattr.SimpleFlattrThing;
import org.shredzone.flattr4j.model.Flattr;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
@ -33,6 +24,24 @@ import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import de.danoeh.antennapod.core.BuildConfig;
import de.danoeh.antennapod.core.ClientConfig;
import de.danoeh.antennapod.core.asynctask.FlattrClickWorker;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedImage;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.FeedPreferences;
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.util.QueueAccess;
import de.danoeh.antennapod.core.util.flattr.FlattrStatus;
import de.danoeh.antennapod.core.util.flattr.FlattrThing;
import de.danoeh.antennapod.core.util.flattr.SimpleFlattrThing;
/**
* Provides methods for writing data to AntennaPod's database.
* In general, DBWriter-methods will be executed on an internal ExecutorService.
@ -819,7 +828,8 @@ public class DBWriter {
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
for (String key : urls.keySet()) {
if (BuildConfig.DEBUG) Log.d(TAG, "Replacing URL " + key + " with url " + urls.get(key));
if (BuildConfig.DEBUG)
Log.d(TAG, "Replacing URL " + key + " with url " + urls.get(key));
adapter.setFeedDownloadUrl(key, urls.get(key));
}
@ -976,4 +986,35 @@ public class DBWriter {
}
});
}
/**
* Sort the FeedItems in the queue with the given Comparator.
*
* @param context A context that is used for opening a database connection.
* @param comparator FeedItem comparator
* @param broadcastUpdate true if this operation should trigger a QueueUpdateBroadcast. This option should be set to
* false if the caller wants to avoid unexpected updates of the GUI.
*/
public static Future<?> sortQueue(final Context context, final Comparator<FeedItem> comparator, final boolean broadcastUpdate) {
return dbExec.submit(new Runnable() {
@Override
public void run() {
final PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
final List<FeedItem> queue = DBReader.getQueue(context, adapter);
if (queue != null) {
Collections.sort(queue, comparator);
adapter.setQueue(queue);
if (broadcastUpdate) {
EventDistributor.getInstance()
.sendQueueUpdateBroadcast();
}
} else {
Log.e(TAG, "sortQueue: Could not load queue");
}
adapter.close();
}
});
}
}

View File

@ -92,8 +92,9 @@ public class DownloadRequester {
private void download(Context context, FeedFile item, FeedFile container, File dest,
boolean overwriteIfExists, String username, String password, boolean deleteOnFailure, Bundle arguments) {
final boolean partiallyDownloadedFileExists = item.getFile_url() != null;
if (!isDownloadingFile(item)) {
if (!isFilenameAvailable(dest.toString()) || (deleteOnFailure && dest.exists())) {
if (!isFilenameAvailable(dest.toString()) || (!partiallyDownloadedFileExists && dest.exists())) {
if (BuildConfig.DEBUG)
Log.d(TAG, "Filename already used.");
if (isFilenameAvailable(dest.toString()) && overwriteIfExists) {
@ -254,8 +255,7 @@ public class DownloadRequester {
* Cancels all running downloads
*/
public synchronized void cancelAllDownloads(Context context) {
if (BuildConfig.DEBUG)
Log.d(TAG, "Cancelling all running downloads");
Log.d(TAG, "Cancelling all running downloads");
context.sendBroadcast(new Intent(
DownloadService.ACTION_CANCEL_ALL_DOWNLOADS));
}
@ -377,10 +377,13 @@ public class DownloadRequester {
String URLBaseFilename = URLUtil.guessFileName(media.getDownload_url(),
null, media.getMime_type());
;
if (titleBaseFilename != "") {
if (!titleBaseFilename.equals("")) {
// Append extension
final int FILENAME_MAX_LENGTH = 220;
if (titleBaseFilename.length() > FILENAME_MAX_LENGTH) {
titleBaseFilename = titleBaseFilename.substring(0, FILENAME_MAX_LENGTH);
}
filename = titleBaseFilename + FilenameUtils.EXTENSION_SEPARATOR +
FilenameUtils.getExtension(URLBaseFilename);
} else {

View File

@ -0,0 +1,36 @@
package de.danoeh.antennapod.core.storage;
import android.content.Context;
import java.util.List;
import de.danoeh.antennapod.core.feed.FeedItem;
public interface EpisodeCleanupAlgorithm<T> {
/**
* Deletes downloaded episodes that are no longer needed. What episodes are deleted and how many
* of them depends on the implementation.
*
* @param context Can be used for accessing the database
* @param parameter An additional parameter. This parameter is either returned by getDefaultCleanupParameter
* or getPerformCleanupParameter.
* @return The number of episodes that were deleted.
*/
public int performCleanup(Context context, T parameter);
/**
* Returns a parameter for performCleanup. The implementation of this interface should decide how much
* space to free to satisfy the episode cache conditions. If the conditions are already satisfied, this
* method should not have any effects.
*/
public T getDefaultCleanupParameter(Context context);
/**
* Returns a parameter for performCleanup.
*
* @param items A list of FeedItems that are about to be downloaded. The implementation of this interface
* should decide how much space to free to satisfy the episode cache conditions.
*/
public T getPerformCleanupParameter(Context context, List<FeedItem> items);
}

View File

@ -0,0 +1,32 @@
package de.danoeh.antennapod.core.util;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
/**
* Created by Tom on 1/5/15.
*/
public class PowerUtils {
private static final String TAG = "PowerUtils";
private PowerUtils() {
}
/**
* @return true if the device is charging
*/
public static boolean deviceCharging(Context context) {
// from http://developer.android.com/training/monitoring-device-state/battery-monitoring.html
IntentFilter iFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus = context.registerReceiver(null, iFilter);
int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
return (status == BatteryManager.BATTERY_STATUS_CHARGING ||
status == BatteryManager.BATTERY_STATUS_FULL);
}
}

View File

@ -0,0 +1,89 @@
package de.danoeh.antennapod.core.util;
import android.content.Context;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.storage.DBWriter;
import java.util.Comparator;
/**
* Provides method for sorting the queue according to rules.
*/
public class QueueSorter {
public enum Rule {
ALPHA_ASC,
ALPHA_DESC,
DATE_ASC,
DATE_DESC,
DURATION_ASC,
DURATION_DESC
}
public static void sort(final Context context, final Rule rule, final boolean broadcastUpdate) {
Comparator<FeedItem> comparator = null;
switch (rule) {
case ALPHA_ASC:
comparator = new Comparator<FeedItem>() {
public int compare(FeedItem f1, FeedItem f2) {
return f1.getTitle().compareTo(f2.getTitle());
}
};
break;
case ALPHA_DESC:
comparator = new Comparator<FeedItem>() {
public int compare(FeedItem f1, FeedItem f2) {
return f2.getTitle().compareTo(f1.getTitle());
}
};
break;
case DATE_ASC:
comparator = new Comparator<FeedItem>() {
public int compare(FeedItem f1, FeedItem f2) {
return f1.getPubDate().compareTo(f2.getPubDate());
}
};
break;
case DATE_DESC:
comparator = new Comparator<FeedItem>() {
public int compare(FeedItem f1, FeedItem f2) {
return f2.getPubDate().compareTo(f1.getPubDate());
}
};
break;
case DURATION_ASC:
comparator = new Comparator<FeedItem>() {
public int compare(FeedItem f1, FeedItem f2) {
FeedMedia f1Media = f1.getMedia();
FeedMedia f2Media = f2.getMedia();
int duration1 = f1Media != null ? f1Media.getDuration() : -1;
int duration2 = f2Media != null ? f2Media.getDuration() : -1;
if (duration1 == -1 || duration2 == -1)
return duration2 - duration1;
else
return duration1 - duration2;
}
};
break;
case DURATION_DESC:
comparator = new Comparator<FeedItem>() {
public int compare(FeedItem f1, FeedItem f2) {
FeedMedia f1Media = f1.getMedia();
FeedMedia f2Media = f2.getMedia();
int duration1 = f1Media != null ? f1Media.getDuration() : -1;
int duration2 = f2Media != null ? f2Media.getDuration() : -1;
return -1 * (duration1 - duration2);
}
};
default:
}
if (comparator != null) {
DBWriter.sortQueue(context, comparator, broadcastUpdate);
}
}
}

View File

@ -28,7 +28,7 @@ import de.danoeh.antennapod.core.util.ShownotesProvider;
public class Timeline {
private static final String TAG = "Timeline";
private static final String WEBVIEW_STYLE = "@font-face { font-family: 'Roboto-Light'; src: url('file:///android_asset/Roboto-Light.ttf'); } * { color: %s; font-family: roboto-Light; font-size: 11pt; } a { font-style: normal; text-decoration: none; font-weight: normal; color: #00A8DF; } a.timecode { color: #669900; } img { display: block; margin: 10 auto; max-width: %s; height: auto; } body { margin: %dpx %dpx %dpx %dpx; }";
private static final String WEBVIEW_STYLE = "@font-face { font-family: 'Roboto-Light'; src: url('file:///android_asset/Roboto-Light.ttf'); } * { color: %s; font-family: roboto-Light; font-size: 13pt; } a { font-style: normal; text-decoration: none; font-weight: normal; color: #00A8DF; } a.timecode { color: #669900; } img { display: block; margin: 10 auto; max-width: %s; height: auto; } body { margin: %dpx %dpx %dpx %dpx; }";
private ShownotesProvider shownotesProvider;

Binary file not shown.

After

Width:  |  Height:  |  Size: 572 B

Some files were not shown because too many files have changed in this diff Show More