diff --git a/README b/README index 2f989bf7..7ab734cd 100644 --- a/README +++ b/README @@ -20,21 +20,45 @@ along with Transdroid. If not, see . --- Some code/libraries are used in the project: - Base16Encoder (Apache OpenJPA) - Marc Prud'hommeaux (Apache License, Version 2.0) - MultipartEntity (AOSP) - Apache Software Foundation (Apache License, Version 2.0) + ActionBarSherlock + Jake Wharton + Apache License, Version 2.0 + http://actionbarsherlock.com/ + AndroidAnnotations + Pierre-Yves Ricau (eBusinessInformations) et al. + Apache License, Version 2.0 + http://androidannotations.org/ + Crouton + Code: Benjamin Weiss (Neofonie Mobile Gmbh) et al. + Idea: Cyril Mottier + Apache License, Version 2.0 + https://github.com/keyboardsurfer/Crouton + Base16Encoder + Marc Prud'hommeaux + Apache OpenJPA + http://openjpa.apache.org/ + MultipartEntity + Apache Software Foundation + Apache License, Version 2.0 http://source.android.com/ RssParser (learning-android) - Tane Piper (Public Domain) + Tane Piper + Public Domain http://github.com/digitalspaghetti/learning-android Base64 - Robert Harder (Public Domain) + Robert Harder + Public Domain http://iharder.net/base64 android-xmlrpc - pskink et al. (Apache License, Version 2.0) + pskink et al. + Apache License, Version 2.0 http://code.google.com/p/android-xmlrpc/ android-ColorPickerPreference - Daniel Nilsson and Sergey Margaritov (Apache License, Version 2.0) + Daniel Nilsson and Sergey Margaritov + Apache License, Version 2.0 https://github.com/attenzione/android-ColorPickerPreference + CheckableRelativeLayout + Cédric Caron (MarvinLabs) + Public Domain + http://www.marvinlabs.com/2010/10/custom-listview-ability-check-items/ diff --git a/android/.classpath b/android/.classpath index 6b9842fa..98efdebb 100644 --- a/android/.classpath +++ b/android/.classpath @@ -5,5 +5,6 @@ + diff --git a/android/src/org/transdroid/gui/TorrentsFragment.java b/android/src/org/transdroid/gui/TorrentsFragment.java index d92653b7..a9ae176e 100644 --- a/android/src/org/transdroid/gui/TorrentsFragment.java +++ b/android/src/org/transdroid/gui/TorrentsFragment.java @@ -1749,7 +1749,7 @@ public class TorrentsFragment extends SherlockFragment implements IDaemonCallbac // Sort the new list of torrents allTorrents = ((RetrieveTaskSuccessResult) result).getTorrents(); - Collections.sort(allTorrents, new TorrentsComparator(daemon, sortSetting, sortReversed)); + Collections.sort(allTorrents, new TorrentsComparator(daemon.getType(), sortSetting, sortReversed)); // Sort the new list of labels allLabels = ((RetrieveTaskSuccessResult) result).getLabels(); @@ -1936,7 +1936,7 @@ public class TorrentsFragment extends SherlockFragment implements IDaemonCallbac if (!(getTorrentListAdapter() == null || getTorrentListAdapter().getCount() == 0)) { // Sort the shown list of torrents using the new sortBy criteria - Collections.sort(allTorrents, new TorrentsComparator(daemon, sortSetting, sortReversed)); + Collections.sort(allTorrents, new TorrentsComparator(daemon.getType(), sortSetting, sortReversed)); updateTorrentsView(true); } diff --git a/core/.classpath b/core/.classpath new file mode 100644 index 00000000..68860191 --- /dev/null +++ b/core/.classpath @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/core/.factorypath b/core/.factorypath new file mode 100644 index 00000000..d528d92e --- /dev/null +++ b/core/.factorypath @@ -0,0 +1,3 @@ + + + diff --git a/core/.project b/core/.project new file mode 100644 index 00000000..1b341fee --- /dev/null +++ b/core/.project @@ -0,0 +1,33 @@ + + + Transdroid Core + + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + + diff --git a/core/.settings/org.eclipse.jdt.apt.core.prefs b/core/.settings/org.eclipse.jdt.apt.core.prefs new file mode 100644 index 00000000..7d52ece5 --- /dev/null +++ b/core/.settings/org.eclipse.jdt.apt.core.prefs @@ -0,0 +1,4 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.apt.aptEnabled=true +org.eclipse.jdt.apt.genSrcDir=.apt_generated +org.eclipse.jdt.apt.reconcileEnabled=true diff --git a/core/.settings/org.eclipse.jdt.core.prefs b/core/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..0b3561ab --- /dev/null +++ b/core/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.processAnnotations=enabled diff --git a/core/AndroidManifest.xml b/core/AndroidManifest.xml new file mode 100644 index 00000000..e30ef514 --- /dev/null +++ b/core/AndroidManifest.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/core/compile-libs/androidannotations-3.0-SNAPSHOT.jar b/core/compile-libs/androidannotations-3.0-SNAPSHOT.jar new file mode 100644 index 00000000..310da002 Binary files /dev/null and b/core/compile-libs/androidannotations-3.0-SNAPSHOT.jar differ diff --git a/core/libs/androidannotations-api-3.0-SNAPSHOT.jar b/core/libs/androidannotations-api-3.0-SNAPSHOT.jar new file mode 100644 index 00000000..bf0bfae5 Binary files /dev/null and b/core/libs/androidannotations-api-3.0-SNAPSHOT.jar differ diff --git a/core/libs/ormlite-android-4.24.jar b/core/libs/ormlite-android-4.24.jar new file mode 100644 index 00000000..d876135e Binary files /dev/null and b/core/libs/ormlite-android-4.24.jar differ diff --git a/core/libs/ormlite-core-4.24.jar b/core/libs/ormlite-core-4.24.jar new file mode 100644 index 00000000..a9ab582b Binary files /dev/null and b/core/libs/ormlite-core-4.24.jar differ diff --git a/core/proguard-project.txt b/core/proguard-project.txt new file mode 100644 index 00000000..f2fe1559 --- /dev/null +++ b/core/proguard-project.txt @@ -0,0 +1,20 @@ +# To enable ProGuard in your project, edit project.properties +# to define the proguard.config property as described in that file. +# +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in ${sdk.dir}/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the ProGuard +# include property in project.properties. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/core/project.properties b/core/project.properties new file mode 100644 index 00000000..0a043162 --- /dev/null +++ b/core/project.properties @@ -0,0 +1,18 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +# Project target. +target=android-17 +android.library.reference.1=../external/JakeWharton-ActionBarSherlock/library +android.library.reference.2=../external/ColorPickerPreference +android.library=true +android.library.reference.3=../external/Crouton/library diff --git a/core/res/drawable-hdpi/ab_bottom_solid_transdroid.9.png b/core/res/drawable-hdpi/ab_bottom_solid_transdroid.9.png new file mode 100644 index 00000000..64793735 Binary files /dev/null and b/core/res/drawable-hdpi/ab_bottom_solid_transdroid.9.png differ diff --git a/core/res/drawable-hdpi/ab_bottom_solid_transdroid2.9.png b/core/res/drawable-hdpi/ab_bottom_solid_transdroid2.9.png new file mode 100644 index 00000000..af25e462 Binary files /dev/null and b/core/res/drawable-hdpi/ab_bottom_solid_transdroid2.9.png differ diff --git a/core/res/drawable-hdpi/ab_solid_transdroid.9.png b/core/res/drawable-hdpi/ab_solid_transdroid.9.png new file mode 100644 index 00000000..12ece624 Binary files /dev/null and b/core/res/drawable-hdpi/ab_solid_transdroid.9.png differ diff --git a/core/res/drawable-hdpi/ab_solid_transdroid2.9.png b/core/res/drawable-hdpi/ab_solid_transdroid2.9.png new file mode 100644 index 00000000..6344cbc2 Binary files /dev/null and b/core/res/drawable-hdpi/ab_solid_transdroid2.9.png differ diff --git a/core/res/drawable-hdpi/ab_stacked_solid_transdroid.9.png b/core/res/drawable-hdpi/ab_stacked_solid_transdroid.9.png new file mode 100644 index 00000000..005445ac Binary files /dev/null and b/core/res/drawable-hdpi/ab_stacked_solid_transdroid.9.png differ diff --git a/core/res/drawable-hdpi/ab_stacked_solid_transdroid2.9.png b/core/res/drawable-hdpi/ab_stacked_solid_transdroid2.9.png new file mode 100644 index 00000000..a48652d3 Binary files /dev/null and b/core/res/drawable-hdpi/ab_stacked_solid_transdroid2.9.png differ diff --git a/core/res/drawable-hdpi/ab_texture_tile_transdroid2.png b/core/res/drawable-hdpi/ab_texture_tile_transdroid2.png new file mode 100644 index 00000000..87cc8211 Binary files /dev/null and b/core/res/drawable-hdpi/ab_texture_tile_transdroid2.png differ diff --git a/core/res/drawable-hdpi/ab_transparent_transdroid.9.png b/core/res/drawable-hdpi/ab_transparent_transdroid.9.png new file mode 100644 index 00000000..fc241b9f Binary files /dev/null and b/core/res/drawable-hdpi/ab_transparent_transdroid.9.png differ diff --git a/core/res/drawable-hdpi/ab_transparent_transdroid2.9.png b/core/res/drawable-hdpi/ab_transparent_transdroid2.9.png new file mode 100644 index 00000000..e517f83b Binary files /dev/null and b/core/res/drawable-hdpi/ab_transparent_transdroid2.9.png differ diff --git a/core/res/drawable-hdpi/btn_cab_done_default_transdroid2.9.png b/core/res/drawable-hdpi/btn_cab_done_default_transdroid2.9.png new file mode 100644 index 00000000..fc6944ea Binary files /dev/null and b/core/res/drawable-hdpi/btn_cab_done_default_transdroid2.9.png differ diff --git a/core/res/drawable-hdpi/btn_cab_done_focused_transdroid2.9.png b/core/res/drawable-hdpi/btn_cab_done_focused_transdroid2.9.png new file mode 100644 index 00000000..72a72d8b Binary files /dev/null and b/core/res/drawable-hdpi/btn_cab_done_focused_transdroid2.9.png differ diff --git a/core/res/drawable-hdpi/btn_cab_done_pressed_transdroid2.9.png b/core/res/drawable-hdpi/btn_cab_done_pressed_transdroid2.9.png new file mode 100644 index 00000000..8c69a5cf Binary files /dev/null and b/core/res/drawable-hdpi/btn_cab_done_pressed_transdroid2.9.png differ diff --git a/core/res/drawable-hdpi/cab_background_bottom_transdroid2.9.png b/core/res/drawable-hdpi/cab_background_bottom_transdroid2.9.png new file mode 100644 index 00000000..d61abca3 Binary files /dev/null and b/core/res/drawable-hdpi/cab_background_bottom_transdroid2.9.png differ diff --git a/core/res/drawable-hdpi/cab_background_top_transdroid2.9.png b/core/res/drawable-hdpi/cab_background_top_transdroid2.9.png new file mode 100644 index 00000000..6bc2078c Binary files /dev/null and b/core/res/drawable-hdpi/cab_background_top_transdroid2.9.png differ diff --git a/core/res/drawable-hdpi/ic_action_discard_dark.png b/core/res/drawable-hdpi/ic_action_discard_dark.png new file mode 100644 index 00000000..ffd19d9e Binary files /dev/null and b/core/res/drawable-hdpi/ic_action_discard_dark.png differ diff --git a/core/res/drawable-hdpi/ic_action_discard_light.png b/core/res/drawable-hdpi/ic_action_discard_light.png new file mode 100644 index 00000000..e9ce89e0 Binary files /dev/null and b/core/res/drawable-hdpi/ic_action_discard_light.png differ diff --git a/core/res/drawable-hdpi/ic_action_labels_dark.png b/core/res/drawable-hdpi/ic_action_labels_dark.png new file mode 100644 index 00000000..432e7c00 Binary files /dev/null and b/core/res/drawable-hdpi/ic_action_labels_dark.png differ diff --git a/core/res/drawable-hdpi/ic_action_labels_light.png b/core/res/drawable-hdpi/ic_action_labels_light.png new file mode 100644 index 00000000..9b093a67 Binary files /dev/null and b/core/res/drawable-hdpi/ic_action_labels_light.png differ diff --git a/core/res/drawable-hdpi/ic_action_new_dark.png b/core/res/drawable-hdpi/ic_action_new_dark.png new file mode 100644 index 00000000..ad8ada6b Binary files /dev/null and b/core/res/drawable-hdpi/ic_action_new_dark.png differ diff --git a/core/res/drawable-hdpi/ic_action_new_light.png b/core/res/drawable-hdpi/ic_action_new_light.png new file mode 100644 index 00000000..5741995c Binary files /dev/null and b/core/res/drawable-hdpi/ic_action_new_light.png differ diff --git a/core/res/drawable-hdpi/ic_action_pause_dark.png b/core/res/drawable-hdpi/ic_action_pause_dark.png new file mode 100644 index 00000000..6b435bb0 Binary files /dev/null and b/core/res/drawable-hdpi/ic_action_pause_dark.png differ diff --git a/core/res/drawable-hdpi/ic_action_pause_light.png b/core/res/drawable-hdpi/ic_action_pause_light.png new file mode 100644 index 00000000..9661cfbb Binary files /dev/null and b/core/res/drawable-hdpi/ic_action_pause_light.png differ diff --git a/core/res/drawable-hdpi/ic_action_priority_high_dark.png b/core/res/drawable-hdpi/ic_action_priority_high_dark.png new file mode 100644 index 00000000..84478f76 Binary files /dev/null and b/core/res/drawable-hdpi/ic_action_priority_high_dark.png differ diff --git a/core/res/drawable-hdpi/ic_action_priority_high_light.png b/core/res/drawable-hdpi/ic_action_priority_high_light.png new file mode 100644 index 00000000..4edf7a36 Binary files /dev/null and b/core/res/drawable-hdpi/ic_action_priority_high_light.png differ diff --git a/core/res/drawable-hdpi/ic_action_priority_low_dark.png b/core/res/drawable-hdpi/ic_action_priority_low_dark.png new file mode 100644 index 00000000..8802cad4 Binary files /dev/null and b/core/res/drawable-hdpi/ic_action_priority_low_dark.png differ diff --git a/core/res/drawable-hdpi/ic_action_priority_low_light.png b/core/res/drawable-hdpi/ic_action_priority_low_light.png new file mode 100644 index 00000000..23ada0ca Binary files /dev/null and b/core/res/drawable-hdpi/ic_action_priority_low_light.png differ diff --git a/core/res/drawable-hdpi/ic_action_priority_normal_dark.png b/core/res/drawable-hdpi/ic_action_priority_normal_dark.png new file mode 100644 index 00000000..7f7890f2 Binary files /dev/null and b/core/res/drawable-hdpi/ic_action_priority_normal_dark.png differ diff --git a/core/res/drawable-hdpi/ic_action_priority_normal_light.png b/core/res/drawable-hdpi/ic_action_priority_normal_light.png new file mode 100644 index 00000000..0246a1b7 Binary files /dev/null and b/core/res/drawable-hdpi/ic_action_priority_normal_light.png differ diff --git a/core/res/drawable-hdpi/ic_action_priority_off_dark.png b/core/res/drawable-hdpi/ic_action_priority_off_dark.png new file mode 100644 index 00000000..ab37b556 Binary files /dev/null and b/core/res/drawable-hdpi/ic_action_priority_off_dark.png differ diff --git a/core/res/drawable-hdpi/ic_action_priority_off_light.png b/core/res/drawable-hdpi/ic_action_priority_off_light.png new file mode 100644 index 00000000..7bf6101d Binary files /dev/null and b/core/res/drawable-hdpi/ic_action_priority_off_light.png differ diff --git a/core/res/drawable-hdpi/ic_action_refresh_dark.png b/core/res/drawable-hdpi/ic_action_refresh_dark.png new file mode 100644 index 00000000..bb9d855f Binary files /dev/null and b/core/res/drawable-hdpi/ic_action_refresh_dark.png differ diff --git a/core/res/drawable-hdpi/ic_action_refresh_light.png b/core/res/drawable-hdpi/ic_action_refresh_light.png new file mode 100644 index 00000000..479aca46 Binary files /dev/null and b/core/res/drawable-hdpi/ic_action_refresh_light.png differ diff --git a/core/res/drawable-hdpi/ic_action_remove_dark.png b/core/res/drawable-hdpi/ic_action_remove_dark.png new file mode 100644 index 00000000..094eea58 Binary files /dev/null and b/core/res/drawable-hdpi/ic_action_remove_dark.png differ diff --git a/core/res/drawable-hdpi/ic_action_remove_light.png b/core/res/drawable-hdpi/ic_action_remove_light.png new file mode 100644 index 00000000..cde36e1f Binary files /dev/null and b/core/res/drawable-hdpi/ic_action_remove_light.png differ diff --git a/core/res/drawable-hdpi/ic_action_resume_dark.png b/core/res/drawable-hdpi/ic_action_resume_dark.png new file mode 100644 index 00000000..738aae1a Binary files /dev/null and b/core/res/drawable-hdpi/ic_action_resume_dark.png differ diff --git a/core/res/drawable-hdpi/ic_action_resume_light.png b/core/res/drawable-hdpi/ic_action_resume_light.png new file mode 100644 index 00000000..b4f692fc Binary files /dev/null and b/core/res/drawable-hdpi/ic_action_resume_light.png differ diff --git a/core/res/drawable-hdpi/ic_action_rss_dark.png b/core/res/drawable-hdpi/ic_action_rss_dark.png new file mode 100644 index 00000000..02ec51ef Binary files /dev/null and b/core/res/drawable-hdpi/ic_action_rss_dark.png differ diff --git a/core/res/drawable-hdpi/ic_action_rss_light.png b/core/res/drawable-hdpi/ic_action_rss_light.png new file mode 100644 index 00000000..6aa10203 Binary files /dev/null and b/core/res/drawable-hdpi/ic_action_rss_light.png differ diff --git a/core/res/drawable-hdpi/ic_action_search_dark.png b/core/res/drawable-hdpi/ic_action_search_dark.png new file mode 100644 index 00000000..f12e005e Binary files /dev/null and b/core/res/drawable-hdpi/ic_action_search_dark.png differ diff --git a/core/res/drawable-hdpi/ic_action_search_light.png b/core/res/drawable-hdpi/ic_action_search_light.png new file mode 100644 index 00000000..e6b70451 Binary files /dev/null and b/core/res/drawable-hdpi/ic_action_search_light.png differ diff --git a/core/res/drawable-hdpi/ic_action_sort_by_size_dark.png b/core/res/drawable-hdpi/ic_action_sort_by_size_dark.png new file mode 100644 index 00000000..cbb5f451 Binary files /dev/null and b/core/res/drawable-hdpi/ic_action_sort_by_size_dark.png differ diff --git a/core/res/drawable-hdpi/ic_action_sort_by_size_light.png b/core/res/drawable-hdpi/ic_action_sort_by_size_light.png new file mode 100644 index 00000000..3b34aaf8 Binary files /dev/null and b/core/res/drawable-hdpi/ic_action_sort_by_size_light.png differ diff --git a/core/res/drawable-hdpi/ic_action_start_dark.png b/core/res/drawable-hdpi/ic_action_start_dark.png new file mode 100644 index 00000000..df8a2ca2 Binary files /dev/null and b/core/res/drawable-hdpi/ic_action_start_dark.png differ diff --git a/core/res/drawable-hdpi/ic_action_start_light.png b/core/res/drawable-hdpi/ic_action_start_light.png new file mode 100644 index 00000000..e70f0413 Binary files /dev/null and b/core/res/drawable-hdpi/ic_action_start_light.png differ diff --git a/core/res/drawable-hdpi/ic_action_stop_dark.png b/core/res/drawable-hdpi/ic_action_stop_dark.png new file mode 100644 index 00000000..dd5d6a1c Binary files /dev/null and b/core/res/drawable-hdpi/ic_action_stop_dark.png differ diff --git a/core/res/drawable-hdpi/ic_action_stop_light.png b/core/res/drawable-hdpi/ic_action_stop_light.png new file mode 100644 index 00000000..9c2f96da Binary files /dev/null and b/core/res/drawable-hdpi/ic_action_stop_light.png differ diff --git a/core/res/drawable-hdpi/ic_action_trackers_dark.png b/core/res/drawable-hdpi/ic_action_trackers_dark.png new file mode 100644 index 00000000..97231b91 Binary files /dev/null and b/core/res/drawable-hdpi/ic_action_trackers_dark.png differ diff --git a/core/res/drawable-hdpi/ic_action_trackers_light.png b/core/res/drawable-hdpi/ic_action_trackers_light.png new file mode 100644 index 00000000..89fa0cd0 Binary files /dev/null and b/core/res/drawable-hdpi/ic_action_trackers_light.png differ diff --git a/core/res/drawable-hdpi/ic_action_turtle_disabled_light.png b/core/res/drawable-hdpi/ic_action_turtle_disabled_light.png new file mode 100644 index 00000000..a99cd104 Binary files /dev/null and b/core/res/drawable-hdpi/ic_action_turtle_disabled_light.png differ diff --git a/core/res/drawable-hdpi/ic_action_turtle_enabled.png b/core/res/drawable-hdpi/ic_action_turtle_enabled.png new file mode 100644 index 00000000..1532eb39 Binary files /dev/null and b/core/res/drawable-hdpi/ic_action_turtle_enabled.png differ diff --git a/core/res/drawable-hdpi/ic_action_website_dark.png b/core/res/drawable-hdpi/ic_action_website_dark.png new file mode 100644 index 00000000..e154afdb Binary files /dev/null and b/core/res/drawable-hdpi/ic_action_website_dark.png differ diff --git a/core/res/drawable-hdpi/ic_action_website_light.png b/core/res/drawable-hdpi/ic_action_website_light.png new file mode 100644 index 00000000..6a2bc885 Binary files /dev/null and b/core/res/drawable-hdpi/ic_action_website_light.png differ diff --git a/core/res/drawable-hdpi/ic_activity_torrents.png b/core/res/drawable-hdpi/ic_activity_torrents.png new file mode 100644 index 00000000..63728265 Binary files /dev/null and b/core/res/drawable-hdpi/ic_activity_torrents.png differ diff --git a/core/res/drawable-hdpi/ic_empty_details_dark.png b/core/res/drawable-hdpi/ic_empty_details_dark.png new file mode 100644 index 00000000..718615e5 Binary files /dev/null and b/core/res/drawable-hdpi/ic_empty_details_dark.png differ diff --git a/core/res/drawable-hdpi/ic_empty_details_light.png b/core/res/drawable-hdpi/ic_empty_details_light.png new file mode 100644 index 00000000..6a1b5aa9 Binary files /dev/null and b/core/res/drawable-hdpi/ic_empty_details_light.png differ diff --git a/core/res/drawable-hdpi/ic_launcher.png b/core/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 00000000..d6181101 Binary files /dev/null and b/core/res/drawable-hdpi/ic_launcher.png differ diff --git a/core/res/drawable-hdpi/list_focused_transdroid.9.png b/core/res/drawable-hdpi/list_focused_transdroid.9.png new file mode 100644 index 00000000..b342ccbb Binary files /dev/null and b/core/res/drawable-hdpi/list_focused_transdroid.9.png differ diff --git a/core/res/drawable-hdpi/list_focused_transdroid2.9.png b/core/res/drawable-hdpi/list_focused_transdroid2.9.png new file mode 100644 index 00000000..411da949 Binary files /dev/null and b/core/res/drawable-hdpi/list_focused_transdroid2.9.png differ diff --git a/core/res/drawable-hdpi/menu_dropdown_panel_transdroid.9.png b/core/res/drawable-hdpi/menu_dropdown_panel_transdroid.9.png new file mode 100644 index 00000000..bab5e3ba Binary files /dev/null and b/core/res/drawable-hdpi/menu_dropdown_panel_transdroid.9.png differ diff --git a/core/res/drawable-hdpi/menu_dropdown_panel_transdroid2.9.png b/core/res/drawable-hdpi/menu_dropdown_panel_transdroid2.9.png new file mode 100644 index 00000000..a1148598 Binary files /dev/null and b/core/res/drawable-hdpi/menu_dropdown_panel_transdroid2.9.png differ diff --git a/core/res/drawable-hdpi/menu_hardkey_panel_transdroid.9.png b/core/res/drawable-hdpi/menu_hardkey_panel_transdroid.9.png new file mode 100644 index 00000000..40a8f3eb Binary files /dev/null and b/core/res/drawable-hdpi/menu_hardkey_panel_transdroid.9.png differ diff --git a/core/res/drawable-hdpi/progress_bg_transdroid.9.png b/core/res/drawable-hdpi/progress_bg_transdroid.9.png new file mode 100644 index 00000000..3d5c707d Binary files /dev/null and b/core/res/drawable-hdpi/progress_bg_transdroid.9.png differ diff --git a/core/res/drawable-hdpi/progress_bg_transdroid2.9.png b/core/res/drawable-hdpi/progress_bg_transdroid2.9.png new file mode 100644 index 00000000..3b183e07 Binary files /dev/null and b/core/res/drawable-hdpi/progress_bg_transdroid2.9.png differ diff --git a/core/res/drawable-hdpi/progress_primary_transdroid.9.png b/core/res/drawable-hdpi/progress_primary_transdroid.9.png new file mode 100644 index 00000000..44a3c436 Binary files /dev/null and b/core/res/drawable-hdpi/progress_primary_transdroid.9.png differ diff --git a/core/res/drawable-hdpi/progress_primary_transdroid2.9.png b/core/res/drawable-hdpi/progress_primary_transdroid2.9.png new file mode 100644 index 00000000..8a6b5208 Binary files /dev/null and b/core/res/drawable-hdpi/progress_primary_transdroid2.9.png differ diff --git a/core/res/drawable-hdpi/progress_secondary_transdroid.9.png b/core/res/drawable-hdpi/progress_secondary_transdroid.9.png new file mode 100644 index 00000000..32196b2b Binary files /dev/null and b/core/res/drawable-hdpi/progress_secondary_transdroid.9.png differ diff --git a/core/res/drawable-hdpi/progress_secondary_transdroid2.9.png b/core/res/drawable-hdpi/progress_secondary_transdroid2.9.png new file mode 100644 index 00000000..34d2c6ea Binary files /dev/null and b/core/res/drawable-hdpi/progress_secondary_transdroid2.9.png differ diff --git a/core/res/drawable-hdpi/spinner_ab_default_transdroid.9.png b/core/res/drawable-hdpi/spinner_ab_default_transdroid.9.png new file mode 100644 index 00000000..4fd4aeba Binary files /dev/null and b/core/res/drawable-hdpi/spinner_ab_default_transdroid.9.png differ diff --git a/core/res/drawable-hdpi/spinner_ab_default_transdroid2.9.png b/core/res/drawable-hdpi/spinner_ab_default_transdroid2.9.png new file mode 100644 index 00000000..e518eb7d Binary files /dev/null and b/core/res/drawable-hdpi/spinner_ab_default_transdroid2.9.png differ diff --git a/core/res/drawable-hdpi/spinner_ab_disabled_transdroid.9.png b/core/res/drawable-hdpi/spinner_ab_disabled_transdroid.9.png new file mode 100644 index 00000000..d42c97b8 Binary files /dev/null and b/core/res/drawable-hdpi/spinner_ab_disabled_transdroid.9.png differ diff --git a/core/res/drawable-hdpi/spinner_ab_disabled_transdroid2.9.png b/core/res/drawable-hdpi/spinner_ab_disabled_transdroid2.9.png new file mode 100644 index 00000000..b6febf96 Binary files /dev/null and b/core/res/drawable-hdpi/spinner_ab_disabled_transdroid2.9.png differ diff --git a/core/res/drawable-hdpi/spinner_ab_focused_transdroid.9.png b/core/res/drawable-hdpi/spinner_ab_focused_transdroid.9.png new file mode 100644 index 00000000..4761bd26 Binary files /dev/null and b/core/res/drawable-hdpi/spinner_ab_focused_transdroid.9.png differ diff --git a/core/res/drawable-hdpi/spinner_ab_focused_transdroid2.9.png b/core/res/drawable-hdpi/spinner_ab_focused_transdroid2.9.png new file mode 100644 index 00000000..544ae6ae Binary files /dev/null and b/core/res/drawable-hdpi/spinner_ab_focused_transdroid2.9.png differ diff --git a/core/res/drawable-hdpi/spinner_ab_pressed_transdroid.9.png b/core/res/drawable-hdpi/spinner_ab_pressed_transdroid.9.png new file mode 100644 index 00000000..bddc913e Binary files /dev/null and b/core/res/drawable-hdpi/spinner_ab_pressed_transdroid.9.png differ diff --git a/core/res/drawable-hdpi/spinner_ab_pressed_transdroid2.9.png b/core/res/drawable-hdpi/spinner_ab_pressed_transdroid2.9.png new file mode 100644 index 00000000..7d207c52 Binary files /dev/null and b/core/res/drawable-hdpi/spinner_ab_pressed_transdroid2.9.png differ diff --git a/core/res/drawable-hdpi/tab_selected_focused_transdroid.9.png b/core/res/drawable-hdpi/tab_selected_focused_transdroid.9.png new file mode 100644 index 00000000..36fec49a Binary files /dev/null and b/core/res/drawable-hdpi/tab_selected_focused_transdroid.9.png differ diff --git a/core/res/drawable-hdpi/tab_selected_focused_transdroid2.9.png b/core/res/drawable-hdpi/tab_selected_focused_transdroid2.9.png new file mode 100644 index 00000000..15e12068 Binary files /dev/null and b/core/res/drawable-hdpi/tab_selected_focused_transdroid2.9.png differ diff --git a/core/res/drawable-hdpi/tab_selected_pressed_transdroid.9.png b/core/res/drawable-hdpi/tab_selected_pressed_transdroid.9.png new file mode 100644 index 00000000..aa07ad6f Binary files /dev/null and b/core/res/drawable-hdpi/tab_selected_pressed_transdroid.9.png differ diff --git a/core/res/drawable-hdpi/tab_selected_pressed_transdroid2.9.png b/core/res/drawable-hdpi/tab_selected_pressed_transdroid2.9.png new file mode 100644 index 00000000..cca1aeb2 Binary files /dev/null and b/core/res/drawable-hdpi/tab_selected_pressed_transdroid2.9.png differ diff --git a/core/res/drawable-hdpi/tab_selected_transdroid.9.png b/core/res/drawable-hdpi/tab_selected_transdroid.9.png new file mode 100644 index 00000000..d59bde58 Binary files /dev/null and b/core/res/drawable-hdpi/tab_selected_transdroid.9.png differ diff --git a/core/res/drawable-hdpi/tab_selected_transdroid2.9.png b/core/res/drawable-hdpi/tab_selected_transdroid2.9.png new file mode 100644 index 00000000..ca828066 Binary files /dev/null and b/core/res/drawable-hdpi/tab_selected_transdroid2.9.png differ diff --git a/core/res/drawable-hdpi/tab_unselected_focused_transdroid.9.png b/core/res/drawable-hdpi/tab_unselected_focused_transdroid.9.png new file mode 100644 index 00000000..40613b47 Binary files /dev/null and b/core/res/drawable-hdpi/tab_unselected_focused_transdroid.9.png differ diff --git a/core/res/drawable-hdpi/tab_unselected_focused_transdroid2.9.png b/core/res/drawable-hdpi/tab_unselected_focused_transdroid2.9.png new file mode 100644 index 00000000..d8ed7709 Binary files /dev/null and b/core/res/drawable-hdpi/tab_unselected_focused_transdroid2.9.png differ diff --git a/core/res/drawable-hdpi/tab_unselected_pressed_transdroid.9.png b/core/res/drawable-hdpi/tab_unselected_pressed_transdroid.9.png new file mode 100644 index 00000000..ef83039c Binary files /dev/null and b/core/res/drawable-hdpi/tab_unselected_pressed_transdroid.9.png differ diff --git a/core/res/drawable-hdpi/tab_unselected_pressed_transdroid2.9.png b/core/res/drawable-hdpi/tab_unselected_pressed_transdroid2.9.png new file mode 100644 index 00000000..bd9026bc Binary files /dev/null and b/core/res/drawable-hdpi/tab_unselected_pressed_transdroid2.9.png differ diff --git a/core/res/drawable-hdpi/tab_unselected_transdroid2.9.png b/core/res/drawable-hdpi/tab_unselected_transdroid2.9.png new file mode 100644 index 00000000..c442c393 Binary files /dev/null and b/core/res/drawable-hdpi/tab_unselected_transdroid2.9.png differ diff --git a/core/res/drawable-mdpi/ab_bottom_solid_transdroid.9.png b/core/res/drawable-mdpi/ab_bottom_solid_transdroid.9.png new file mode 100644 index 00000000..135904dc Binary files /dev/null and b/core/res/drawable-mdpi/ab_bottom_solid_transdroid.9.png differ diff --git a/core/res/drawable-mdpi/ab_bottom_solid_transdroid2.9.png b/core/res/drawable-mdpi/ab_bottom_solid_transdroid2.9.png new file mode 100644 index 00000000..77ef47fe Binary files /dev/null and b/core/res/drawable-mdpi/ab_bottom_solid_transdroid2.9.png differ diff --git a/core/res/drawable-mdpi/ab_solid_transdroid.9.png b/core/res/drawable-mdpi/ab_solid_transdroid.9.png new file mode 100644 index 00000000..fa3d4284 Binary files /dev/null and b/core/res/drawable-mdpi/ab_solid_transdroid.9.png differ diff --git a/core/res/drawable-mdpi/ab_solid_transdroid2.9.png b/core/res/drawable-mdpi/ab_solid_transdroid2.9.png new file mode 100644 index 00000000..9b1e02aa Binary files /dev/null and b/core/res/drawable-mdpi/ab_solid_transdroid2.9.png differ diff --git a/core/res/drawable-mdpi/ab_stacked_solid_transdroid.9.png b/core/res/drawable-mdpi/ab_stacked_solid_transdroid.9.png new file mode 100644 index 00000000..fd09cb18 Binary files /dev/null and b/core/res/drawable-mdpi/ab_stacked_solid_transdroid.9.png differ diff --git a/core/res/drawable-mdpi/ab_stacked_solid_transdroid2.9.png b/core/res/drawable-mdpi/ab_stacked_solid_transdroid2.9.png new file mode 100644 index 00000000..3c78f52c Binary files /dev/null and b/core/res/drawable-mdpi/ab_stacked_solid_transdroid2.9.png differ diff --git a/core/res/drawable-mdpi/ab_texture_tile_transdroid2.png b/core/res/drawable-mdpi/ab_texture_tile_transdroid2.png new file mode 100644 index 00000000..87cc8211 Binary files /dev/null and b/core/res/drawable-mdpi/ab_texture_tile_transdroid2.png differ diff --git a/core/res/drawable-mdpi/ab_transparent_transdroid.9.png b/core/res/drawable-mdpi/ab_transparent_transdroid.9.png new file mode 100644 index 00000000..8c3b514f Binary files /dev/null and b/core/res/drawable-mdpi/ab_transparent_transdroid.9.png differ diff --git a/core/res/drawable-mdpi/ab_transparent_transdroid2.9.png b/core/res/drawable-mdpi/ab_transparent_transdroid2.9.png new file mode 100644 index 00000000..f24375ca Binary files /dev/null and b/core/res/drawable-mdpi/ab_transparent_transdroid2.9.png differ diff --git a/core/res/drawable-mdpi/btn_cab_done_default_transdroid2.9.png b/core/res/drawable-mdpi/btn_cab_done_default_transdroid2.9.png new file mode 100644 index 00000000..da14cdbc Binary files /dev/null and b/core/res/drawable-mdpi/btn_cab_done_default_transdroid2.9.png differ diff --git a/core/res/drawable-mdpi/btn_cab_done_focused_transdroid2.9.png b/core/res/drawable-mdpi/btn_cab_done_focused_transdroid2.9.png new file mode 100644 index 00000000..89b06832 Binary files /dev/null and b/core/res/drawable-mdpi/btn_cab_done_focused_transdroid2.9.png differ diff --git a/core/res/drawable-mdpi/btn_cab_done_pressed_transdroid2.9.png b/core/res/drawable-mdpi/btn_cab_done_pressed_transdroid2.9.png new file mode 100644 index 00000000..767cf60e Binary files /dev/null and b/core/res/drawable-mdpi/btn_cab_done_pressed_transdroid2.9.png differ diff --git a/core/res/drawable-mdpi/cab_background_bottom_transdroid2.9.png b/core/res/drawable-mdpi/cab_background_bottom_transdroid2.9.png new file mode 100644 index 00000000..df024689 Binary files /dev/null and b/core/res/drawable-mdpi/cab_background_bottom_transdroid2.9.png differ diff --git a/core/res/drawable-mdpi/cab_background_top_transdroid2.9.png b/core/res/drawable-mdpi/cab_background_top_transdroid2.9.png new file mode 100644 index 00000000..02b6f3b6 Binary files /dev/null and b/core/res/drawable-mdpi/cab_background_top_transdroid2.9.png differ diff --git a/core/res/drawable-mdpi/ic_action_discard.png b/core/res/drawable-mdpi/ic_action_discard.png new file mode 100644 index 00000000..a8ee5f25 Binary files /dev/null and b/core/res/drawable-mdpi/ic_action_discard.png differ diff --git a/core/res/drawable-mdpi/ic_action_discard_light.png b/core/res/drawable-mdpi/ic_action_discard_light.png new file mode 100644 index 00000000..cedb1085 Binary files /dev/null and b/core/res/drawable-mdpi/ic_action_discard_light.png differ diff --git a/core/res/drawable-mdpi/ic_action_labels_dark.png b/core/res/drawable-mdpi/ic_action_labels_dark.png new file mode 100644 index 00000000..b85d7c58 Binary files /dev/null and b/core/res/drawable-mdpi/ic_action_labels_dark.png differ diff --git a/core/res/drawable-mdpi/ic_action_labels_light.png b/core/res/drawable-mdpi/ic_action_labels_light.png new file mode 100644 index 00000000..8567d5e4 Binary files /dev/null and b/core/res/drawable-mdpi/ic_action_labels_light.png differ diff --git a/core/res/drawable-mdpi/ic_action_new_dark.png b/core/res/drawable-mdpi/ic_action_new_dark.png new file mode 100644 index 00000000..4d5d484b Binary files /dev/null and b/core/res/drawable-mdpi/ic_action_new_dark.png differ diff --git a/core/res/drawable-mdpi/ic_action_new_light.png b/core/res/drawable-mdpi/ic_action_new_light.png new file mode 100644 index 00000000..884c9d27 Binary files /dev/null and b/core/res/drawable-mdpi/ic_action_new_light.png differ diff --git a/core/res/drawable-mdpi/ic_action_pause_dark.png b/core/res/drawable-mdpi/ic_action_pause_dark.png new file mode 100644 index 00000000..a5aee6f2 Binary files /dev/null and b/core/res/drawable-mdpi/ic_action_pause_dark.png differ diff --git a/core/res/drawable-mdpi/ic_action_pause_light.png b/core/res/drawable-mdpi/ic_action_pause_light.png new file mode 100644 index 00000000..01858e34 Binary files /dev/null and b/core/res/drawable-mdpi/ic_action_pause_light.png differ diff --git a/core/res/drawable-mdpi/ic_action_priority_high_dark.png b/core/res/drawable-mdpi/ic_action_priority_high_dark.png new file mode 100644 index 00000000..9cfeeebc Binary files /dev/null and b/core/res/drawable-mdpi/ic_action_priority_high_dark.png differ diff --git a/core/res/drawable-mdpi/ic_action_priority_high_light.png b/core/res/drawable-mdpi/ic_action_priority_high_light.png new file mode 100644 index 00000000..bef007c6 Binary files /dev/null and b/core/res/drawable-mdpi/ic_action_priority_high_light.png differ diff --git a/core/res/drawable-mdpi/ic_action_priority_low_dark.png b/core/res/drawable-mdpi/ic_action_priority_low_dark.png new file mode 100644 index 00000000..89927f63 Binary files /dev/null and b/core/res/drawable-mdpi/ic_action_priority_low_dark.png differ diff --git a/core/res/drawable-mdpi/ic_action_priority_low_light.png b/core/res/drawable-mdpi/ic_action_priority_low_light.png new file mode 100644 index 00000000..44e768fa Binary files /dev/null and b/core/res/drawable-mdpi/ic_action_priority_low_light.png differ diff --git a/core/res/drawable-mdpi/ic_action_priority_normal_dark.png b/core/res/drawable-mdpi/ic_action_priority_normal_dark.png new file mode 100644 index 00000000..7dd2c7bd Binary files /dev/null and b/core/res/drawable-mdpi/ic_action_priority_normal_dark.png differ diff --git a/core/res/drawable-mdpi/ic_action_priority_normal_light.png b/core/res/drawable-mdpi/ic_action_priority_normal_light.png new file mode 100644 index 00000000..4a471f36 Binary files /dev/null and b/core/res/drawable-mdpi/ic_action_priority_normal_light.png differ diff --git a/core/res/drawable-mdpi/ic_action_priority_off_dark.png b/core/res/drawable-mdpi/ic_action_priority_off_dark.png new file mode 100644 index 00000000..8d9c72d5 Binary files /dev/null and b/core/res/drawable-mdpi/ic_action_priority_off_dark.png differ diff --git a/core/res/drawable-mdpi/ic_action_priority_off_light.png b/core/res/drawable-mdpi/ic_action_priority_off_light.png new file mode 100644 index 00000000..d7766bc2 Binary files /dev/null and b/core/res/drawable-mdpi/ic_action_priority_off_light.png differ diff --git a/core/res/drawable-mdpi/ic_action_refresh_dark.png b/core/res/drawable-mdpi/ic_action_refresh_dark.png new file mode 100644 index 00000000..bd611e8e Binary files /dev/null and b/core/res/drawable-mdpi/ic_action_refresh_dark.png differ diff --git a/core/res/drawable-mdpi/ic_action_refresh_light.png b/core/res/drawable-mdpi/ic_action_refresh_light.png new file mode 100644 index 00000000..63e70e17 Binary files /dev/null and b/core/res/drawable-mdpi/ic_action_refresh_light.png differ diff --git a/core/res/drawable-mdpi/ic_action_remove_dark.png b/core/res/drawable-mdpi/ic_action_remove_dark.png new file mode 100644 index 00000000..3336760d Binary files /dev/null and b/core/res/drawable-mdpi/ic_action_remove_dark.png differ diff --git a/core/res/drawable-mdpi/ic_action_remove_light.png b/core/res/drawable-mdpi/ic_action_remove_light.png new file mode 100644 index 00000000..9f4c3d6a Binary files /dev/null and b/core/res/drawable-mdpi/ic_action_remove_light.png differ diff --git a/core/res/drawable-mdpi/ic_action_resume_dark.png b/core/res/drawable-mdpi/ic_action_resume_dark.png new file mode 100644 index 00000000..28e81379 Binary files /dev/null and b/core/res/drawable-mdpi/ic_action_resume_dark.png differ diff --git a/core/res/drawable-mdpi/ic_action_resume_light.png b/core/res/drawable-mdpi/ic_action_resume_light.png new file mode 100644 index 00000000..937e0299 Binary files /dev/null and b/core/res/drawable-mdpi/ic_action_resume_light.png differ diff --git a/core/res/drawable-mdpi/ic_action_rss_dark.png b/core/res/drawable-mdpi/ic_action_rss_dark.png new file mode 100644 index 00000000..2de867b8 Binary files /dev/null and b/core/res/drawable-mdpi/ic_action_rss_dark.png differ diff --git a/core/res/drawable-mdpi/ic_action_rss_light.png b/core/res/drawable-mdpi/ic_action_rss_light.png new file mode 100644 index 00000000..2c5d0933 Binary files /dev/null and b/core/res/drawable-mdpi/ic_action_rss_light.png differ diff --git a/core/res/drawable-mdpi/ic_action_search_dark.png b/core/res/drawable-mdpi/ic_action_search_dark.png new file mode 100644 index 00000000..587d9e0b Binary files /dev/null and b/core/res/drawable-mdpi/ic_action_search_dark.png differ diff --git a/core/res/drawable-mdpi/ic_action_search_light.png b/core/res/drawable-mdpi/ic_action_search_light.png new file mode 100644 index 00000000..3aa64404 Binary files /dev/null and b/core/res/drawable-mdpi/ic_action_search_light.png differ diff --git a/core/res/drawable-mdpi/ic_action_sort_by_size_dark.png b/core/res/drawable-mdpi/ic_action_sort_by_size_dark.png new file mode 100644 index 00000000..aa921e76 Binary files /dev/null and b/core/res/drawable-mdpi/ic_action_sort_by_size_dark.png differ diff --git a/core/res/drawable-mdpi/ic_action_sort_by_size_light.png b/core/res/drawable-mdpi/ic_action_sort_by_size_light.png new file mode 100644 index 00000000..af004e5f Binary files /dev/null and b/core/res/drawable-mdpi/ic_action_sort_by_size_light.png differ diff --git a/core/res/drawable-mdpi/ic_action_start_dark.png b/core/res/drawable-mdpi/ic_action_start_dark.png new file mode 100644 index 00000000..6a40cd5f Binary files /dev/null and b/core/res/drawable-mdpi/ic_action_start_dark.png differ diff --git a/core/res/drawable-mdpi/ic_action_start_light.png b/core/res/drawable-mdpi/ic_action_start_light.png new file mode 100644 index 00000000..1e3bc97a Binary files /dev/null and b/core/res/drawable-mdpi/ic_action_start_light.png differ diff --git a/core/res/drawable-mdpi/ic_action_stop_dark.png b/core/res/drawable-mdpi/ic_action_stop_dark.png new file mode 100644 index 00000000..20df4158 Binary files /dev/null and b/core/res/drawable-mdpi/ic_action_stop_dark.png differ diff --git a/core/res/drawable-mdpi/ic_action_stop_light.png b/core/res/drawable-mdpi/ic_action_stop_light.png new file mode 100644 index 00000000..0c1a0a97 Binary files /dev/null and b/core/res/drawable-mdpi/ic_action_stop_light.png differ diff --git a/core/res/drawable-mdpi/ic_action_trackers_dark.png b/core/res/drawable-mdpi/ic_action_trackers_dark.png new file mode 100644 index 00000000..539bad7d Binary files /dev/null and b/core/res/drawable-mdpi/ic_action_trackers_dark.png differ diff --git a/core/res/drawable-mdpi/ic_action_trackers_light.png b/core/res/drawable-mdpi/ic_action_trackers_light.png new file mode 100644 index 00000000..f9c51464 Binary files /dev/null and b/core/res/drawable-mdpi/ic_action_trackers_light.png differ diff --git a/core/res/drawable-mdpi/ic_action_turtle_disabled.png b/core/res/drawable-mdpi/ic_action_turtle_disabled.png new file mode 100644 index 00000000..70155517 Binary files /dev/null and b/core/res/drawable-mdpi/ic_action_turtle_disabled.png differ diff --git a/core/res/drawable-mdpi/ic_action_turtle_enabled.png b/core/res/drawable-mdpi/ic_action_turtle_enabled.png new file mode 100644 index 00000000..fedbfc9b Binary files /dev/null and b/core/res/drawable-mdpi/ic_action_turtle_enabled.png differ diff --git a/core/res/drawable-mdpi/ic_action_website_dark.png b/core/res/drawable-mdpi/ic_action_website_dark.png new file mode 100644 index 00000000..41b56ec9 Binary files /dev/null and b/core/res/drawable-mdpi/ic_action_website_dark.png differ diff --git a/core/res/drawable-mdpi/ic_action_website_light.png b/core/res/drawable-mdpi/ic_action_website_light.png new file mode 100644 index 00000000..f146cf99 Binary files /dev/null and b/core/res/drawable-mdpi/ic_action_website_light.png differ diff --git a/core/res/drawable-mdpi/ic_activity_torrents.png b/core/res/drawable-mdpi/ic_activity_torrents.png new file mode 100644 index 00000000..ce606a71 Binary files /dev/null and b/core/res/drawable-mdpi/ic_activity_torrents.png differ diff --git a/core/res/drawable-mdpi/ic_empty_details_dark.png b/core/res/drawable-mdpi/ic_empty_details_dark.png new file mode 100644 index 00000000..5bd58335 Binary files /dev/null and b/core/res/drawable-mdpi/ic_empty_details_dark.png differ diff --git a/core/res/drawable-mdpi/ic_empty_details_light.png b/core/res/drawable-mdpi/ic_empty_details_light.png new file mode 100644 index 00000000..92ed5fa0 Binary files /dev/null and b/core/res/drawable-mdpi/ic_empty_details_light.png differ diff --git a/core/res/drawable-mdpi/ic_launcher.png b/core/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 00000000..c153fddf Binary files /dev/null and b/core/res/drawable-mdpi/ic_launcher.png differ diff --git a/core/res/drawable-mdpi/list_focused_transdroid.9.png b/core/res/drawable-mdpi/list_focused_transdroid.9.png new file mode 100644 index 00000000..cc6c75f9 Binary files /dev/null and b/core/res/drawable-mdpi/list_focused_transdroid.9.png differ diff --git a/core/res/drawable-mdpi/list_focused_transdroid2.9.png b/core/res/drawable-mdpi/list_focused_transdroid2.9.png new file mode 100644 index 00000000..469e1e0e Binary files /dev/null and b/core/res/drawable-mdpi/list_focused_transdroid2.9.png differ diff --git a/core/res/drawable-mdpi/menu_dropdown_panel_transdroid.9.png b/core/res/drawable-mdpi/menu_dropdown_panel_transdroid.9.png new file mode 100644 index 00000000..2b2458ef Binary files /dev/null and b/core/res/drawable-mdpi/menu_dropdown_panel_transdroid.9.png differ diff --git a/core/res/drawable-mdpi/menu_dropdown_panel_transdroid2.9.png b/core/res/drawable-mdpi/menu_dropdown_panel_transdroid2.9.png new file mode 100644 index 00000000..ea341b50 Binary files /dev/null and b/core/res/drawable-mdpi/menu_dropdown_panel_transdroid2.9.png differ diff --git a/core/res/drawable-mdpi/menu_hardkey_panel_transdroid.9.png b/core/res/drawable-mdpi/menu_hardkey_panel_transdroid.9.png new file mode 100644 index 00000000..19b2feb5 Binary files /dev/null and b/core/res/drawable-mdpi/menu_hardkey_panel_transdroid.9.png differ diff --git a/core/res/drawable-mdpi/progress_bg_transdroid.9.png b/core/res/drawable-mdpi/progress_bg_transdroid.9.png new file mode 100644 index 00000000..9372a60f Binary files /dev/null and b/core/res/drawable-mdpi/progress_bg_transdroid.9.png differ diff --git a/core/res/drawable-mdpi/progress_bg_transdroid2.9.png b/core/res/drawable-mdpi/progress_bg_transdroid2.9.png new file mode 100644 index 00000000..71753a4b Binary files /dev/null and b/core/res/drawable-mdpi/progress_bg_transdroid2.9.png differ diff --git a/core/res/drawable-mdpi/progress_primary_transdroid.9.png b/core/res/drawable-mdpi/progress_primary_transdroid.9.png new file mode 100644 index 00000000..27192ef1 Binary files /dev/null and b/core/res/drawable-mdpi/progress_primary_transdroid.9.png differ diff --git a/core/res/drawable-mdpi/progress_primary_transdroid2.9.png b/core/res/drawable-mdpi/progress_primary_transdroid2.9.png new file mode 100644 index 00000000..2bbbbb9a Binary files /dev/null and b/core/res/drawable-mdpi/progress_primary_transdroid2.9.png differ diff --git a/core/res/drawable-mdpi/progress_secondary_transdroid.9.png b/core/res/drawable-mdpi/progress_secondary_transdroid.9.png new file mode 100644 index 00000000..2b240eae Binary files /dev/null and b/core/res/drawable-mdpi/progress_secondary_transdroid.9.png differ diff --git a/core/res/drawable-mdpi/progress_secondary_transdroid2.9.png b/core/res/drawable-mdpi/progress_secondary_transdroid2.9.png new file mode 100644 index 00000000..9dd0f6cb Binary files /dev/null and b/core/res/drawable-mdpi/progress_secondary_transdroid2.9.png differ diff --git a/core/res/drawable-mdpi/spinner_ab_default_transdroid.9.png b/core/res/drawable-mdpi/spinner_ab_default_transdroid.9.png new file mode 100644 index 00000000..9aeafee2 Binary files /dev/null and b/core/res/drawable-mdpi/spinner_ab_default_transdroid.9.png differ diff --git a/core/res/drawable-mdpi/spinner_ab_default_transdroid2.9.png b/core/res/drawable-mdpi/spinner_ab_default_transdroid2.9.png new file mode 100644 index 00000000..5e1dd470 Binary files /dev/null and b/core/res/drawable-mdpi/spinner_ab_default_transdroid2.9.png differ diff --git a/core/res/drawable-mdpi/spinner_ab_disabled_transdroid.9.png b/core/res/drawable-mdpi/spinner_ab_disabled_transdroid.9.png new file mode 100644 index 00000000..88dd4415 Binary files /dev/null and b/core/res/drawable-mdpi/spinner_ab_disabled_transdroid.9.png differ diff --git a/core/res/drawable-mdpi/spinner_ab_disabled_transdroid2.9.png b/core/res/drawable-mdpi/spinner_ab_disabled_transdroid2.9.png new file mode 100644 index 00000000..38025ad3 Binary files /dev/null and b/core/res/drawable-mdpi/spinner_ab_disabled_transdroid2.9.png differ diff --git a/core/res/drawable-mdpi/spinner_ab_focused_transdroid.9.png b/core/res/drawable-mdpi/spinner_ab_focused_transdroid.9.png new file mode 100644 index 00000000..63a0fda5 Binary files /dev/null and b/core/res/drawable-mdpi/spinner_ab_focused_transdroid.9.png differ diff --git a/core/res/drawable-mdpi/spinner_ab_focused_transdroid2.9.png b/core/res/drawable-mdpi/spinner_ab_focused_transdroid2.9.png new file mode 100644 index 00000000..e8c8b0f7 Binary files /dev/null and b/core/res/drawable-mdpi/spinner_ab_focused_transdroid2.9.png differ diff --git a/core/res/drawable-mdpi/spinner_ab_pressed_transdroid.9.png b/core/res/drawable-mdpi/spinner_ab_pressed_transdroid.9.png new file mode 100644 index 00000000..15652b11 Binary files /dev/null and b/core/res/drawable-mdpi/spinner_ab_pressed_transdroid.9.png differ diff --git a/core/res/drawable-mdpi/spinner_ab_pressed_transdroid2.9.png b/core/res/drawable-mdpi/spinner_ab_pressed_transdroid2.9.png new file mode 100644 index 00000000..c9d4e8b6 Binary files /dev/null and b/core/res/drawable-mdpi/spinner_ab_pressed_transdroid2.9.png differ diff --git a/core/res/drawable-mdpi/tab_selected_focused_transdroid.9.png b/core/res/drawable-mdpi/tab_selected_focused_transdroid.9.png new file mode 100644 index 00000000..ac6ffee0 Binary files /dev/null and b/core/res/drawable-mdpi/tab_selected_focused_transdroid.9.png differ diff --git a/core/res/drawable-mdpi/tab_selected_focused_transdroid2.9.png b/core/res/drawable-mdpi/tab_selected_focused_transdroid2.9.png new file mode 100644 index 00000000..87a5b7c6 Binary files /dev/null and b/core/res/drawable-mdpi/tab_selected_focused_transdroid2.9.png differ diff --git a/core/res/drawable-mdpi/tab_selected_pressed_transdroid.9.png b/core/res/drawable-mdpi/tab_selected_pressed_transdroid.9.png new file mode 100644 index 00000000..690f41de Binary files /dev/null and b/core/res/drawable-mdpi/tab_selected_pressed_transdroid.9.png differ diff --git a/core/res/drawable-mdpi/tab_selected_pressed_transdroid2.9.png b/core/res/drawable-mdpi/tab_selected_pressed_transdroid2.9.png new file mode 100644 index 00000000..5cfe08f0 Binary files /dev/null and b/core/res/drawable-mdpi/tab_selected_pressed_transdroid2.9.png differ diff --git a/core/res/drawable-mdpi/tab_selected_transdroid.9.png b/core/res/drawable-mdpi/tab_selected_transdroid.9.png new file mode 100644 index 00000000..1aa38d48 Binary files /dev/null and b/core/res/drawable-mdpi/tab_selected_transdroid.9.png differ diff --git a/core/res/drawable-mdpi/tab_selected_transdroid2.9.png b/core/res/drawable-mdpi/tab_selected_transdroid2.9.png new file mode 100644 index 00000000..79e55713 Binary files /dev/null and b/core/res/drawable-mdpi/tab_selected_transdroid2.9.png differ diff --git a/core/res/drawable-mdpi/tab_unselected_focused_transdroid.9.png b/core/res/drawable-mdpi/tab_unselected_focused_transdroid.9.png new file mode 100644 index 00000000..f9c9c6bd Binary files /dev/null and b/core/res/drawable-mdpi/tab_unselected_focused_transdroid.9.png differ diff --git a/core/res/drawable-mdpi/tab_unselected_focused_transdroid2.9.png b/core/res/drawable-mdpi/tab_unselected_focused_transdroid2.9.png new file mode 100644 index 00000000..1a55fa4b Binary files /dev/null and b/core/res/drawable-mdpi/tab_unselected_focused_transdroid2.9.png differ diff --git a/core/res/drawable-mdpi/tab_unselected_pressed_transdroid.9.png b/core/res/drawable-mdpi/tab_unselected_pressed_transdroid.9.png new file mode 100644 index 00000000..2acd0bbe Binary files /dev/null and b/core/res/drawable-mdpi/tab_unselected_pressed_transdroid.9.png differ diff --git a/core/res/drawable-mdpi/tab_unselected_pressed_transdroid2.9.png b/core/res/drawable-mdpi/tab_unselected_pressed_transdroid2.9.png new file mode 100644 index 00000000..57a75c8c Binary files /dev/null and b/core/res/drawable-mdpi/tab_unselected_pressed_transdroid2.9.png differ diff --git a/core/res/drawable-mdpi/tab_unselected_transdroid2.9.png b/core/res/drawable-mdpi/tab_unselected_transdroid2.9.png new file mode 100644 index 00000000..7f723870 Binary files /dev/null and b/core/res/drawable-mdpi/tab_unselected_transdroid2.9.png differ diff --git a/core/res/drawable-xhdpi/ab_bottom_solid_transdroid.9.png b/core/res/drawable-xhdpi/ab_bottom_solid_transdroid.9.png new file mode 100644 index 00000000..abf5694b Binary files /dev/null and b/core/res/drawable-xhdpi/ab_bottom_solid_transdroid.9.png differ diff --git a/core/res/drawable-xhdpi/ab_bottom_solid_transdroid2.9.png b/core/res/drawable-xhdpi/ab_bottom_solid_transdroid2.9.png new file mode 100644 index 00000000..b1a11071 Binary files /dev/null and b/core/res/drawable-xhdpi/ab_bottom_solid_transdroid2.9.png differ diff --git a/core/res/drawable-xhdpi/ab_solid_transdroid.9.png b/core/res/drawable-xhdpi/ab_solid_transdroid.9.png new file mode 100644 index 00000000..143a16f4 Binary files /dev/null and b/core/res/drawable-xhdpi/ab_solid_transdroid.9.png differ diff --git a/core/res/drawable-xhdpi/ab_solid_transdroid2.9.png b/core/res/drawable-xhdpi/ab_solid_transdroid2.9.png new file mode 100644 index 00000000..550ceee8 Binary files /dev/null and b/core/res/drawable-xhdpi/ab_solid_transdroid2.9.png differ diff --git a/core/res/drawable-xhdpi/ab_stacked_solid_transdroid.9.png b/core/res/drawable-xhdpi/ab_stacked_solid_transdroid.9.png new file mode 100644 index 00000000..f9a1e1f2 Binary files /dev/null and b/core/res/drawable-xhdpi/ab_stacked_solid_transdroid.9.png differ diff --git a/core/res/drawable-xhdpi/ab_stacked_solid_transdroid2.9.png b/core/res/drawable-xhdpi/ab_stacked_solid_transdroid2.9.png new file mode 100644 index 00000000..61130d91 Binary files /dev/null and b/core/res/drawable-xhdpi/ab_stacked_solid_transdroid2.9.png differ diff --git a/core/res/drawable-xhdpi/ab_texture_tile_transdroid2.png b/core/res/drawable-xhdpi/ab_texture_tile_transdroid2.png new file mode 100644 index 00000000..4cd50792 Binary files /dev/null and b/core/res/drawable-xhdpi/ab_texture_tile_transdroid2.png differ diff --git a/core/res/drawable-xhdpi/ab_transparent_transdroid.9.png b/core/res/drawable-xhdpi/ab_transparent_transdroid.9.png new file mode 100644 index 00000000..24020e23 Binary files /dev/null and b/core/res/drawable-xhdpi/ab_transparent_transdroid.9.png differ diff --git a/core/res/drawable-xhdpi/ab_transparent_transdroid2.9.png b/core/res/drawable-xhdpi/ab_transparent_transdroid2.9.png new file mode 100644 index 00000000..78739156 Binary files /dev/null and b/core/res/drawable-xhdpi/ab_transparent_transdroid2.9.png differ diff --git a/core/res/drawable-xhdpi/btn_cab_done_default_transdroid2.9.png b/core/res/drawable-xhdpi/btn_cab_done_default_transdroid2.9.png new file mode 100644 index 00000000..5d9632ed Binary files /dev/null and b/core/res/drawable-xhdpi/btn_cab_done_default_transdroid2.9.png differ diff --git a/core/res/drawable-xhdpi/btn_cab_done_focused_transdroid2.9.png b/core/res/drawable-xhdpi/btn_cab_done_focused_transdroid2.9.png new file mode 100644 index 00000000..57e1c92e Binary files /dev/null and b/core/res/drawable-xhdpi/btn_cab_done_focused_transdroid2.9.png differ diff --git a/core/res/drawable-xhdpi/btn_cab_done_pressed_transdroid2.9.png b/core/res/drawable-xhdpi/btn_cab_done_pressed_transdroid2.9.png new file mode 100644 index 00000000..56b13fa9 Binary files /dev/null and b/core/res/drawable-xhdpi/btn_cab_done_pressed_transdroid2.9.png differ diff --git a/core/res/drawable-xhdpi/cab_background_bottom_transdroid2.9.png b/core/res/drawable-xhdpi/cab_background_bottom_transdroid2.9.png new file mode 100644 index 00000000..9272cc29 Binary files /dev/null and b/core/res/drawable-xhdpi/cab_background_bottom_transdroid2.9.png differ diff --git a/core/res/drawable-xhdpi/cab_background_top_transdroid2.9.png b/core/res/drawable-xhdpi/cab_background_top_transdroid2.9.png new file mode 100644 index 00000000..ca1b570b Binary files /dev/null and b/core/res/drawable-xhdpi/cab_background_top_transdroid2.9.png differ diff --git a/core/res/drawable-xhdpi/ic_action_discard_dark.png b/core/res/drawable-xhdpi/ic_action_discard_dark.png new file mode 100644 index 00000000..412b3335 Binary files /dev/null and b/core/res/drawable-xhdpi/ic_action_discard_dark.png differ diff --git a/core/res/drawable-xhdpi/ic_action_discard_light.png b/core/res/drawable-xhdpi/ic_action_discard_light.png new file mode 100644 index 00000000..98c73da1 Binary files /dev/null and b/core/res/drawable-xhdpi/ic_action_discard_light.png differ diff --git a/core/res/drawable-xhdpi/ic_action_labels_dark.png b/core/res/drawable-xhdpi/ic_action_labels_dark.png new file mode 100644 index 00000000..8fdcd1a2 Binary files /dev/null and b/core/res/drawable-xhdpi/ic_action_labels_dark.png differ diff --git a/core/res/drawable-xhdpi/ic_action_labels_light.png b/core/res/drawable-xhdpi/ic_action_labels_light.png new file mode 100644 index 00000000..c1ec9727 Binary files /dev/null and b/core/res/drawable-xhdpi/ic_action_labels_light.png differ diff --git a/core/res/drawable-xhdpi/ic_action_new_dark.png b/core/res/drawable-xhdpi/ic_action_new_dark.png new file mode 100644 index 00000000..23b9a1c1 Binary files /dev/null and b/core/res/drawable-xhdpi/ic_action_new_dark.png differ diff --git a/core/res/drawable-xhdpi/ic_action_new_light.png b/core/res/drawable-xhdpi/ic_action_new_light.png new file mode 100644 index 00000000..9b48a63d Binary files /dev/null and b/core/res/drawable-xhdpi/ic_action_new_light.png differ diff --git a/core/res/drawable-xhdpi/ic_action_pause_dark.png b/core/res/drawable-xhdpi/ic_action_pause_dark.png new file mode 100644 index 00000000..333c1b24 Binary files /dev/null and b/core/res/drawable-xhdpi/ic_action_pause_dark.png differ diff --git a/core/res/drawable-xhdpi/ic_action_pause_light.png b/core/res/drawable-xhdpi/ic_action_pause_light.png new file mode 100644 index 00000000..97d6f91a Binary files /dev/null and b/core/res/drawable-xhdpi/ic_action_pause_light.png differ diff --git a/core/res/drawable-xhdpi/ic_action_priority_high_dark.png b/core/res/drawable-xhdpi/ic_action_priority_high_dark.png new file mode 100644 index 00000000..5932124c Binary files /dev/null and b/core/res/drawable-xhdpi/ic_action_priority_high_dark.png differ diff --git a/core/res/drawable-xhdpi/ic_action_priority_high_light.png b/core/res/drawable-xhdpi/ic_action_priority_high_light.png new file mode 100644 index 00000000..c20807d6 Binary files /dev/null and b/core/res/drawable-xhdpi/ic_action_priority_high_light.png differ diff --git a/core/res/drawable-xhdpi/ic_action_priority_low_dark.png b/core/res/drawable-xhdpi/ic_action_priority_low_dark.png new file mode 100644 index 00000000..d0b6ba5f Binary files /dev/null and b/core/res/drawable-xhdpi/ic_action_priority_low_dark.png differ diff --git a/core/res/drawable-xhdpi/ic_action_priority_low_light.png b/core/res/drawable-xhdpi/ic_action_priority_low_light.png new file mode 100644 index 00000000..abf14070 Binary files /dev/null and b/core/res/drawable-xhdpi/ic_action_priority_low_light.png differ diff --git a/core/res/drawable-xhdpi/ic_action_priority_normal_dark.png b/core/res/drawable-xhdpi/ic_action_priority_normal_dark.png new file mode 100644 index 00000000..8c1f81af Binary files /dev/null and b/core/res/drawable-xhdpi/ic_action_priority_normal_dark.png differ diff --git a/core/res/drawable-xhdpi/ic_action_priority_normal_light.png b/core/res/drawable-xhdpi/ic_action_priority_normal_light.png new file mode 100644 index 00000000..9405ed21 Binary files /dev/null and b/core/res/drawable-xhdpi/ic_action_priority_normal_light.png differ diff --git a/core/res/drawable-xhdpi/ic_action_priority_off_dark.png b/core/res/drawable-xhdpi/ic_action_priority_off_dark.png new file mode 100644 index 00000000..f6b9ff22 Binary files /dev/null and b/core/res/drawable-xhdpi/ic_action_priority_off_dark.png differ diff --git a/core/res/drawable-xhdpi/ic_action_priority_off_light.png b/core/res/drawable-xhdpi/ic_action_priority_off_light.png new file mode 100644 index 00000000..4424a0c3 Binary files /dev/null and b/core/res/drawable-xhdpi/ic_action_priority_off_light.png differ diff --git a/core/res/drawable-xhdpi/ic_action_refresh_dark.png b/core/res/drawable-xhdpi/ic_action_refresh_dark.png new file mode 100644 index 00000000..a7fdc0df Binary files /dev/null and b/core/res/drawable-xhdpi/ic_action_refresh_dark.png differ diff --git a/core/res/drawable-xhdpi/ic_action_refresh_light.png b/core/res/drawable-xhdpi/ic_action_refresh_light.png new file mode 100644 index 00000000..e6212cf6 Binary files /dev/null and b/core/res/drawable-xhdpi/ic_action_refresh_light.png differ diff --git a/core/res/drawable-xhdpi/ic_action_remove_dark.png b/core/res/drawable-xhdpi/ic_action_remove_dark.png new file mode 100644 index 00000000..f391760e Binary files /dev/null and b/core/res/drawable-xhdpi/ic_action_remove_dark.png differ diff --git a/core/res/drawable-xhdpi/ic_action_remove_light.png b/core/res/drawable-xhdpi/ic_action_remove_light.png new file mode 100644 index 00000000..ca7d159f Binary files /dev/null and b/core/res/drawable-xhdpi/ic_action_remove_light.png differ diff --git a/core/res/drawable-xhdpi/ic_action_resume_dark.png b/core/res/drawable-xhdpi/ic_action_resume_dark.png new file mode 100644 index 00000000..fe6b5588 Binary files /dev/null and b/core/res/drawable-xhdpi/ic_action_resume_dark.png differ diff --git a/core/res/drawable-xhdpi/ic_action_resume_light.png b/core/res/drawable-xhdpi/ic_action_resume_light.png new file mode 100644 index 00000000..61b8d595 Binary files /dev/null and b/core/res/drawable-xhdpi/ic_action_resume_light.png differ diff --git a/core/res/drawable-xhdpi/ic_action_rss_dark.png b/core/res/drawable-xhdpi/ic_action_rss_dark.png new file mode 100644 index 00000000..dcd88e1c Binary files /dev/null and b/core/res/drawable-xhdpi/ic_action_rss_dark.png differ diff --git a/core/res/drawable-xhdpi/ic_action_rss_light.png b/core/res/drawable-xhdpi/ic_action_rss_light.png new file mode 100644 index 00000000..03365510 Binary files /dev/null and b/core/res/drawable-xhdpi/ic_action_rss_light.png differ diff --git a/core/res/drawable-xhdpi/ic_action_search_dark.png b/core/res/drawable-xhdpi/ic_action_search_dark.png new file mode 100644 index 00000000..3549f84d Binary files /dev/null and b/core/res/drawable-xhdpi/ic_action_search_dark.png differ diff --git a/core/res/drawable-xhdpi/ic_action_search_light.png b/core/res/drawable-xhdpi/ic_action_search_light.png new file mode 100644 index 00000000..804420ae Binary files /dev/null and b/core/res/drawable-xhdpi/ic_action_search_light.png differ diff --git a/core/res/drawable-xhdpi/ic_action_sort_by_size_light.png b/core/res/drawable-xhdpi/ic_action_sort_by_size_light.png new file mode 100644 index 00000000..59da08e4 Binary files /dev/null and b/core/res/drawable-xhdpi/ic_action_sort_by_size_light.png differ diff --git a/core/res/drawable-xhdpi/ic_action_start_dark.png b/core/res/drawable-xhdpi/ic_action_start_dark.png new file mode 100644 index 00000000..51124993 Binary files /dev/null and b/core/res/drawable-xhdpi/ic_action_start_dark.png differ diff --git a/core/res/drawable-xhdpi/ic_action_start_light.png b/core/res/drawable-xhdpi/ic_action_start_light.png new file mode 100644 index 00000000..2d67d31e Binary files /dev/null and b/core/res/drawable-xhdpi/ic_action_start_light.png differ diff --git a/core/res/drawable-xhdpi/ic_action_stop_dark.png b/core/res/drawable-xhdpi/ic_action_stop_dark.png new file mode 100644 index 00000000..ee5eda25 Binary files /dev/null and b/core/res/drawable-xhdpi/ic_action_stop_dark.png differ diff --git a/core/res/drawable-xhdpi/ic_action_stop_light.png b/core/res/drawable-xhdpi/ic_action_stop_light.png new file mode 100644 index 00000000..9a23e3d4 Binary files /dev/null and b/core/res/drawable-xhdpi/ic_action_stop_light.png differ diff --git a/core/res/drawable-xhdpi/ic_action_trackers_dark.png b/core/res/drawable-xhdpi/ic_action_trackers_dark.png new file mode 100644 index 00000000..2caf74c9 Binary files /dev/null and b/core/res/drawable-xhdpi/ic_action_trackers_dark.png differ diff --git a/core/res/drawable-xhdpi/ic_action_trackers_light.png b/core/res/drawable-xhdpi/ic_action_trackers_light.png new file mode 100644 index 00000000..b607e604 Binary files /dev/null and b/core/res/drawable-xhdpi/ic_action_trackers_light.png differ diff --git a/core/res/drawable-xhdpi/ic_action_turtle_disabled.png b/core/res/drawable-xhdpi/ic_action_turtle_disabled.png new file mode 100644 index 00000000..12068f67 Binary files /dev/null and b/core/res/drawable-xhdpi/ic_action_turtle_disabled.png differ diff --git a/core/res/drawable-xhdpi/ic_action_turtle_enabled.png b/core/res/drawable-xhdpi/ic_action_turtle_enabled.png new file mode 100644 index 00000000..8bfedf1e Binary files /dev/null and b/core/res/drawable-xhdpi/ic_action_turtle_enabled.png differ diff --git a/core/res/drawable-xhdpi/ic_action_website_dark.png b/core/res/drawable-xhdpi/ic_action_website_dark.png new file mode 100644 index 00000000..9b77be96 Binary files /dev/null and b/core/res/drawable-xhdpi/ic_action_website_dark.png differ diff --git a/core/res/drawable-xhdpi/ic_action_website_light.png b/core/res/drawable-xhdpi/ic_action_website_light.png new file mode 100644 index 00000000..bd6b8682 Binary files /dev/null and b/core/res/drawable-xhdpi/ic_action_website_light.png differ diff --git a/core/res/drawable-xhdpi/ic_activity_torrents.png b/core/res/drawable-xhdpi/ic_activity_torrents.png new file mode 100644 index 00000000..25614e00 Binary files /dev/null and b/core/res/drawable-xhdpi/ic_activity_torrents.png differ diff --git a/core/res/drawable-xhdpi/ic_empty_details_dark.png b/core/res/drawable-xhdpi/ic_empty_details_dark.png new file mode 100644 index 00000000..7b446989 Binary files /dev/null and b/core/res/drawable-xhdpi/ic_empty_details_dark.png differ diff --git a/core/res/drawable-xhdpi/ic_empty_details_light.png b/core/res/drawable-xhdpi/ic_empty_details_light.png new file mode 100644 index 00000000..055caa04 Binary files /dev/null and b/core/res/drawable-xhdpi/ic_empty_details_light.png differ diff --git a/core/res/drawable-xhdpi/ic_launcher.png b/core/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 00000000..258c1fe5 Binary files /dev/null and b/core/res/drawable-xhdpi/ic_launcher.png differ diff --git a/core/res/drawable-xhdpi/list_focused_transdroid.9.png b/core/res/drawable-xhdpi/list_focused_transdroid.9.png new file mode 100644 index 00000000..368d15f6 Binary files /dev/null and b/core/res/drawable-xhdpi/list_focused_transdroid.9.png differ diff --git a/core/res/drawable-xhdpi/list_focused_transdroid2.9.png b/core/res/drawable-xhdpi/list_focused_transdroid2.9.png new file mode 100644 index 00000000..d41b7ecd Binary files /dev/null and b/core/res/drawable-xhdpi/list_focused_transdroid2.9.png differ diff --git a/core/res/drawable-xhdpi/menu_dropdown_panel_transdroid.9.png b/core/res/drawable-xhdpi/menu_dropdown_panel_transdroid.9.png new file mode 100644 index 00000000..2974663c Binary files /dev/null and b/core/res/drawable-xhdpi/menu_dropdown_panel_transdroid.9.png differ diff --git a/core/res/drawable-xhdpi/menu_dropdown_panel_transdroid2.9.png b/core/res/drawable-xhdpi/menu_dropdown_panel_transdroid2.9.png new file mode 100644 index 00000000..3d9f6149 Binary files /dev/null and b/core/res/drawable-xhdpi/menu_dropdown_panel_transdroid2.9.png differ diff --git a/core/res/drawable-xhdpi/menu_hardkey_panel_transdroid.9.png b/core/res/drawable-xhdpi/menu_hardkey_panel_transdroid.9.png new file mode 100644 index 00000000..97c03063 Binary files /dev/null and b/core/res/drawable-xhdpi/menu_hardkey_panel_transdroid.9.png differ diff --git a/core/res/drawable-xhdpi/progress_bg_transdroid.9.png b/core/res/drawable-xhdpi/progress_bg_transdroid.9.png new file mode 100644 index 00000000..8b4853aa Binary files /dev/null and b/core/res/drawable-xhdpi/progress_bg_transdroid.9.png differ diff --git a/core/res/drawable-xhdpi/progress_bg_transdroid2.9.png b/core/res/drawable-xhdpi/progress_bg_transdroid2.9.png new file mode 100644 index 00000000..5ffc2acc Binary files /dev/null and b/core/res/drawable-xhdpi/progress_bg_transdroid2.9.png differ diff --git a/core/res/drawable-xhdpi/progress_primary_transdroid.9.png b/core/res/drawable-xhdpi/progress_primary_transdroid.9.png new file mode 100644 index 00000000..b1c9444a Binary files /dev/null and b/core/res/drawable-xhdpi/progress_primary_transdroid.9.png differ diff --git a/core/res/drawable-xhdpi/progress_primary_transdroid2.9.png b/core/res/drawable-xhdpi/progress_primary_transdroid2.9.png new file mode 100644 index 00000000..e6d5511a Binary files /dev/null and b/core/res/drawable-xhdpi/progress_primary_transdroid2.9.png differ diff --git a/core/res/drawable-xhdpi/progress_secondary_transdroid.9.png b/core/res/drawable-xhdpi/progress_secondary_transdroid.9.png new file mode 100644 index 00000000..48cc8e57 Binary files /dev/null and b/core/res/drawable-xhdpi/progress_secondary_transdroid.9.png differ diff --git a/core/res/drawable-xhdpi/progress_secondary_transdroid2.9.png b/core/res/drawable-xhdpi/progress_secondary_transdroid2.9.png new file mode 100644 index 00000000..8676a0fd Binary files /dev/null and b/core/res/drawable-xhdpi/progress_secondary_transdroid2.9.png differ diff --git a/core/res/drawable-xhdpi/spinner_ab_default_transdroid.9.png b/core/res/drawable-xhdpi/spinner_ab_default_transdroid.9.png new file mode 100644 index 00000000..14b1401d Binary files /dev/null and b/core/res/drawable-xhdpi/spinner_ab_default_transdroid.9.png differ diff --git a/core/res/drawable-xhdpi/spinner_ab_default_transdroid2.9.png b/core/res/drawable-xhdpi/spinner_ab_default_transdroid2.9.png new file mode 100644 index 00000000..f738a44c Binary files /dev/null and b/core/res/drawable-xhdpi/spinner_ab_default_transdroid2.9.png differ diff --git a/core/res/drawable-xhdpi/spinner_ab_disabled_transdroid.9.png b/core/res/drawable-xhdpi/spinner_ab_disabled_transdroid.9.png new file mode 100644 index 00000000..c9dfbd60 Binary files /dev/null and b/core/res/drawable-xhdpi/spinner_ab_disabled_transdroid.9.png differ diff --git a/core/res/drawable-xhdpi/spinner_ab_disabled_transdroid2.9.png b/core/res/drawable-xhdpi/spinner_ab_disabled_transdroid2.9.png new file mode 100644 index 00000000..79d24c9f Binary files /dev/null and b/core/res/drawable-xhdpi/spinner_ab_disabled_transdroid2.9.png differ diff --git a/core/res/drawable-xhdpi/spinner_ab_focused_transdroid.9.png b/core/res/drawable-xhdpi/spinner_ab_focused_transdroid.9.png new file mode 100644 index 00000000..e4eddc17 Binary files /dev/null and b/core/res/drawable-xhdpi/spinner_ab_focused_transdroid.9.png differ diff --git a/core/res/drawable-xhdpi/spinner_ab_focused_transdroid2.9.png b/core/res/drawable-xhdpi/spinner_ab_focused_transdroid2.9.png new file mode 100644 index 00000000..e52a8dd4 Binary files /dev/null and b/core/res/drawable-xhdpi/spinner_ab_focused_transdroid2.9.png differ diff --git a/core/res/drawable-xhdpi/spinner_ab_pressed_transdroid.9.png b/core/res/drawable-xhdpi/spinner_ab_pressed_transdroid.9.png new file mode 100644 index 00000000..95f9b4c6 Binary files /dev/null and b/core/res/drawable-xhdpi/spinner_ab_pressed_transdroid.9.png differ diff --git a/core/res/drawable-xhdpi/spinner_ab_pressed_transdroid2.9.png b/core/res/drawable-xhdpi/spinner_ab_pressed_transdroid2.9.png new file mode 100644 index 00000000..ec063706 Binary files /dev/null and b/core/res/drawable-xhdpi/spinner_ab_pressed_transdroid2.9.png differ diff --git a/core/res/drawable-xhdpi/tab_selected_focused_transdroid.9.png b/core/res/drawable-xhdpi/tab_selected_focused_transdroid.9.png new file mode 100644 index 00000000..1c6ccb7b Binary files /dev/null and b/core/res/drawable-xhdpi/tab_selected_focused_transdroid.9.png differ diff --git a/core/res/drawable-xhdpi/tab_selected_focused_transdroid2.9.png b/core/res/drawable-xhdpi/tab_selected_focused_transdroid2.9.png new file mode 100644 index 00000000..34fcda3b Binary files /dev/null and b/core/res/drawable-xhdpi/tab_selected_focused_transdroid2.9.png differ diff --git a/core/res/drawable-xhdpi/tab_selected_pressed_transdroid.9.png b/core/res/drawable-xhdpi/tab_selected_pressed_transdroid.9.png new file mode 100644 index 00000000..bed352bd Binary files /dev/null and b/core/res/drawable-xhdpi/tab_selected_pressed_transdroid.9.png differ diff --git a/core/res/drawable-xhdpi/tab_selected_pressed_transdroid2.9.png b/core/res/drawable-xhdpi/tab_selected_pressed_transdroid2.9.png new file mode 100644 index 00000000..bfb3d453 Binary files /dev/null and b/core/res/drawable-xhdpi/tab_selected_pressed_transdroid2.9.png differ diff --git a/core/res/drawable-xhdpi/tab_selected_transdroid.9.png b/core/res/drawable-xhdpi/tab_selected_transdroid.9.png new file mode 100644 index 00000000..794323de Binary files /dev/null and b/core/res/drawable-xhdpi/tab_selected_transdroid.9.png differ diff --git a/core/res/drawable-xhdpi/tab_selected_transdroid2.9.png b/core/res/drawable-xhdpi/tab_selected_transdroid2.9.png new file mode 100644 index 00000000..0c90c530 Binary files /dev/null and b/core/res/drawable-xhdpi/tab_selected_transdroid2.9.png differ diff --git a/core/res/drawable-xhdpi/tab_unselected_focused_transdroid.9.png b/core/res/drawable-xhdpi/tab_unselected_focused_transdroid.9.png new file mode 100644 index 00000000..564f40f5 Binary files /dev/null and b/core/res/drawable-xhdpi/tab_unselected_focused_transdroid.9.png differ diff --git a/core/res/drawable-xhdpi/tab_unselected_focused_transdroid2.9.png b/core/res/drawable-xhdpi/tab_unselected_focused_transdroid2.9.png new file mode 100644 index 00000000..90d55085 Binary files /dev/null and b/core/res/drawable-xhdpi/tab_unselected_focused_transdroid2.9.png differ diff --git a/core/res/drawable-xhdpi/tab_unselected_pressed_transdroid.9.png b/core/res/drawable-xhdpi/tab_unselected_pressed_transdroid.9.png new file mode 100644 index 00000000..3f885d60 Binary files /dev/null and b/core/res/drawable-xhdpi/tab_unselected_pressed_transdroid.9.png differ diff --git a/core/res/drawable-xhdpi/tab_unselected_pressed_transdroid2.9.png b/core/res/drawable-xhdpi/tab_unselected_pressed_transdroid2.9.png new file mode 100644 index 00000000..b5d5335c Binary files /dev/null and b/core/res/drawable-xhdpi/tab_unselected_pressed_transdroid2.9.png differ diff --git a/core/res/drawable-xhdpi/tab_unselected_transdroid2.9.png b/core/res/drawable-xhdpi/tab_unselected_transdroid2.9.png new file mode 100644 index 00000000..6fd9a251 Binary files /dev/null and b/core/res/drawable-xhdpi/tab_unselected_transdroid2.9.png differ diff --git a/core/res/drawable-xxhdpi/ic_launcher.png b/core/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 00000000..6f6ea1ce Binary files /dev/null and b/core/res/drawable-xxhdpi/ic_launcher.png differ diff --git a/core/res/drawable/ab_background_textured_transdroid2.xml b/core/res/drawable/ab_background_textured_transdroid2.xml new file mode 100644 index 00000000..d08217b4 --- /dev/null +++ b/core/res/drawable/ab_background_textured_transdroid2.xml @@ -0,0 +1,21 @@ + + + + \ No newline at end of file diff --git a/core/res/drawable/btn_cab_done_transdroid2.xml b/core/res/drawable/btn_cab_done_transdroid2.xml new file mode 100644 index 00000000..ba1783fc --- /dev/null +++ b/core/res/drawable/btn_cab_done_transdroid2.xml @@ -0,0 +1,27 @@ + + + + + + + + diff --git a/core/res/drawable/loading_progress_dark.xml b/core/res/drawable/loading_progress_dark.xml new file mode 100644 index 00000000..326e7639 --- /dev/null +++ b/core/res/drawable/loading_progress_dark.xml @@ -0,0 +1,5 @@ + + diff --git a/core/res/drawable/loading_progress_light.xml b/core/res/drawable/loading_progress_light.xml new file mode 100644 index 00000000..ed73f056 --- /dev/null +++ b/core/res/drawable/loading_progress_light.xml @@ -0,0 +1,5 @@ + + diff --git a/core/res/drawable/pressed_background_transdroid.xml b/core/res/drawable/pressed_background_transdroid.xml new file mode 100644 index 00000000..d59067c2 --- /dev/null +++ b/core/res/drawable/pressed_background_transdroid.xml @@ -0,0 +1,21 @@ + + + + + + diff --git a/core/res/drawable/pressed_background_transdroid2.xml b/core/res/drawable/pressed_background_transdroid2.xml new file mode 100644 index 00000000..4998b8fd --- /dev/null +++ b/core/res/drawable/pressed_background_transdroid2.xml @@ -0,0 +1,22 @@ + + + + + + diff --git a/core/res/drawable/section_header.xml b/core/res/drawable/section_header.xml new file mode 100644 index 00000000..d6094f63 --- /dev/null +++ b/core/res/drawable/section_header.xml @@ -0,0 +1,12 @@ + + + + + + + + + \ No newline at end of file diff --git a/core/res/drawable/selectable_background_transdroid.xml b/core/res/drawable/selectable_background_transdroid.xml new file mode 100644 index 00000000..77db9571 --- /dev/null +++ b/core/res/drawable/selectable_background_transdroid.xml @@ -0,0 +1,25 @@ + + + + + + + + + \ No newline at end of file diff --git a/core/res/drawable/selectable_background_transdroid2.xml b/core/res/drawable/selectable_background_transdroid2.xml new file mode 100644 index 00000000..268cdcd4 --- /dev/null +++ b/core/res/drawable/selectable_background_transdroid2.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/core/res/drawable/spinner_background_ab_transdroid.xml b/core/res/drawable/spinner_background_ab_transdroid.xml new file mode 100644 index 00000000..32edfe7b --- /dev/null +++ b/core/res/drawable/spinner_background_ab_transdroid.xml @@ -0,0 +1,27 @@ + + + + + + + + + diff --git a/core/res/drawable/spinner_background_ab_transdroid2.xml b/core/res/drawable/spinner_background_ab_transdroid2.xml new file mode 100644 index 00000000..d1adeb93 --- /dev/null +++ b/core/res/drawable/spinner_background_ab_transdroid2.xml @@ -0,0 +1,28 @@ + + + + + + + + + diff --git a/core/res/drawable/tab_indicator_ab_transdroid.xml b/core/res/drawable/tab_indicator_ab_transdroid.xml new file mode 100644 index 00000000..5345f3e5 --- /dev/null +++ b/core/res/drawable/tab_indicator_ab_transdroid.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/core/res/drawable/tab_indicator_ab_transdroid2.xml b/core/res/drawable/tab_indicator_ab_transdroid2.xml new file mode 100644 index 00000000..c940e202 --- /dev/null +++ b/core/res/drawable/tab_indicator_ab_transdroid2.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/core/res/layout-v14/actionbar_progressitem.xml b/core/res/layout-v14/actionbar_progressitem.xml new file mode 100644 index 00000000..f6dfd64a --- /dev/null +++ b/core/res/layout-v14/actionbar_progressitem.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + diff --git a/core/res/layout-w600dp/activity_torrents.xml b/core/res/layout-w600dp/activity_torrents.xml new file mode 100644 index 00000000..746cc9e0 --- /dev/null +++ b/core/res/layout-w600dp/activity_torrents.xml @@ -0,0 +1,27 @@ + + + + + + + + + \ No newline at end of file diff --git a/core/res/layout-w900dp/activity_torrents.xml b/core/res/layout-w900dp/activity_torrents.xml new file mode 100644 index 00000000..a686fff0 --- /dev/null +++ b/core/res/layout-w900dp/activity_torrents.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/core/res/layout/actionbar_navigation.xml b/core/res/layout/actionbar_navigation.xml new file mode 100644 index 00000000..203d103f --- /dev/null +++ b/core/res/layout/actionbar_navigation.xml @@ -0,0 +1,27 @@ + + + + + + + + \ No newline at end of file diff --git a/core/res/layout/actionbar_progressitem.xml b/core/res/layout/actionbar_progressitem.xml new file mode 100644 index 00000000..5123e8df --- /dev/null +++ b/core/res/layout/actionbar_progressitem.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + diff --git a/core/res/layout/actionbar_serverstatus.xml b/core/res/layout/actionbar_serverstatus.xml new file mode 100644 index 00000000..e5370b6a --- /dev/null +++ b/core/res/layout/actionbar_serverstatus.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/res/layout/activity_details.xml b/core/res/layout/activity_details.xml new file mode 100644 index 00000000..f1d8b7c8 --- /dev/null +++ b/core/res/layout/activity_details.xml @@ -0,0 +1,16 @@ + + + + + + + \ No newline at end of file diff --git a/core/res/layout/activity_torrents.xml b/core/res/layout/activity_torrents.xml new file mode 100644 index 00000000..feb3cba7 --- /dev/null +++ b/core/res/layout/activity_torrents.xml @@ -0,0 +1,16 @@ + + + + + + + \ No newline at end of file diff --git a/core/res/layout/dialog_about.xml b/core/res/layout/dialog_about.xml new file mode 100644 index 00000000..ce92e4a2 --- /dev/null +++ b/core/res/layout/dialog_about.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/core/res/layout/dialog_changelog.xml b/core/res/layout/dialog_changelog.xml new file mode 100644 index 00000000..dc51afd1 --- /dev/null +++ b/core/res/layout/dialog_changelog.xml @@ -0,0 +1,15 @@ + + + + + + diff --git a/core/res/layout/fragment_details.xml b/core/res/layout/fragment_details.xml new file mode 100644 index 00000000..e60c310b --- /dev/null +++ b/core/res/layout/fragment_details.xml @@ -0,0 +1,39 @@ + + + + + + + + + + \ No newline at end of file diff --git a/core/res/layout/fragment_details_header.xml b/core/res/layout/fragment_details_header.xml new file mode 100644 index 00000000..907b71d5 --- /dev/null +++ b/core/res/layout/fragment_details_header.xml @@ -0,0 +1,169 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/res/layout/fragment_filters.xml b/core/res/layout/fragment_filters.xml new file mode 100644 index 00000000..7ccfb746 --- /dev/null +++ b/core/res/layout/fragment_filters.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/core/res/layout/fragment_torrents.xml b/core/res/layout/fragment_torrents.xml new file mode 100644 index 00000000..292df0b1 --- /dev/null +++ b/core/res/layout/fragment_torrents.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/res/layout/list_item_filter.xml b/core/res/layout/list_item_filter.xml new file mode 100644 index 00000000..8d1e4444 --- /dev/null +++ b/core/res/layout/list_item_filter.xml @@ -0,0 +1,16 @@ + + + + + + \ No newline at end of file diff --git a/core/res/layout/list_item_separator.xml b/core/res/layout/list_item_separator.xml new file mode 100644 index 00000000..c844caa6 --- /dev/null +++ b/core/res/layout/list_item_separator.xml @@ -0,0 +1,15 @@ + + + + + + \ No newline at end of file diff --git a/core/res/layout/list_item_simple.xml b/core/res/layout/list_item_simple.xml new file mode 100644 index 00000000..0a62b6d7 --- /dev/null +++ b/core/res/layout/list_item_simple.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/core/res/layout/list_item_torrent.xml b/core/res/layout/list_item_torrent.xml new file mode 100644 index 00000000..e2a0ffe4 --- /dev/null +++ b/core/res/layout/list_item_torrent.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/res/layout/list_item_torrentfile.xml b/core/res/layout/list_item_torrentfile.xml new file mode 100644 index 00000000..84a43658 --- /dev/null +++ b/core/res/layout/list_item_torrentfile.xml @@ -0,0 +1,40 @@ + + + + + + + + + + \ No newline at end of file diff --git a/core/res/menu/activity_deleteableprefs.xml b/core/res/menu/activity_deleteableprefs.xml new file mode 100644 index 00000000..64e8baae --- /dev/null +++ b/core/res/menu/activity_deleteableprefs.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/core/res/menu/activity_details.xml b/core/res/menu/activity_details.xml new file mode 100644 index 00000000..cbfb2613 --- /dev/null +++ b/core/res/menu/activity_details.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/core/res/menu/activity_torrents.xml b/core/res/menu/activity_torrents.xml new file mode 100644 index 00000000..b275c63f --- /dev/null +++ b/core/res/menu/activity_torrents.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/res/menu/dialog_about.xml b/core/res/menu/dialog_about.xml new file mode 100644 index 00000000..cb82ee19 --- /dev/null +++ b/core/res/menu/dialog_about.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/core/res/menu/fragment_details.xml b/core/res/menu/fragment_details.xml new file mode 100644 index 00000000..ac38581d --- /dev/null +++ b/core/res/menu/fragment_details.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/res/menu/fragment_details_file.xml b/core/res/menu/fragment_details_file.xml new file mode 100644 index 00000000..d0f4a8ba --- /dev/null +++ b/core/res/menu/fragment_details_file.xml @@ -0,0 +1,28 @@ + + + + + + + + + \ No newline at end of file diff --git a/core/res/menu/fragment_torrents_cab.xml b/core/res/menu/fragment_torrents_cab.xml new file mode 100644 index 00000000..afb60120 --- /dev/null +++ b/core/res/menu/fragment_torrents_cab.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/res/values-land/dimens.xml b/core/res/values-land/dimens.xml new file mode 100644 index 00000000..28365483 --- /dev/null +++ b/core/res/values-land/dimens.xml @@ -0,0 +1,13 @@ + + + + 15sp + 12sp + 1dp + 2dp + 19sp + 12sp + 12sp + 53dp + + \ No newline at end of file diff --git a/core/res/values-sw500dp/bools.xml b/core/res/values-sw500dp/bools.xml new file mode 100644 index 00000000..f701a217 --- /dev/null +++ b/core/res/values-sw500dp/bools.xml @@ -0,0 +1,5 @@ + + + + false + \ No newline at end of file diff --git a/core/res/values-sw600dp/dimens.xml b/core/res/values-sw600dp/dimens.xml new file mode 100644 index 00000000..f4926c5c --- /dev/null +++ b/core/res/values-sw600dp/dimens.xml @@ -0,0 +1,27 @@ + + + + 16dp + 8dp + 16dp + + + 14sp + 17sp + 18sp + 27sp + 40sp + 14sp + + + 18sp + 15sp + 125dp + 2dp + 5dp + 25sp + 17sp + 14sp + 63dp + + \ No newline at end of file diff --git a/core/res/values-v11/styles_transdroid_dark.xml b/core/res/values-v11/styles_transdroid_dark.xml new file mode 100644 index 00000000..bf417f3e --- /dev/null +++ b/core/res/values-v11/styles_transdroid_dark.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/res/values-v11/styles_transdroid_light.xml b/core/res/values-v11/styles_transdroid_light.xml new file mode 100644 index 00000000..03a595ff --- /dev/null +++ b/core/res/values-v11/styles_transdroid_light.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/core/res/values-v14/styles_transdroid_dark.xml b/core/res/values-v14/styles_transdroid_dark.xml new file mode 100644 index 00000000..62a740a1 --- /dev/null +++ b/core/res/values-v14/styles_transdroid_dark.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/res/values-v14/styles_transdroid_light.xml b/core/res/values-v14/styles_transdroid_light.xml new file mode 100644 index 00000000..242309f8 --- /dev/null +++ b/core/res/values-v14/styles_transdroid_light.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/core/res/values-v16/styles.xml b/core/res/values-v16/styles.xml new file mode 100644 index 00000000..b3cef559 --- /dev/null +++ b/core/res/values-v16/styles.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/res/values/attrs.xml b/core/res/values/attrs.xml new file mode 100644 index 00000000..1fc1ff87 --- /dev/null +++ b/core/res/values/attrs.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/res/values/bools.xml b/core/res/values/bools.xml new file mode 100644 index 00000000..63d71d3e --- /dev/null +++ b/core/res/values/bools.xml @@ -0,0 +1,5 @@ + + + + true + \ No newline at end of file diff --git a/core/res/values/changelog.xml b/core/res/values/changelog.xml new file mode 100644 index 00000000..a6b4dcbc --- /dev/null +++ b/core/res/values/changelog.xml @@ -0,0 +1,10 @@ + + + +Transdroid 2.0.0-alpha1\n +- Totally reworked Holo-style interface\n +- Provide tablet interface on smaller tablets\n +\n +Older changes: http://www.transdroid.org/about/changelog/ + + \ No newline at end of file diff --git a/core/res/values/colors.xml b/core/res/values/colors.xml new file mode 100644 index 00000000..5203dfd2 --- /dev/null +++ b/core/res/values/colors.xml @@ -0,0 +1,7 @@ + + + #8acc12 + #7dbb21 + #c81113 + #aada62 + diff --git a/core/res/values/colors_transdroid_dark.xml b/core/res/values/colors_transdroid_dark.xml new file mode 100644 index 00000000..9ee708af --- /dev/null +++ b/core/res/values/colors_transdroid_dark.xml @@ -0,0 +1,24 @@ + + + + + #CCaada62 + #000 + #fff + #fff + diff --git a/core/res/values/colors_transdroid_light.xml b/core/res/values/colors_transdroid_light.xml new file mode 100644 index 00000000..f3a6aee3 --- /dev/null +++ b/core/res/values/colors_transdroid_light.xml @@ -0,0 +1,25 @@ + + + + + #CC8ACC12 + #fff + #000 + #000 + diff --git a/core/res/values/dimens.xml b/core/res/values/dimens.xml new file mode 100644 index 00000000..c98149c1 --- /dev/null +++ b/core/res/values/dimens.xml @@ -0,0 +1,27 @@ + + + + 16dp + 8dp + 16dp + + + 12sp + 15sp + 17sp + 24sp + 35sp + 14sp + + + 17sp + 14sp + 105dp + 2dp + 3dp + 21sp + 15sp + 13sp + 53dp + + \ No newline at end of file diff --git a/core/res/values/strings.xml b/core/res/values/strings.xml new file mode 100644 index 00000000..a5608379 --- /dev/null +++ b/core/res/values/strings.xml @@ -0,0 +1,286 @@ + + + + Add + From file + From URL + Scan barcode + Search + Refresh + RSS + Enable turle mode + Disable turle mode + Sort list + Name + Status + Date done + Date added + Upload speed + Ratio + Filter list + Settings + Help + Start + Normal start + Force start + Stop + Resume + Pause + Remove + Remove torrent + Remove and delete data + Set label + Update trackers + Off + Low + Normal + High + Remote play in VLC + Download using (S)FTP + Remove settings + Visit transdroid.org + + Transdroid allows you to monitor and manage the torrent client you run at home or on your seedbox. Setting things up can be a bit tricky, but we offer step-by-step guides and promise it\'ll be worth it! + Connected, but no torrent are active within the current filter + Select a torrent to view its details + SERVERS + STATUS + LABELS + All + Downloading + Uploading + Active + Inactive + + %1$d torrent selected + %1$d torrents selected + + + %1$d files selected + %1$d files selected + + Select all + Invert selection + + STATUS: %1$s + Waiting to check… + Verifying local data… + Waiting to download %s + Error… + %1$s OF %2$s (%3$s) + %1$s, UPLOADED %2$s + SINCE %1$s + ~ %1$s + ETA %1$s + OF %1$s + UNKNOWN ETA + RATIO %1$s + %1$s OF %2$s PEERS + ↑ %1$s + %1$s ↓ + Downloading + Seeding + Paused + Queued + Stopped + Unknown status + Not downloaded + Low priority + Normal priority + High priority + TRACKERS + ERRORS + FILES + + All labels + Unlabeled + New label + Setting a label is not supported by your client + + %1$s added (refreshing) + %1$s removed + %1$s removed and data deleted + %1$s resumed (refreshing) + %1$s stopped (refreshing) + %1$s started (refreshing) + %1$s paused (refreshing) + Torrents paused (refreshing) + Torrents resumed (refreshing) + Torrents stopped (refreshing) + Torrents started (refreshing) + Trackers updated + Label set to \'%1$s\' + Torrent moved to \'%1$s\' + File priorities updated + + Torrent search + Search for torrents + The Barcode Scanner could not be found. Would you like to install it from the Play Store? + No compatible file manager could not be found. Would you like to install IO File Manager from the Play Store? + + Servers + Add new server + Search sites + Set default site + Add web search site + RSS feeds + Add RSS feed + Other settings + + Name + Optional personal name + Direct search URL + %s will be replaced by the search query + Feed URL + Requires authentication + Opens links in the webbrowser for user login + + Server type + IP or host name + Port number + User name + Password + Deluge web password + Advanced settings + Local IP or host + When connected to the specified local network + Local network + The server\'s local network SSID + Folder + Usually empty + SCGI mount point + Optional settings + Finished notification + Notify when a torrent finishes + New torrent notification + Nofity when a torrent was added + Server OS + Download directory + Manually set absolute path for remote connections + Connection timeout + Number of seconds before a connection attempt is aborted + Base (S)FTP url + For example ftp://me@server/downloads/ + (S)FTP password + Use SSL + Connect using https + Custom SSL thumbprint (SHA-1) + Permit only connections to this specific certificate + Accept all SSL certificates + Allow all connections from any thumbprint + + Background notifications + Enable notifications + Enables the background service + Interval + How often to check the server + Sound + Vibrate + LED colour + If supported by your device + Support AWD notifications + Show torrent counter in ADW Launcher + + System + Check for updates + Check transdroid.org for latest app version + Use dark UI theme + Requires a restart to take effect + Import settings + Transdroid will try to import server, web search, RSS and system settings from: %1$s + Settings successfully imported + Export settings + Transdroid will export server (including passwords), web search, RSS and system settings to the following plain text JSON file: %1$s + Settings successfully exported + Send error log + Get support or report a bug + View install guides + Available at transdroid.org/download + Recent changes + About Transdroid + + BitComet + Bitflu 1.2+ + BitTorrent 6+ + Buffalo NAS -1.31 + Deluge 1.2+ + DLink Router BT + Ktorrent + qBittorrent + rTorrent + Torrentflux-b4rt + Transmission + µTorrent + Vuze + + + daemon_bitcomet + daemon_bitflu + daemon_bittorrent + daemon_buffalonas + daemon_deluge + daemon_dlinkrouterbt + daemon_ktorrent + daemon_qbittorrent + daemon_rtorrent + daemon_tfb4rt + daemon_transmission + daemon_utorrent + daemon_vuze + + + Windows + Mac + Linux + + + type_windows + type_mac + type_linux + + + 1 minute + 10 minutes + 30 minutes + 1 hour + 3 hours + 12 hours + 1 day + + + 60 + 600 + 1800 + 3600 + 10800 + 43200 + 86400 + + + Error during communication; check your connection + Internal error building request + Error parsing server response (please check your settings) + Web interface not connected to a running daemon + Access denied (please check your settings) + Can\'t read .torrent file + Error while parsing the RSS feed + This URL is not well-formed + Your web search URL is invalid: + Input is not a valid IP address or host name + Port number is always numeric + Directory paths end with a / or \ + Timeout can not be empty and is a positive number + The RSS feed item didn\'t provide an URL enclosure or link tag pointing to the .torrent file + The RSS feed item does not provide a link to browse to + URL is not a (valid) RSS feed + SD card not available to read/write + File does not seem to contain Transdroid settings + The settings file could not be found + Can\'t write to the settings file + + Transdroid + \u00A9 Eric Kok, 2312 development + Published under GNU General Public License v3 + Manage your torrents from your Android device + + \ No newline at end of file diff --git a/core/res/values/styles.xml b/core/res/values/styles.xml new file mode 100644 index 00000000..1d68bca0 --- /dev/null +++ b/core/res/values/styles.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/res/values/styles_transdroid_dark.xml b/core/res/values/styles_transdroid_dark.xml new file mode 100644 index 00000000..d3fbf97d --- /dev/null +++ b/core/res/values/styles_transdroid_dark.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/res/values/styles_transdroid_light.xml b/core/res/values/styles_transdroid_light.xml new file mode 100644 index 00000000..ebf32fcf --- /dev/null +++ b/core/res/values/styles_transdroid_light.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/res/xml/pref_main.xml b/core/res/xml/pref_main.xml new file mode 100644 index 00000000..09c61a50 --- /dev/null +++ b/core/res/xml/pref_main.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/res/xml/pref_notifications.xml b/core/res/xml/pref_notifications.xml new file mode 100644 index 00000000..972a477e --- /dev/null +++ b/core/res/xml/pref_notifications.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/res/xml/pref_rssfeed.xml b/core/res/xml/pref_rssfeed.xml new file mode 100644 index 00000000..401c5aea --- /dev/null +++ b/core/res/xml/pref_rssfeed.xml @@ -0,0 +1,21 @@ + + + + + + + + + + \ No newline at end of file diff --git a/core/res/xml/pref_server.xml b/core/res/xml/pref_server.xml new file mode 100644 index 00000000..453c06b1 --- /dev/null +++ b/core/res/xml/pref_server.xml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/res/xml/pref_system.xml b/core/res/xml/pref_system.xml new file mode 100644 index 00000000..b0858671 --- /dev/null +++ b/core/res/xml/pref_system.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/res/xml/pref_websearch.xml b/core/res/xml/pref_websearch.xml new file mode 100644 index 00000000..f2efdec1 --- /dev/null +++ b/core/res/xml/pref_websearch.xml @@ -0,0 +1,16 @@ + + + + + + + + \ No newline at end of file diff --git a/core/res/xml/searchable.xml b/core/res/xml/searchable.xml new file mode 100644 index 00000000..9f811c8c --- /dev/null +++ b/core/res/xml/searchable.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/core/src/com/actionbarsherlock/view/SherlockListView.java b/core/src/com/actionbarsherlock/view/SherlockListView.java new file mode 100644 index 00000000..3b2df828 --- /dev/null +++ b/core/src/com/actionbarsherlock/view/SherlockListView.java @@ -0,0 +1,337 @@ +package com.actionbarsherlock.view; + +import android.annotation.TargetApi; +import android.content.Context; +import android.util.AttributeSet; +import android.util.SparseBooleanArray; +import android.view.View; +import android.widget.AdapterView; +import android.widget.Checkable; +import android.widget.ListView; + +import com.actionbarsherlock.app.SherlockActivity; +import com.actionbarsherlock.app.SherlockFragmentActivity; +import com.actionbarsherlock.view.ActionMode; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuItem; + +/** + * Provides backwards compatible multiple choice ActionMode support on Froyo+ using ActionBarSherlock. + */ +public class SherlockListView extends ListView { + // API 11+ reference, but ok because the value will be inlined. + public static final int CHOICE_MODE_MULTIPLE_MODAL_COMPAT = CHOICE_MODE_MULTIPLE_MODAL; + + /** + * Wrapper to intercept delegation of long click events, and pass to {@link #doLongPress} + */ + class OnItemLongClickListenerWrapper implements OnItemLongClickListener { + private OnItemLongClickListener wrapped; + + public void setWrapped(OnItemLongClickListener listener) { + this.wrapped = listener; + } + + @Override + public boolean onItemLongClick(AdapterView view, View child, int position, long id) { + // this would be easier if AbsListView.performLongPress wasn't package + // protected :-( + boolean handled = doLongPress(child, position, id); + if (!handled && wrapped != null) { + return wrapped.onItemLongClick(view, child, position, id); + } + return true; + } + } + + /** + * Hijack the onLongClickListener so we can intercept delegation. + */ + @Override + public void setOnItemLongClickListener(OnItemLongClickListener listener) { + if (longClickListenerWrapper == null) { + longClickListenerWrapper = new OnItemLongClickListenerWrapper(); + } + longClickListenerWrapper.setWrapped(listener); + super.setOnItemLongClickListener(longClickListenerWrapper); + } + + /** + * A MultiChoiceModeListener receives events for {@link AbsListView#CHOICE_MODE_MULTIPLE_MODAL}. It acts as the + * {@link ActionMode.Callback} for the selection mode and also receives + * {@link #onItemCheckedStateChanged(ActionMode, int, long, boolean)} events when the user selects and deselects + * list items. + */ + @SuppressWarnings("javadoc") + public interface MultiChoiceModeListenerCompat extends ActionMode.Callback { + /** + * Called when an item is checked or unchecked during selection mode. + * @param mode The {@link ActionMode} providing the selection mode + * @param position Adapter position of the item that was checked or unchecked + * @param id Adapter ID of the item that was checked or unchecked + * @param checked true if the item is now checked, false if the item is now unchecked. + */ + public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked); + } + + class MultiChoiceModeWrapper implements MultiChoiceModeListenerCompat { + private MultiChoiceModeListenerCompat wrapped; + + public void setWrapped(MultiChoiceModeListenerCompat wrapped) { + this.wrapped = wrapped; + } + + @Override + public boolean onCreateActionMode(ActionMode mode, Menu menu) { + if (wrapped == null) { + return false; + } + if (wrapped.onCreateActionMode(mode, menu)) { + // Initialize checked graphic state? + setLongClickable(false); + return true; + } + return false; + } + + @Override + public boolean onPrepareActionMode(ActionMode mode, Menu menu) { + if (wrapped == null) { + return false; + } + return wrapped.onPrepareActionMode(mode, menu); + } + + @Override + public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + if (wrapped == null) { + return false; + } + return wrapped.onActionItemClicked(mode, item); + } + + @Override + public void onDestroyActionMode(ActionMode mode) { + if (wrapped == null) { + return; + } + wrapped.onDestroyActionMode(mode); + actionMode = null; + + // Ending selection mode means deselecting everything. + clearChoices(); + checkedItemCount = 0; + updateOnScreenCheckedViews(); + invalidateViews(); + setLongClickable(true); + requestLayout(); + invalidate(); + } + + @Override + public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) { + if (wrapped == null) { + return; + } + wrapped.onItemCheckedStateChanged(mode, position, id, checked); + + // If there are no items selected we no longer need the selection mode. + if (checkedItemCount == 0) { + mode.finish(); + } + } + } + + private com.actionbarsherlock.view.ActionMode actionMode; + private OnItemLongClickListenerWrapper longClickListenerWrapper; + private MultiChoiceModeWrapper choiceModeListener; + private int choiceMode; + private int checkedItemCount; + + public SherlockListView(Context context) { + super(context); + init(context); + } + + public SherlockListView(Context context, AttributeSet attrs) { + super(context, attrs); + init(context); + } + + public SherlockListView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(context); + } + + void init(Context context) { + if (isInEditMode()) { + // Ignore when viewing in the UI designer + return; + } + if (!(context instanceof SherlockActivity || context instanceof SherlockFragmentActivity)) { + throw new IllegalStateException( + "This view must be hosted in a SherlockActivity or SherlockFragmentActivity"); + } + setOnItemLongClickListener(null); + } + + @Override + public void setChoiceMode(int mode) { + choiceMode = mode; + if (actionMode != null) { + actionMode.finish(); + actionMode = null; + } + if (choiceMode != CHOICE_MODE_NONE) { + if (mode == CHOICE_MODE_MULTIPLE_MODAL_COMPAT) { + clearChoices(); + checkedItemCount = 0; + setLongClickable(true); + updateOnScreenCheckedViews(); + requestLayout(); + invalidate(); + mode = CHOICE_MODE_MULTIPLE; + } + super.setChoiceMode(mode); + } + } + + @Override + public int getChoiceMode() { + return choiceMode; + } + + public void setMultiChoiceModeListener(MultiChoiceModeListenerCompat listener) { + if (choiceModeListener == null) { + choiceModeListener = new MultiChoiceModeWrapper(); + } + choiceModeListener.setWrapped(listener); + } + + @Override + public boolean performItemClick(View view, int position, long id) { + boolean handled = false; + boolean dispatchItemClick = true; + boolean checkStateChanged = false; + if (choiceMode != CHOICE_MODE_NONE) { + handled = true; + if (choiceMode == CHOICE_MODE_MULTIPLE + || (choiceMode == CHOICE_MODE_MULTIPLE_MODAL_COMPAT && actionMode != null)) { + boolean newValue = !getCheckedItemPositions().get(position); + setItemChecked(position, newValue); + if (actionMode != null) { + choiceModeListener.onItemCheckedStateChanged(actionMode, position, id, newValue); + dispatchItemClick = false; + } + checkStateChanged = true; + return false; + } else if (choiceMode == CHOICE_MODE_SINGLE) { + boolean newValue = !getCheckedItemPositions().get(position); + setItemChecked(position, newValue); + checkStateChanged = true; + } + if (checkStateChanged) { + updateOnScreenCheckedViews(); + } + } + if (dispatchItemClick) { + handled |= super.performItemClick(view, position, id); + } + return handled; + } + + /** + * Perform a quick, in-place update of the checked or activated state on all visible item views. This should only be + * called when a valid choice mode is active. + *

+ * (Taken verbatim from AbsListView.java) + */ + @TargetApi(11) + private void updateOnScreenCheckedViews() { + final int firstPos = getFirstVisiblePosition(); + final int count = getChildCount(); + final boolean useActivated = getContext().getApplicationInfo().targetSdkVersion >= android.os.Build.VERSION_CODES.HONEYCOMB; + for (int i = 0; i < count; i++) { + final View child = getChildAt(i); + final int position = firstPos + i; + + if (child instanceof Checkable) { + ((Checkable) child).setChecked(getCheckedItemPositions().get(position)); + } else if (useActivated) { + child.setActivated(getCheckedItemPositions().get(position)); + } + } + } + + public ActionMode startActionMode(ActionMode.Callback callback) { + if (actionMode != null) { + return actionMode; + } + Context context = getContext(); + if (context instanceof SherlockActivity) { + actionMode = ((SherlockActivity) getContext()).startActionMode(callback); + } else if (context instanceof SherlockFragmentActivity) { + actionMode = ((SherlockFragmentActivity) context).startActionMode(callback); + } else { + throw new IllegalStateException( + "This view must be hosted in a SherlockActivity or SherlockFragmentActivity"); + } + return actionMode; + } + + boolean doLongPress(final View child, final int longPressPosition, final long longPressId) { + if (choiceMode == CHOICE_MODE_MULTIPLE_MODAL_COMPAT) { + if (actionMode == null && (actionMode = startActionMode(choiceModeListener)) != null) { + setItemChecked(longPressPosition, true); + } + return true; + } + return false; + } + + /** + * Sets the checked state of the specified position. The is only valid if the choice mode has been set to + * {@link #CHOICE_MODE_SINGLE} or {@link #CHOICE_MODE_MULTIPLE}. + * @param position The item whose checked state is to be checked + * @param value The new checked state for the item + */ + @Override + public void setItemChecked(int position, boolean value) { + if (choiceMode == CHOICE_MODE_NONE) { + return; + } + SparseBooleanArray checkStates = getCheckedItemPositions(); + + // Start selection mode if needed. We don't need to if we're unchecking + // something. + if (value && choiceMode == CHOICE_MODE_MULTIPLE_MODAL_COMPAT && actionMode == null) { + actionMode = startActionMode(choiceModeListener); + } + + if (choiceMode == CHOICE_MODE_MULTIPLE || choiceMode == CHOICE_MODE_MULTIPLE_MODAL) { + // boolean oldValue = checkStates.get(position); + checkStates.put(position, value); + if (value) { + checkedItemCount++; + } else { + checkedItemCount--; + } + if (actionMode != null) { + final long id = getAdapter().getItemId(position); + choiceModeListener.onItemCheckedStateChanged(actionMode, position, id, value); + } + } else { + if (value || isItemChecked(position)) { + checkStates.clear(); + } + // this may end up selecting the value we just cleared but this way + // we ensure length of checkStates is 1, a fact getCheckedItemPosition + // relies on + if (value) { + checkStates.put(position, true); + } + } + requestLayout(); + invalidate(); + } +} diff --git a/core/src/com/commonsware/cwac/merge/MergeAdapter.java b/core/src/com/commonsware/cwac/merge/MergeAdapter.java new file mode 100644 index 00000000..a713b862 --- /dev/null +++ b/core/src/com/commonsware/cwac/merge/MergeAdapter.java @@ -0,0 +1,481 @@ +/*** + Copyright (c) 2008-2009 CommonsWare, LLC + Portions (c) 2009 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 com.commonsware.cwac.merge; + +import android.database.DataSetObserver; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ListAdapter; +import android.widget.SectionIndexer; +import java.util.ArrayList; +import java.util.List; +import com.commonsware.cwac.sacklist.SackOfViewsAdapter; + +/** + * Adapter that merges multiple child adapters and views + * into a single contiguous whole. + * + * Adapters used as pieces within MergeAdapter must have + * view type IDs monotonically increasing from 0. Ideally, + * adapters also have distinct ranges for their row ids, as + * returned by getItemId(). + * + */ +public class MergeAdapter extends BaseAdapter implements SectionIndexer { + protected PieceStateRoster pieces=new PieceStateRoster(); + + /** + * Stock constructor, simply chaining to the superclass. + */ + public MergeAdapter() { + super(); + } + + /** + * Adds a new adapter to the roster of things to appear in + * the aggregate list. + * + * @param adapter + * Source for row views for this section + */ + public void addAdapter(ListAdapter adapter) { + pieces.add(adapter); + adapter.registerDataSetObserver(new CascadeDataSetObserver()); + } + + /** + * Adds a new View to the roster of things to appear in + * the aggregate list. + * + * @param view + * Single view to add + */ + public void addView(View view) { + addView(view, false); + } + + /** + * Adds a new View to the roster of things to appear in + * the aggregate list. + * + * @param view + * Single view to add + * @param enabled + * false if views are disabled, true if enabled + */ + public void addView(View view, boolean enabled) { + ArrayList list=new ArrayList(1); + + list.add(view); + + addViews(list, enabled); + } + + /** + * Adds a list of views to the roster of things to appear + * in the aggregate list. + * + * @param views + * List of views to add + */ + public void addViews(List views) { + addViews(views, false); + } + + /** + * Adds a list of views to the roster of things to appear + * in the aggregate list. + * + * @param views + * List of views to add + * @param enabled + * false if views are disabled, true if enabled + */ + public void addViews(List views, boolean enabled) { + if (enabled) { + addAdapter(new EnabledSackAdapter(views)); + } + else { + addAdapter(new SackOfViewsAdapter(views)); + } + } + + /** + * Get the data item associated with the specified + * position in the data set. + * + * @param position + * Position of the item whose data we want + */ + @Override + public Object getItem(int position) { + for (ListAdapter piece : getPieces()) { + int size=piece.getCount(); + + if (position < size) { + return(piece.getItem(position)); + } + + position-=size; + } + + return(null); + } + + /** + * Get the adapter associated with the specified position + * in the data set. + * + * @param position + * Position of the item whose adapter we want + */ + public ListAdapter getAdapter(int position) { + for (ListAdapter piece : getPieces()) { + int size=piece.getCount(); + + if (position < size) { + return(piece); + } + + position-=size; + } + + return(null); + } + + /** + * How many items are in the data set represented by this + * Adapter. + */ + @Override + public int getCount() { + int total=0; + + for (ListAdapter piece : getPieces()) { + total+=piece.getCount(); + } + + return(total); + } + + /** + * Returns the number of types of Views that will be + * created by getView(). + */ + @Override + public int getViewTypeCount() { + int total=0; + + for (PieceState piece : pieces.getRawPieces()) { + total+=piece.adapter.getViewTypeCount(); + } + + return(Math.max(total, 1)); // needed for + // setListAdapter() before + // content add' + } + + /** + * Get the type of View that will be created by getView() + * for the specified item. + * + * @param position + * Position of the item whose data we want + */ + @Override + public int getItemViewType(int position) { + int typeOffset=0; + int result=-1; + + for (PieceState piece : pieces.getRawPieces()) { + if (piece.isActive) { + int size=piece.adapter.getCount(); + + if (position < size) { + result=typeOffset + piece.adapter.getItemViewType(position); + break; + } + + position-=size; + } + + typeOffset+=piece.adapter.getViewTypeCount(); + } + + return(result); + } + + /** + * Are all items in this ListAdapter enabled? If yes it + * means all items are selectable and clickable. + */ + @Override + public boolean areAllItemsEnabled() { + return(false); + } + + /** + * Returns true if the item at the specified position is + * not a separator. + * + * @param position + * Position of the item whose data we want + */ + @Override + public boolean isEnabled(int position) { + for (ListAdapter piece : getPieces()) { + int size=piece.getCount(); + + if (position < size) { + return(piece.isEnabled(position)); + } + + position-=size; + } + + return(false); + } + + /** + * Get a View that displays the data at the specified + * position in the data set. + * + * @param position + * Position of the item whose data we want + * @param convertView + * View to recycle, if not null + * @param parent + * ViewGroup containing the returned View + */ + @Override + public View getView(int position, View convertView, ViewGroup parent) { + for (ListAdapter piece : getPieces()) { + int size=piece.getCount(); + + if (position < size) { + + return(piece.getView(position, convertView, parent)); + } + + position-=size; + } + + return(null); + } + + /** + * Get the row id associated with the specified position + * in the list. + * + * @param position + * Position of the item whose data we want + */ + @Override + public long getItemId(int position) { + for (ListAdapter piece : getPieces()) { + int size=piece.getCount(); + + if (position < size) { + return(piece.getItemId(position)); + } + + position-=size; + } + + return(-1); + } + + @Override + public int getPositionForSection(int section) { + int position=0; + + for (ListAdapter piece : getPieces()) { + if (piece instanceof SectionIndexer) { + Object[] sections=((SectionIndexer)piece).getSections(); + int numSections=0; + + if (sections != null) { + numSections=sections.length; + } + + if (section < numSections) { + return(position + ((SectionIndexer)piece).getPositionForSection(section)); + } + else if (sections != null) { + section-=numSections; + } + } + + position+=piece.getCount(); + } + + return(0); + } + + @Override + public int getSectionForPosition(int position) { + int section=0; + + for (ListAdapter piece : getPieces()) { + int size=piece.getCount(); + + if (position < size) { + if (piece instanceof SectionIndexer) { + return(section + ((SectionIndexer)piece).getSectionForPosition(position)); + } + + return(0); + } + else { + if (piece instanceof SectionIndexer) { + Object[] sections=((SectionIndexer)piece).getSections(); + + if (sections != null) { + section+=sections.length; + } + } + } + + position-=size; + } + + return(0); + } + + @Override + public Object[] getSections() { + ArrayList sections=new ArrayList(); + + for (ListAdapter piece : getPieces()) { + if (piece instanceof SectionIndexer) { + Object[] curSections=((SectionIndexer)piece).getSections(); + + if (curSections != null) { + for (Object section : curSections) { + sections.add(section); + } + } + } + } + + if (sections.size() == 0) { + return(new String[0]); + } + + return(sections.toArray(new Object[0])); + } + + public void setActive(ListAdapter adapter, boolean isActive) { + pieces.setActive(adapter, isActive); + notifyDataSetChanged(); + } + + public void setActive(View v, boolean isActive) { + pieces.setActive(v, isActive); + notifyDataSetChanged(); + } + + protected List getPieces() { + return(pieces.getPieces()); + } + + private static class PieceState { + ListAdapter adapter; + boolean isActive=true; + + PieceState(ListAdapter adapter, boolean isActive) { + this.adapter=adapter; + this.isActive=isActive; + } + } + + private static class PieceStateRoster { + protected ArrayList pieces=new ArrayList(); + protected ArrayList active=null; + + void add(ListAdapter adapter) { + pieces.add(new PieceState(adapter, true)); + } + + void setActive(ListAdapter adapter, boolean isActive) { + for (PieceState state : pieces) { + if (state.adapter==adapter) { + state.isActive=isActive; + active=null; + break; + } + } + } + + void setActive(View v, boolean isActive) { + for (PieceState state : pieces) { + if (state.adapter instanceof SackOfViewsAdapter && + ((SackOfViewsAdapter)state.adapter).hasView(v)) { + state.isActive=isActive; + active=null; + break; + } + } + } + + List getRawPieces() { + return(pieces); + } + + List getPieces() { + if (active == null) { + active=new ArrayList(); + + for (PieceState state : pieces) { + if (state.isActive) { + active.add(state.adapter); + } + } + } + + return(active); + } + } + + private static class EnabledSackAdapter extends SackOfViewsAdapter { + public EnabledSackAdapter(List views) { + super(views); + } + + @Override + public boolean areAllItemsEnabled() { + return(true); + } + + @Override + public boolean isEnabled(int position) { + return(true); + } + } + + private class CascadeDataSetObserver extends DataSetObserver { + @Override + public void onChanged() { + notifyDataSetChanged(); + } + + @Override + public void onInvalidated() { + notifyDataSetInvalidated(); + } + } +} \ No newline at end of file diff --git a/core/src/com/commonsware/cwac/sacklist/SackOfViewsAdapter.java b/core/src/com/commonsware/cwac/sacklist/SackOfViewsAdapter.java new file mode 100644 index 00000000..2d248e58 --- /dev/null +++ b/core/src/com/commonsware/cwac/sacklist/SackOfViewsAdapter.java @@ -0,0 +1,177 @@ +/*** + Copyright (c) 2008-2009 CommonsWare, LLC + Portions (c) 2009 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 com.commonsware.cwac.sacklist; + +import java.util.ArrayList; +import java.util.List; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; + +/** + * Adapter that simply returns row views from a list. + * + * If you supply a size, you must implement newView(), to + * create a required view. The adapter will then cache these + * views. + * + * If you supply a list of views in the constructor, that + * list will be used directly. If any elements in the list + * are null, then newView() will be called just for those + * slots. + * + * Subclasses may also wish to override areAllItemsEnabled() + * (default: false) and isEnabled() (default: false), if some + * of their rows should be selectable. + * + * It is assumed each view is unique, and therefore will not + * get recycled. + * + * Note that this adapter is not designed for long lists. It + * is more for screens that should behave like a list. This + * is particularly useful if you combine this with other + * adapters (e.g., SectionedAdapter) that might have an + * arbitrary number of rows, so it all appears seamless. + */ +public class SackOfViewsAdapter extends BaseAdapter { + private List views=null; + + /** + * Constructor creating an empty list of views, but with + * a specified count. Subclasses must override newView(). + */ + public SackOfViewsAdapter(int count) { + super(); + + views=new ArrayList(count); + + for (int i=0;i views) { + super(); + + this.views=views; + } + + /** + * Get the data item associated with the specified + * position in the data set. + * @param position Position of the item whose data we want + */ + @Override + public Object getItem(int position) { + return(views.get(position)); + } + + /** + * How many items are in the data set represented by this + * Adapter. + */ + @Override + public int getCount() { + return(views.size()); + } + + /** + * Returns the number of types of Views that will be + * created by getView(). + */ + @Override + public int getViewTypeCount() { + return(getCount()); + } + + /** + * Get the type of View that will be created by getView() + * for the specified item. + * @param position Position of the item whose data we want + */ + @Override + public int getItemViewType(int position) { + return(position); + } + + /** + * Are all items in this ListAdapter enabled? If yes it + * means all items are selectable and clickable. + */ + @Override + public boolean areAllItemsEnabled() { + return(false); + } + + /** + * Returns true if the item at the specified position is + * not a separator. + * @param position Position of the item whose data we want + */ + @Override + public boolean isEnabled(int position) { + return(false); + } + + /** + * Get a View that displays the data at the specified + * position in the data set. + * @param position Position of the item whose data we want + * @param convertView View to recycle, if not null + * @param parent ViewGroup containing the returned View + */ + @Override + public View getView(int position, View convertView, + ViewGroup parent) { + View result=views.get(position); + + if (result==null) { + result=newView(position, parent); + views.set(position, result); + } + + return(result); + } + + /** + * Get the row id associated with the specified position + * in the list. + * @param position Position of the item whose data we want + */ + @Override + public long getItemId(int position) { + return(position); + } + + public boolean hasView(View v) { + return(views.contains(v)); + } + + /** + * Create a new View to go into the list at the specified + * position. + * @param position Position of the item whose data we want + * @param parent ViewGroup containing the returned View + */ + protected View newView(int position, ViewGroup parent) { + throw new RuntimeException("You must override newView()!"); + } +} \ No newline at end of file diff --git a/core/src/fr/marvinlabs/widget/CheckableRelativeLayout.java b/core/src/fr/marvinlabs/widget/CheckableRelativeLayout.java new file mode 100644 index 00000000..91932561 --- /dev/null +++ b/core/src/fr/marvinlabs/widget/CheckableRelativeLayout.java @@ -0,0 +1,100 @@ +package fr.marvinlabs.widget; + +import java.util.ArrayList; +import java.util.List; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Checkable; +import android.widget.RelativeLayout; + +/** + * Extension of a relative layout to provide a checkable behaviour + * + * @author marvinlabs + */ +public class CheckableRelativeLayout extends RelativeLayout implements Checkable { + + private boolean isChecked; + private List checkableViews; + + public CheckableRelativeLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + initialise(attrs); + } + + public CheckableRelativeLayout(Context context, AttributeSet attrs) { + super(context, attrs); + initialise(attrs); + } + + public CheckableRelativeLayout(Context context) { + super(context); + initialise(null); + } + + /* + * @see android.widget.Checkable#isChecked() + */ + public boolean isChecked() { + return isChecked; + } + + /* + * @see android.widget.Checkable#setChecked(boolean) + */ + public void setChecked(boolean isChecked) { + this.isChecked = isChecked; + for (Checkable c : checkableViews) { + c.setChecked(isChecked); + } + } + + /* + * @see android.widget.Checkable#toggle() + */ + public void toggle() { + this.isChecked = !this.isChecked; + for (Checkable c : checkableViews) { + c.toggle(); + } + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + + final int childCount = this.getChildCount(); + for (int i = 0; i < childCount; ++i) { + findCheckableChildren(this.getChildAt(i)); + } + } + + /** + * Read the custom XML attributes + */ + private void initialise(AttributeSet attrs) { + this.isChecked = false; + this.checkableViews = new ArrayList(5); + } + + /** + * Add to our checkable list all the children of the view that implement the + * interface Checkable + */ + private void findCheckableChildren(View v) { + if (v instanceof Checkable) { + this.checkableViews.add((Checkable) v); + } + + if (v instanceof ViewGroup) { + final ViewGroup vg = (ViewGroup) v; + final int childCount = vg.getChildCount(); + for (int i = 0; i < childCount; ++i) { + findCheckableChildren(vg.getChildAt(i)); + } + } + } +} diff --git a/core/src/fr/marvinlabs/widget/InertCheckBox.java b/core/src/fr/marvinlabs/widget/InertCheckBox.java new file mode 100644 index 00000000..5dd3080b --- /dev/null +++ b/core/src/fr/marvinlabs/widget/InertCheckBox.java @@ -0,0 +1,70 @@ +package fr.marvinlabs.widget; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.widget.CheckBox; + +/** + * CheckBox that does not react to any user event in order to let the container handle them. + */ +public class InertCheckBox extends CheckBox { + + // Provide the same constructors as the superclass + public InertCheckBox(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + // Provide the same constructors as the superclass + public InertCheckBox(Context context, AttributeSet attrs) { + super(context, attrs); + } + + // Provide the same constructors as the superclass + public InertCheckBox(Context context) { + super(context); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + // Make the checkbox not respond to any user event + return false; + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + // Make the checkbox not respond to any user event + return false; + } + + @Override + public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) { + // Make the checkbox not respond to any user event + return false; + } + + @Override + public boolean onKeyPreIme(int keyCode, KeyEvent event) { + // Make the checkbox not respond to any user event + return false; + } + + @Override + public boolean onKeyShortcut(int keyCode, KeyEvent event) { + // Make the checkbox not respond to any user event + return false; + } + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + // Make the checkbox not respond to any user event + return false; + } + + @Override + public boolean onTrackballEvent(MotionEvent event) { + // Make the checkbox not respond to any user event + return false; + } +} diff --git a/core/src/org/transdroid/core/app/search/GoogleWebSearchBarcodeResolver.java b/core/src/org/transdroid/core/app/search/GoogleWebSearchBarcodeResolver.java new file mode 100644 index 00000000..d681deba --- /dev/null +++ b/core/src/org/transdroid/core/app/search/GoogleWebSearchBarcodeResolver.java @@ -0,0 +1,140 @@ +/* + * This file is part of Transdroid + * + * Transdroid is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Transdroid is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Transdroid. If not, see . + * + */ +package org.transdroid.core.app.search; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; + +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.DefaultHttpClient; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.transdroid.daemon.util.HttpHelper; + +public class GoogleWebSearchBarcodeResolver { + + public static final String apiUrl = "http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=%s"; + + public static String resolveBarcode(String barcode) { + + try { + // We use the Google AJAX Search API to get a JSON-formatted list of web search results + String callUrl = apiUrl.replace("%s", barcode); + DefaultHttpClient httpclient = new DefaultHttpClient(); + HttpGet httpget = new HttpGet(callUrl); + HttpResponse response = httpclient.execute(httpget); + InputStream instream = response.getEntity().getContent(); + String result = HttpHelper.ConvertStreamToString(instream); + JSONArray results = new JSONObject(result).getJSONObject("responseData").getJSONArray("results"); + + // We will combine and filter multiple results, if there are any + if (results.length() < 1) { + return null; + } + return stripGarbage(results, barcode); + } catch (Exception e) { + return null; + } + + } + + private static String stripGarbage(JSONArray results, String barcode) throws JSONException { + + String good = " abcdefghijklmnopqrstuvwxyz"; + final int MAX_TITLE_CONSIDER = 4; + final int MAX_MISSING = 1; + final int MIN_TITLE_CONSIDER = 2; + + // First gather the titles for the first MAX_TITLE_CONSIDER results + List titles = new ArrayList(); + for (int i = 0; i < results.length() && i < MAX_TITLE_CONSIDER; i++) { + + String title = results.getJSONObject(i).getString("titleNoFormatting"); + + // Make string lowercase first + title = title.toLowerCase(Locale.US); + + // Remove the barcode number if it's there + title = title.replace(barcode, ""); + + // Remove unwanted words and HTML special chars + for (String rem : new String[] { "dvd", "blu-ray", "bluray", "&", """, "'", "<", ">" }) { + title = title.replace(rem, ""); + } + + // Remove all non-alphanumeric (and space) characters + String result = ""; + for ( int j = 0; j < title.length(); j++ ) { + if ( good.indexOf(title.charAt(j)) >= 0 ) + result += title.charAt(j); + } + + // Remove double spaces + while (result.contains(" ")) { + result = result.replace(" ", " "); + } + + titles.add(result); + + } + + // Only retain the words that are missing in at most one of the search result titles + List allWords = new ArrayList(); + for (String title : titles) { + for (String word : Arrays.asList(title.split(" "))) { + if (!allWords.contains(word)) { + allWords.add(word); + } + } + } + List remainingWords = new ArrayList(); + int allowMissing = Math.min(MAX_MISSING, Math.max(titles.size() - MIN_TITLE_CONSIDER, 0)); + for (String word : allWords) { + + int missing = 0; + for (String title : titles) { + if (!title.contains(word)) { + // The word is not contained in this result title + missing++; + if (missing > allowMissing) { + // Already misssing more than once, no need to look further + break; + } + } + } + if (missing <= allowMissing) { + // The word was only missing at most once, so we keep it + remainingWords.add(word); + } + } + + // Now the query is the concatenation of the words remaining; with spaces in between + String query = ""; + for (String word : remainingWords) { + query += " " + word; + } + return query.length() > 0? query.substring(1): null; + + } + +} diff --git a/core/src/org/transdroid/core/app/search/SearchHelper.java b/core/src/org/transdroid/core/app/search/SearchHelper.java new file mode 100644 index 00000000..1eca6cb5 --- /dev/null +++ b/core/src/org/transdroid/core/app/search/SearchHelper.java @@ -0,0 +1,116 @@ +package org.transdroid.core.app.search; + +import java.util.ArrayList; +import java.util.List; + +import org.androidannotations.annotations.EBean; +import org.androidannotations.annotations.EBean.Scope; +import org.androidannotations.annotations.RootContext; + +import android.content.ContentProviderClient; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; + +@EBean(scope = Scope.Singleton) +public class SearchHelper { + + static final int CURSOR_SEARCH_ID = 0; + static final int CURSOR_SEARCH_NAME = 1; + static final int CURSOR_SEARCH_TORRENTURL = 2; + static final int CURSOR_SEARCH_DETAILSURL = 3; + static final int CURSOR_SEARCH_SIZE = 4; + static final int CURSOR_SEARCH_ADDED = 5; + static final int CURSOR_SEARCH_SEEDERS = 6; + static final int CURSOR_SEARCH_LEECHERS = 7; + + static final int CURSOR_SITE_ID = 0; + static final int CURSOR_SITE_CODE = 1; + static final int CURSOR_SITE_NAME = 2; + static final int CURSOR_SITE_RSSURL = 3; + + @RootContext + protected Context context; + + public enum SearchSortOrder { + Combined, BySeeders + } + + /** + * Return whether the Torrent Search package is installed and available to query against + * @return True if the available sites can be retrieved from the content provider, false otherwise + */ + public boolean isTorrentSearchInstalled() { + return getAvailableSites() != null; + } + + /** + * Queries the Torrent Search package for all available in-app search sites. This method is synchronous. + * @return A list of available search sites as POJOs, or null if the Torrent Search package is not installed + */ + public List getAvailableSites() { + + // Try to access the TorrentSitesProvider of the Torrent Search app + Uri uri = Uri.parse("content://org.transdroid.search.torrentsitesprovider/sites"); + ContentProviderClient test = context.getContentResolver().acquireContentProviderClient(uri); + if (test == null) { + // Torrent Search package is not yet installed + return null; + } + + // Query the available in-app torrent search sites + Cursor cursor = context.getContentResolver().query(uri, null, null, null, null); + if (cursor.moveToFirst()) { + List sites = new ArrayList(); + do { + // Read the cursor fields into the SearchSite object + sites.add(new SearchSite(cursor.getInt(CURSOR_SITE_ID), cursor.getString(CURSOR_SITE_CODE), cursor + .getString(CURSOR_SITE_NAME), cursor.getString(CURSOR_SITE_RSSURL))); + } while (cursor.moveToNext()); + cursor.close(); + return sites; + } + + return null; + + } + + /** + * Queries the Torrent Search module to search for torrents on the web. This method is synchornous and should always + * be called in a background thread. + * @param query The search query to pass to the torrent site + * @param site The site to search, as retrieved from the TorrentSitesProvider, or null if the Torrent Search package + * @param sortBy.name() The sort order to request from the torrent site, if supported + * @return A list of torrent search results as POJOs, or null if the Torrent Search package is not installed + */ + public List search(String query, SearchSite site, SearchSortOrder sortBy) { + + // Try to query the TorrentSearchProvider to search for torrents on the web + Uri uri = Uri.parse("content://org.transdroid.search.torrentsearchprovider/search/" + query); + Cursor cursor; + if (site == null) { + // If no explicit site was supplied, rely on the Torrent Search package's default + cursor = context.getContentResolver().query(uri, null, null, null, sortBy.name()); + } else { + cursor = context.getContentResolver().query(uri, null, "SITE = ?", new String[] { site.getKey() }, + sortBy.name()); + } + if (cursor.moveToFirst()) { + List results = new ArrayList(); + do { + // Read the cursor fields into the SearchResult object + results.add(new SearchResult(cursor.getInt(CURSOR_SEARCH_ID), cursor.getString(CURSOR_SEARCH_NAME), + cursor.getString(CURSOR_SEARCH_TORRENTURL), cursor.getString(CURSOR_SEARCH_DETAILSURL), cursor + .getString(CURSOR_SEARCH_SIZE), cursor.getLong(CURSOR_SEARCH_ADDED), cursor + .getString(CURSOR_SEARCH_SEEDERS), cursor.getString(CURSOR_SEARCH_LEECHERS))); + } while (cursor.moveToNext()); + cursor.close(); + return results; + } + + // Torrent Search package is not yet installed + return null; + + } + +} diff --git a/core/src/org/transdroid/core/app/search/SearchResult.java b/core/src/org/transdroid/core/app/search/SearchResult.java new file mode 100644 index 00000000..6bbfaa76 --- /dev/null +++ b/core/src/org/transdroid/core/app/search/SearchResult.java @@ -0,0 +1,64 @@ +package org.transdroid.core.app.search; + +import java.util.Date; + +/** + * Represents a search result as retrieved by querying the Torrent Search package. + * @author Eric Kok + */ +public class SearchResult { + + private final int id; + private final String name; + private final String torrentUrl; + private final String detailsUrl; + private final String size; + private final Date addedOn; + private final String seeders; + private final String leechers; + + public SearchResult(int id, String name, String torrentUrl, String detailsUrl, String size, long addedOnTime, + String seeders, String leechers) { + this.id = id; + this.name = name; + this.torrentUrl = torrentUrl; + this.detailsUrl = detailsUrl; + this.size = size; + this.addedOn = (addedOnTime == -1L) ? null : new Date(addedOnTime); + this.seeders = seeders; + this.leechers = leechers; + } + + public int getId() { + return id; + } + + public String getName() { + return name; + } + + public String getTorrentUrl() { + return torrentUrl; + } + + public String getDetailsUrl() { + return detailsUrl; + } + + public String getSize() { + return size; + } + + public Date getAddedOn() { + return addedOn; + } + + public String getSeeders() { + return seeders; + } + + public String getLeechers() { + return leechers; + } + +} diff --git a/core/src/org/transdroid/core/app/search/SearchSite.java b/core/src/org/transdroid/core/app/search/SearchSite.java new file mode 100644 index 00000000..a30ad09e --- /dev/null +++ b/core/src/org/transdroid/core/app/search/SearchSite.java @@ -0,0 +1,40 @@ +package org.transdroid.core.app.search; + +import org.transdroid.core.gui.lists.SimpleListItem; + +/** + * Represents an available torrent site that can be searched using the Torrent Search package. + * @author Eric Kok + */ +public class SearchSite implements SimpleListItem { + + private final int id; + private final String key; + private final String name; + private final String rssFeedUrl; + + public SearchSite(int id, String key, String name, String rssFeedUrl) { + this.id = id; + this.key = key; + this.name = name; + this.rssFeedUrl = rssFeedUrl; + } + + public int getId() { + return id; + } + + public String getKey() { + return key; + } + + @Override + public String getName() { + return name; + } + + public String getRssFeedUrl() { + return rssFeedUrl; + } + +} diff --git a/core/src/org/transdroid/core/app/settings/ApplicationSettings.java b/core/src/org/transdroid/core/app/settings/ApplicationSettings.java new file mode 100644 index 00000000..6d376232 --- /dev/null +++ b/core/src/org/transdroid/core/app/settings/ApplicationSettings.java @@ -0,0 +1,356 @@ +package org.transdroid.core.app.settings; + +import java.util.ArrayList; +import java.util.List; + +import org.androidannotations.annotations.EBean; +import org.androidannotations.annotations.EBean.Scope; +import org.androidannotations.annotations.RootContext; +import org.transdroid.daemon.Daemon; +import org.transdroid.daemon.OS; +import org.transdroid.daemon.TorrentsSortBy; + +import android.content.Context; +import android.content.SharedPreferences; +import android.content.SharedPreferences.Editor; +import android.preference.PreferenceManager; + +/** + * Singleton object to access all application settings, including stored servers, web search sites and RSS feeds. + * @author Eric Kok + */ +@EBean(scope = Scope.Singleton) +public class ApplicationSettings { + + @RootContext + protected Context context; + private SharedPreferences prefs; + + protected ApplicationSettings(Context context) { + prefs = PreferenceManager.getDefaultSharedPreferences(context); + } + + /** + * Returns all available user-configured servers + * @return A list of all stored server settings objects + */ + public List getServerSettings() { + List servers = new ArrayList(); + for (int i = 0; i <= getMaxServer(); i++) { + servers.add(getServerSetting(i)); + } + return servers; + } + + /** + * Returns the order number/identifying key of the last server + * @return The zero-based order number (index) of the last stored server settings + */ + public int getMaxServer() { + for (int i = 0; true; i++) { + if (prefs.getString("server_type_" + i, null) == null) + return i - 1; + } + } + + /** + * Returns the user-specified server settings for a specific server. WARNING: This method does not check if the + * settings actually exist and may rely on empty defaults if called not a non-existing server. + * @param order The order number/identifying key of the settings to retrieve + * @return The server settings object, loaded from shared preferences + */ + public ServerSetting getServerSetting(int order) { + // @formatter:off + Daemon type = Daemon.fromCode(prefs.getString("server_type_" + order, null)); + boolean ssl = prefs.getBoolean("server_sslenabled_" + order, false); + String defaultPort = Integer.toString(Daemon.getDefaultPortNumber(type, ssl)); + return new ServerSetting(order, + prefs.getString("server_name_" + order, null), + type, + prefs.getString("server_address_" + order, null), + prefs.getString("server_localaddress_" + order, null), + prefs.getString("server_localnetwork_" + order, null), + Integer.parseInt(prefs.getString("server_port_" + order, defaultPort)), + ssl, + prefs.getBoolean("server_ssltrustall_" + order, false), + prefs.getString("server_ssltrustkey_" + order, null), + prefs.getString("server_folder_" + order, null), + prefs.getBoolean("server_useauth_" + order, true), + prefs.getString("server_user_" + order, null), + prefs.getString("server_pass_" + order, null), + prefs.getString("server_extrapass_" + order, null), + OS.fromCode(prefs.getString("server_os_" + order, "type_linux")), + prefs.getString("server_downloaddir_" + order, null), + prefs.getString("server_ftpurl_" + order, null), + prefs.getString("server_ftppass_" + order, null), + Integer.parseInt(prefs.getString("server_timeout_"+ order, "8")), + prefs.getBoolean("server_alarmfinished_" + order, true), + prefs.getBoolean("server_alarmnew_" + order, false), false); + // @formatter:on + } + + /** + * Removes all settings related to a configured server. Since servers are ordered, the order of the remaining + * servers will be updated accordingly. + * @param order The identifying order number/key of the settings to remove + */ + public void removeServerSettings(int order) { + if (prefs.getString("server_type_" + order, null) == null) + return; // The settings that were requested to be removed do not exist + + // Copy all settings higher than the supplied order number to the previous spot + Editor edit = prefs.edit(); + int max = getMaxServer(); + for (int i = order; i < max; i++) { + edit.putString("server_name_" + i, prefs.getString("server_name_" + (i + 1), null)); + edit.putString("server_type_" + i, prefs.getString("server_type_" + (i + 1), null)); + edit.putString("server_address_" + i, prefs.getString("server_address_" + (i + 1), null)); + edit.putString("server_localaddress_" + i, prefs.getString("server_localaddress_" + (i + 1), null)); + edit.putString("server_localnetwork_" + i, prefs.getString("server_localnetwork_" + (i + 1), null)); + edit.putString("server_port_" + i, prefs.getString("server_port_" + (i + 1), null)); + edit.putBoolean("server_sslenabled_" + i, prefs.getBoolean("server_sslenabled_" + (i + 1), false)); + edit.putBoolean("server_ssltrustall_" + i, prefs.getBoolean("server_ssltrustall_" + (i + 1), false)); + edit.putString("server_ssltrustkey_" + i, prefs.getString("server_ssltrustkey_" + (i + 1), null)); + edit.putString("server_folder_" + i, prefs.getString("server_folder_" + (i + 1), null)); + edit.putBoolean("server_useauth_" + i, prefs.getBoolean("server_useauth_" + (i + 1), true)); + edit.putString("server_user_" + i, prefs.getString("server_user_" + (i + 1), null)); + edit.putString("server_pass_" + i, prefs.getString("server_pass_" + (i + 1), null)); + edit.putString("server_extrapass_" + i, prefs.getString("server_extrapass_" + (i + 1), null)); + edit.putString("server_os_" + i, prefs.getString("server_os_" + (i + 1), null)); + edit.putString("server_downloaddir_" + i, prefs.getString("server_downloaddir_" + (i + 1), null)); + edit.putString("server_ftpurl_" + i, prefs.getString("server_ftpurl_" + (i + 1), null)); + edit.putString("server_ftppass_" + i, prefs.getString("server_ftppass_" + (i + 1), null)); + edit.putString("server_timeout_" + i, prefs.getString("server_timeout_" + (i + 1), null)); + edit.putBoolean("server_alarmfinished_" + i, prefs.getBoolean("server_alarmfinished_" + (i + 1), true)); + edit.putBoolean("server_alarmfinished_" + i, prefs.getBoolean("server_alarmfinished_" + (i + 1), false)); + } + + // Remove the last settings, of which we are now sure are no longer required + edit.remove("server_name_" + max); + edit.remove("server_type_" + max); + edit.remove("server_address_" + max); + edit.remove("server_localaddress_" + max); + edit.remove("server_localnetwork_" + max); + edit.remove("server_port_" + max); + edit.remove("server_sslenabled_" + max); + edit.remove("server_ssltrustall_" + max); + edit.remove("server_ssltrustkey_" + max); + edit.remove("server_folder_" + max); + edit.remove("server_useauth_" + max); + edit.remove("server_user_" + max); + edit.remove("server_pass_" + max); + edit.remove("server_extrapass_" + max); + edit.remove("server_os_" + max); + edit.remove("server_downloaddir_" + max); + edit.remove("server_ftpurl_" + max); + edit.remove("server_ftppass_" + max); + edit.remove("server_timeout_" + max); + edit.remove("server_alarmfinished_" + max); + edit.remove("server_alarmfinished_" + max); + edit.commit(); + + } + + /** + * Returns the settings of the server that was last used by the user. As opposed to getLastUsedServerKey(int), this + * method checks whether a server was already registered as being last used and check whether the server still + * exists. It returns the first server if that fails. If no servers are configured, null is returned. + * @return A server settings object of the last used server (or, if not known, the first server), or null if no + * servers exist + */ + public ServerSetting getLastUsedServer() { + int max = getMaxServer(); // Zero-based index, so with max == 0 there is 1 server + if (max < 0) { + // No servers configured + return null; + } + int last = getLastUsedServerKey(); + if (last < 0 || last > max) { + // Last server was never set or no longer exists + return getServerSetting(0); + } + return getServerSetting(last); + } + + /** + * Returns the order number/unique key of the server that the used last used; use with getServerSettings(int) or + * call getLastUsedServer directly. WARNING: the returned integer may no longer refer to a known server settings + * object: check the bounds. + * @return An integer indicating the order number/key or the last used server, or -1 if it was not set + */ + public int getLastUsedServerKey() { + return prefs.getInt("system_lastusedserver", -1); + } + + /** + * Registers some server as being the last used by the user + * @param server The settings of the server that the user last used + */ + public void setLastUsedServer(ServerSetting server) { + setLastUsedServerKey(server.getOrder()); + } + + /** + * Registers the order number/unique key of some server as being last used by the user + * @param order The key identifying the specific server + */ + public void setLastUsedServerKey(int order) { + prefs.edit().putInt("system_lastusedserver", order).commit(); + } + + /** + * Returns all available user-configured web-based (as opped to in-app) search sites + * @return A list of all stored web search site settings objects + */ + public List getWebsearchSettings() { + List websearches = new ArrayList(); + for (int i = 0; i <= getMaxWebsearch(); i++) { + websearches.add(getWebsearchSetting(i)); + } + return websearches; + } + + /** + * Returns the order number/identifying key of the last web search site + * @return The zero-based order number (index) of the last stored web search site + */ + public int getMaxWebsearch() { + for (int i = 0; true; i++) { + if (prefs.getString("websearch_baseurl_" + i, null) == null) + return i - 1; + } + } + + /** + * Returns the user-specified web-based search site setting for a specific site + * @param order The order number/identifying key of the settings to retrieve + * @return The web search site settings object, loaded from shared preferences + */ + public WebsearchSetting getWebsearchSetting(int order) { + // @formatter:off + return new WebsearchSetting(order, + prefs.getString("websearch_name_" + order, null), + prefs.getString("websearch_baseurl_" + order, null)); + // @formatter:on + } + + /** + * Removes all settings related to a configured web-based search site. Since sites are ordered, the order of the + * remaining sites will be updated accordingly. + * @param order The identifying order number/key of the settings to remove + */ + public void removeWebsearchSettings(int order) { + if (prefs.getString("websearch_baseurl_" + order, null) == null) + return; // The settings that were requested to be removed do not exist + + // Copy all settings higher than the supplied order number to the previous spot + Editor edit = prefs.edit(); + int max = getMaxWebsearch(); + for (int i = order; i < max; i++) { + edit.putString("websearch_name_" + i, prefs.getString("websearch_name_" + (i + 1), null)); + edit.putString("websearch_baseurl_" + i, prefs.getString("websearch_baseurl_" + (i + 1), null)); + } + + // Remove the last settings, of which we are now sure are no longer required + edit.remove("websearch_name_" + max); + edit.remove("websearch_baseurl_" + max); + edit.commit(); + + } + + /** + * Returns all available user-configured RSS feeds + * @return A list of all stored RSS feed settings objects + */ + public List getRssfeedSettings() { + List rssfeeds = new ArrayList(); + for (int i = 0; i <= getMaxRssfeed(); i++) { + rssfeeds.add(getRssfeedSetting(i)); + } + return rssfeeds; + } + + /** + * Returns the order number/identifying key of the last stored RSS feed + * @return The zero-based order number (index) of the last stored RSS feed + */ + public int getMaxRssfeed() { + for (int i = 0; true; i++) { + if (prefs.getString("rssfeed_url_" + i, null) == null) + return i - 1; + } + } + + /** + * Returns the user-specified RSS feed setting for a specific feed + * @param order The order number/identifying key of the settings to retrieve + * @return The RSS feed settings object, loaded from shared preferences + */ + public RssfeedSetting getRssfeedSetting(int order) { + // @formatter:off + return new RssfeedSetting(order, + prefs.getString("rssfeed_name_" + order, null), + prefs.getString("rssfeed_url_" + order, null), + prefs.getBoolean("rssfeed_reqauth_" + order, false), + prefs.getString("rssfeed_lastnew_" + order, null)); + // @formatter:on + } + + /** + * Removes all settings related to a configured RSS feed. Since feeds are ordered, the order of the remaining feeds + * will be updated accordingly. + * @param order The identifying order number/key of the settings to remove + */ + public void removeRssfeedSettings(int order) { + if (prefs.getString("rssfeed_url_" + order, null) == null) + return; // The settings that were requested to be removed do not exist + + // Copy all settings higher than the supplied order number to the previous spot + Editor edit = prefs.edit(); + int max = getMaxRssfeed(); + for (int i = order; i < max; i++) { + edit.putString("rssfeed_name_" + i, prefs.getString("rssfeed_name_" + (i + 1), null)); + edit.putString("rssfeed_url_" + i, prefs.getString("rssfeed_url_" + (i + 1), null)); + edit.putBoolean("rssfeed_reqauth_" + i, prefs.getBoolean("rssfeed_reqauth_" + (i + 1), false)); + edit.putString("rssfeed_lastnew_" + i, prefs.getString("rssfeed_lastnew_" + (i + 1), null)); + } + + // Remove the last settings, of which we are now sure are no longer required + edit.remove("rssfeed_name_" + max); + edit.remove("rssfeed_url_" + max); + edit.remove("rssfeed_reqauth_" + max); + edit.remove("rssfeed_lastnew_" + max); + edit.commit(); + + } + + /** + * Registers the torrents list sort order as being last used by the user + * @param currentSortOrder The sort order property the user selected last + * @param currentSortAscending The sort order direction that was last used + */ + public void setLastUsedSortOrder(TorrentsSortBy currentSortOrder, boolean currentSortAscending) { + prefs.edit().putInt("system_lastusedsortorder", currentSortOrder.getCode()).commit(); + prefs.edit().putBoolean("system_lastusedsortdirection", currentSortAscending).commit(); + } + + /** + * Returns the sort order property that the user last used. Use together with {@link #getLastUsedSortDescending()} to + * get the full last used sort settings. + * @return The last used sort order enumeration value + */ + public TorrentsSortBy getLastUsedSortOrder() { + return TorrentsSortBy.getStatus(prefs.getInt("system_lastusedsortorder", TorrentsSortBy.Alphanumeric.getCode())); + } + + /** + * Returns the sort order direction that the user last used. Use together with {@link #getLastUsedSortOrder()} to + * get the full last used sort settings. + * @return True if the last used sort direction was descending, false otherwise (i.e. the default ascending + * direction) + */ + public boolean getLastUsedSortDescending() { + return prefs.getBoolean("system_lastusedsortdirection", false); + } + +} diff --git a/core/src/org/transdroid/core/app/settings/NotificationSettings.java b/core/src/org/transdroid/core/app/settings/NotificationSettings.java new file mode 100644 index 00000000..58bd4003 --- /dev/null +++ b/core/src/org/transdroid/core/app/settings/NotificationSettings.java @@ -0,0 +1,97 @@ +package org.transdroid.core.app.settings; + +import org.androidannotations.annotations.EBean; +import org.androidannotations.annotations.EBean.Scope; +import org.androidannotations.annotations.RootContext; +import org.transdroid.core.R; + +import android.content.Context; +import android.content.SharedPreferences; +import android.net.Uri; +import android.preference.PreferenceManager; +import android.provider.Settings; + +/** + * Allows instantiation of the settings specified in R.xml.pref_notifications. + * @author Eric Kok + */ +@EBean(scope = Scope.Singleton) +public class NotificationSettings { + + @RootContext + protected Context context; + private SharedPreferences prefs; + + protected NotificationSettings(Context context) { + prefs = PreferenceManager.getDefaultSharedPreferences(context); + } + + /** + * Whether the background service is enabled, i.e. whether the user want to receive notifications + * @return True if the server should be checked for torrent status updates + */ + public boolean isEnabled() { + return prefs.getBoolean("notifications_enabled", true); + } + + private String getRawInverval() { + return prefs.getString("notifications_interval", "10800"); + } + + /** + * Returns the interval between two server checks + * @return The interval, in milliseconds + */ + public Long getInvervalInMilliseconds() { + return Long.parseLong(getRawInverval()) * 1000L; + } + + private String getRawSound() { + return prefs.getString("notifications_sound", null); + } + + /** + * Returns the sound (ring tone) to play on a new notification, or null if it should not play any + * @return Either the user-specified sound, null if the user specified 'Silent' or the system default notification sound + */ + public Uri getSound() { + String raw = getRawSound(); + if (raw == null) + return null; + if (raw.equals("")) + return Settings.System.DEFAULT_NOTIFICATION_URI; + return Uri.parse(raw); + } + + /** + * Whether the device should vibrate on a new notification + * @return + */ + public boolean shouldVibrate() { + return prefs.getBoolean("notifications_vibrate", false); + } + + private int getRawLedColour() { + return prefs.getInt("notifications_ledcolour", -1); + } + + /** + * Returns the LED colour to use on a new notification + * @return The integer value of the user-specified or default colour + */ + public int getDesiredLedColour() { + int raw = getRawLedColour(); + if (raw <= 0) + return context.getResources().getColor(R.color.ledgreen); + return raw; + } + + /** + * Whether the background service should report to ADW Launcher + * @return True if the user want Transdroid to report to ADW Launcher + */ + public boolean shouldReportToAdwLauncher() { + return prefs.getBoolean("notifications_adwnotify", false); + } + +} diff --git a/core/src/org/transdroid/core/app/settings/RssfeedSetting.java b/core/src/org/transdroid/core/app/settings/RssfeedSetting.java new file mode 100644 index 00000000..cd90f932 --- /dev/null +++ b/core/src/org/transdroid/core/app/settings/RssfeedSetting.java @@ -0,0 +1,80 @@ +package org.transdroid.core.app.settings; + +import org.transdroid.core.gui.lists.SimpleListItem; + +import android.net.Uri; +import android.text.TextUtils; + +/** + * Represents a user-specified RSS feed. + * @author Eric Kok + */ +public class RssfeedSetting implements SimpleListItem { + + private static final String DEFAULT_NAME = "Default"; + + private final int order; + private final String name; + private final String url; + private final boolean requiresAuth; + private String lastNew; + + public RssfeedSetting(int order, String name, String baseUrl, boolean needsAuth, String lastNew) { + this.order = order; + this.name = name; + this.url = baseUrl; + this.requiresAuth = needsAuth; + this.lastNew = lastNew; + } + + public int getOrder() { + return order; + } + + @Override + public String getName() { + if (!TextUtils.isEmpty(name)) + return name; + if (!TextUtils.isEmpty(url)) { + String host = Uri.parse(url).getHost(); + return host == null? DEFAULT_NAME: host; + } + return DEFAULT_NAME; + } + + public String getUrl() { + return url; + } + + public boolean requiresExternalAuthentication() { + return requiresAuth; + } + + /** + * Returns the URL of the item that was the newest last time we checked this feed + * @return The last new item's URL as URL-encoded string + */ + public String getLastNew() { + // TODO: Persist this into Preferences + return this.lastNew; + } + + /** + * Record the URL of what is now the last item we retrieved + * @param lastNew The URL of the last new item as URL-encoded string + */ + public void setLastNew(String lastNew) { + this.lastNew = lastNew; + } + + /** + * Returns a nicely formatted identifier containing (a portion of) the feed URL + * @return A string to identify this feed's URL + */ + public String getHumanReadableIdentifier() { + String host = Uri.parse(url).getHost(); + String path = Uri.parse(url).getPath(); + return (host == null? null: host + (path == null? "": path)); + } + +} diff --git a/core/src/org/transdroid/core/app/settings/ServerSetting.java b/core/src/org/transdroid/core/app/settings/ServerSetting.java new file mode 100644 index 00000000..2b8690f0 --- /dev/null +++ b/core/src/org/transdroid/core/app/settings/ServerSetting.java @@ -0,0 +1,236 @@ +package org.transdroid.core.app.settings; + +import org.transdroid.core.gui.lists.SimpleListItem; +import org.transdroid.daemon.Daemon; +import org.transdroid.daemon.DaemonSettings; +import org.transdroid.daemon.IDaemonAdapter; +import org.transdroid.daemon.OS; + +import android.net.Uri; +import android.text.TextUtils; + +/** + * Represents a user-configured remote server. + * @author Eric Kok + */ +public class ServerSetting implements SimpleListItem { + + private static final String DEFAULT_NAME = "Default"; + + private final int key; + private final String name; + private final Daemon type; + private final String address; + private final String localAddress; + private final String localNetwork; + private final int port; + private final String folder; + private final boolean useAuthentication; + private final String username; + private final String password; + private final String extraPass; + private final OS os; + private final String downloadDir; + private final String ftpUrl; + private final String ftpPassword; + private final int timeout; + private final boolean alarmOnFinishedDownload; + private final boolean alarmOnNewTorrent; + private final boolean ssl; + private final boolean sslTrustAll; + private final String sslTrustKey; + private final boolean isAutoGenerated; + + /** + * Creates a daemon settings instance, providing full connection details + * @param name A name used to identify this server to the user + * @param type The server daemon type + * @param address The server domain name or IP address + * @param localAddress The server domain or IP address when connected to the server's local network + * @param localNetwork The server's local network SSID + * @param port The port on which the server daemon is running + * @param sslTrustKey The specific key that will be accepted. + * @param folder The server folder (like a virtual sub-folder or an SCGI mount point) + * @param useAuthentication Whether to use basic authentication + * @param username The user name to provide during authentication + * @param password The password to provide during authentication + * @param extraPass The Deluge web interface password + * @param downloadDir The default download directory (which may also be used as base directory for file paths) + * @param ftpUrl The partial URL to connect to when requesting FTP-style transfers + * @param timeout The number of seconds to wait before timing out a connection attempt + * @param isAutoGenerated Whether this setting was generated rather than manually inputed by the user + */ + public ServerSetting(int key, String name, Daemon type, String address, String localAddress, String localNetwork, + int port, boolean ssl, boolean sslTrustAll, String sslTrustKey, String folder, boolean useAuthentication, + String username, String password, String extraPass, OS os, String downloadDir, String ftpUrl, + String ftpPassword, int timeout, boolean alarmOnFinishedDownload, boolean alarmOnNewTorrent, + boolean isAutoGenerated) { + this.key = key; + this.name = name; + this.type = type; + this.address = address; + this.localAddress = localAddress; + this.localNetwork = localNetwork; + this.port = port; + this.ssl = ssl; + this.sslTrustAll = sslTrustAll; + this.sslTrustKey = sslTrustKey; + this.folder = folder; + this.useAuthentication = useAuthentication; + this.username = username; + this.password = password; + this.extraPass = extraPass; + this.os = os; + this.downloadDir = downloadDir; + this.ftpUrl = ftpUrl; + this.ftpPassword = ftpPassword; + this.timeout = timeout; + this.alarmOnFinishedDownload = alarmOnFinishedDownload; + this.alarmOnNewTorrent = alarmOnNewTorrent; + this.isAutoGenerated = isAutoGenerated; + } + + @Override + public String getName() { + if (!TextUtils.isEmpty(name)) + return name; + if (!TextUtils.isEmpty(address)) { + String host = Uri.parse(address).getHost(); + return host == null? DEFAULT_NAME: host; + } + return DEFAULT_NAME; + } + + public Daemon getType() { + return type; + } + + public String getAddress() { + return address; + } + + public String getLocalAddress() { + return localAddress; + } + + public String getLocalNetwork() { + return localNetwork; + } + + public int getPort() { + return port; + } + + public boolean getSsl() { + return ssl; + } + + public boolean getSslTrustAll() { + return sslTrustAll; + } + + public String getSslTrustKey() { + return sslTrustKey; + } + + public String getFolder() { + return folder; + } + + public boolean shouldUseAuthentication() { + return useAuthentication; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public String getExtraPassword() { + return extraPass; + } + + public OS getOS() { + return os; + } + + public String getDownloadDir() { + return downloadDir; + } + + public String getFtpUrl() { + return ftpUrl; + } + + public String getFtpPassword() { + return ftpPassword; + } + + public int getTimeoutInMilliseconds() { + return timeout * 1000; + } + + public boolean shouldAlarmOnFinishedDownload() { + return alarmOnFinishedDownload; + } + + public boolean shouldAlarmOnNewTorrent() { + return alarmOnNewTorrent; + } + + public boolean isAutoGenerated() { + return isAutoGenerated; + } + + public int getOrder() { + return this.key; + } + + public String getHumanReadableIdentifier() { + if (isAutoGenerated) { + // Hide the 'implementation details'; just give the username and server + return (this.shouldUseAuthentication() && !TextUtils.isEmpty(this.getUsername()) ? this.getUsername() + "@" + : "") + getAddress(); + } + return (this.ssl ? "https://" : "http://") + + (this.shouldUseAuthentication() && !TextUtils.isEmpty(this.getUsername()) ? this.getUsername() + "@" + : "") + getAddress() + ":" + getPort() + + (Daemon.supportsCustomFolder(getType()) && getFolder() != null ? getFolder() : ""); + } + + @Override + public boolean equals(Object o) { + if (o instanceof ServerSetting) { + // Directly compare order numbers/unique keys + return ((ServerSetting) o).getOrder() == this.key; + } else if (o instanceof DaemonSettings) { + // Old-style DaemonSettings objects can be equal if they were constructed from a ServerSettings object: + // idString should reflect the local key/order + return ((DaemonSettings) o).getIdString().equals(Integer.toString(this.key)); + } + // Other objects are never equal to this + return false; + } + + /** + * Returns the appropriate daemon adapter to which tasks can be executed, in accordance with this server's settings + * @return An IDaemonAdapter instance of the specific torrent client daemon type + */ + public IDaemonAdapter createServerAdapter() { + return type.createAdapter(convertToDaemonSettings()); + } + + private DaemonSettings convertToDaemonSettings() { + // Convert local server settings into an old-style DaemonSetting object + // The local integer key is converted to the idString string + // TODO: Add localaddress and localnetwork to DaemonSettings, or solve properly rework the Connect library + // handling of settings + return new DaemonSettings(name, type, address, port, ssl, sslTrustAll, sslTrustKey, + folder, useAuthentication, username, password, extraPass, os, downloadDir, ftpUrl, ftpPassword, + timeout, alarmOnFinishedDownload, alarmOnNewTorrent, Integer.toString(key), isAutoGenerated); + } + +} diff --git a/core/src/org/transdroid/core/app/settings/SettingsPersistence.java b/core/src/org/transdroid/core/app/settings/SettingsPersistence.java new file mode 100644 index 00000000..f860cf09 --- /dev/null +++ b/core/src/org/transdroid/core/app/settings/SettingsPersistence.java @@ -0,0 +1,269 @@ +package org.transdroid.core.app.settings; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.IOException; + +import org.androidannotations.annotations.Bean; +import org.androidannotations.annotations.EBean; +import org.androidannotations.annotations.EBean.Scope; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.transdroid.daemon.util.HttpHelper; + +import android.content.SharedPreferences; +import android.content.SharedPreferences.Editor; +import android.os.Environment; + +/** + * Singleton class that can persist user settings (servers, RSS feeds, etc.) to and from a plain text JSON file. + * + * @author Eric Kok + */ +@EBean(scope = Scope.Singleton) +public class SettingsPersistence { + + @Bean + protected ApplicationSettings applicationSettings; + @Bean + protected SystemSettings systemSettings; + + public static final String DEFAULT_SETTINGS_DIR = Environment.getExternalStorageDirectory().toString() + + "/Transdroid"; + public static final String DEFAULT_SETTINGS_FILENAME = "/settings.json"; + public static final File DEFAULT_SETTINGS_FILE = new File(DEFAULT_SETTINGS_DIR + DEFAULT_SETTINGS_FILENAME); + + /** + * Synchronously reads the server, web searches, RSS feed, background service and system settings from a file in + * JSON format. + * @param settingsFile The local file to write the settings to + * @throws FileNotFoundException Thrown when the settings file doesn't exist or couln't be read + * @throws JSONException Thrown when the file did not contain valid JSON content + */ + public void importSettings(SharedPreferences prefs, File settingsFile) throws FileNotFoundException, + JSONException { + + Editor editor = prefs.edit(); + + // Read the settings file + String raw = HttpHelper.ConvertStreamToString(new FileInputStream(settingsFile)); + JSONObject json = new JSONObject(raw); + + // Import servers + if (json.has("servers")) { + JSONArray servers = json.getJSONArray("servers"); + for (int i = 0; i < servers.length(); i++) { + JSONObject server = servers.getJSONObject(i); + String postfix = Integer.toString(applicationSettings.getMaxServer() + 1 + i); + + if (server.has("name")) + editor.putString("server_name_" + postfix, server.getString("name")); + if (server.has("type")) + editor.putString("server_type_" + postfix, server.getString("type")); + if (server.has("host")) + editor.putString("server_address_" + postfix, server.getString("host")); + if (server.has("local_network")) + editor.putString("server_localnetwork_" + postfix, server.getString("local_network")); + if (server.has("local_host")) + editor.putString("server_localaddress_" + postfix, server.getString("local_host")); + if (server.has("port")) + editor.putString("server_port_" + postfix, server.getString("port")); + if (server.has("ssl")) + editor.putBoolean("server_sslenabled_" + postfix, server.getBoolean("ssl")); + if (server.has("ssl_accept_all")) + editor.putBoolean("server_ssltrustall_" + postfix, server.getBoolean("ssl_accept_all")); + if (server.has("ssl_trust_key")) + editor.putString("server_ssltrustkey_" + postfix, server.getString("ssl_trust_key")); + if (server.has("folder")) + editor.putString("server_folder_" + postfix, server.getString("folder")); + if (server.has("use_auth")) + editor.putBoolean("server_useauth_" + postfix, server.getBoolean("use_auth")); + if (server.has("username")) + editor.putString("server_user_" + postfix, server.getString("username")); + if (server.has("password")) + editor.putString("server_pass_" + postfix, server.getString("password")); + if (server.has("extra_password")) + editor.putString("server_extrapass_" + postfix, server.getString("extra_password")); + if (server.has("os_type")) + editor.putString("server_os_" + postfix, server.getString("os_type")); + if (server.has("downloads_dir")) + editor.putString("server_downloaddir_" + postfix, server.getString("downloads_dir")); + if (server.has("base_ftp_url")) + editor.putString("server_ftpurl_" + postfix, server.getString("base_ftp_url")); + if (server.has("ftp_password")) + editor.putString("server_ftppass_" + postfix, server.getString("ftp_password")); + if (server.has("server_timeout")) + editor.putString("server_timeout_" + postfix, server.getString("server_timeout")); + if (server.has("download_alarm")) + editor.putBoolean("server_alarmfinished_" + postfix, server.getBoolean("download_alarm")); + if (server.has("new_torrent_alarm")) + editor.putBoolean("server_alarmnew_" + postfix, server.getBoolean("new_torrent_alarm")); + + } + } + + // Import web search sites + if (json.has("websites")) { + JSONArray sites = json.getJSONArray("websites"); + for (int i = 0; i < sites.length(); i++) { + JSONObject site = sites.getJSONObject(i); + String postfix = Integer.toString(applicationSettings.getMaxWebsearch() + 1 + i); + + if (site.has("name")) + editor.putString("websearch_name_" + postfix, site.getString("name")); + if (site.has("url")) + editor.putString("websearch_baseurl_" + postfix, site.getString("url")); + + } + } + + // Import RSS feeds + if (json.has("rssfeeds")) { + JSONArray feeds = json.getJSONArray("rssfeeds"); + for (int i = 0; i < feeds.length(); i++) { + JSONObject feed = feeds.getJSONObject(i); + String postfix = Integer.toString(applicationSettings.getMaxRssfeed() + 1 + i); + + if (feed.has("name")) + editor.putString("rssfeed_name_" + postfix, feed.getString("name")); + if (feed.has("url")) + editor.putString("rssfeed_url_" + postfix, feed.getString("url")); + if (feed.has("needs_auth")) + editor.putBoolean("rssfeed_reqauth_" + postfix, feed.getBoolean("needs_auth")); + if (feed.has("last_seen")) + editor.putString("rssfeed_lastnew_" + postfix, feed.getString("last_seen")); + + } + } + + // Import background service and system settings + if (json.has("alarm_enabled")) + editor.putBoolean("notifications_enabled", json.getBoolean("alarm_enabled")); + if (json.has("alarm_interval")) + editor.putString("notifications_interval", json.getString("alarm_interval")); + if (json.has("alarm_sound_uri")) + editor.putString("notifications_sound", json.getString("alarm_sound_uri")); + if (json.has("alarm_vibrate")) + editor.putBoolean("notifications_vibrate", json.getBoolean("alarm_vibrate")); + if (json.has("alarm_ledcolour")) + editor.putInt("notifications_ledcolour", json.getInt("alarm_ledcolour")); + if (json.has("alarm_adwnotifications")) + editor.putBoolean("notifications_adwnotify", json.getBoolean("alarm_adwnotifications")); + if (json.has("system_checkupdates")) + editor.putBoolean("system_checkupdates", json.getBoolean("system_checkupdates")); + if (json.has("system_usedarktheme")) + editor.putBoolean("system_usedarktheme", json.getBoolean("system_usedarktheme")); + + editor.commit(); + + } + + /** + * Synchronously writes the server, web searches, RSS feed, background service and system settings to a file in JSON + * format. + * @param prefs The application-global preferences object to write settings to + * @param settingsFile The local file to read the settings from + * @throws JSONException Thrown when the JSON content could not be constructed properly + * @throws IOException Thrown when the settings file could not be created or written to + */ + public void exportSettings(SharedPreferences prefs, File settingsFile) throws JSONException, IOException { + + // Create a single JSON object that will contain all settings + JSONObject json = new JSONObject(); + + // Convert server settings into JSON + JSONArray servers = new JSONArray(); + int i = 0; + String postfixi = "0"; + while (prefs.contains("server_type_" + postfixi)) { + + JSONObject server = new JSONObject(); + server.put("name", prefs.getString("server_name_" + postfixi, null)); + server.put("type", prefs.getString("server_type_" + postfixi, null)); + server.put("host", prefs.getString("server_address_" + postfixi, null)); + server.put("local_network", prefs.getString("server_localnetwork_" + postfixi, null)); + server.put("local_host", prefs.getString("server_localaddress_" + postfixi, null)); + server.put("port", prefs.getString("server_port_" + postfixi, null)); + server.put("ssl", prefs.getBoolean("server_sslenabled_" + postfixi, false)); + server.put("ssl_accept_all", prefs.getBoolean("server_ssltrustall_" + postfixi, false)); + server.put("ssl_trust_key", prefs.getString("server_ssltrustkey_" + postfixi, null)); + server.put("folder", prefs.getString("server_folder_" + postfixi, null)); + server.put("use_auth", prefs.getBoolean("server_useauth_" + postfixi, true)); + server.put("username", prefs.getString("server_user_" + postfixi, null)); + server.put("password", prefs.getString("server_pass_" + postfixi, null)); + server.put("extra_password", prefs.getString("server_extrapass_" + postfixi, null)); + server.put("os_type", prefs.getString("server_os_" + postfixi, null)); + server.put("downloads_dir", prefs.getString("server_downloaddir_" + postfixi, null)); + server.put("base_ftp_url", prefs.getString("server_ftpurl_" + postfixi, null)); + server.put("ftp_password", prefs.getString("server_ftppass_" + postfixi, null)); + server.put("server_timeout", prefs.getString("server_timeout_" + postfixi, null)); + server.put("download_alarm", prefs.getBoolean("server_alarmfinished_" + postfixi, false)); + server.put("new_torrent_alarm", prefs.getBoolean("server_alarmnew_" + postfixi, false)); + + servers.put(server); + i++; + postfixi = Integer.toString(i); + } + json.put("servers", servers); + + // Convert web search settings into JSON + JSONArray sites = new JSONArray(); + int j = 0; + String postfixj = "0"; + while (prefs.contains("websearch_baseurl_" + postfixj)) { + + JSONObject site = new JSONObject(); + site.put("name", prefs.getString("websearch_name_" + postfixj, null)); + site.put("url", prefs.getString("websearch_baseurl_" + postfixj, null)); + + sites.put(site); + j++; + postfixj = Integer.toString(j); + } + json.put("websites", sites); + + // Convert RSS feed settings into JSON + JSONArray feeds = new JSONArray(); + int k = 0; + String postfixk = "0"; + while (prefs.contains("rssfeed_url_" + postfixk)) { + + JSONObject feed = new JSONObject(); + feed.put("name", prefs.getString("rssfeed_name_" + postfixk, null)); + feed.put("url", prefs.getString("rssfeed_url_" + postfixk, null)); + feed.put("needs_auth", prefs.getBoolean("rssfeed_reqauth_" + postfixk, false)); + feed.put("last_seen", prefs.getString("rssfeed_lastnew_" + postfixk, null)); + + feeds.put(feed); + k++; + postfixk = Integer.toString(k); + } + json.put("rssfeeds", feeds); + + // Convert background service and system settings into JSON + json.put("alarm_enabled", prefs.getBoolean("notifications_enabled", true)); + json.put("alarm_interval", prefs.getString("notifications_interval", null)); + json.put("alarm_sound_uri", prefs.getString("notifications_sound", null)); + json.put("alarm_vibrate", prefs.getBoolean("notifications_vibrate", false)); + json.put("alarm_ledcolour", prefs.getInt("notifications_ledcolour", -1)); + json.put("alarm_adwnotifications", prefs.getBoolean("notifications_adwnotify", false)); + json.put("system_checkupdates", prefs.getBoolean("system_checkupdates", true)); + json.put("system_usedarktheme", prefs.getBoolean("system_usedarktheme", false)); + + // Serialise the JSON object to a file + if (settingsFile.exists()) { + settingsFile.delete(); + } + settingsFile.getParentFile().mkdirs(); + settingsFile.createNewFile(); + FileWriter writer = new FileWriter(settingsFile); + writer.write(json.toString(2)); + writer.flush(); + writer.close(); + + } + +} diff --git a/core/src/org/transdroid/core/app/settings/SystemSettings.java b/core/src/org/transdroid/core/app/settings/SystemSettings.java new file mode 100644 index 00000000..ccb38918 --- /dev/null +++ b/core/src/org/transdroid/core/app/settings/SystemSettings.java @@ -0,0 +1,34 @@ +package org.transdroid.core.app.settings; + +import org.androidannotations.annotations.EBean; +import org.androidannotations.annotations.RootContext; +import org.androidannotations.annotations.EBean.Scope; + +import android.content.Context; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; + +/** + * Allows instantiation of the settings specified in R.xml.pref_system. + * @author Eric Kok + */ +@EBean(scope = Scope.Singleton) +public class SystemSettings { + + @RootContext + protected Context context; + private SharedPreferences prefs; + + protected SystemSettings(Context context) { + prefs = PreferenceManager.getDefaultSharedPreferences(context); + } + + public boolean checkForUpdates() { + return prefs.getBoolean("system_checkupdates", true); + } + + public boolean useDarkTheme() { + return prefs.getBoolean("system_usedarktheme", false); + } + +} diff --git a/core/src/org/transdroid/core/app/settings/WebsearchSetting.java b/core/src/org/transdroid/core/app/settings/WebsearchSetting.java new file mode 100644 index 00000000..76923215 --- /dev/null +++ b/core/src/org/transdroid/core/app/settings/WebsearchSetting.java @@ -0,0 +1,58 @@ +package org.transdroid.core.app.settings; + +import org.transdroid.core.gui.lists.SimpleListItem; + +import android.net.Uri; +import android.text.TextUtils; + +/** + * Represents a user-specified website that can be searched (by starting the browser, rather than in-app) + * @author Eric Kok + */ +public class WebsearchSetting implements SimpleListItem { + + private static final String DEFAULT_NAME = "Default"; + private static final String KEY_PREFIX = "websearch_"; + + private final int order; + private final String name; + private final String baseUrl; + + public WebsearchSetting(int order, String name, String baseUrl) { + this.order = order; + this.name = name; + this.baseUrl = baseUrl; + } + + public int getOrder() { + return order; + } + + @Override + public String getName() { + if (!TextUtils.isEmpty(name)) + return name; + if (!TextUtils.isEmpty(baseUrl)) { + String host = Uri.parse(baseUrl).getHost(); + return host == null? DEFAULT_NAME: host; + } + return DEFAULT_NAME; + } + + public String getBaseUrl() { + return baseUrl; + } + + public String getKey() { + return KEY_PREFIX + getOrder(); + } + + /** + * Returns a nicely formatted identifier containing (a portion of) the search base URL + * @return A string to identify this site's search URL + */ + public String getHumanReadableIdentifier() { + return Uri.parse(baseUrl).getHost(); + } + +} diff --git a/core/src/org/transdroid/core/gui/DetailsActivity.java b/core/src/org/transdroid/core/gui/DetailsActivity.java new file mode 100644 index 00000000..ce514a90 --- /dev/null +++ b/core/src/org/transdroid/core/gui/DetailsActivity.java @@ -0,0 +1,309 @@ +package org.transdroid.core.gui; + +import java.util.ArrayList; +import java.util.List; + +import org.androidannotations.annotations.AfterViews; +import org.androidannotations.annotations.Background; +import org.androidannotations.annotations.Bean; +import org.androidannotations.annotations.EActivity; +import org.androidannotations.annotations.Extra; +import org.androidannotations.annotations.FragmentById; +import org.androidannotations.annotations.InstanceState; +import org.androidannotations.annotations.OptionsItem; +import org.androidannotations.annotations.OptionsMenu; +import org.androidannotations.annotations.UiThread; +import org.transdroid.core.R; +import org.transdroid.core.app.settings.*; +import org.transdroid.core.gui.lists.LocalTorrent; +import org.transdroid.core.gui.log.Log; +import org.transdroid.core.gui.navigation.NavigationHelper; +import org.transdroid.daemon.Daemon; +import org.transdroid.daemon.IDaemonAdapter; +import org.transdroid.daemon.Priority; +import org.transdroid.daemon.Torrent; +import org.transdroid.daemon.TorrentDetails; +import org.transdroid.daemon.TorrentFile; +import org.transdroid.daemon.task.DaemonTaskFailureResult; +import org.transdroid.daemon.task.DaemonTaskResult; +import org.transdroid.daemon.task.DaemonTaskSuccessResult; +import org.transdroid.daemon.task.GetFileListTask; +import org.transdroid.daemon.task.GetFileListTaskSuccessResult; +import org.transdroid.daemon.task.GetTorrentDetailsTask; +import org.transdroid.daemon.task.GetTorrentDetailsTaskSuccessResult; +import org.transdroid.daemon.task.PauseTask; +import org.transdroid.daemon.task.RemoveTask; +import org.transdroid.daemon.task.ResumeTask; +import org.transdroid.daemon.task.RetrieveTask; +import org.transdroid.daemon.task.RetrieveTaskSuccessResult; +import org.transdroid.daemon.task.SetDownloadLocationTask; +import org.transdroid.daemon.task.SetFilePriorityTask; +import org.transdroid.daemon.task.SetLabelTask; +import org.transdroid.daemon.task.SetTrackersTask; +import org.transdroid.daemon.task.StartTask; +import org.transdroid.daemon.task.StopTask; + +import android.annotation.TargetApi; +import android.content.Intent; +import android.os.Build; +import android.os.Bundle; + +import com.actionbarsherlock.app.SherlockFragmentActivity; + +import de.keyboardsurfer.android.widget.crouton.Crouton; + +/** + * An activity that holds a single torrents details fragment. It is used on devices (i.e. phones) where there is no room + * to show details in the {@link TorrentsActivity} directly. Task execution, such as loading of more details and + * updating file priorities, is performed in this activity via background methods. + * @author Eric Kok + */ +@EActivity(resName = "activity_details") +@OptionsMenu(resName = "activity_details") +public class DetailsActivity extends SherlockFragmentActivity implements TorrentTasksExecutor { + + @Extra + @InstanceState + protected Torrent torrent; + + // Settings + @Bean + protected NavigationHelper navigationHelper; + @Bean + protected ApplicationSettings applicationSettings; + private IDaemonAdapter currentConnection = null; + + // Details view components + @FragmentById(resName = "torrent_details") + protected DetailsFragment fragmentDetails; + + @Override + public void onCreate(Bundle savedInstanceState) { + // Set the theme according to the user preference + if (SystemSettings_.getInstance_(this).useDarkTheme()) { + setTheme(R.style.TransdroidTheme_Dark); + getSupportActionBar().setIcon(R.drawable.ic_activity_torrents); + } + super.onCreate(savedInstanceState); + } + + @AfterViews + protected void init() { + + // We require a torrent to be specified; otherwise close the activity + if (torrent == null) { + finish(); + return; + } + + // Simple action bar with up, torrent name as title and refresh button + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setTitle(NavigationHelper.buildCondensedFontString(torrent.getName())); + + // Connect to the last used server + ServerSetting lastUsed = applicationSettings.getLastUsedServer(); + currentConnection = lastUsed.createServerAdapter(); + + // Show details and load fine stats and torrent files + fragmentDetails.updateTorrent(torrent); + + } + + @Override + protected void onDestroy() { + Crouton.cancelAllCroutons(); + super.onDestroy(); + } + + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + @OptionsItem(android.R.id.home) + protected void navigateUp() { + TorrentsActivity_.intent(this).flags(Intent.FLAG_ACTIVITY_CLEAR_TOP).start(); + } + + @OptionsItem(resName = "action_refresh") + protected void refreshScreen() { + fragmentDetails.updateIsLoading(true); + refreshTorrent(); + refreshTorrentDetails(torrent); + refreshTorrentFiles(torrent); + } + + @Background + protected void refreshTorrent() { + DaemonTaskResult result = RetrieveTask.create(currentConnection).execute(); + if (result instanceof RetrieveTaskSuccessResult) { + onTorrentsRetrieved(((RetrieveTaskSuccessResult) result).getTorrents(), + ((RetrieveTaskSuccessResult) result).getLabels()); + } else { + onCommunicationError((DaemonTaskFailureResult) result); + } + } + + @Background + public void refreshTorrentDetails(Torrent torrent) { + if (!Daemon.supportsFineDetails(torrent.getDaemon())) + return; + DaemonTaskResult result = GetTorrentDetailsTask.create(currentConnection, torrent).execute(); + if (result instanceof GetTorrentDetailsTaskSuccessResult) { + onTorrentDetailsRetrieved(torrent, ((GetTorrentDetailsTaskSuccessResult) result).getTorrentDetails()); + } else { + onCommunicationError((DaemonTaskFailureResult) result); + } + } + + @Background + public void refreshTorrentFiles(Torrent torrent) { + if (!Daemon.supportsFileListing(torrent.getDaemon())) + return; + DaemonTaskResult result = GetFileListTask.create(currentConnection, torrent).execute(); + if (result instanceof GetFileListTaskSuccessResult) { + onTorrentFilesRetrieved(torrent, ((GetFileListTaskSuccessResult) result).getFiles()); + } else { + onCommunicationError((DaemonTaskFailureResult) result); + } + } + + @Background + @Override + public void resumeTorrent(Torrent torrent) { + torrent.mimicResume(); + DaemonTaskResult result = ResumeTask.create(currentConnection, torrent).execute(); + if (result instanceof DaemonTaskResult) { + onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_resumed, torrent.getName())); + } else { + onCommunicationError((DaemonTaskFailureResult) result); + } + } + + @Background + @Override + public void pauseTorrent(Torrent torrent) { + torrent.mimicPause(); + DaemonTaskResult result = PauseTask.create(currentConnection, torrent).execute(); + if (result instanceof DaemonTaskResult) { + onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_paused, torrent.getName())); + } else { + onCommunicationError((DaemonTaskFailureResult) result); + } + } + + @Background + @Override + public void startTorrent(Torrent torrent, boolean forced) { + torrent.mimicStart(); + DaemonTaskResult result = StartTask.create(currentConnection, torrent, forced).execute(); + if (result instanceof DaemonTaskResult) { + onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_started, torrent.getName())); + } else { + onCommunicationError((DaemonTaskFailureResult) result); + } + } + + @Background + @Override + public void stopTorrent(Torrent torrent) { + torrent.mimicStop(); + DaemonTaskResult result = StopTask.create(currentConnection, torrent).execute(); + if (result instanceof DaemonTaskResult) { + onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_stopped, torrent.getName())); + } else { + onCommunicationError((DaemonTaskFailureResult) result); + } + } + + @Background + @Override + public void removeTorrent(Torrent torrent, boolean withData) { + DaemonTaskResult result = RemoveTask.create(currentConnection, torrent, withData).execute(); + if (result instanceof DaemonTaskResult) { + onTaskSucceeded( + (DaemonTaskSuccessResult) result, + getString(withData ? R.string.result_removed_with_data : R.string.result_removed, torrent.getName())); + } else { + onCommunicationError((DaemonTaskFailureResult) result); + } + } + + @Background + @Override + public void updateLabel(Torrent torrent, String newLabel) { + torrent.mimicNewLabel(newLabel); + DaemonTaskResult result = SetLabelTask.create(currentConnection, torrent, newLabel).execute(); + if (result instanceof DaemonTaskResult) { + onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_labelset, newLabel)); + } else { + onCommunicationError((DaemonTaskFailureResult) result); + } + } + + @Background + @Override + public void updateTrackers(Torrent torrent, List newTrackers) { + DaemonTaskResult result = SetTrackersTask.create(currentConnection, torrent, newTrackers).execute(); + if (result instanceof DaemonTaskResult) { + onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_trackersupdated)); + } else { + onCommunicationError((DaemonTaskFailureResult) result); + } + } + + @Background + @Override + public void updateLocation(Torrent torrent, String newLocation) { + DaemonTaskResult result = SetDownloadLocationTask.create(currentConnection, torrent, newLocation).execute(); + if (result instanceof DaemonTaskResult) { + onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_locationset, newLocation)); + } else { + onCommunicationError((DaemonTaskFailureResult) result); + } + } + + @Background + @Override + public void updatePriority(Torrent torrent, List files, Priority priority) { + DaemonTaskResult result = SetFilePriorityTask.create(currentConnection, torrent, priority, + new ArrayList(files)).execute(); + if (result instanceof DaemonTaskResult) { + onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_priotitiesset)); + } else { + onCommunicationError((DaemonTaskFailureResult) result); + } + } + + @UiThread + protected void onTaskSucceeded(DaemonTaskSuccessResult result, String successMessage) { + // Refresh the screen as well + refreshTorrent(); + refreshTorrentDetails(torrent); + Crouton.showText(this, successMessage, NavigationHelper.CROUTON_INFO_STYLE); + } + + @UiThread + protected void onTorrentDetailsRetrieved(Torrent torrent, TorrentDetails torrentDetails) { + // Update the details fragment with the new fine details for the shown torrent + fragmentDetails.updateTorrentDetails(torrent, torrentDetails); + } + + @UiThread + protected void onTorrentFilesRetrieved(Torrent torrent, List torrentFiles) { + // Update the details fragment with the newly retrieved list of files + fragmentDetails.updateTorrentFiles(torrent, new ArrayList(torrentFiles)); + } + + @UiThread + protected void onCommunicationError(DaemonTaskFailureResult result) { + Log.i(this, result.getException().toString()); + fragmentDetails.updateIsLoading(false); + Crouton.showText(this, getString(LocalTorrent.getResourceForDaemonException(result.getException())), + NavigationHelper.CROUTON_ERROR_STYLE); + } + + @UiThread + protected void onTorrentsRetrieved(List torrents, List labels) { + // Update the details fragment + fragmentDetails.updateIsLoading(false); + fragmentDetails.perhapsUpdateTorrent(torrents); + } + +} diff --git a/core/src/org/transdroid/core/gui/DetailsFragment.java b/core/src/org/transdroid/core/gui/DetailsFragment.java new file mode 100644 index 00000000..c86f9b51 --- /dev/null +++ b/core/src/org/transdroid/core/gui/DetailsFragment.java @@ -0,0 +1,325 @@ +package org.transdroid.core.gui; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.androidannotations.annotations.AfterViews; +import org.androidannotations.annotations.EFragment; +import org.androidannotations.annotations.FragmentArg; +import org.androidannotations.annotations.InstanceState; +import org.androidannotations.annotations.OptionsItem; +import org.androidannotations.annotations.OptionsMenu; +import org.androidannotations.annotations.ViewById; +import org.transdroid.core.R; +import org.transdroid.core.gui.lists.DetailsAdapter; +import org.transdroid.core.gui.lists.SimpleListItemAdapter; +import org.transdroid.core.gui.navigation.NavigationHelper; +import org.transdroid.core.gui.navigation.SelectionManagerMode; +import org.transdroid.daemon.Daemon; +import org.transdroid.daemon.Priority; +import org.transdroid.daemon.Torrent; +import org.transdroid.daemon.TorrentDetails; +import org.transdroid.daemon.TorrentFile; + +import android.view.View; +import android.widget.ProgressBar; +import android.widget.TextView; + +import com.actionbarsherlock.app.SherlockFragment; +import com.actionbarsherlock.view.ActionMode; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuItem; +import com.actionbarsherlock.view.SherlockListView; +import com.actionbarsherlock.view.SherlockListView.MultiChoiceModeListenerCompat; + +import de.keyboardsurfer.android.widget.crouton.Crouton; + +/** + * Fragment that shows detailed statistics about some torrent. These come from some already fetched {@link Torrent} + * object, but it also retrieves further detailed statistics. The actual execution of tasks is performed by the activity + * that contains this fragment, as per the {@link TorrentTasksExecutor} interface. + * @author Eric Kok + */ +@EFragment(resName = "fragment_details") +@OptionsMenu(resName = "fragment_details") +public class DetailsFragment extends SherlockFragment { + + // Local data + @InstanceState + @FragmentArg + protected Torrent torrent = null; + @InstanceState + protected TorrentDetails torrentDetails = null; + @InstanceState + protected ArrayList torrentFiles = null; + @InstanceState + protected boolean isLoadingTorrent = false; + + // Views + @ViewById(resName = "details_list") + protected SherlockListView detailsList; + @ViewById + protected TextView emptyText; + @ViewById + protected ProgressBar loadingProgress; + + @AfterViews + protected void init() { + + // Set up details adapter (itself containing the actual lists to show), which allows multi-select and fast + // scrolling + detailsList.setAdapter(new DetailsAdapter(getActivity())); + detailsList.setMultiChoiceModeListener(onDetailsSelected); + detailsList.setFastScrollEnabled(true); + if (torrent != null) + updateTorrent(torrent); + if (torrentDetails != null) + updateTorrentDetails(torrent, torrentDetails); + if (torrentFiles != null) + updateTorrentFiles(torrent, torrentFiles); + + } + + /** + * Updates the details adapter header to show the new torrent data. + * @param newTorrent The new torrent object + */ + public void updateTorrent(Torrent newTorrent) { + clear(); + this.torrent = newTorrent; + ((DetailsAdapter) detailsList.getAdapter()).updateTorrent(newTorrent); + // Make the list (with details header) visible + detailsList.setVisibility(View.VISIBLE); + emptyText.setVisibility(View.GONE); + loadingProgress.setVisibility(View.GONE); + // Also update the available actions in the action bar + getActivity().supportInvalidateOptionsMenu(); + // Refresh the detailed statistics (errors) and list of files + getTasksExecutor().refreshTorrentDetails(torrent); + getTasksExecutor().refreshTorrentFiles(torrent); + } + + /** + * Updates the details adapter to show the list of trackers and tracker errors. + * @param checkTorrent The torrent for which the details were retrieved + * @param newTorrentDetails The new fine details object of some torrent + */ + public void updateTorrentDetails(Torrent checkTorrent, TorrentDetails newTorrentDetails) { + // Check if these are actually the details of the torrent we are now showing + if (!torrent.getUniqueID().equals(checkTorrent.getUniqueID())) + return; + this.torrentDetails = newTorrentDetails; + ((DetailsAdapter) detailsList.getAdapter()).updateTrackers(SimpleListItemAdapter.SimpleStringItem + .wrapStringsList(newTorrentDetails.getTrackers())); + ((DetailsAdapter) detailsList.getAdapter()).updateErrors(SimpleListItemAdapter.SimpleStringItem + .wrapStringsList(newTorrentDetails.getErrors())); + } + + /** + * Updates the list adapter to show a new list of torrent files, replacing the old files list. + * @param checkTorrent The torrent for which the details were retrieved + * @param newTorrents The new, updated list of torrent file objects + */ + public void updateTorrentFiles(Torrent checkTorrent, ArrayList newTorrentFiles) { + // Check if these are actually the details of the torrent we are now showing + if (!torrent.getUniqueID().equals(checkTorrent.getUniqueID())) + return; + Collections.sort(newTorrentFiles); + this.torrentFiles = newTorrentFiles; + ((DetailsAdapter) detailsList.getAdapter()).updateTorrentFiles(newTorrentFiles); + } + + /** + * Can be called if some outside activity returned new torrents, so we can perhaps piggyback on this by update our + * data as well. + * @param torrents The last of retrieved torrents + */ + public void perhapsUpdateTorrent(List torrents) { + // Only try to update if we actually were showing a torrent + if (this.torrent == null || torrents == null) + return; + for (Torrent newTorrent : torrents) { + if (newTorrent.getUniqueID().equals(this.torrent.getUniqueID())) { + // Found, so we can update our data as well + updateTorrent(newTorrent); + break; + } + } + } + + /** + * Clear the screen by fully clearing the internal merge list (with header and other lists) + */ + public void clear() { + detailsList.setAdapter(new DetailsAdapter(getActivity())); + detailsList.setVisibility(View.GONE); + emptyText.setVisibility(!isLoadingTorrent ? View.VISIBLE : View.GONE); + loadingProgress.setVisibility(isLoadingTorrent ? View.VISIBLE : View.GONE); + // Note: this.torrent is not cleared as we need to know later what the fragment was originally bound to + torrentDetails = null; + torrentFiles = null; + } + + /** + * Updates the shown screen depending on whether the torrent is loading + * @param isLoading True if the torrent is (re)loading, false otherwise + */ + public void updateIsLoading(boolean isLoading) { + this.isLoadingTorrent = isLoading; + if (isLoadingTorrent) + clear(); + } + + @Override + public void onPrepareOptionsMenu(Menu menu) { + super.onPrepareOptionsMenu(menu); + + if (torrent == null) { + menu.findItem(R.id.action_resume).setVisible(false); + menu.findItem(R.id.action_pause).setVisible(false); + menu.findItem(R.id.action_start).setVisible(false); + menu.findItem(R.id.action_stop).setVisible(false); + menu.findItem(R.id.action_remove).setVisible(false); + menu.findItem(R.id.action_remove_withdata).setVisible(false); + menu.findItem(R.id.action_setlabel).setVisible(false); + menu.findItem(R.id.action_updatetrackers).setVisible(false); + return; + } + // Update action availability + boolean startStop = Daemon.supportsStoppingStarting(torrent.getDaemon()); + menu.findItem(R.id.action_resume).setVisible(torrent.canResume()); + menu.findItem(R.id.action_pause).setVisible(torrent.canPause()); + menu.findItem(R.id.action_start).setVisible(startStop && torrent.canStart()); + menu.findItem(R.id.action_stop).setVisible(startStop && torrent.canStop()); + menu.findItem(R.id.action_remove).setVisible(true); + boolean removeWithData = Daemon.supportsRemoveWithData(torrent.getDaemon()); + menu.findItem(R.id.action_remove_withdata).setVisible(removeWithData); + boolean setLabel = Daemon.supportsSetLabel(torrent.getDaemon()); + menu.findItem(R.id.action_setlabel).setVisible(setLabel); + boolean setTrackers = Daemon.supportsSetTrackers(torrent.getDaemon()); + menu.findItem(R.id.action_updatetrackers).setVisible(setTrackers); + + } + + @OptionsItem(resName = "action_resume") + protected void resumeTorrent() { + getTasksExecutor().resumeTorrent(torrent); + } + + @OptionsItem(resName = "action_pause") + protected void pauseTorrent() { + getTasksExecutor().pauseTorrent(torrent); + } + + @OptionsItem(resName = "action_start_default") + protected void startTorrentDefault() { + getTasksExecutor().startTorrent(torrent, false); + } + + @OptionsItem(resName = "action_start_forced") + protected void startTorrentForced() { + getTasksExecutor().startTorrent(torrent, true); + } + + @OptionsItem(resName = "action_stop") + protected void stopTorrent() { + getTasksExecutor().stopTorrent(torrent); + } + + @OptionsItem(resName = "action_remove_default") + protected void removeTorrentDefault() { + getTasksExecutor().removeTorrent(torrent, false); + } + + @OptionsItem(resName = "action_remove_withdata") + protected void removeTorrentWithData() { + getTasksExecutor().removeTorrent(torrent, true); + } + + @OptionsItem(resName = "action_setlabel") + protected void setLabel() { + // TODO: Show label selection dialog + } + + @OptionsItem(resName = "action_updatetrackers") + protected void updateTrackers() { + // TODO: Show trackers edit dialog + } + + private MultiChoiceModeListenerCompat onDetailsSelected = new MultiChoiceModeListenerCompat() { + + SelectionManagerMode selectionManagerMode; + + @Override + public boolean onCreateActionMode(ActionMode mode, Menu menu) { + // Show contextual action bar to start/stop/remove/etc. torrents in batch mode + mode.getMenuInflater().inflate(R.menu.fragment_details_file, menu); + selectionManagerMode = new SelectionManagerMode(detailsList, R.plurals.navigation_filesselected); + selectionManagerMode.setOnlyCheckClass(TorrentFile.class); + selectionManagerMode.onCreateActionMode(mode, menu); + return true; + } + + @Override + public boolean onPrepareActionMode(ActionMode mode, Menu menu) { + return selectionManagerMode.onPrepareActionMode(mode, menu); + } + + @Override + public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + + // Get checked torrents + List checked = new ArrayList(); + for (int i = 0; i < detailsList.getCheckedItemPositions().size(); i++) { + if (detailsList.getCheckedItemPositions().valueAt(i) + && detailsList.getAdapter().getItem(detailsList.getCheckedItemPositions().keyAt(i)) instanceof TorrentFile) + checked.add((TorrentFile) detailsList.getAdapter().getItem( + detailsList.getCheckedItemPositions().keyAt(i))); + } + + int itemId = item.getItemId(); + if (itemId == R.id.action_download) { + // TODO: Start FTP download command for the selected torrents + Crouton.showText(getActivity(), "TODO: Start FTP download command for the selected torrents", + NavigationHelper.CROUTON_INFO_STYLE); + // for (TorrentFile file : checked) { + // } + mode.finish(); + return true; + } else { + Priority priority = Priority.Off; + if (itemId == R.id.action_priority_low) + priority = Priority.Low; + if (itemId == R.id.action_priority_normal) + priority = Priority.Normal; + if (itemId == R.id.action_priority_high) + priority = Priority.High; + getTasksExecutor().updatePriority(torrent, checked, priority); + mode.finish(); + return true; + } + } + + @Override + public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) { + selectionManagerMode.onItemCheckedStateChanged(mode, position, id, checked); + } + + @Override + public void onDestroyActionMode(ActionMode mode) { + selectionManagerMode.onDestroyActionMode(mode); + } + + }; + + /** + * Returns the object responsible for executing torrent tasks against a connected server + * @return The executor for tasks on some torrent + */ + private TorrentTasksExecutor getTasksExecutor() { + // NOTE: Assumes the activity implements all the required torrent tasks + return (TorrentTasksExecutor) getActivity(); + } + +} diff --git a/core/src/org/transdroid/core/gui/FilterEntryDialog.java b/core/src/org/transdroid/core/gui/FilterEntryDialog.java new file mode 100644 index 00000000..7d97759a --- /dev/null +++ b/core/src/org/transdroid/core/gui/FilterEntryDialog.java @@ -0,0 +1,39 @@ +package org.transdroid.core.gui; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.support.v4.app.DialogFragment; +import android.text.InputType; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; + +public class FilterEntryDialog { + + /** + * Opens a dialog that allows entry of a filter string, which (on confirmation) will be used to filter the list of + * torrents. + * @param activity The activity that opens (and owns) this dialog + */ + public static void startFilterEntry(final TorrentsActivity activity) { + new DialogFragment() { + public android.app.Dialog onCreateDialog(android.os.Bundle savedInstanceState) { + final EditText filterInput = new EditText(activity); + filterInput.setInputType(InputType.TYPE_TEXT_VARIATION_FILTER); + ((InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE)).toggleSoftInput( + InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY); + return new AlertDialog.Builder(activity).setView(filterInput) + .setPositiveButton(android.R.string.ok, new OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + String filterText = filterInput.getText().toString(); + if (activity != null) + activity.filterTorrents(filterText); + } + }).setNegativeButton(android.R.string.cancel, null).create(); + }; + }.show(activity.getSupportFragmentManager(), "filterentry"); + } + +} diff --git a/core/src/org/transdroid/core/gui/SearchHistoryProvider.java b/core/src/org/transdroid/core/gui/SearchHistoryProvider.java new file mode 100644 index 00000000..4a85ebfa --- /dev/null +++ b/core/src/org/transdroid/core/gui/SearchHistoryProvider.java @@ -0,0 +1,26 @@ +package org.transdroid.core.gui; + +import android.content.Context; +import android.content.SearchRecentSuggestionsProvider; +import android.provider.SearchRecentSuggestions; + +/** + * Provides search suggestions by simply returning previous user entries. + * @author Eric Kok + */ +public class SearchHistoryProvider extends SearchRecentSuggestionsProvider { + + public final static String AUTHORITY = "org.transdroid.core.gui.SearchHistoryProvider"; + public final static int MODE = DATABASE_MODE_QUERIES; + + public SearchHistoryProvider() { + super(); + setupSuggestions(AUTHORITY, MODE); + } + + public static void clearHistory(Context context) { + SearchRecentSuggestions suggestions = new SearchRecentSuggestions(context, SearchHistoryProvider.AUTHORITY, + SearchHistoryProvider.MODE); + suggestions.clearHistory(); + } +} diff --git a/core/src/org/transdroid/core/gui/ServerStatusView.java b/core/src/org/transdroid/core/gui/ServerStatusView.java new file mode 100644 index 00000000..8bbda367 --- /dev/null +++ b/core/src/org/transdroid/core/gui/ServerStatusView.java @@ -0,0 +1,65 @@ +package org.transdroid.core.gui; + +import java.util.List; + +import org.androidannotations.annotations.EViewGroup; +import org.androidannotations.annotations.ViewById; +import org.transdroid.daemon.Torrent; +import org.transdroid.daemon.TorrentStatus; +import org.transdroid.daemon.util.FileSizeConverter; + +import android.content.Context; +import android.view.View; +import android.widget.RelativeLayout; +import android.widget.TextView; + +@EViewGroup(resName="actionbar_serverstatus") +public class ServerStatusView extends RelativeLayout { + + @ViewById + protected TextView downcountText, upcountText, downcountSign, upcountSign, downspeedText, upspeedText; + + public ServerStatusView(Context context) { + super(context); + } + + /** + * Updates the statistics as shown in the action bar through this server status view. + * @param torrents The most recently received list of torrents + */ + public void update(List torrents) { + + if (torrents == null) { + downcountText.setText(null); + upcountText.setText(null); + downspeedText.setText(null); + upspeedText.setText(null); + downcountSign.setVisibility(View.INVISIBLE); + upcountSign.setVisibility(View.INVISIBLE); + } + + int downcount = 0, upcount = 0, downspeed = 0, upspeed = 0; + for (Torrent torrent : torrents) { + + // Downloading torrents count towards downloads and uploads, seeding torrents towards uploads + if (torrent.getStatusCode() == TorrentStatus.Downloading) { + downcount++; + upcount++; + } else if (torrent.getStatusCode() == TorrentStatus.Seeding) { + upcount++; + } + downspeed += torrent.getRateDownload(); + upspeed += torrent.getRateUpload(); + + } + + downcountText.setText(Integer.toString(downcount)); + upcountText.setText(Integer.toString(upcount)); + downspeedText.setText(FileSizeConverter.getSize(downspeed)); + upspeedText.setText(FileSizeConverter.getSize(upspeed)); + downcountSign.setVisibility(View.VISIBLE); + upcountSign.setVisibility(View.VISIBLE); + + } + +} diff --git a/core/src/org/transdroid/core/gui/TorrentTasksExecutor.java b/core/src/org/transdroid/core/gui/TorrentTasksExecutor.java new file mode 100644 index 00000000..420d7e10 --- /dev/null +++ b/core/src/org/transdroid/core/gui/TorrentTasksExecutor.java @@ -0,0 +1,21 @@ +package org.transdroid.core.gui; + +import java.util.List; + +import org.transdroid.daemon.Priority; +import org.transdroid.daemon.Torrent; +import org.transdroid.daemon.TorrentFile; + +public interface TorrentTasksExecutor { + void resumeTorrent(Torrent torrent); + void pauseTorrent(Torrent torrent); + void startTorrent(Torrent torrent, boolean forced); + void stopTorrent(Torrent torrent); + void removeTorrent(Torrent torrent, boolean withData); + void updateLabel(Torrent torrent, String newLabel); + void updateTrackers(Torrent torrent, List newTrackers); + void updateLocation(Torrent torrent, String newLocation); + void refreshTorrentDetails(Torrent torrent); + void refreshTorrentFiles(Torrent torrent); + void updatePriority(Torrent torrent, List files, Priority priority); +} \ No newline at end of file diff --git a/core/src/org/transdroid/core/gui/TorrentsActivity.java b/core/src/org/transdroid/core/gui/TorrentsActivity.java new file mode 100644 index 00000000..1d6a10d4 --- /dev/null +++ b/core/src/org/transdroid/core/gui/TorrentsActivity.java @@ -0,0 +1,841 @@ +package org.transdroid.core.gui; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +import org.androidannotations.annotations.AfterViews; +import org.androidannotations.annotations.Background; +import org.androidannotations.annotations.Bean; +import org.androidannotations.annotations.EActivity; +import org.androidannotations.annotations.FragmentById; +import org.androidannotations.annotations.InstanceState; +import org.androidannotations.annotations.OnActivityResult; +import org.androidannotations.annotations.OptionsItem; +import org.androidannotations.annotations.OptionsMenu; +import org.androidannotations.annotations.SystemService; +import org.androidannotations.annotations.UiThread; +import org.androidannotations.annotations.ViewById; +import org.transdroid.core.R; +import org.transdroid.core.app.settings.*; +import org.transdroid.core.gui.lists.LocalTorrent; +import org.transdroid.core.gui.lists.SimpleListItem; +import org.transdroid.core.gui.log.*; +import org.transdroid.core.gui.navigation.*; +import org.transdroid.core.gui.search.BarcodeHelper; +import org.transdroid.core.gui.search.FilePickerHelper; +import org.transdroid.core.gui.search.UrlEntryDialog; +import org.transdroid.core.gui.settings.*; +import org.transdroid.daemon.Daemon; +import org.transdroid.daemon.IDaemonAdapter; +import org.transdroid.daemon.Priority; +import org.transdroid.daemon.Torrent; +import org.transdroid.daemon.TorrentDetails; +import org.transdroid.daemon.TorrentFile; +import org.transdroid.daemon.TorrentsSortBy; +import org.transdroid.daemon.task.AddByFileTask; +import org.transdroid.daemon.task.AddByMagnetUrlTask; +import org.transdroid.daemon.task.AddByUrlTask; +import org.transdroid.daemon.task.DaemonTaskFailureResult; +import org.transdroid.daemon.task.DaemonTaskResult; +import org.transdroid.daemon.task.DaemonTaskSuccessResult; +import org.transdroid.daemon.task.GetFileListTask; +import org.transdroid.daemon.task.GetFileListTaskSuccessResult; +import org.transdroid.daemon.task.GetStatsTask; +import org.transdroid.daemon.task.GetStatsTaskSuccessResult; +import org.transdroid.daemon.task.GetTorrentDetailsTask; +import org.transdroid.daemon.task.GetTorrentDetailsTaskSuccessResult; +import org.transdroid.daemon.task.PauseTask; +import org.transdroid.daemon.task.RemoveTask; +import org.transdroid.daemon.task.ResumeTask; +import org.transdroid.daemon.task.RetrieveTask; +import org.transdroid.daemon.task.RetrieveTaskSuccessResult; +import org.transdroid.daemon.task.SetAlternativeModeTask; +import org.transdroid.daemon.task.SetDownloadLocationTask; +import org.transdroid.daemon.task.SetFilePriorityTask; +import org.transdroid.daemon.task.SetLabelTask; +import org.transdroid.daemon.task.SetTrackersTask; +import org.transdroid.daemon.task.StartTask; +import org.transdroid.daemon.task.StopTask; +import org.transdroid.daemon.util.DLog; + +import android.annotation.TargetApi; +import android.app.SearchManager; +import android.content.ContentResolver; +import android.content.Intent; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.view.View; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; + +import com.actionbarsherlock.app.ActionBar; +import com.actionbarsherlock.app.ActionBar.OnNavigationListener; +import com.actionbarsherlock.app.SherlockFragmentActivity; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuItem; +import com.actionbarsherlock.view.SherlockListView; +import com.actionbarsherlock.widget.SearchView; + +import de.keyboardsurfer.android.widget.crouton.Crouton; + +/** + * Main activity that holds the fragment that shows the torrents list, presents a way to filter the list (via an action + * bar spinner or list side list) and potentially shows a torrent details fragment too, if there is room. Task execution + * such as loading of and adding torrents is performs in this activity, using background methods. Finally, the activity + * offers navigation elements such as access to settings and showing connection issues. + * @author Eric Kok + */ +@EActivity(resName = "activity_torrents") +@OptionsMenu(resName = "activity_torrents") +public class TorrentsActivity extends SherlockFragmentActivity implements OnNavigationListener, TorrentTasksExecutor { + + // Navigation components + @Bean + protected NavigationHelper navigationHelper; + @ViewById + protected SherlockListView filtersList; + protected FilterListAdapter navigationListAdapter = null; + protected FilterListDropDownAdapter navigationSpinnerAdapter = null; + protected ServerStatusView serverStatusView; + @SystemService + protected SearchManager searchManager; + + // Settings + @Bean + protected ApplicationSettings applicationSettings; + @InstanceState + boolean firstStart = true; + boolean skipNextOnNavigationItemSelectedCall = false; + private IDaemonAdapter currentConnection = null; + @InstanceState + protected NavigationFilter currentFilter = null; + @InstanceState + protected boolean turleModeEnabled = false; + + // Contained torrent and details fragments + @FragmentById(resName = "torrent_list") + protected TorrentsFragment fragmentTorrents; + @FragmentById(resName = "torrent_details") + protected DetailsFragment fragmentDetails; + + @Override + public void onCreate(Bundle savedInstanceState) { + // Set the theme according to the user preference + if (SystemSettings_.getInstance_(this).useDarkTheme()) { + setTheme(R.style.TransdroidTheme_Dark); + getSupportActionBar().setIcon(R.drawable.ic_activity_torrents); + } + super.onCreate(savedInstanceState); + } + + @AfterViews + protected void init() { + + // Set up navigation, with an action bar spinner, server status indicator and possibly (if room) with a filter + // list + serverStatusView = ServerStatusView_.build(this); + getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST); + getSupportActionBar().setHomeButtonEnabled(false); + getSupportActionBar().setDisplayShowTitleEnabled(false); + getSupportActionBar().setDisplayShowCustomEnabled(true); + getSupportActionBar().setCustomView(serverStatusView); + navigationSpinnerAdapter = FilterListDropDownAdapter_.getInstance_(this); + // Servers are always added to the action bar spinner + navigationSpinnerAdapter.updateServers(applicationSettings.getServerSettings()); + + // Check if there was room for a dedicated filter list (i.e. on tablets) + if (filtersList != null) { + // The action bar spinner doesn't have to show the 'servers' label, as it will only contain servers + navigationSpinnerAdapter.hideServersLabel(); + // Create dedicated side list adapter and add the status types + navigationListAdapter = FilterListAdapter_.getInstance_(this); + navigationListAdapter.updateStatusTypes(StatusType.getAllStatusTypes(this)); + // Add an empty labels list (which will be updated later, but the adapter needs to be created now) + navigationListAdapter.updateLabels(new ArrayList