Merge branch 'backup' of git://github.com/liesen/AntennaPod into liesen-backup
Conflicts: src/de/danoeh/antennapod/activity/MainActivity.java Moved calls to BackupManager from PodDBAdapter to DBWriter
This commit is contained in:
commit
dc06e81d95
|
@ -35,8 +35,13 @@
|
||||||
android:name="de.danoeh.antennapod.PodcastApp"
|
android:name="de.danoeh.antennapod.PodcastApp"
|
||||||
android:icon="@drawable/ic_launcher"
|
android:icon="@drawable/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
|
android:backupAgent=".backup.OpmlBackupAgent"
|
||||||
|
android:restoreAnyVersion="true"
|
||||||
android:logo="@drawable/ic_launcher"
|
android:logo="@drawable/ic_launcher"
|
||||||
android:theme="@style/Theme.AntennaPod.Light">
|
android:theme="@style/Theme.AntennaPod.Light">
|
||||||
|
<meta-data
|
||||||
|
android:name="com.google.android.backup.api_key"
|
||||||
|
android:value="AEdPqrEAAAAIRUmY27PIgwf_FtdHiT1_QLRM-7VLPnnFQ2Y_zw" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".activity.MainActivity"
|
android:name=".activity.MainActivity"
|
||||||
android:configChanges="keyboardHidden|orientation"
|
android:configChanges="keyboardHidden|orientation"
|
||||||
|
|
|
@ -354,6 +354,9 @@
|
||||||
<string name="new_episodes_count_label">Number of new episodes</string>
|
<string name="new_episodes_count_label">Number of new episodes</string>
|
||||||
<string name="in_progress_episodes_count_label">Number of episodes you have started listening to</string>
|
<string name="in_progress_episodes_count_label">Number of episodes you have started listening to</string>
|
||||||
|
|
||||||
|
<!-- OPML backup -->
|
||||||
|
<string name="backup_restored">"Restored feed subscriptions from backup"</string>
|
||||||
|
|
||||||
<!-- AntennaPodSP -->
|
<!-- AntennaPodSP -->
|
||||||
|
|
||||||
<string name="sp_apps_importing_feeds_msg">Importing subscriptions from single-purpose apps…</string>
|
<string name="sp_apps_importing_feeds_msg">Importing subscriptions from single-purpose apps…</string>
|
||||||
|
|
|
@ -35,152 +35,153 @@ import de.danoeh.antennapod.util.StorageUtils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
/** The activity that is shown when the user launches the app. */
|
/**
|
||||||
|
* The activity that is shown when the user launches the app.
|
||||||
|
*/
|
||||||
public class MainActivity extends ActionBarActivity {
|
public class MainActivity extends ActionBarActivity {
|
||||||
private static final String TAG = "MainActivity";
|
private static final String TAG = "MainActivity";
|
||||||
|
|
||||||
private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED
|
private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED
|
||||||
| EventDistributor.DOWNLOAD_QUEUED;
|
| EventDistributor.DOWNLOAD_QUEUED;
|
||||||
|
|
||||||
private ViewPager viewpager;
|
private ViewPager viewpager;
|
||||||
private TabsAdapter pagerAdapter;
|
private TabsAdapter pagerAdapter;
|
||||||
private ExternalPlayerFragment externalPlayerFragment;
|
private ExternalPlayerFragment externalPlayerFragment;
|
||||||
|
|
||||||
private static boolean appLaunched = false;
|
private static boolean appLaunched = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
setTheme(UserPreferences.getTheme());
|
setTheme(UserPreferences.getTheme());
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
StorageUtils.checkStorageAvailability(this);
|
StorageUtils.checkStorageAvailability(this);
|
||||||
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
|
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
|
||||||
setContentView(R.layout.main);
|
setContentView(R.layout.main);
|
||||||
setVolumeControlStream(AudioManager.STREAM_MUSIC);
|
setVolumeControlStream(AudioManager.STREAM_MUSIC);
|
||||||
|
|
||||||
getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
|
getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
|
||||||
|
|
||||||
viewpager = (ViewPager) findViewById(R.id.viewpager);
|
viewpager = (ViewPager) findViewById(R.id.viewpager);
|
||||||
pagerAdapter = new TabsAdapter(this, viewpager);
|
pagerAdapter = new TabsAdapter(this, viewpager);
|
||||||
|
|
||||||
viewpager.setAdapter(pagerAdapter);
|
viewpager.setAdapter(pagerAdapter);
|
||||||
|
|
||||||
ActionBar.Tab feedsTab = getSupportActionBar().newTab();
|
ActionBar.Tab feedsTab = getSupportActionBar().newTab();
|
||||||
feedsTab.setText(R.string.podcasts_label);
|
feedsTab.setText(R.string.podcasts_label);
|
||||||
ActionBar.Tab episodesTab = getSupportActionBar().newTab();
|
ActionBar.Tab episodesTab = getSupportActionBar().newTab();
|
||||||
episodesTab.setText(R.string.episodes_label);
|
episodesTab.setText(R.string.episodes_label);
|
||||||
|
|
||||||
pagerAdapter.addTab(feedsTab, FeedlistFragment.class, null);
|
pagerAdapter.addTab(feedsTab, FeedlistFragment.class, null);
|
||||||
pagerAdapter.addTab(episodesTab, EpisodesFragment.class, null);
|
pagerAdapter.addTab(episodesTab, EpisodesFragment.class, null);
|
||||||
|
|
||||||
FragmentTransaction transaction = getSupportFragmentManager()
|
FragmentTransaction transaction = getSupportFragmentManager()
|
||||||
.beginTransaction();
|
.beginTransaction();
|
||||||
externalPlayerFragment = new ExternalPlayerFragment();
|
externalPlayerFragment = new ExternalPlayerFragment();
|
||||||
transaction.replace(R.id.playerFragment, externalPlayerFragment);
|
transaction.replace(R.id.playerFragment, externalPlayerFragment);
|
||||||
transaction.commit();
|
transaction.commit();
|
||||||
|
|
||||||
// executed on application start
|
// executed on application start
|
||||||
if (!appLaunched && getIntent().getAction() != null
|
if (!appLaunched && getIntent().getAction() != null
|
||||||
&& getIntent().getAction().equals(Intent.ACTION_MAIN)) {
|
&& getIntent().getAction().equals(Intent.ACTION_MAIN)) {
|
||||||
appLaunched = true;
|
appLaunched = true;
|
||||||
if (DBReader.getNumberOfUnreadItems(this) > 0) {
|
if (DBReader.getNumberOfUnreadItems(this) > 0) {
|
||||||
// select 'episodes' tab
|
// select 'episodes' tab
|
||||||
getSupportActionBar().setSelectedNavigationItem(1);
|
getSupportActionBar().setSelectedNavigationItem(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (savedInstanceState != null) {
|
if (savedInstanceState != null) {
|
||||||
getSupportActionBar().setSelectedNavigationItem(
|
getSupportActionBar().setSelectedNavigationItem(
|
||||||
savedInstanceState.getInt("tab", 0));
|
savedInstanceState.getInt("tab", 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onSaveInstanceState(Bundle outState) {
|
protected void onSaveInstanceState(Bundle outState) {
|
||||||
super.onSaveInstanceState(outState);
|
super.onSaveInstanceState(outState);
|
||||||
outState.putInt("tab", getSupportActionBar()
|
outState.putInt("tab", getSupportActionBar()
|
||||||
.getSelectedNavigationIndex());
|
.getSelectedNavigationIndex());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPause() {
|
protected void onPause() {
|
||||||
super.onPause();
|
super.onPause();
|
||||||
EventDistributor.getInstance().unregister(contentUpdate);
|
EventDistributor.getInstance().unregister(contentUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
StorageUtils.checkStorageAvailability(this);
|
StorageUtils.checkStorageAvailability(this);
|
||||||
updateProgressBarVisibility();
|
updateProgressBarVisibility();
|
||||||
EventDistributor.getInstance().register(contentUpdate);
|
EventDistributor.getInstance().register(contentUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
|
||||||
|
|
||||||
private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
|
@Override
|
||||||
|
public void update(EventDistributor eventDistributor, Integer arg) {
|
||||||
|
if ((EVENTS & arg) != 0) {
|
||||||
|
if (BuildConfig.DEBUG)
|
||||||
|
Log.d(TAG, "Received contentUpdate Intent.");
|
||||||
|
updateProgressBarVisibility();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@Override
|
private void updateProgressBarVisibility() {
|
||||||
public void update(EventDistributor eventDistributor, Integer arg) {
|
if (DownloadService.isRunning
|
||||||
if ((EVENTS & arg) != 0) {
|
&& DownloadRequester.getInstance().isDownloadingFeeds()) {
|
||||||
if (BuildConfig.DEBUG)
|
setSupportProgressBarIndeterminateVisibility(true);
|
||||||
Log.d(TAG, "Received contentUpdate Intent.");
|
} else {
|
||||||
updateProgressBarVisibility();
|
setSupportProgressBarIndeterminateVisibility(false);
|
||||||
}
|
}
|
||||||
}
|
supportInvalidateOptionsMenu();
|
||||||
};
|
}
|
||||||
|
|
||||||
private void updateProgressBarVisibility() {
|
@Override
|
||||||
if (DownloadService.isRunning
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
&& DownloadRequester.getInstance().isDownloadingFeeds()) {
|
switch (item.getItemId()) {
|
||||||
setSupportProgressBarIndeterminateVisibility(true);
|
case R.id.add_feed:
|
||||||
} else {
|
startActivity(new Intent(this, AddFeedActivity.class));
|
||||||
setSupportProgressBarIndeterminateVisibility(false);
|
return true;
|
||||||
}
|
case R.id.all_feed_refresh:
|
||||||
supportInvalidateOptionsMenu();
|
DBTasks.refreshAllFeeds(this, null);
|
||||||
}
|
return true;
|
||||||
|
case R.id.show_downloads:
|
||||||
|
startActivity(new Intent(this, DownloadActivity.class));
|
||||||
|
return true;
|
||||||
|
case R.id.show_preferences:
|
||||||
|
startActivity(new Intent(this, PreferenceActivity.class));
|
||||||
|
return true;
|
||||||
|
case R.id.show_player:
|
||||||
|
startActivity(PlaybackService.getPlayerActivityIntent(this));
|
||||||
|
return true;
|
||||||
|
case R.id.show_playback_history:
|
||||||
|
startActivity(new Intent(this, PlaybackHistoryActivity.class));
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||||
switch (item.getItemId()) {
|
|
||||||
case R.id.add_feed:
|
|
||||||
startActivity(new Intent(this, AddFeedActivity.class));
|
|
||||||
return true;
|
|
||||||
case R.id.all_feed_refresh:
|
|
||||||
DBTasks.refreshAllFeeds(this, null);
|
|
||||||
return true;
|
|
||||||
case R.id.show_downloads:
|
|
||||||
startActivity(new Intent(this, DownloadActivity.class));
|
|
||||||
return true;
|
|
||||||
case R.id.show_preferences:
|
|
||||||
startActivity(new Intent(this, PreferenceActivity.class));
|
|
||||||
return true;
|
|
||||||
case R.id.show_player:
|
|
||||||
startActivity(PlaybackService.getPlayerActivityIntent(this));
|
|
||||||
return true;
|
|
||||||
case R.id.show_playback_history:
|
|
||||||
startActivity(new Intent(this, PlaybackHistoryActivity.class));
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return super.onOptionsItemSelected(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
|
||||||
super.onPrepareOptionsMenu(menu);
|
super.onPrepareOptionsMenu(menu);
|
||||||
MenuItem refreshAll = menu.findItem(R.id.all_feed_refresh);
|
MenuItem refreshAll = menu.findItem(R.id.all_feed_refresh);
|
||||||
if (DownloadService.isRunning
|
if (DownloadService.isRunning
|
||||||
&& DownloadRequester.getInstance().isDownloadingFeeds()) {
|
&& DownloadRequester.getInstance().isDownloadingFeeds()) {
|
||||||
refreshAll.setVisible(false);
|
refreshAll.setVisible(false);
|
||||||
} else {
|
} else {
|
||||||
refreshAll.setVisible(true);
|
refreshAll.setVisible(true);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
super.onCreateOptionsMenu(menu);
|
super.onCreateOptionsMenu(menu);
|
||||||
MenuInflater inflater = getMenuInflater();
|
MenuInflater inflater = getMenuInflater();
|
||||||
inflater.inflate(R.menu.main, menu);
|
inflater.inflate(R.menu.main, menu);
|
||||||
|
|
||||||
SearchManager searchManager =
|
SearchManager searchManager =
|
||||||
(SearchManager) getSystemService(Context.SEARCH_SERVICE);
|
(SearchManager) getSystemService(Context.SEARCH_SERVICE);
|
||||||
|
@ -198,87 +199,87 @@ public class MainActivity extends ActionBarActivity {
|
||||||
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class TabsAdapter extends FragmentPagerAdapter implements
|
public static class TabsAdapter extends FragmentPagerAdapter implements
|
||||||
ActionBar.TabListener, ViewPager.OnPageChangeListener {
|
ActionBar.TabListener, ViewPager.OnPageChangeListener {
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final ActionBar mActionBar;
|
private final ActionBar mActionBar;
|
||||||
private final ViewPager mViewPager;
|
private final ViewPager mViewPager;
|
||||||
private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
|
private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
|
||||||
|
|
||||||
static final class TabInfo {
|
static final class TabInfo {
|
||||||
private final Class<?> clss;
|
private final Class<?> clss;
|
||||||
private final Bundle args;
|
private final Bundle args;
|
||||||
|
|
||||||
TabInfo(Class<?> _class, Bundle _args) {
|
TabInfo(Class<?> _class, Bundle _args) {
|
||||||
clss = _class;
|
clss = _class;
|
||||||
args = _args;
|
args = _args;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public TabsAdapter(MainActivity activity, ViewPager pager) {
|
public TabsAdapter(MainActivity activity, ViewPager pager) {
|
||||||
super(activity.getSupportFragmentManager());
|
super(activity.getSupportFragmentManager());
|
||||||
mContext = activity;
|
mContext = activity;
|
||||||
mActionBar = activity.getSupportActionBar();
|
mActionBar = activity.getSupportActionBar();
|
||||||
mViewPager = pager;
|
mViewPager = pager;
|
||||||
mViewPager.setAdapter(this);
|
mViewPager.setAdapter(this);
|
||||||
mViewPager.setOnPageChangeListener(this);
|
mViewPager.setOnPageChangeListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addTab(ActionBar.Tab tab, Class<?> clss, Bundle args) {
|
public void addTab(ActionBar.Tab tab, Class<?> clss, Bundle args) {
|
||||||
TabInfo info = new TabInfo(clss, args);
|
TabInfo info = new TabInfo(clss, args);
|
||||||
tab.setTag(info);
|
tab.setTag(info);
|
||||||
tab.setTabListener(this);
|
tab.setTabListener(this);
|
||||||
mTabs.add(info);
|
mTabs.add(info);
|
||||||
mActionBar.addTab(tab);
|
mActionBar.addTab(tab);
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getCount() {
|
public int getCount() {
|
||||||
return mTabs.size();
|
return mTabs.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Fragment getItem(int position) {
|
public Fragment getItem(int position) {
|
||||||
TabInfo info = mTabs.get(position);
|
TabInfo info = mTabs.get(position);
|
||||||
return Fragment.instantiate(mContext, info.clss.getName(),
|
return Fragment.instantiate(mContext, info.clss.getName(),
|
||||||
info.args);
|
info.args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPageScrolled(int position, float positionOffset,
|
public void onPageScrolled(int position, float positionOffset,
|
||||||
int positionOffsetPixels) {
|
int positionOffsetPixels) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPageSelected(int position) {
|
public void onPageSelected(int position) {
|
||||||
mActionBar.setSelectedNavigationItem(position);
|
mActionBar.setSelectedNavigationItem(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPageScrollStateChanged(int state) {
|
public void onPageScrollStateChanged(int state) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
|
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
|
||||||
Object tag = tab.getTag();
|
Object tag = tab.getTag();
|
||||||
for (int i = 0; i < mTabs.size(); i++) {
|
for (int i = 0; i < mTabs.size(); i++) {
|
||||||
if (mTabs.get(i) == tag) {
|
if (mTabs.get(i) == tag) {
|
||||||
mViewPager.setCurrentItem(i);
|
mViewPager.setCurrentItem(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
|
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
|
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,212 @@
|
||||||
|
package de.danoeh.antennapod.backup;
|
||||||
|
|
||||||
|
import android.app.backup.BackupAgentHelper;
|
||||||
|
import android.app.backup.BackupDataInputStream;
|
||||||
|
import android.app.backup.BackupDataOutput;
|
||||||
|
import android.app.backup.BackupHelper;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.ParcelFileDescriptor;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import de.danoeh.antennapod.BuildConfig;
|
||||||
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.io.Writer;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.security.DigestInputStream;
|
||||||
|
import java.security.DigestOutputStream;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import de.danoeh.antennapod.AppConfig;
|
||||||
|
import de.danoeh.antennapod.feed.Feed;
|
||||||
|
import de.danoeh.antennapod.opml.OpmlElement;
|
||||||
|
import de.danoeh.antennapod.opml.OpmlReader;
|
||||||
|
import de.danoeh.antennapod.opml.OpmlWriter;
|
||||||
|
import de.danoeh.antennapod.storage.DBReader;
|
||||||
|
import de.danoeh.antennapod.storage.DownloadRequestException;
|
||||||
|
import de.danoeh.antennapod.storage.DownloadRequester;
|
||||||
|
import de.danoeh.antennapod.util.LangUtils;
|
||||||
|
|
||||||
|
public class OpmlBackupAgent extends BackupAgentHelper {
|
||||||
|
private static final String OPML_BACKUP_KEY = "opml";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
addHelper(OPML_BACKUP_KEY, new OpmlBackupHelper(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final void LOGD(String tag, String msg) {
|
||||||
|
if (BuildConfig.DEBUG && Log.isLoggable(tag, Log.DEBUG)) {
|
||||||
|
Log.d(tag, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final void LOGD(String tag, String msg, Throwable tr) {
|
||||||
|
if (BuildConfig.DEBUG && Log.isLoggable(tag, Log.DEBUG)) {
|
||||||
|
Log.d(tag, msg, tr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Class for backing up and restoring the OPML file. */
|
||||||
|
private static class OpmlBackupHelper implements BackupHelper {
|
||||||
|
private static final String TAG = "OpmlBackupHelper";
|
||||||
|
|
||||||
|
private static final String OPML_ENTITY_KEY = "antennapod-feeds.opml";
|
||||||
|
|
||||||
|
private final Context mContext;
|
||||||
|
|
||||||
|
/** Checksum of restored OPML file */
|
||||||
|
private byte[] mChecksum;
|
||||||
|
|
||||||
|
public OpmlBackupHelper(Context context) {
|
||||||
|
mContext = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) {
|
||||||
|
Log.d(TAG, "Performing backup");
|
||||||
|
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
|
||||||
|
MessageDigest digester = null;
|
||||||
|
Writer writer;
|
||||||
|
|
||||||
|
try {
|
||||||
|
digester = MessageDigest.getInstance("MD5");
|
||||||
|
writer = new OutputStreamWriter(new DigestOutputStream(byteStream, digester),
|
||||||
|
LangUtils.UTF_8);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
writer = new OutputStreamWriter(byteStream, LangUtils.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Write OPML
|
||||||
|
new OpmlWriter().writeDocument(DBReader.getFeedList(mContext), writer);
|
||||||
|
|
||||||
|
// Compare checksum of new and old file to see if we need to perform a backup at all
|
||||||
|
if (digester != null) {
|
||||||
|
byte[] newChecksum = digester.digest();
|
||||||
|
LOGD(TAG, "New checksum: " + new BigInteger(1, newChecksum).toString(16));
|
||||||
|
|
||||||
|
// Get the old checksum
|
||||||
|
if (oldState != null) {
|
||||||
|
FileInputStream inState = new FileInputStream(oldState.getFileDescriptor());
|
||||||
|
int len = inState.read();
|
||||||
|
|
||||||
|
if (len != -1) {
|
||||||
|
byte[] oldChecksum = new byte[len];
|
||||||
|
inState.read(oldChecksum);
|
||||||
|
LOGD(TAG, "Old checksum: " + new BigInteger(1, oldChecksum).toString(16));
|
||||||
|
|
||||||
|
if (Arrays.equals(oldChecksum, newChecksum)) {
|
||||||
|
LOGD(TAG, "Checksums are the same; won't backup");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeNewStateDescription(newState, newChecksum);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGD(TAG, "Backing up OPML");
|
||||||
|
byte[] bytes = byteStream.toByteArray();
|
||||||
|
data.writeEntityHeader(OPML_ENTITY_KEY, bytes.length);
|
||||||
|
data.writeEntityData(bytes, bytes.length);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "Error during backup", e);
|
||||||
|
} finally {
|
||||||
|
if (writer != null) {
|
||||||
|
try {
|
||||||
|
writer.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void restoreEntity(BackupDataInputStream data) {
|
||||||
|
LOGD(TAG, "Backup restore");
|
||||||
|
|
||||||
|
if (!OPML_ENTITY_KEY.equals(data.getKey())) {
|
||||||
|
LOGD(TAG, "Unknown entity key: " + data.getKey());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageDigest digester = null;
|
||||||
|
Reader reader;
|
||||||
|
|
||||||
|
try {
|
||||||
|
digester = MessageDigest.getInstance("MD5");
|
||||||
|
reader = new InputStreamReader(new DigestInputStream(data, digester),
|
||||||
|
LangUtils.UTF_8);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
reader = new InputStreamReader(data, LangUtils.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
ArrayList<OpmlElement> opmlElements = new OpmlReader().readDocument(reader);
|
||||||
|
mChecksum = digester == null ? null : digester.digest();
|
||||||
|
DownloadRequester downloader = DownloadRequester.getInstance();
|
||||||
|
Date lastUpdated = new Date();
|
||||||
|
|
||||||
|
for (OpmlElement opmlElem : opmlElements) {
|
||||||
|
Feed feed = new Feed(opmlElem.getXmlUrl(), lastUpdated, opmlElem.getText());
|
||||||
|
|
||||||
|
try {
|
||||||
|
downloader.downloadFeed(mContext, feed);
|
||||||
|
} catch (DownloadRequestException e) {
|
||||||
|
LOGD(TAG, "Error while restoring/downloading feed", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (XmlPullParserException e) {
|
||||||
|
Log.e(TAG, "Error while parsing the OPML file", e);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "Failed to restore OPML backup", e);
|
||||||
|
} finally {
|
||||||
|
if (reader != null) {
|
||||||
|
try {
|
||||||
|
reader.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeNewStateDescription(ParcelFileDescriptor newState) {
|
||||||
|
writeNewStateDescription(newState, mChecksum);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the new state description, which is the checksum of the OPML file.
|
||||||
|
*
|
||||||
|
* @param newState
|
||||||
|
* @param checksum
|
||||||
|
*/
|
||||||
|
private void writeNewStateDescription(ParcelFileDescriptor newState, byte[] checksum) {
|
||||||
|
if (checksum == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
FileOutputStream outState = new FileOutputStream(newState.getFileDescriptor());
|
||||||
|
outState.write(checksum.length);
|
||||||
|
outState.write(checksum);
|
||||||
|
outState.flush();
|
||||||
|
outState.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "Failed to write new state description", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -74,6 +74,7 @@ public class UserPreferences implements
|
||||||
private String playbackSpeed;
|
private String playbackSpeed;
|
||||||
private String[] playbackSpeedArray;
|
private String[] playbackSpeedArray;
|
||||||
private boolean pauseForFocusLoss;
|
private boolean pauseForFocusLoss;
|
||||||
|
private boolean isFreshInstall;
|
||||||
|
|
||||||
private UserPreferences(Context context) {
|
private UserPreferences(Context context) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
|
@ -282,6 +283,11 @@ public class UserPreferences implements
|
||||||
return instance.pauseForFocusLoss;
|
return instance.pauseForFocusLoss;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isFreshInstall() {
|
||||||
|
instanceAvailable();
|
||||||
|
return instance.isFreshInstall;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSharedPreferenceChanged(SharedPreferences sp, String key) {
|
public void onSharedPreferenceChanged(SharedPreferences sp, String key) {
|
||||||
if (BuildConfig.DEBUG)
|
if (BuildConfig.DEBUG)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package de.danoeh.antennapod.storage;
|
package de.danoeh.antennapod.storage;
|
||||||
|
|
||||||
|
import android.app.backup.BackupManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
@ -197,6 +198,9 @@ public class DBWriter {
|
||||||
|
|
||||||
GpodnetPreferences.addRemovedFeed(feed.getDownload_url());
|
GpodnetPreferences.addRemovedFeed(feed.getDownload_url());
|
||||||
EventDistributor.getInstance().sendFeedUpdateBroadcast();
|
EventDistributor.getInstance().sendFeedUpdateBroadcast();
|
||||||
|
|
||||||
|
BackupManager backupManager = new BackupManager(context);
|
||||||
|
backupManager.dataChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -695,6 +699,9 @@ public class DBWriter {
|
||||||
|
|
||||||
GpodnetPreferences.addAddedFeed(feed.getDownload_url());
|
GpodnetPreferences.addAddedFeed(feed.getDownload_url());
|
||||||
EventDistributor.getInstance().sendFeedUpdateBroadcast();
|
EventDistributor.getInstance().sendFeedUpdateBroadcast();
|
||||||
|
|
||||||
|
BackupManager backupManager = new BackupManager(context);
|
||||||
|
backupManager.dataChanged();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package de.danoeh.antennapod.storage;
|
package de.danoeh.antennapod.storage;
|
||||||
|
|
||||||
|
import android.app.backup.BackupManager;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
|
@ -386,6 +387,7 @@ public class PodDBAdapter {
|
||||||
Log.d(this.toString(), "Updating existing Feed in db");
|
Log.d(this.toString(), "Updating existing Feed in db");
|
||||||
db.update(TABLE_NAME_FEEDS, values, KEY_ID + "=?",
|
db.update(TABLE_NAME_FEEDS, values, KEY_ID + "=?",
|
||||||
new String[]{String.valueOf(feed.getId())});
|
new String[]{String.valueOf(feed.getId())});
|
||||||
|
|
||||||
}
|
}
|
||||||
return feed.getId();
|
return feed.getId();
|
||||||
}
|
}
|
||||||
|
@ -844,6 +846,7 @@ public class PodDBAdapter {
|
||||||
removeFeedItem(item);
|
removeFeedItem(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
db.delete(TABLE_NAME_FEEDS, KEY_ID + "=?",
|
db.delete(TABLE_NAME_FEEDS, KEY_ID + "=?",
|
||||||
new String[]{String.valueOf(feed.getId())});
|
new String[]{String.valueOf(feed.getId())});
|
||||||
db.setTransactionSuccessful();
|
db.setTransactionSuccessful();
|
||||||
|
|
Loading…
Reference in New Issue