diff options
35 files changed, 2819 insertions, 2 deletions
diff --git a/vdrmanager/AndroidManifest.xml b/vdrmanager/AndroidManifest.xml index afece76..13377ad 100644 --- a/vdrmanager/AndroidManifest.xml +++ b/vdrmanager/AndroidManifest.xml @@ -7,7 +7,7 @@ <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> - + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="14" /> @@ -94,6 +94,11 @@ <activity android:name=".gui.VdrListActivity" android:configChanges="orientation|locale"/> <activity android:name=".gui.VdrPreferencesActivity" android:configChanges="orientation|locale"/> + + <activity android:name=".backup.BackupActivity" android:configChanges="orientation|locale"/> + <activity android:name=".backup.RestoreActivity" android:configChanges="orientation|locale" /> + <activity android:name=".backup.RestoreChooserActivity" android:configChanges="orientation|locale" /> + <activity android:name=".backup.BackupSettingsActivity" android:configChanges="orientation|locale"/> </application> diff --git a/vdrmanager/res/values/backup.xml b/vdrmanager/res/values/backup.xml new file mode 100644 index 0000000..4759e4d --- /dev/null +++ b/vdrmanager/res/values/backup.xml @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <string name="generic_progress_title" translation_description="The title on a progress dialog asking the user to wait."> Please wait...</string> + <string name="generic_confirm_title" translation_description="The title on a confirmation dialog asking the user if he is sure?"> Are you sure?</string> + + <!-- SD Card --> + <string name="sd_card_canceled" translation_description="When importing/saving to the SD card, the message indicating that the + operation was canceled."> The operation was canceled.</string> + <string name="sd_card_error_no_storage" translation_description="When importing/saving to the SD card, the error message indicating that + no SD card is found."> No SD card is found.</string> + <string name="sd_card_import_error" translation_description="When importing from the SD card, the error message indicating not able + to import."> Unable to import from SD card.</string> + <string name="sd_card_import_error_no_file" translation_description="When importing from the SD card, the error message indicating that no + file is found on a SD card directory. + + %1$s: the SD card directory. E.g., '/mnt/sdcard/mybillingbuddy/'."> No file found in %1$s.</string> + <string name="sd_card_import_progress_message" translation_description="When importing from the SD card, the progress message."> Importing from SD card...</string> + <string name="sd_card_import_success" translation_description="When importing from the SD card, the success message."> Finished importing from SD card.</string> + <string name="sd_card_save_error" translation_description="When saving to the SD card, the error message indicating not able to + save."> Unable to save to SD card.</string> + <string name="sd_card_save_error_create_dir" translation_description="When saving to the SD card, the error message indicating not able to + create a directory on the SD card."> Unable to create a directory on SD card.</string> + <string name="sd_card_save_progress_message" translation_description="When saving to the SD card, the progress message."> Saving to SD card...</string> + <string name="sd_card_save_success" translation_description="When saving to the SD card, the success message"> Finished saving to SD card.</string> + <!-- Settings Backup --> + + <string name="settings_backup_summary">Backup/Restore you data and settings</string> + <string name="settings_backup_now" translation_description="In the 'Backup' settings, under the 'SD card' section, the + option to backup to the SD card now. + + Character limit: ~26 (1 line)"> Backup now</string> + <string name="settings_backup_now_summary">Creates a snapshot of the current data and the settings</string> + <string name="settings_backup_now_progress_message" translation_description="In the 'Backup' settings, after pressing the 'Backup now' option, the + progress message telling the user that it is writing all data and settings to the SD card."> Writing all data and settings...</string> + <string name="settings_backup_restore" translation_description="In the 'Backup' settings, under the 'SD card' section, the + option to restore everything from the SD card. + + Character limit: ~26 (1 line)"> Restore now</string> + <string name="settings_backup_restore_summary">Restore a snapshot of the settings and the data</string> + <!-- Settings Backup --> + <string name="settings_backup" translation_description="In the 'Settings' page, the option to change the data backup related + settings. + + Character limit: ~26 (1 line)"> Backup</string> + <string name="settings_backup_restore_confirm_message" translation_description="In the 'Backup' settings, after pressing the 'Restore now' option, the + message in the alert dialog asking the user to confirm that he wants to restore from a backup + and it will overwrite all the current data and settings."> Restoring a backup from the SD card will overwrite all your current data and settings.</string> + <string name="settings_backup_restore_no_backup" translation_description="In the 'Backup' settings, after pressing the 'Restore now' option, the + message in the alert dialog informing the user that there is no backup to restore from."> No backup found.</string> + <string name="settings_backup_restore_progress_message" translation_description="In the 'Backup' settings, after pressing the 'Restore now' option, the + progress message informing that My Billinbg Buddy is reading all data and settings from the backup + on the SD card."> Reading all data and settings...</string> + <string name="settings_backup_restore_select_title" translation_description="In the 'Backup' settings, after pressing the 'Restore now' option, the + title in the picker dialog asking the user to select a backup."> Select a backup to restore</string> + <string name="settings_backup_key">settingsBackup</string> + <string name="settings_backup_now_key">settingsBackupNow</string> + <string name="settings_backup_restore_key">settingsBackupRestore</string> + <string name="choose">Choose a backup file</string> + +</resources>
\ No newline at end of file diff --git a/vdrmanager/res/xml/backup_settings.xml b/vdrmanager/res/xml/backup_settings.xml new file mode 100644 index 0000000..dfe91c6 --- /dev/null +++ b/vdrmanager/res/xml/backup_settings.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright 2012 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--> +<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" + android:title="@string/settings_backup" android:summary="@string/settings_backup_summary"> + <Preference + android:key="@string/settings_backup_now_key" + android:persistent="false" + android:title="@string/settings_backup_now" + android:summary="@string/settings_backup_now_summary" + /> + <Preference + android:key="@string/settings_backup_restore_key" + android:persistent="false" + android:title="@string/settings_backup_restore" + android:summary="@string/settings_backup_restore_summary" + /> +</PreferenceScreen>
\ No newline at end of file diff --git a/vdrmanager/res/xml/preferences.xml b/vdrmanager/res/xml/preferences.xml index 8a819dd..dffe0ab 100644 --- a/vdrmanager/res/xml/preferences.xml +++ b/vdrmanager/res/xml/preferences.xml @@ -72,4 +72,11 @@ android:title="@string/gui_custom_locale_title" /> </PreferenceCategory> + + <Preference + android:key="@string/settings_backup_key" + android:persistent="false" + android:summary="@string/settings_backup_summary" + android:title="@string/settings_backup" /> + </PreferenceScreen>
\ No newline at end of file diff --git a/vdrmanager/src/de/bjusystems/vdrmanager/backup/AbstractSettingsActivity.java b/vdrmanager/src/de/bjusystems/vdrmanager/backup/AbstractSettingsActivity.java new file mode 100644 index 0000000..ef6dd5e --- /dev/null +++ b/vdrmanager/src/de/bjusystems/vdrmanager/backup/AbstractSettingsActivity.java @@ -0,0 +1,70 @@ +/* + * Copyright 2012 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package de.bjusystems.vdrmanager.backup; + +import android.content.Context; +import android.os.Bundle; +import android.preference.PreferenceActivity; +import android.preference.PreferenceManager; +import android.speech.tts.TextToSpeech; +import android.view.MenuItem; + +/** + * An abstract activity for all the settings activities. + * + * @author Jimmy Shih + */ +public class AbstractSettingsActivity extends PreferenceActivity { + + private BackupPreferencesListener backupPreferencesListener; + + @SuppressWarnings("deprecation") + @Override + protected void onCreate(Bundle bundle) { + super.onCreate(bundle); + setVolumeControlStream(TextToSpeech.Engine.DEFAULT_STREAM); + ApiAdapterFactory.getApiAdapter().configureActionBarHomeAsUp(this); + + PreferenceManager preferenceManager = getPreferenceManager(); + preferenceManager.setSharedPreferencesName(Constants.SETTINGS_NAME); + preferenceManager.setSharedPreferencesMode(Context.MODE_PRIVATE); + + // Set up automatic preferences backup + backupPreferencesListener = ApiAdapterFactory.getApiAdapter() + .getBackupPreferencesListener(this); + preferenceManager.getSharedPreferences() + .registerOnSharedPreferenceChangeListener(backupPreferencesListener); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() != android.R.id.home) { + return super.onOptionsItemSelected(item); + } + finish(); + return true; + } + + @SuppressWarnings("deprecation") + @Override + protected void onDestroy() { + super.onDestroy(); + PreferenceManager preferenceManager = getPreferenceManager(); + preferenceManager.getSharedPreferences() + .unregisterOnSharedPreferenceChangeListener(backupPreferencesListener); + } +} diff --git a/vdrmanager/src/de/bjusystems/vdrmanager/backup/Api10Adapter.java b/vdrmanager/src/de/bjusystems/vdrmanager/backup/Api10Adapter.java new file mode 100644 index 0000000..0527f89 --- /dev/null +++ b/vdrmanager/src/de/bjusystems/vdrmanager/backup/Api10Adapter.java @@ -0,0 +1,30 @@ +/* + * Copyright 2012 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package de.bjusystems.vdrmanager.backup; + +import android.annotation.TargetApi; + + +/** + * API level 10 specific implementation of the {@link ApiAdapter}. + * + * @author Jimmy Shih + */ +@TargetApi(10) +public class Api10Adapter extends Api9Adapter { + + +} diff --git a/vdrmanager/src/de/bjusystems/vdrmanager/backup/Api11Adapter.java b/vdrmanager/src/de/bjusystems/vdrmanager/backup/Api11Adapter.java new file mode 100644 index 0000000..ec26635 --- /dev/null +++ b/vdrmanager/src/de/bjusystems/vdrmanager/backup/Api11Adapter.java @@ -0,0 +1,112 @@ +/* + * Copyright 2012 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package de.bjusystems.vdrmanager.backup; + + +import java.util.List; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.app.SearchManager; +import android.content.Context; +import android.view.MenuItem; +import android.widget.ArrayAdapter; +import android.widget.ListView; +import android.widget.SearchView; + +/** + * API level 11 specific implementation of the {@link ApiAdapter}. + * + * @author Jimmy Shih + */ +@TargetApi(11) +public class Api11Adapter extends Api10Adapter { + + @Override + public void hideTitle(Activity activity) { + // Do nothing + } + + @Override + public void configureActionBarHomeAsUp(Activity activity) { + activity.getActionBar().setDisplayHomeAsUpEnabled(true); + } + + @Override + public void configureListViewContextualMenu(final Activity activity, ListView listView, + final ContextualActionModeCallback contextualActionModeCallback) { + /* + + listView.setOnItemLongClickListener(new OnItemLongClickListener() { + ActionMode actionMode; + @Override + public boolean onItemLongClick( + AdapterView<?> parent, View view, final int position, final long id) { + if (actionMode != null) { + return false; + } + actionMode = activity.startActionMode(new ActionMode.Callback() { + @Override + public boolean onCreateActionMode(ActionMode mode, Menu menu) { + mode.getMenuInflater().inflate(R.menu.list_context_menu, menu); + return true; + } + @Override + public boolean onPrepareActionMode(ActionMode mode, Menu menu) { + // Return false to indicate no change. + return false; + } + @Override + public void onDestroyActionMode(ActionMode mode) { + actionMode = null; + } + @Override + public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + mode.finish(); + return contextualActionModeCallback.onClick(item.getItemId(), position, id); + } + }); + TextView textView = (TextView) view.findViewById(R.id.list_item_name); + if (textView != null) { + actionMode.setTitle(textView.getText()); + } + view.setSelected(true); + return true; + } + }); + */ + }; + + @Override + public void configureSearchWidget(Activity activity, final MenuItem menuItem) { + SearchManager searchManager = (SearchManager) activity.getSystemService(Context.SEARCH_SERVICE); + SearchView searchView = (SearchView) menuItem.getActionView(); + searchView.setSearchableInfo(searchManager.getSearchableInfo(activity.getComponentName())); + searchView.setQueryRefinementEnabled(true); + } + + @Override + public boolean handleSearchMenuSelection(Activity activity) { + // Returns false to allow the platform to expand the search widget. + return false; + } + + @Override + public <T> void addAllToArrayAdapter(ArrayAdapter<T> arrayAdapter, List<T> items) { + arrayAdapter.addAll(items); + } +} diff --git a/vdrmanager/src/de/bjusystems/vdrmanager/backup/Api14Adapter.java b/vdrmanager/src/de/bjusystems/vdrmanager/backup/Api14Adapter.java new file mode 100644 index 0000000..0425191 --- /dev/null +++ b/vdrmanager/src/de/bjusystems/vdrmanager/backup/Api14Adapter.java @@ -0,0 +1,73 @@ +/* + * Copyright 2012 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package de.bjusystems.vdrmanager.backup; + +import android.annotation.TargetApi; +import android.app.ActionBar; +import android.app.Activity; +import android.view.MenuItem; +import android.widget.SearchView; + +/** + * API level 14 specific implementation of the {@link ApiAdapter}. + * + * @author Jimmy Shih + */ +@TargetApi(14) +public class Api14Adapter extends Api11Adapter { + + @Override + public void configureActionBarHomeAsUp(Activity activity) { + ActionBar actionBar = activity.getActionBar(); + actionBar.setHomeButtonEnabled(true); + actionBar.setDisplayHomeAsUpEnabled(true); + } + + @Override + public void configureSearchWidget(Activity activity, final MenuItem menuItem) { + super.configureSearchWidget(activity, menuItem); + SearchView searchView = (SearchView) menuItem.getActionView(); + searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { + @Override + public boolean onQueryTextSubmit(String query) { + menuItem.collapseActionView(); + return false; + } + @Override + public boolean onQueryTextChange(String newText) { + return false; + } + }); + searchView.setOnSuggestionListener(new SearchView.OnSuggestionListener() { + @Override + public boolean onSuggestionSelect(int position) { + return false; + } + @Override + public boolean onSuggestionClick(int position) { + menuItem.collapseActionView(); + return false; + } + }); + } + + @Override + public boolean handleSearchKey(MenuItem menuItem) { + menuItem.expandActionView(); + return true; + } +} diff --git a/vdrmanager/src/de/bjusystems/vdrmanager/backup/Api7Adapter.java b/vdrmanager/src/de/bjusystems/vdrmanager/backup/Api7Adapter.java new file mode 100644 index 0000000..b89168a --- /dev/null +++ b/vdrmanager/src/de/bjusystems/vdrmanager/backup/Api7Adapter.java @@ -0,0 +1,107 @@ +/* + * Copyright 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package de.bjusystems.vdrmanager.backup; + + +import java.util.List; + +import android.app.Activity; +import android.content.Context; +import android.content.SharedPreferences; +import android.content.SharedPreferences.Editor; +import android.view.MenuItem; +import android.view.Window; +import android.widget.ArrayAdapter; +import android.widget.ListView; + +/** + * API level 7 specific implementation of the {@link ApiAdapter}. + * + * @author Bartlomiej Niechwiej + */ +public class Api7Adapter implements ApiAdapter { + + + @Override + public BackupPreferencesListener getBackupPreferencesListener(Context context) { + return new BackupPreferencesListener() { + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + // Do nothing + } + }; + } + + @Override + public void applyPreferenceChanges(Editor editor) { + editor.commit(); + } + + @Override + public void enableStrictMode() { + // Not supported + } + + @Override + public byte[] copyByteArray(byte[] input, int start, int end) { + int length = end - start; + byte[] output = new byte[length]; + System.arraycopy(input, start, output, 0, length); + return output; + } + + + + @Override + public void hideTitle(Activity activity) { + activity.requestWindowFeature(Window.FEATURE_NO_TITLE); + } + + @Override + public void configureActionBarHomeAsUp(Activity activity) { + // Do nothing + } + + @Override + public void configureListViewContextualMenu(Activity activity, ListView listView, + ContextualActionModeCallback contextualActionModeCallback) { + activity.registerForContextMenu(listView); + } + + @Override + public void configureSearchWidget(Activity activity, MenuItem menuItem) { + // Do nothing + } + + @Override + public boolean handleSearchMenuSelection(Activity activity) { + activity.onSearchRequested(); + return true; + } + + @Override + public <T> void addAllToArrayAdapter(ArrayAdapter<T> arrayAdapter, List<T> items) { + for (T item : items) { + arrayAdapter.add(item); + } + } + + @Override + public boolean handleSearchKey(MenuItem menuItem) { + // Return false and allow the framework to handle the search key. + return false; + } +} diff --git a/vdrmanager/src/de/bjusystems/vdrmanager/backup/Api8Adapter.java b/vdrmanager/src/de/bjusystems/vdrmanager/backup/Api8Adapter.java new file mode 100644 index 0000000..1fc61d3 --- /dev/null +++ b/vdrmanager/src/de/bjusystems/vdrmanager/backup/Api8Adapter.java @@ -0,0 +1,32 @@ +/* + * Copyright 2012 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package de.bjusystems.vdrmanager.backup; + +import android.content.Context; + +/** + * API level 8 specific implementation of the {@link ApiAdapter}. + * + * @author Jimmy Shih + */ +public class Api8Adapter extends Api7Adapter { + + + @Override + public BackupPreferencesListener getBackupPreferencesListener(Context context) { + return new Api8BackupPreferencesListener(context); + } +} diff --git a/vdrmanager/src/de/bjusystems/vdrmanager/backup/Api8BackupPreferencesListener.java b/vdrmanager/src/de/bjusystems/vdrmanager/backup/Api8BackupPreferencesListener.java new file mode 100644 index 0000000..90e3976 --- /dev/null +++ b/vdrmanager/src/de/bjusystems/vdrmanager/backup/Api8BackupPreferencesListener.java @@ -0,0 +1,43 @@ +/* + * Copyright 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package de.bjusystems.vdrmanager.backup; + +import android.annotation.TargetApi; +import android.app.backup.BackupManager; +import android.content.Context; +import android.content.SharedPreferences; + +/** + * Implementation of {@link BackupPreferencesListener} that calls the + * {@link BackupManager}. <br> + * For API Level 8 or higher. + * + * @author Jimmy Shih + */ +@TargetApi(8) +public class Api8BackupPreferencesListener implements BackupPreferencesListener { + + private final BackupManager backupManager; + + public Api8BackupPreferencesListener(Context context) { + this.backupManager = new BackupManager(context); + } + + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + backupManager.dataChanged(); + } +} diff --git a/vdrmanager/src/de/bjusystems/vdrmanager/backup/Api9Adapter.java b/vdrmanager/src/de/bjusystems/vdrmanager/backup/Api9Adapter.java new file mode 100644 index 0000000..151f317 --- /dev/null +++ b/vdrmanager/src/de/bjusystems/vdrmanager/backup/Api9Adapter.java @@ -0,0 +1,58 @@ +/* + * Copyright 2012 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package de.bjusystems.vdrmanager.backup; + +import java.util.Arrays; + +import android.annotation.TargetApi; +import android.content.SharedPreferences.Editor; +import android.os.StrictMode; +import android.util.Log; + +/** + * API level 9 specific implementation of the {@link ApiAdapter}. + * + * @author Rodrigo Damazio + */ +@TargetApi(9) +public class Api9Adapter extends Api8Adapter { + + @Override + public void applyPreferenceChanges(Editor editor) { + // Apply asynchronously + editor.apply(); + } + + @Override + public void enableStrictMode() { + Log.d(Constants.TAG, "Enabling strict mode"); + StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() + .detectDiskWrites() + .detectNetwork() + .penaltyLog() + .build()); + StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() + .detectAll() + .penaltyLog() + .build()); + } + + @Override + public byte[] copyByteArray(byte[] input, int start, int end) { + return Arrays.copyOfRange(input, start, end); + } + +} diff --git a/vdrmanager/src/de/bjusystems/vdrmanager/backup/ApiAdapter.java b/vdrmanager/src/de/bjusystems/vdrmanager/backup/ApiAdapter.java new file mode 100644 index 0000000..aa13297 --- /dev/null +++ b/vdrmanager/src/de/bjusystems/vdrmanager/backup/ApiAdapter.java @@ -0,0 +1,152 @@ +/* + * Copyright 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package de.bjusystems.vdrmanager.backup; + +import java.util.List; + +import android.app.Activity; +import android.content.Context; +import android.content.SharedPreferences; +import android.view.MenuItem; +import android.widget.ArrayAdapter; +import android.widget.ListView; + +/** + * A set of methods that may be implemented differently depending on the Android + * API level. + * + * @author Bartlomiej Niechwiej + */ +public interface ApiAdapter { + + + + /** + * Gets a {@link BackupPreferencesListener}. + * <p> + * Due to changes in API level 8. + * + * @param context the context + */ + public BackupPreferencesListener getBackupPreferencesListener(Context context); + + /** + * Applies all the changes done to a given preferences editor. Changes may or + * may not be applied immediately. + * <p> + * Due to changes in API level 9. + * + * @param editor the editor + */ + public void applyPreferenceChanges(SharedPreferences.Editor editor); + + /** + * Enables strict mode where supported, only if this is a development build. + * <p> + * Due to changes in API level 9. + */ + public void enableStrictMode(); + + /** + * Copies elements from an input byte array into a new byte array, from + * indexes start (inclusive) to end (exclusive). The end index must be less + * than or equal to the input length. + * <p> + * Due to changes in API level 9. + * + * @param input the input byte array + * @param start the start index + * @param end the end index + * @return a new array containing elements from the input byte array. + */ + public byte[] copyByteArray(byte[] input, int start, int end); + + + + + /** + * Hides the title. If the platform supports the action bar, do nothing. + * Ideally, with the action bar, we would like to collapse the navigation tabs + * into the action bar. However, collapsing is not supported by the + * compatibility library. + * <p> + * Due to changes in API level 11. + * + * @param activity the activity + */ + public void hideTitle(Activity activity); + + /** + * Configures the action bar with the Home button as an Up button. If the + * platform doesn't support the action bar, do nothing. + * <p> + * Due to changes in API level 11. + * + * @param activity the activity + */ + public void configureActionBarHomeAsUp(Activity activity); + + /** + * Configures the list view context menu. + * <p> + * Due to changes in API level 11. + * + * @param activity the activity + * @param listView the list view + * @param contextualActionModeCallback the callback when an item is selected + * in the contextual action mode + */ + public void configureListViewContextualMenu(Activity activity, ListView listView, + ContextualActionModeCallback contextualActionModeCallback); + + /** + * Configures the search widget. + * + * Due to changes in API level 11. + * + * @param activity the activity + * @param menuItem the search menu item + */ + public void configureSearchWidget(Activity activity, MenuItem menuItem); + + /** + * Handles the search menu selection. Returns true if handled. + * + * Due to changes in API level 11. + * + * @param activity the activity + */ + public boolean handleSearchMenuSelection(Activity activity); + + /** + * Adds all items to an array adapter. + * + * Due to changes in API level 11. + *s + * @param arrayAdapter the array adapter + * @param items list of items + */ + public <T> void addAllToArrayAdapter(ArrayAdapter<T> arrayAdapter, List<T> items); + + /** + * Handles the search key press. Returns true if handled. + * + * Due to changes in API level 14. + * + * @param menu the search menu + */ + public boolean handleSearchKey(MenuItem menu); +} diff --git a/vdrmanager/src/de/bjusystems/vdrmanager/backup/ApiAdapterFactory.java b/vdrmanager/src/de/bjusystems/vdrmanager/backup/ApiAdapterFactory.java new file mode 100644 index 0000000..5a14c10 --- /dev/null +++ b/vdrmanager/src/de/bjusystems/vdrmanager/backup/ApiAdapterFactory.java @@ -0,0 +1,56 @@ +/* + * Copyright 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package de.bjusystems.vdrmanager.backup; + +import android.os.Build; + +/** + * A factory to get the {@link ApiAdapter} for the current device. + * + * @author Rodrigo Damazio + */ +public class ApiAdapterFactory { + + private static ApiAdapter apiAdapter; + + /** + * Gets the {@link ApiAdapter} for the current device. + */ + public static ApiAdapter getApiAdapter() { + if (apiAdapter == null) { + if (Build.VERSION.SDK_INT >= 14) { + apiAdapter = new Api14Adapter(); + return apiAdapter; + } else if (Build.VERSION.SDK_INT >= 11) { + apiAdapter = new Api11Adapter(); + return apiAdapter; + } else if (Build.VERSION.SDK_INT >= 10) { + apiAdapter = new Api10Adapter(); + return apiAdapter; + } else if (Build.VERSION.SDK_INT >= 9) { + apiAdapter = new Api9Adapter(); + return apiAdapter; + } else if (Build.VERSION.SDK_INT >= 8) { + apiAdapter = new Api8Adapter(); + return apiAdapter; + } else { + apiAdapter = new Api7Adapter(); + return apiAdapter; + } + } + return apiAdapter; + } +} diff --git a/vdrmanager/src/de/bjusystems/vdrmanager/backup/BackupActivity.java b/vdrmanager/src/de/bjusystems/vdrmanager/backup/BackupActivity.java new file mode 100644 index 0000000..ed0da12 --- /dev/null +++ b/vdrmanager/src/de/bjusystems/vdrmanager/backup/BackupActivity.java @@ -0,0 +1,89 @@ +/* + * Copyright 2012 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package de.bjusystems.vdrmanager.backup; + +import android.app.Activity; +import android.app.Dialog; +import android.content.DialogInterface; +import android.os.Bundle; +import android.widget.Toast; +import de.bjusystems.vdrmanager.R; + +/** + * Activity to backup data to the SD card. + * + * @author Jimmy Shih + */ +public class BackupActivity extends Activity { + + private static final int DIALOG_PROGRESS_ID = 0; + + private BackupAsyncTask backupAsyncTask; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Object retained = getLastNonConfigurationInstance(); + if (retained instanceof BackupAsyncTask) { + backupAsyncTask = (BackupAsyncTask) retained; + backupAsyncTask.setActivity(this); + } else { + backupAsyncTask = new BackupAsyncTask(this); + backupAsyncTask.execute(); + } + } + + @Override + public Object onRetainNonConfigurationInstance() { + backupAsyncTask.setActivity(null); + return backupAsyncTask; + } + + @Override + protected Dialog onCreateDialog(int id) { + if (id != DIALOG_PROGRESS_ID) { + return null; + } + return DialogUtils.createSpinnerProgressDialog( + this, R.string.settings_backup_now_progress_message, new DialogInterface.OnCancelListener() { + @Override + public void onCancel(DialogInterface dialog) { + finish(); + } + }); + } + + /** + * Invokes when the associated AsyncTask completes. + * + * @param success true if the AsyncTask is successful + * @param messageId message id to display to user + */ + public void onAsyncTaskCompleted(boolean success, int messageId) { + removeDialog(DIALOG_PROGRESS_ID); + Toast.makeText(this, messageId, success ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG).show(); + finish(); + } + + /** + * Shows the progress dialog. + */ + public void showProgressDialog() { + showDialog(DIALOG_PROGRESS_ID); + } +} diff --git a/vdrmanager/src/de/bjusystems/vdrmanager/backup/BackupAsyncTask.java b/vdrmanager/src/de/bjusystems/vdrmanager/backup/BackupAsyncTask.java new file mode 100644 index 0000000..8e98ac0 --- /dev/null +++ b/vdrmanager/src/de/bjusystems/vdrmanager/backup/BackupAsyncTask.java @@ -0,0 +1,110 @@ +/* + * Copyright 2012 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package de.bjusystems.vdrmanager.backup; + + +import java.io.IOException; + +import android.os.AsyncTask; +import android.util.Log; +import de.bjusystems.vdrmanager.R; + +/** + * AsyncTask to backup data to the SD card. + * + * @author Jimmy Shih + */ +public class BackupAsyncTask extends AsyncTask<Void, Integer, Boolean> { + + private static final String TAG = BackupAsyncTask.class.getSimpleName(); + + private BackupActivity backupActivity; + private final ExternalFileBackup externalFileBackup; + + // true if the AsyncTask result is success + private boolean success; + + // true if the AsyncTask has completed + private boolean completed; + + // message id to return to the activity + private int messageId; + + /** + * Creates an AsyncTask. + * + * @param backupActivity the activity currently associated with this + * AsyncTask + */ + public BackupAsyncTask(BackupActivity backupActivity) { + this.backupActivity = backupActivity; + this.externalFileBackup = new ExternalFileBackup(backupActivity); + success = false; + completed = false; + messageId = R.string.sd_card_save_error; + } + + /** + * Sets the current {@link BackupActivity} associated with this AyncTask. + * + * @param activity the current {@link BackupActivity}, can be null + */ + public void setActivity(BackupActivity activity) { + this.backupActivity = activity; + if (completed && backupActivity != null) { + backupActivity.onAsyncTaskCompleted(success, messageId); + } + } + + @Override + protected void onPreExecute() { + if (backupActivity != null) { + backupActivity.showProgressDialog(); + } + } + + @Override + protected Boolean doInBackground(Void... params) { + if (!FileUtils.isSdCardAvailable()) { + messageId = R.string.sd_card_error_no_storage; + return false; + } + + if (!externalFileBackup.isBackupsDirectoryAvailable(true)) { + messageId = R.string.sd_card_save_error_create_dir; + return false; + } + + try { + externalFileBackup.writeToDefaultFile(); + messageId = R.string.sd_card_save_success; + return true; + } catch (IOException e) { + Log.d(TAG, "IO exception", e); + return false; + } + } + + @Override + protected void onPostExecute(Boolean result) { + success = result; + completed = true; + if (backupActivity != null) { + backupActivity.onAsyncTaskCompleted(success, messageId); + } + } +} diff --git a/vdrmanager/src/de/bjusystems/vdrmanager/backup/BackupPreferencesListener.java b/vdrmanager/src/de/bjusystems/vdrmanager/backup/BackupPreferencesListener.java new file mode 100644 index 0000000..c9a5b2f --- /dev/null +++ b/vdrmanager/src/de/bjusystems/vdrmanager/backup/BackupPreferencesListener.java @@ -0,0 +1,27 @@ +/* + * Copyright 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package de.bjusystems.vdrmanager.backup; + +import android.content.SharedPreferences.OnSharedPreferenceChangeListener; + +/** + * Shared preferences listener which notifies the backup system about new data + * being available for backup. + * + * @author Rodrigo Damazio + */ +public interface BackupPreferencesListener extends OnSharedPreferenceChangeListener { +}
\ No newline at end of file diff --git a/vdrmanager/src/de/bjusystems/vdrmanager/backup/BackupSettingsActivity.java b/vdrmanager/src/de/bjusystems/vdrmanager/backup/BackupSettingsActivity.java new file mode 100644 index 0000000..e906a20 --- /dev/null +++ b/vdrmanager/src/de/bjusystems/vdrmanager/backup/BackupSettingsActivity.java @@ -0,0 +1,121 @@ +/* + * Copyright 2012 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package de.bjusystems.vdrmanager.backup; + + +import android.app.Dialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.SharedPreferences.OnSharedPreferenceChangeListener; +import android.os.Bundle; +import android.preference.Preference; +import android.preference.Preference.OnPreferenceClickListener; +import android.preference.PreferenceManager; +import de.bjusystems.vdrmanager.R; + +/** + * An activity for accessing the backup settings. + * + * @author Jimmy Shih + */ +public class BackupSettingsActivity extends AbstractSettingsActivity { + + private static final int DIALOG_CONFIRM_RESTORE_ID = 0; + + Preference backupPreference; + Preference restorePreference; + + /* + * Note that sharedPreferenceChangeListenr cannot be an anonymous inner class. + * Anonymous inner class will get garbage collected. + */ + private final OnSharedPreferenceChangeListener + sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() { + @Override + public void onSharedPreferenceChanged(SharedPreferences preferences, String key) { + // Note that key can be null + //if (PreferencesUtils.getKey(BackupSettingsActivity.this, R.string.recording_track_id_key) + // .equals(key)) { + //updateUi(); + //} + } + }; + + @SuppressWarnings("deprecation") + @Override + protected void onCreate(Bundle bundle) { + super.onCreate(bundle); + PreferenceManager.getDefaultSharedPreferences(this) + .registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener); + + addPreferencesFromResource(R.xml.backup_settings); + backupPreference = findPreference(getString(R.string.settings_backup_now_key)); + backupPreference.setOnPreferenceClickListener(new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + Intent intent = IntentUtils.newIntent(BackupSettingsActivity.this, BackupActivity.class); + startActivity(intent); + return true; + } + }); + restorePreference = findPreference(getString(R.string.settings_backup_restore_key)); + restorePreference.setOnPreferenceClickListener(new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + showDialog(DIALOG_CONFIRM_RESTORE_ID); + return true; + } + }); + } + + @Override + protected Dialog onCreateDialog(int id) { + if (id != DIALOG_CONFIRM_RESTORE_ID) { + return null; + } + return DialogUtils.createConfirmationDialog(this, + R.string.settings_backup_restore_confirm_message, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + Intent intent = IntentUtils.newIntent( + BackupSettingsActivity.this, RestoreChooserActivity.class); + startActivity(intent); + } + }); + } + + @Override + protected void onResume() { + super.onResume(); + //updateUi(); + } + +// /** +// * Updates the UI based on the recording state. +// */ +// private void updateUi() { +// boolean isRecording = PreferencesUtils.getLong(this, R.string.recording_track_id_key) +// != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT; +// backupPreference.setEnabled(!isRecording); +// restorePreference.setEnabled(!isRecording); +// backupPreference.setSummary(isRecording ? R.string.settings_not_while_recording +// : R.string.settings_backup_now_summary); +// restorePreference.setSummary(isRecording ? R.string.settings_not_while_recording +// : R.string.settings_backup_restore_summary); +// } +} diff --git a/vdrmanager/src/de/bjusystems/vdrmanager/backup/Constants.java b/vdrmanager/src/de/bjusystems/vdrmanager/backup/Constants.java new file mode 100644 index 0000000..491ec6f --- /dev/null +++ b/vdrmanager/src/de/bjusystems/vdrmanager/backup/Constants.java @@ -0,0 +1,135 @@ +/* + * Copyright 2008 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package de.bjusystems.vdrmanager.backup; + +/** + * Constants used by the MyTracks application. + * + * @author Leif Hendrik Wilden + */ +public abstract class Constants { + + /** + * Should be used by all log statements + */ + public static final String TAG = "vdrmanager"; + + /** + * Name of the top-level directory inside the SD card where our files will + * be read from/written to. + */ + public static final String SDCARD_TOP_DIR = "vdrmanager"; + + /** + * The number of distance readings to smooth to get a stable signal. + */ + public static final int DISTANCE_SMOOTHING_FACTOR = 25; + + /** + * The number of elevation readings to smooth to get a somewhat accurate + * signal. + */ + public static final int ELEVATION_SMOOTHING_FACTOR = 25; + + /** + * The number of grade readings to smooth to get a somewhat accurate signal. + */ + public static final int GRADE_SMOOTHING_FACTOR = 5; + + /** + * The number of speed reading to smooth to get a somewhat accurate signal. + */ + public static final int SPEED_SMOOTHING_FACTOR = 25; + + /** + * Maximum number of track points displayed by the map overlay. + */ + public static final int MAX_DISPLAYED_TRACK_POINTS = 10000; + + /** + * Target number of track points displayed by the map overlay. + * We may display more than this number of points. + */ + public static final int TARGET_DISPLAYED_TRACK_POINTS = 5000; + + /** + * Maximum number of track points ever loaded at once from the provider into + * memory. + * With a recording frequency of 2 seconds, 15000 corresponds to 8.3 hours. + */ + public static final int MAX_LOADED_TRACK_POINTS = 20000; + + /** + * Maximum number of track points ever loaded at once from the provider into + * memory in a single call to read points. + */ + public static final int MAX_LOADED_TRACK_POINTS_PER_BATCH = 1000; + + /** + * Maximum number of way points displayed by the map overlay. + */ + public static final int MAX_DISPLAYED_WAYPOINTS_POINTS = 128; + + /** + * Maximum number of way points that will be loaded at one time. + */ + public static final int MAX_LOADED_WAYPOINTS_POINTS = 10000; + + /** + * Any time segment where the distance traveled is less than this value will + * not be considered moving. + */ + public static final double MAX_NO_MOVEMENT_DISTANCE = 2; + + /** + * Anything faster than that (in meters per second) will be considered moving. + */ + public static final double MAX_NO_MOVEMENT_SPEED = 0.224; + + /** + * Ignore any acceleration faster than this. + * Will ignore any speeds that imply accelaration greater than 2g's + * 2g = 19.6 m/s^2 = 0.0002 m/ms^2 = 0.02 m/(m*ms) + */ + public static final double MAX_ACCELERATION = 0.02; + + /** Maximum age of a GPS location to be considered current. */ + public static final long MAX_LOCATION_AGE_MS = 60 * 1000; // 1 minute + + /** Maximum age of a network location to be considered current. */ + public static final long MAX_NETWORK_AGE_MS = 1000 * 60 * 10; // 10 minutes + + /** + * The type of account that we can use for gdata uploads. + */ + public static final String ACCOUNT_TYPE = "com.google"; + + /** + * The name of extra intent property to indicate whether we want to resume + * a previously recorded track. + */ + public static final String + RESUME_TRACK_EXTRA_NAME = "com.google.android.apps.mytracks.RESUME_TRACK"; + + public static final String MAPSHOP_BASE_URL = "https://maps.google.com/maps/ms"; + + public static final String SETTINGS_NAME = "SettingsActivity"; + + /** + * This is an abstract utility class. + */ + protected Constants() { } +} diff --git a/vdrmanager/src/de/bjusystems/vdrmanager/backup/ContentTypeIds.java b/vdrmanager/src/de/bjusystems/vdrmanager/backup/ContentTypeIds.java new file mode 100644 index 0000000..bf48983 --- /dev/null +++ b/vdrmanager/src/de/bjusystems/vdrmanager/backup/ContentTypeIds.java @@ -0,0 +1,33 @@ +/* + * Copyright 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package de.bjusystems.vdrmanager.backup; + +/** + * Utilities for serializing primitive types. + * + * @author Rodrigo Damazio + */ +public class ContentTypeIds { + public static final byte BOOLEAN_TYPE_ID = 0; + public static final byte LONG_TYPE_ID = 1; + public static final byte INT_TYPE_ID = 2; + public static final byte FLOAT_TYPE_ID = 3; + public static final byte DOUBLE_TYPE_ID = 4; + public static final byte STRING_TYPE_ID = 5; + public static final byte BLOB_TYPE_ID = 6; + + private ContentTypeIds() { /* Not instantiable */ } +} diff --git a/vdrmanager/src/de/bjusystems/vdrmanager/backup/ContextualActionModeCallback.java b/vdrmanager/src/de/bjusystems/vdrmanager/backup/ContextualActionModeCallback.java new file mode 100644 index 0000000..d79d4be --- /dev/null +++ b/vdrmanager/src/de/bjusystems/vdrmanager/backup/ContextualActionModeCallback.java @@ -0,0 +1,34 @@ +/* + * Copyright 2012 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package de.bjusystems.vdrmanager.backup; + +/** + * Callback when an item in the contextual action mode is selected. + * + * @author Jimmy Shih + */ +public interface ContextualActionModeCallback { + + /** + * Invoked when an item is selected. + * + * @param itemId the context menu item id + * @param position the position of the selected row + * @param id the id of the selected row, if available + */ + public boolean onClick(int itemId, int position, long id); +}
\ No newline at end of file diff --git a/vdrmanager/src/de/bjusystems/vdrmanager/backup/DialogUtils.java b/vdrmanager/src/de/bjusystems/vdrmanager/backup/DialogUtils.java new file mode 100644 index 0000000..c9b716c --- /dev/null +++ b/vdrmanager/src/de/bjusystems/vdrmanager/backup/DialogUtils.java @@ -0,0 +1,101 @@ +/* + * Copyright 2012 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package de.bjusystems.vdrmanager.backup; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.ProgressDialog; +import android.content.Context; +import android.content.DialogInterface; +import de.bjusystems.vdrmanager.R; + +/** + * Utilities for creating dialogs. + * + * @author Jimmy Shih + */ +public class DialogUtils { + + private DialogUtils() {} + + /** + * Creates a confirmation dialog. + * + * @param context the context + * @param messageId the confirmation message id + * @param onClickListener the listener to invoke when the user clicks OK + */ + public static Dialog createConfirmationDialog( + Context context, int messageId, DialogInterface.OnClickListener onClickListener) { + return new AlertDialog.Builder(context) + .setCancelable(true) + .setIcon(android.R.drawable.ic_dialog_alert) + .setMessage(context.getString(messageId)) + .setNegativeButton(android.R.string.cancel, null) + .setPositiveButton(android.R.string.ok, onClickListener) + .setTitle(R.string.generic_confirm_title) + .create(); + } + + /** + * Creates a spinner progress dialog. + * + * @param context the context + * @param messageId the progress message id + * @param onCancelListener the cancel listener + */ + public static ProgressDialog createSpinnerProgressDialog( + Context context, int messageId, DialogInterface.OnCancelListener onCancelListener) { + return createProgressDialog(true, context, messageId, onCancelListener); + } + + /** + * Creates a horizontal progress dialog. + * + * @param context the context + * @param messageId the progress message id + * @param onCancelListener the cancel listener + * @param formatArgs the format arguments for the messageId + */ + public static ProgressDialog createHorizontalProgressDialog(Context context, int messageId, + DialogInterface.OnCancelListener onCancelListener, Object... formatArgs) { + return createProgressDialog(false, context, messageId, onCancelListener, formatArgs); + } + + /** + * Creates a progress dialog. + * + * @param spinner true to use the spinner style + * @param context the context + * @param messageId the progress message id + * @param onCancelListener the cancel listener + * @param formatArgs the format arguments for the message id + */ + private static ProgressDialog createProgressDialog(boolean spinner, Context context, + int messageId, DialogInterface.OnCancelListener onCancelListener, Object... formatArgs) { + ProgressDialog progressDialog = new ProgressDialog(context); + progressDialog.setCancelable(true); + progressDialog.setIcon(android.R.drawable.ic_dialog_info); + progressDialog.setIndeterminate(true); + progressDialog.setMessage(context.getString(messageId, formatArgs)); + progressDialog.setOnCancelListener(onCancelListener); + progressDialog.setProgressStyle(spinner ? ProgressDialog.STYLE_SPINNER + : ProgressDialog.STYLE_HORIZONTAL); + progressDialog.setTitle(R.string.generic_progress_title); + return progressDialog; + } +} diff --git a/vdrmanager/src/de/bjusystems/vdrmanager/backup/ExternalFileBackup.java b/vdrmanager/src/de/bjusystems/vdrmanager/backup/ExternalFileBackup.java new file mode 100644 index 0000000..22bb1dd --- /dev/null +++ b/vdrmanager/src/de/bjusystems/vdrmanager/backup/ExternalFileBackup.java @@ -0,0 +1,276 @@ +/* + * Copyright 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package de.bjusystems.vdrmanager.backup; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.TimeZone; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.zip.ZipOutputStream; + +import android.content.Context; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; +import android.util.Log; +import de.bjusystems.vdrmanager.data.db.OrmDatabaseHelper; + +/** + * Handler for writing or reading single-file backups. + * + * @author Rodrigo Damazio + */ +class ExternalFileBackup { + // Filename format - in UTC + private static final SimpleDateFormat BACKUP_FILENAME_FORMAT = new SimpleDateFormat( + "'backup-'yyyy-MM-dd_HH-mm-ss'.zip'"); + static { + BACKUP_FILENAME_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC")); + } + + private static final String BACKUPS_SUBDIR = "backups"; + private static final int BACKUP_FORMAT_VERSION = 1; + private static final String ZIP_ENTRY_NAME = "backup.mybillingbuddy.v" + + BACKUP_FORMAT_VERSION; + private static final int COMPRESSION_LEVEL = 8; + + private final Context context; + + public ExternalFileBackup(Context context) { + this.context = context; + } + + /** + * Returns whether the backups directory is (or can be made) available. + * + * @param create + * whether to try creating the directory if it doesn't exist + */ + public boolean isBackupsDirectoryAvailable(boolean create) { + return getBackupsDirectory(create) != null; + } + + /** + * Returns the backup directory, or null if not available. + * + * @param create + * whether to try creating the directory if it doesn't exist + */ + private File getBackupsDirectory(boolean create) { + String dirName = FileUtils.buildExternalDirectoryPath(BACKUPS_SUBDIR); + final File dir = new File(dirName); + Log.d(Constants.TAG, "Dir: " + dir.getAbsolutePath()); + if (create) { + // Try to create - if that fails, return null + return FileUtils.ensureDirectoryExists(dir) ? dir : null; + } else { + // Return it if it already exists, otherwise return null + return dir.isDirectory() ? dir : null; + } + } + + /** + * Returns a list of available backups to be restored. + */ + public Date[] getAvailableBackups() { + File dir = getBackupsDirectory(false); + if (dir == null) { + return null; + } + String[] fileNames = dir.list(); + + List<Date> backupDates = new ArrayList<Date>(fileNames.length); + for (int i = 0; i < fileNames.length; i++) { + String fileName = fileNames[i]; + try { + backupDates.add(BACKUP_FILENAME_FORMAT.parse(fileName)); + } catch (ParseException e) { + // Not a backup file, ignore + } + } + + return backupDates.toArray(new Date[backupDates.size()]); + } + + /** + * Writes the backup to the default file. + */ + public void writeToDefaultFile() throws IOException { + writeToFile(getFileForDate(new Date())); + } + + /** + * Restores the backup from the given date. + */ + public void restoreFromDate(Date when) throws IOException { + restoreFromFile(getFileForDate(when)); + } + + public void restoreFromFile(String path ) throws IOException { + restoreFromFile(new File(path)); + } + + /** + * Produces the proper file descriptor for the given backup date. + */ + private File getFileForDate(Date when) { + File dir = getBackupsDirectory(false); + String fileName = BACKUP_FILENAME_FORMAT.format(when); + File file = new File(dir, fileName); + return file; + } + + /** + * Synchronously writes a backup to the given file. + */ + private void writeToFile(File outputFile) throws IOException { + Log.d(Constants.TAG, + "Writing backup to file " + outputFile.getAbsolutePath()); + + // Create all the auxiliary classes that will do the writing + //DatabaseDumper trackDumper = new DatabaseDumper( + // BackupColumns.TRACKS_BACKUP_COLUMNS, + //BackupColumns.TRACKS_BACKUP_COLUMN_TYPES, false); + //DatabaseDumper waypointDumper = new DatabaseDumper( + // BackupColumns.WAYPOINTS_BACKUP_COLUMNS, + //BackupColumns.WAYPOINTS_BACKUP_COLUMN_TYPES, false); + //DatabaseDumper pointDumper = new DatabaseDumper( + // BackupColumns.POINTS_BACKUP_COLUMNS, + //BackupColumns.POINTS_BACKUP_COLUMN_TYPES, false); + + // Open the target for writing + FileOutputStream outputStream = new FileOutputStream(outputFile); + ZipOutputStream compressedStream = new ZipOutputStream(outputStream); + compressedStream.setLevel(COMPRESSION_LEVEL); + compressedStream.putNextEntry(new ZipEntry(ZIP_ENTRY_NAME)); + DataOutputStream outWriter = new DataOutputStream(compressedStream); + + try { + // Dump the entire contents of each table +// ContentResolver contentResolver = context.getContentResolver(); +// Cursor tracksCursor = contentResolver.query( +// TracksColumns.CONTENT_URI, null, null, null, null); +// try { +// trackDumper.writeAllRows(tracksCursor, outWriter); +// } finally { +// tracksCursor.close(); +// } +// +// Cursor waypointsCursor = contentResolver.query( +// WaypointsColumns.CONTENT_URI, null, null, null, null); +// try { +// waypointDumper.writeAllRows(waypointsCursor, outWriter); +// } finally { +// waypointsCursor.close(); +// } +// +// Cursor pointsCursor = contentResolver.query( +// TrackPointsColumns.CONTENT_URI, null, null, null, null); +// try { +// pointDumper.writeAllRows(pointsCursor, outWriter); +// } finally { +// pointsCursor.close(); +// } + + // Dump preferences + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); + PreferenceBackupHelper preferencesHelper = new PreferenceBackupHelper(); + preferencesHelper.exportPreferences(preferences, outWriter); + + + File f = new File(OrmDatabaseHelper.getDataBaseFile()); + if(f.exists()){ + compressedStream.putNextEntry(new ZipEntry(OrmDatabaseHelper.DATABASE_NAME)); + IOUtils.copy(new FileInputStream(OrmDatabaseHelper.getDataBaseFile()), outWriter); + } + + + + + + + } catch (IOException e) { + // We tried to delete the partially created file, but do nothing + // if that also fails. + if (!outputFile.delete()) { + Log.w(Constants.TAG, + "Failed to delete file " + outputFile.getAbsolutePath()); + } + + throw e; + } finally { + compressedStream.closeEntry(); + compressedStream.close(); + } + } + + /** + * Synchronously restores the backup from the given file. + */ + private void restoreFromFile(File inputFile) throws IOException { + Log.d(Constants.TAG, + "Restoring from file " + inputFile.getAbsolutePath()); + + + + ZipFile zipFile = new ZipFile(inputFile, ZipFile.OPEN_READ); + ZipEntry zipEntry = zipFile.getEntry(ZIP_ENTRY_NAME); + if (zipEntry == null) { + throw new IOException("Invalid backup ZIP file"); + } + + InputStream compressedStream = zipFile.getInputStream(zipEntry); + DataInputStream reader = new DataInputStream(compressedStream); + + try { + + // Restore preferences + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); + PreferenceBackupHelper preferencesHelper = new PreferenceBackupHelper(); + preferencesHelper.importPreferences(reader, preferences); + + + + + zipEntry = zipFile.getEntry(OrmDatabaseHelper.DATABASE_NAME); + if (zipEntry != null) { + IOUtils.copy(zipFile.getInputStream(zipEntry), new FileOutputStream(OrmDatabaseHelper.getDataBaseFile())); + deleteJournal(OrmDatabaseHelper.getDataBaseFile()); + } + + } finally { + compressedStream.close(); + zipFile.close(); + } + } + + private static void deleteJournal(String db){ + if(db == null){ + return; + } + new File(db+"-journal").delete(); + } +} diff --git a/vdrmanager/src/de/bjusystems/vdrmanager/backup/FileUtils.java b/vdrmanager/src/de/bjusystems/vdrmanager/backup/FileUtils.java new file mode 100644 index 0000000..d53a80b --- /dev/null +++ b/vdrmanager/src/de/bjusystems/vdrmanager/backup/FileUtils.java @@ -0,0 +1,199 @@ +/* + * Copyright 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package de.bjusystems.vdrmanager.backup; + + +import android.os.Environment; + +import java.io.File; + +/** + * Utilities for dealing with files. + * + * @author Rodrigo Damazio + */ +public class FileUtils { + + private FileUtils() {} + + /** + * The maximum FAT32 path length. See the FAT32 spec at + * http://msdn.microsoft.com/en-us/windows/hardware/gg463080 + */ + static final int MAX_FAT32_PATH_LENGTH = 260; + + /** + * Returns whether the SD card is available. + */ + public static boolean isSdCardAvailable() { + return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()); + } + + /** + * Ensures the given directory exists by creating it and its parents if + * necessary. + * + * @return whether the directory exists (either already existed or was + * successfully created) + */ + public static boolean ensureDirectoryExists(File dir) { + if (dir.exists() && dir.isDirectory()) { + return true; + } + if (dir.mkdirs()) { + return true; + } + return false; + } + + /** + * Builds a path inside the My Tracks directory in the SD card. + * + * @param components the path components inside the mytracks directory + * @return the full path to the destination + */ + public static String buildExternalDirectoryPath(String... components) { + StringBuilder dirNameBuilder = new StringBuilder(); + dirNameBuilder.append(Environment.getExternalStorageDirectory()); + dirNameBuilder.append(File.separatorChar); + dirNameBuilder.append(Constants.SDCARD_TOP_DIR); + for (String component : components) { + dirNameBuilder.append(File.separatorChar); + dirNameBuilder.append(component); + } + return dirNameBuilder.toString(); + } + + /** + * Builds a filename with the given base name (prefix) and the given + * extension, possibly adding a suffix to ensure the file doesn't exist. + * + * @param directory the directory the file will live in + * @param fileBaseName the prefix for the file name + * @param extension the file's extension + * @return the complete file name, without the directory + */ + public static synchronized String buildUniqueFileName( + File directory, String fileBaseName, String extension) { + return buildUniqueFileName(directory, fileBaseName, extension, 0); + } + + /** + * Builds a filename with the given base and the given extension, possibly + * adding a suffix to ensure the file doesn't exist. + * + * @param directory the directory the filename will be located in + * @param base the base for the filename + * @param extension the extension for the filename + * @param suffix the first numeric suffix to try to use, or 0 for none + * @return the complete filename, without the directory + */ + private static String buildUniqueFileName( + File directory, String base, String extension, int suffix) { + String suffixName = ""; + if (suffix > 0) { + suffixName += "(" + Integer.toString(suffix) + ")"; + } + suffixName += "." + extension; + + String baseName = sanitizeFileName(base); + baseName = truncateFileName(directory, baseName, suffixName); + String fullName = baseName + suffixName; + + if (!new File(directory, fullName).exists()) { + return fullName; + } + return buildUniqueFileName(directory, base, extension, suffix + 1); + } + + /** + * Sanitizes the name as a valid fat32 filename. For simplicity, fat32 + * filename characters may be any combination of letters, digits, or + * characters with code point values greater than 127. Replaces the invalid + * characters with "_" and collapses multiple "_" together. + * + * @param name name + */ + static String sanitizeFileName(String name) { + StringBuffer buffer = new StringBuffer(name.length()); + for (int i = 0; i < name.length(); i++) { + int codePoint = name.codePointAt(i); + char character = name.charAt(i); + if (Character.isLetterOrDigit(character) || codePoint > 127 || isSpecialFat32(character)) { + buffer.appendCodePoint(codePoint); + } else { + buffer.append("_"); + } + } + String result = buffer.toString(); + return result.replaceAll("_+", "_"); + } + + /** + * Returns true if it is a special FAT32 character. + * + * @param character the character + */ + private static boolean isSpecialFat32(char character) { + switch (character) { + case '$': + case '%': + case '\'': + case '-': + case '_': + case '@': + case '~': + case '`': + case '!': + case '(': + case ')': + case '{': + case '}': + case '^': + case '#': + case '&': + case '+': + case ',': + case ';': + case '=': + case '[': + case ']': + case ' ': + return true; + default: + return false; + } + } + + /** + * Truncates the name if necessary so the filename path length (directory + + * name + suffix) meets the Fat32 path limit. + * + * @param directory directory + * @param name name + * @param suffix suffix + */ + static String truncateFileName(File directory, String name, String suffix) { + // 1 at the end accounts for the FAT32 filename trailing NUL character + int requiredLength = directory.getPath().length() + suffix.length() + 1; + if (name.length() + requiredLength > MAX_FAT32_PATH_LENGTH) { + int limit = MAX_FAT32_PATH_LENGTH - requiredLength; + return name.substring(0, limit); + } else { + return name; + } + } +} diff --git a/vdrmanager/src/de/bjusystems/vdrmanager/backup/IOUtils.java b/vdrmanager/src/de/bjusystems/vdrmanager/backup/IOUtils.java new file mode 100644 index 0000000..53be61c --- /dev/null +++ b/vdrmanager/src/de/bjusystems/vdrmanager/backup/IOUtils.java @@ -0,0 +1,217 @@ +package de.bjusystems.vdrmanager.backup; + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.io.StringWriter; + +/** + * General IO stream manipulation utilities. + * <p> + * This class provides static utility methods for input/output operations. + * <ul> + * <li>closeQuietly - these methods close a stream ignoring nulls and exceptions + * <li>toXxx/read - these methods read data from a stream + * <li>write - these methods write data to a stream + * <li>copy - these methods copy all the data from one stream to another + * <li>contentEquals - these methods compare the content of two streams + * </ul> + * <p> + * The byte-to-char methods and char-to-byte methods involve a conversion step. + * Two methods are provided in each case, one that uses the platform default + * encoding and the other which allows you to specify an encoding. You are + * encouraged to always specify an encoding because relying on the platform + * default can lead to unexpected results, for example when moving from + * development to production. + * <p> + * All the methods in this class that read a stream are buffered internally. + * This means that there is no cause to use a <code>BufferedInputStream</code> + * or <code>BufferedReader</code>. The default buffer size of 4K has been shown + * to be efficient in tests. + * <p> + * Wherever possible, the methods in this class do <em>not</em> flush or close + * the stream. This is to avoid making non-portable assumptions about the + * streams' origin and further use. Thus the caller is still responsible for + * closing streams after use. + * <p> + * Origin of code: Excalibur. + * + * @author Peter Donald + * @author Jeff Turner + * @author Matthew Hawthorne + * @author Stephen Colebourne + * @author Gareth Davis + * @author Ian Springer + * @author Niall Pemberton + * @author Sandy McArthur + * @version $Id: IOUtils.java 481854 2006-12-03 18:30:07Z scolebourne $ + */ +public class IOUtils { + // NOTE: This class is focussed on InputStream, OutputStream, Reader and + // Writer. Each method should take at least one of these as a parameter, + // or return one of them. + + /** + * The Unix directory separator character. + */ + public static final char DIR_SEPARATOR_UNIX = '/'; + /** + * The Windows directory separator character. + */ + public static final char DIR_SEPARATOR_WINDOWS = '\\'; + /** + * The system directory separator character. + */ + public static final char DIR_SEPARATOR = File.separatorChar; + /** + * The Unix line separator string. + */ + public static final String LINE_SEPARATOR_UNIX = "\n"; + /** + * The Windows line separator string. + */ + public static final String LINE_SEPARATOR_WINDOWS = "\r\n"; + /** + * The system line separator string. + */ + public static final String LINE_SEPARATOR; + static { + // avoid security issues + StringWriter buf = new StringWriter(4); + PrintWriter out = new PrintWriter(buf); + out.println(); + LINE_SEPARATOR = buf.toString(); + } + + /** + * The default buffer size to use. + */ + private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; + + /** + * Instances should NOT be constructed in standard programming. + */ + public IOUtils() { + super(); + } + + // copy from InputStream + // ----------------------------------------------------------------------- + /** + * Copy bytes from an <code>InputStream</code> to an + * <code>OutputStream</code>. + * <p> + * This method buffers the input internally, so there is no need to use a + * <code>BufferedInputStream</code>. + * <p> + * Large streams (over 2GB) will return a bytes copied value of + * <code>-1</code> after the copy has completed since the correct number of + * bytes cannot be returned as an int. For large streams use the + * <code>copyLarge(InputStream, OutputStream)</code> method. + * + * @param input + * the <code>InputStream</code> to read from + * @param output + * the <code>OutputStream</code> to write to + * @return the number of bytes copied + * @throws NullPointerException + * if the input or output is null + * @throws IOException + * if an I/O error occurs + * @throws ArithmeticException + * if the byte count is too large + * @since Commons IO 1.1 + */ + public static int copy(final InputStream input, final OutputStream output) + throws IOException { + long count = copyLarge(input, output); + if (count > Integer.MAX_VALUE) { + return -1; + } + return (int) count; + } + + /** + * Copy bytes from a large (over 2GB) <code>InputStream</code> to an + * <code>OutputStream</code>. + * <p> + * This method buffers the input internally, so there is no need to use a + * <code>BufferedInputStream</code>. + * + * @param input + * the <code>InputStream</code> to read from + * @param output + * the <code>OutputStream</code> to write to + * @return the number of bytes copied + * @throws NullPointerException + * if the input or output is null + * @throws IOException + * if an I/O error occurs + * @since Commons IO 1.3 + */ + public static long copyLarge(final InputStream input, + final OutputStream output) throws IOException { + byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; + long count = 0; + int n = 0; + while (-1 != (n = input.read(buffer))) { + output.write(buffer, 0, n); + count += n; + } + return count; + } + /** + * Unconditionally close an <code>InputStream</code>. + * <p> + * Equivalent to {@link InputStream#close()}, except any exceptions will be ignored. + * This is typically used in finally blocks. + * + * @param input the InputStream to close, may be null or already closed + */ + public static void closeQuietly(InputStream input) { + try { + if (input != null) { + input.close(); + } + } catch (IOException ioe) { + // ignore + } + } + + /** + * Unconditionally close an <code>OutputStream</code>. + * <p> + * Equivalent to {@link OutputStream#close()}, except any exceptions will be ignored. + * This is typically used in finally blocks. + * + * @param output the OutputStream to close, may be null or already closed + */ + public static void closeQuietly(OutputStream output) { + try { + if (output != null) { + output.close(); + } + } catch (IOException ioe) { + // ignore + } + } +} diff --git a/vdrmanager/src/de/bjusystems/vdrmanager/backup/IntentUtils.java b/vdrmanager/src/de/bjusystems/vdrmanager/backup/IntentUtils.java new file mode 100644 index 0000000..43131b4 --- /dev/null +++ b/vdrmanager/src/de/bjusystems/vdrmanager/backup/IntentUtils.java @@ -0,0 +1,47 @@ +/* + * Copyright 2012 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package de.bjusystems.vdrmanager.backup; + + +import android.content.Context; +import android.content.Intent; + +/** + * Utilities for creating intents. + * + * @author Jimmy Shih + */ +public class IntentUtils { + + public static final String TEXT_PLAIN_TYPE = "text/plain"; + + private IntentUtils() {} + + /** + * Creates an intent with {@link Intent#FLAG_ACTIVITY_CLEAR_TOP} and + * {@link Intent#FLAG_ACTIVITY_NEW_TASK}. + * + * @param context the context + * @param cls the class + */ + public static final Intent newIntent(Context context, Class<?> cls) { + return new Intent(context, cls).addFlags( + Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); + } + + +} diff --git a/vdrmanager/src/de/bjusystems/vdrmanager/backup/LineIterator.java b/vdrmanager/src/de/bjusystems/vdrmanager/backup/LineIterator.java new file mode 100644 index 0000000..c60ee66 --- /dev/null +++ b/vdrmanager/src/de/bjusystems/vdrmanager/backup/LineIterator.java @@ -0,0 +1,5 @@ +package de.bjusystems.vdrmanager.backup; + +public class LineIterator { + +} diff --git a/vdrmanager/src/de/bjusystems/vdrmanager/backup/PreferenceBackupHelper.java b/vdrmanager/src/de/bjusystems/vdrmanager/backup/PreferenceBackupHelper.java new file mode 100644 index 0000000..30a32b4 --- /dev/null +++ b/vdrmanager/src/de/bjusystems/vdrmanager/backup/PreferenceBackupHelper.java @@ -0,0 +1,172 @@ +/* + * Copyright 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package de.bjusystems.vdrmanager.backup; + + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Map; + +import android.content.SharedPreferences; +import android.content.SharedPreferences.Editor; + +/** + * Helper for backing up and restoring shared preferences. + * + * @author Rodrigo Damazio + */ +class PreferenceBackupHelper { + + private static final int BUFFER_SIZE = 2048; + + /** + * Exports all shared preferences from the given object as a byte array. + * + * @param preferences the preferences to export + * @return the corresponding byte array + * @throws IOException if there are any errors while writing to the byte array + */ + public byte[] exportPreferences(SharedPreferences preferences) + throws IOException { + ByteArrayOutputStream bufStream = new ByteArrayOutputStream(BUFFER_SIZE); + DataOutputStream outWriter = new DataOutputStream(bufStream); + exportPreferences(preferences, outWriter); + + return bufStream.toByteArray(); + } + + /** + * Exports all shared preferences from the given object into the given output + * stream. + * + * @param preferences the preferences to export + * @param outWriter the stream to write them to + * @throws IOException if there are any errors while writing the output + */ + public void exportPreferences( + SharedPreferences preferences, + DataOutputStream outWriter) throws IOException { + Map<String, ?> values = preferences.getAll(); + + outWriter.writeInt(values.size()); + for (Map.Entry<String, ?> entry : values.entrySet()) { + writePreference(entry.getKey(), entry.getValue(), outWriter); + } + outWriter.flush(); + } + + /** + * Imports all preferences from the given byte array. + * + * @param data the byte array to read preferences from + * @param preferences the shared preferences to edit + * @throws IOException if there are any errors while reading + */ + public void importPreferences(byte[] data, SharedPreferences preferences) + throws IOException { + ByteArrayInputStream bufStream = new ByteArrayInputStream(data); + DataInputStream reader = new DataInputStream(bufStream); + + importPreferences(reader, preferences); + } + + /** + * Imports all preferences from the given stream. + * + * @param reader the stream to read from + * @param preferences the shared preferences to edit + * @throws IOException if there are any errors while reading + */ + public void importPreferences(DataInputStream reader, + SharedPreferences preferences) throws IOException { + Editor editor = preferences.edit(); + editor.clear(); + + int numPreferences = reader.readInt(); + for (int i = 0; i < numPreferences; i++) { + String name = reader.readUTF(); + byte typeId = reader.readByte(); + readAndSetPreference(name, typeId, reader, editor); + } + ApiAdapterFactory.getApiAdapter().applyPreferenceChanges(editor); + } + + /** + * Reads a single preference and sets it into the given editor. + * + * @param name the name of the preference to read + * @param typeId the type ID of the preference to read + * @param reader the reader to read from + * @param editor the editor to set the preference in + * @throws IOException if there are errors while reading + */ + private void readAndSetPreference(String name, byte typeId, + DataInputStream reader, Editor editor) throws IOException { + switch (typeId) { + case ContentTypeIds.BOOLEAN_TYPE_ID: + editor.putBoolean(name, reader.readBoolean()); + return; + case ContentTypeIds.LONG_TYPE_ID: + editor.putLong(name, reader.readLong()); + return; + case ContentTypeIds.FLOAT_TYPE_ID: + editor.putFloat(name, reader.readFloat()); + return; + case ContentTypeIds.INT_TYPE_ID: + editor.putInt(name, reader.readInt()); + return; + case ContentTypeIds.STRING_TYPE_ID: + editor.putString(name, reader.readUTF()); + return; + } + } + + /** + * Writes a single preference. + * + * @param name the name of the preference to write + * @param value the correctly-typed value of the preference + * @param writer the writer to write to + * @throws IOException if there are errors while writing + */ + private void writePreference(String name, Object value, DataOutputStream writer) + throws IOException { + writer.writeUTF(name); + + if (value instanceof Boolean) { + writer.writeByte(ContentTypeIds.BOOLEAN_TYPE_ID); + writer.writeBoolean((Boolean) value); + } else if (value instanceof Integer) { + writer.writeByte(ContentTypeIds.INT_TYPE_ID); + writer.writeInt((Integer) value); + } else if (value instanceof Long) { + writer.writeByte(ContentTypeIds.LONG_TYPE_ID); + writer.writeLong((Long) value); + } else if (value instanceof Float) { + writer.writeByte(ContentTypeIds.FLOAT_TYPE_ID); + writer.writeFloat((Float) value); + } else if (value instanceof String) { + writer.writeByte(ContentTypeIds.STRING_TYPE_ID); + writer.writeUTF((String) value); + } else { + throw new IllegalArgumentException( + "Type " + value.getClass().getName() + " not supported"); + } + } +} diff --git a/vdrmanager/src/de/bjusystems/vdrmanager/backup/RestoreActivity.java b/vdrmanager/src/de/bjusystems/vdrmanager/backup/RestoreActivity.java new file mode 100644 index 0000000..97e4749 --- /dev/null +++ b/vdrmanager/src/de/bjusystems/vdrmanager/backup/RestoreActivity.java @@ -0,0 +1,114 @@ +/* + * Copyright 2012 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package de.bjusystems.vdrmanager.backup; + + + +import java.util.Date; + +import android.app.Activity; +import android.app.Dialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; +import android.widget.Toast; +import de.bjusystems.vdrmanager.R; +import de.bjusystems.vdrmanager.gui.PreferencesActivity; + +/** + * An activity to restore data from the SD card. + * + * @author Jimmy Shih + */ +public class RestoreActivity extends Activity { + + public static final String EXTRA_DATE = "date"; + public static final String EXTRA_FILE = "file"; + + private static final String TAG = RestoreActivity.class.getSimpleName(); + private static final int DIALOG_PROGRESS_ID = 0; + + private RestoreAsyncTask restoreAsyncTask; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Object retained = getLastNonConfigurationInstance(); + if (retained instanceof RestoreAsyncTask) { + restoreAsyncTask = (RestoreAsyncTask) retained; + restoreAsyncTask.setActivity(this); + } else { + + long date = -1L; + String file = getIntent().getStringExtra(EXTRA_FILE); + if(file != null){ + restoreAsyncTask = new RestoreAsyncTask(this, file); + } else if( (date = getIntent().getLongExtra(EXTRA_DATE, -1L)) != -1L) { + restoreAsyncTask = new RestoreAsyncTask(this, new Date(date)); + } else { + Log.d(TAG, "Invalid date or file"); + finish(); + return; + } + restoreAsyncTask.execute(); + } + } + + @Override + public Object onRetainNonConfigurationInstance() { + restoreAsyncTask.setActivity(null); + return restoreAsyncTask; + } + + @Override + protected Dialog onCreateDialog(int id) { + if (id != DIALOG_PROGRESS_ID) { + return null; + } + return DialogUtils.createSpinnerProgressDialog(this, + R.string.settings_backup_restore_progress_message, new DialogInterface.OnCancelListener() { + @Override + public void onCancel(DialogInterface dialog) { + finish(); + } + }); + } + + /** + * Invokes when the associated AsyncTask completes. + * + * @param success true if the AsyncTask is successful + * @param messageId message id to display to user + */ + public void onAsyncTaskCompleted(boolean success, int messageId) { + removeDialog(DIALOG_PROGRESS_ID); + Toast.makeText(this, messageId, success ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG).show(); + //Intent intent = IntentUtils.newIntent(this, FixUpJobs.class); + //intent.putExtra(Extras.AFTER_RESTORE, Boolean.TRUE); + Intent intent = IntentUtils.newIntent(this, PreferencesActivity.class); + startActivity(intent); + } + + /** + * Shows the progress dialog. + */ + public void showProgressDialog() { + showDialog(DIALOG_PROGRESS_ID); + } +} diff --git a/vdrmanager/src/de/bjusystems/vdrmanager/backup/RestoreAsyncTask.java b/vdrmanager/src/de/bjusystems/vdrmanager/backup/RestoreAsyncTask.java new file mode 100644 index 0000000..37373f3 --- /dev/null +++ b/vdrmanager/src/de/bjusystems/vdrmanager/backup/RestoreAsyncTask.java @@ -0,0 +1,136 @@ +/* + * Copyright 2012 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package de.bjusystems.vdrmanager.backup; + +import java.io.IOException; +import java.util.Date; + +import android.os.AsyncTask; +import android.util.Log; +import de.bjusystems.vdrmanager.R; + +/** + * AsyncTask to restore data from the SD card. + * + * @author Jimmy Shih + */ +public class RestoreAsyncTask extends AsyncTask<Void, Integer, Boolean> { + + private static final String TAG = RestoreAsyncTask.class.getSimpleName(); + + private RestoreActivity restoreActivity; + private final Date date; + private final String path; + private final ExternalFileBackup externalFileBackup; + + // true if the AsyncTask result is success + private boolean success; + + // true if the AsyncTask has completed + private boolean completed; + + // message id to return to the activity + private int messageId; + + /** + * Creates an AsyncTask. + * + * @param restoreActivity + * the activity currently associated with this AsyncTask + * @param date + * the date to retore from + */ + public RestoreAsyncTask(RestoreActivity restoreActivity, Date date) { + this(restoreActivity, date, null); + } + + /** + * Creates an AsyncTask. + * + * @param restoreActivity + * the activity currently associated with this AsyncTask + * @param date + * the date to retore from + */ + public RestoreAsyncTask(RestoreActivity restoreActivity, String path) { + this(restoreActivity, null, path); + } + + /** + * Creates an AsyncTask. + * + * @param restoreActivity + * the activity currently associated with this AsyncTask + * @param date + * the date to retore from + */ + public RestoreAsyncTask(RestoreActivity restoreActivity, Date date, + String path) { + this.restoreActivity = restoreActivity; + this.date = date; + this.path = path; + this.externalFileBackup = new ExternalFileBackup(restoreActivity); + success = false; + completed = false; + messageId = R.string.sd_card_import_error; + } + + /** + * Sets the current {@link RestoreActivity} associated with this AyncTask. + * + * @param activity + * the current {@link RestoreActivity}, can be null + */ + public void setActivity(RestoreActivity activity) { + this.restoreActivity = activity; + if (completed && restoreActivity != null) { + restoreActivity.onAsyncTaskCompleted(success, messageId); + } + } + + @Override + protected void onPreExecute() { + if (restoreActivity != null) { + restoreActivity.showProgressDialog(); + } + } + + @Override + protected Boolean doInBackground(Void... params) { + try { + if (path != null) { + externalFileBackup.restoreFromFile(path); + } else { + externalFileBackup.restoreFromDate(date); + } + messageId = R.string.sd_card_import_success; + return true; + } catch (IOException e) { + Log.d(TAG, "IO exception", e); + return false; + } + } + + @Override + protected void onPostExecute(Boolean result) { + success = result; + completed = true; + if (restoreActivity != null) { + restoreActivity.onAsyncTaskCompleted(success, messageId); + } + } +} diff --git a/vdrmanager/src/de/bjusystems/vdrmanager/backup/RestoreChooserActivity.java b/vdrmanager/src/de/bjusystems/vdrmanager/backup/RestoreChooserActivity.java new file mode 100644 index 0000000..cba6ab0 --- /dev/null +++ b/vdrmanager/src/de/bjusystems/vdrmanager/backup/RestoreChooserActivity.java @@ -0,0 +1,127 @@ +/* + * Copyright 2012 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package de.bjusystems.vdrmanager.backup; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.Date; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Bundle; +import android.widget.Toast; +import de.bjusystems.vdrmanager.R; +import de.bjusystems.vdrmanager.gui.Utils; + +/** + * An activity to choose a date to restore from. + * + * @author Jimmy Shih + */ +public class RestoreChooserActivity extends Activity { + + private static final Comparator<Date> REVERSE_DATE_ORDER = new Comparator<Date>() { + @Override + public int compare(Date s1, Date s2) { + return s2.compareTo(s1); + } + }; + private static final int DIALOG_CHOOSER_ID = 0; + + private Date[] backupDates; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + ExternalFileBackup externalFileBackup = new ExternalFileBackup(this); + + // Get the list of existing backups + if (!FileUtils.isSdCardAvailable()) { + Toast.makeText(this, R.string.sd_card_error_no_storage, + Toast.LENGTH_LONG).show(); + finish(); + return; + } + + if (!externalFileBackup.isBackupsDirectoryAvailable(false)) { + + Toast.makeText(this, R.string.settings_backup_restore_no_backup, + Toast.LENGTH_LONG).show(); + finish(); + return; + } + doStuff(externalFileBackup); + + } + + private void doStuff(ExternalFileBackup externalFileBackup) { + backupDates = externalFileBackup.getAvailableBackups(); + if (backupDates == null || backupDates.length == 0) { + Toast.makeText(this, R.string.settings_backup_restore_no_backup, + Toast.LENGTH_LONG).show(); + finish(); + return; + } + + if (backupDates.length == 1) { + Intent intent = IntentUtils.newIntent(this, RestoreActivity.class) + .putExtra(RestoreActivity.EXTRA_DATE, + backupDates[0].getTime()); + startActivity(intent); + finish(); + return; + } + + Arrays.sort(backupDates, REVERSE_DATE_ORDER); + showDialog(DIALOG_CHOOSER_ID); + } + + @Override + protected Dialog onCreateDialog(int id) { + if (id != DIALOG_CHOOSER_ID) { + return null; + } + String items[] = new String[backupDates.length]; + for (int i = 0; i < backupDates.length; i++) { + items[i] = Utils.formatDateTime(this, backupDates[i].getTime()); + } + return new AlertDialog.Builder(this) + .setCancelable(true) + .setItems(items, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + Intent intent = IntentUtils.newIntent( + RestoreChooserActivity.this, + RestoreActivity.class).putExtra( + RestoreActivity.EXTRA_DATE, + backupDates[which].getTime()); + startActivity(intent); + finish(); + } + }).setOnCancelListener(new DialogInterface.OnCancelListener() { + @Override + public void onCancel(DialogInterface dialog) { + finish(); + } + }).setTitle(R.string.settings_backup_restore_select_title) + .create(); + } +} diff --git a/vdrmanager/src/de/bjusystems/vdrmanager/data/db/OrmDatabaseHelper.java b/vdrmanager/src/de/bjusystems/vdrmanager/data/db/OrmDatabaseHelper.java index ab4a0ed..3feb8b0 100644 --- a/vdrmanager/src/de/bjusystems/vdrmanager/data/db/OrmDatabaseHelper.java +++ b/vdrmanager/src/de/bjusystems/vdrmanager/data/db/OrmDatabaseHelper.java @@ -31,7 +31,7 @@ public class OrmDatabaseHelper extends OrmLiteSqliteOpenHelper { public static final String TAG = OrmDatabaseHelper.class.getName(); // name of the database file for your application -- change to something // appropriate for your app - private static final String DATABASE_NAME = "vdrmanager.db"; + public static final String DATABASE_NAME = "vdrmanager.db"; // any time you make changes to your database objects, you may have to // increase the database version private static final int DATABASE_VERSION = 2; @@ -42,6 +42,10 @@ public class OrmDatabaseHelper extends OrmLiteSqliteOpenHelper { super(context, DATABASE_NAME, null, DATABASE_VERSION); } + + public static String getDataBaseFile(){ + return "/data/data/de.bjusystems.vdrmanager/databases/" + DATABASE_NAME; + } /** * This is called when the database is first created. Usually you should * call createTable statements here to create the tables that will store diff --git a/vdrmanager/src/de/bjusystems/vdrmanager/gui/BaseActivity.java b/vdrmanager/src/de/bjusystems/vdrmanager/gui/BaseActivity.java index 0257d16..466a31d 100644 --- a/vdrmanager/src/de/bjusystems/vdrmanager/gui/BaseActivity.java +++ b/vdrmanager/src/de/bjusystems/vdrmanager/gui/BaseActivity.java @@ -3,6 +3,7 @@ package de.bjusystems.vdrmanager.gui; import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.Intent; +import android.content.res.Configuration; import android.os.Bundle; import android.view.Gravity; import android.view.Menu; @@ -80,6 +81,11 @@ public abstract class BaseActivity<Result, T extends ListView> extends ICSBaseAc } @Override + public void onConfigurationChanged(Configuration newConfig) { + Preferences.setLocale(this); + super.onConfigurationChanged(newConfig); + } + @Override protected void onResume() { Preferences.setLocale(this); // Preferences.init(this); diff --git a/vdrmanager/src/de/bjusystems/vdrmanager/gui/PreferencesActivity.java b/vdrmanager/src/de/bjusystems/vdrmanager/gui/PreferencesActivity.java index 0015492..9555e8a 100644 --- a/vdrmanager/src/de/bjusystems/vdrmanager/gui/PreferencesActivity.java +++ b/vdrmanager/src/de/bjusystems/vdrmanager/gui/PreferencesActivity.java @@ -11,6 +11,7 @@ import android.preference.Preference.OnPreferenceClickListener; import android.preference.PreferenceManager; import de.bjusystems.vdrmanager.R; import de.bjusystems.vdrmanager.app.VdrManagerApp; +import de.bjusystems.vdrmanager.backup.BackupSettingsActivity; import de.bjusystems.vdrmanager.data.Preferences; public class PreferencesActivity extends BasePreferencesActivity implements @@ -34,6 +35,18 @@ public class PreferencesActivity extends BasePreferencesActivity implements Preferences.getPreferenceFile(this)); this.addPreferencesFromResource(R.xml.preferences); + + Preference backupPreference = findPreference(getString(R.string.settings_backup_key)); + backupPreference.setOnPreferenceClickListener(new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + Intent intent = new Intent(PreferencesActivity.this, BackupSettingsActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(intent); + return true; + } + }); + updateChildPreferences(); } diff --git a/vdrmanager/src/de/bjusystems/vdrmanager/gui/Utils.java b/vdrmanager/src/de/bjusystems/vdrmanager/gui/Utils.java index e7832bb..50e472c 100644 --- a/vdrmanager/src/de/bjusystems/vdrmanager/gui/Utils.java +++ b/vdrmanager/src/de/bjusystems/vdrmanager/gui/Utils.java @@ -20,6 +20,7 @@ import android.net.Uri; import android.text.Spannable; import android.text.SpannableString; import android.text.TextUtils; +import android.text.format.DateUtils; import android.text.style.ForegroundColorSpan; import android.util.Log; import android.util.Pair; @@ -354,4 +355,17 @@ public class Utils { t.setGravity(Gravity.CENTER, 0, 0); t.show(); } + + /** + * Formats the date and time based on user's phone date/time preferences. + * + * @param context the context + * @param time the time in milliseconds + */ + + public static String formatDateTime(Context context, long time) { + return android.text.format.DateFormat.getDateFormat(context).format(time) + " " + + DateUtils.formatDateTime(context, time, DateUtils.FORMAT_SHOW_TIME).toString(); + } + } |