Browse Source

Merging upstream and fix conflicts

pull/222/head
Kumaresan Rajeswaran 9 years ago
parent
commit
d26973468e
  1. 2
      README.md
  2. 38
      app/build.gradle
  3. 4
      app/src/main/AndroidManifest.xml
  4. 8
      app/src/main/java/org/transdroid/core/app/settings/ApplicationSettings.java
  5. 5
      app/src/main/java/org/transdroid/core/app/settings/ServerSetting.java
  6. 2
      app/src/main/java/org/transdroid/core/app/settings/SystemSettings.java
  7. 126
      app/src/main/java/org/transdroid/core/gui/DetailsActivity.java
  8. 351
      app/src/main/java/org/transdroid/core/gui/DetailsFragment.java
  9. 59
      app/src/main/java/org/transdroid/core/gui/ServerSelectionView.java
  10. 29
      app/src/main/java/org/transdroid/core/gui/ServerStatusView.java
  11. 19
      app/src/main/java/org/transdroid/core/gui/TorrentTasksExecutor.java
  12. 660
      app/src/main/java/org/transdroid/core/gui/TorrentsActivity.java
  13. 151
      app/src/main/java/org/transdroid/core/gui/TorrentsFragment.java
  14. 17
      app/src/main/java/org/transdroid/core/gui/lists/NoProgressHeaderTransformer.java
  15. 12
      app/src/main/java/org/transdroid/core/gui/lists/SimpleListItemView.java
  16. 43
      app/src/main/java/org/transdroid/core/gui/lists/TorrentDetailsView.java
  17. 9
      app/src/main/java/org/transdroid/core/gui/lists/TorrentFileView.java
  18. 12
      app/src/main/java/org/transdroid/core/gui/lists/TorrentProgressBar.java
  19. 2
      app/src/main/java/org/transdroid/core/gui/lists/TorrentStatusLayout.java
  20. 17
      app/src/main/java/org/transdroid/core/gui/lists/TorrentView.java
  21. 16
      app/src/main/java/org/transdroid/core/gui/lists/TorrentsAdapter.java
  22. 2
      app/src/main/java/org/transdroid/core/gui/navigation/DialogHelper.java
  23. 20
      app/src/main/java/org/transdroid/core/gui/navigation/FilterListAdapter.java
  24. 70
      app/src/main/java/org/transdroid/core/gui/navigation/FilterListDropDownAdapter.java
  25. 10
      app/src/main/java/org/transdroid/core/gui/navigation/FilterListItemAdapter.java
  26. 11
      app/src/main/java/org/transdroid/core/gui/navigation/FilterListItemView.java
  27. 12
      app/src/main/java/org/transdroid/core/gui/navigation/FilterSeparatorView.java
  28. 32
      app/src/main/java/org/transdroid/core/gui/navigation/Label.java
  29. 14
      app/src/main/java/org/transdroid/core/gui/navigation/NavigationFilter.java
  30. 181
      app/src/main/java/org/transdroid/core/gui/navigation/NavigationHelper.java
  31. 52
      app/src/main/java/org/transdroid/core/gui/navigation/NavigationSelectionView.java
  32. 9
      app/src/main/java/org/transdroid/core/gui/navigation/RefreshableActivity.java
  33. 14
      app/src/main/java/org/transdroid/core/gui/navigation/SelectionManagerMode.java
  34. 117
      app/src/main/java/org/transdroid/core/gui/navigation/SetLabelDialog.java
  35. 70
      app/src/main/java/org/transdroid/core/gui/navigation/SetStorageLocationDialog.java
  36. 73
      app/src/main/java/org/transdroid/core/gui/navigation/SetTrackersDialog.java
  37. 94
      app/src/main/java/org/transdroid/core/gui/navigation/SetTransferRatesDialog.java
  38. 23
      app/src/main/java/org/transdroid/core/gui/rss/RssfeedLoader.java
  39. 24
      app/src/main/java/org/transdroid/core/gui/rss/RssfeedView.java
  40. 75
      app/src/main/java/org/transdroid/core/gui/rss/RssfeedsActivity.java
  41. 16
      app/src/main/java/org/transdroid/core/gui/rss/RssfeedsAdapter.java
  42. 34
      app/src/main/java/org/transdroid/core/gui/rss/RssfeedsFragment.java
  43. 13
      app/src/main/java/org/transdroid/core/gui/rss/RssitemStatusLayout.java
  44. 15
      app/src/main/java/org/transdroid/core/gui/rss/RssitemView.java
  45. 33
      app/src/main/java/org/transdroid/core/gui/rss/RssitemsActivity.java
  46. 16
      app/src/main/java/org/transdroid/core/gui/rss/RssitemsAdapter.java
  47. 33
      app/src/main/java/org/transdroid/core/gui/rss/RssitemsFragment.java
  48. 4
      app/src/main/java/org/transdroid/core/gui/search/BarcodeHelper.java
  49. 69
      app/src/main/java/org/transdroid/core/gui/search/SearchActivity.java
  50. 18
      app/src/main/java/org/transdroid/core/gui/search/SearchResultsAdapter.java
  51. 172
      app/src/main/java/org/transdroid/core/gui/search/SearchResultsFragment.java
  52. 9
      app/src/main/java/org/transdroid/core/gui/search/SearchSettingSelectionView.java
  53. 11
      app/src/main/java/org/transdroid/core/gui/search/SearchSettingsDropDownAdapter.java
  54. 20
      app/src/main/java/org/transdroid/core/gui/search/SearchSiteView.java
  55. 16
      app/src/main/java/org/transdroid/core/gui/search/SearchSitesAdapter.java
  56. 57
      app/src/main/java/org/transdroid/core/gui/search/UrlEntryDialog.java
  57. 4
      app/src/main/java/org/transdroid/core/gui/settings/HelpSettingsActivity.java
  58. 9
      app/src/main/java/org/transdroid/core/gui/settings/KeyBoundPreferencesActivity.java
  59. 77
      app/src/main/java/org/transdroid/core/gui/settings/MainSettingsActivity.java
  60. 20
      app/src/main/java/org/transdroid/core/gui/settings/NotificationSettingsActivity.java
  61. 117
      app/src/main/java/org/transdroid/core/gui/settings/OverflowPreference.java
  62. 72
      app/src/main/java/org/transdroid/core/gui/settings/PreferenceCompatActivity.java
  63. 7
      app/src/main/java/org/transdroid/core/gui/settings/RssfeedSettingsActivity.java
  64. 7
      app/src/main/java/org/transdroid/core/gui/settings/ServerSettingsActivity.java
  65. 49
      app/src/main/java/org/transdroid/core/gui/settings/SystemSettingsActivity.java
  66. 6
      app/src/main/java/org/transdroid/core/gui/settings/WebsearchPreference.java
  67. 7
      app/src/main/java/org/transdroid/core/gui/settings/WebsearchSettingsActivity.java
  68. 2
      app/src/main/java/org/transdroid/core/seedbox/SeedstuffSettingsActivity.java
  69. 2
      app/src/main/java/org/transdroid/core/seedbox/XirvikDediSettingsActivity.java
  70. 2
      app/src/main/java/org/transdroid/core/seedbox/XirvikSemiSettingsActivity.java
  71. 48
      app/src/main/java/org/transdroid/core/seedbox/XirvikSharedSettingsActivity.java
  72. 10
      app/src/main/java/org/transdroid/core/service/ConnectivityHelper.java
  73. 10
      app/src/main/java/org/transdroid/core/widget/ListWidgetConfigActivity.java
  74. 21
      app/src/main/java/org/transdroid/daemon/Daemon.java
  75. 192
      app/src/main/java/org/transdroid/daemon/Qbittorrent/QbittorrentAdapter.java
  76. 106
      app/src/main/java/org/transdroid/daemon/Rtorrent/RtorrentAdapter.java
  77. 450
      app/src/main/java/org/transdroid/daemon/Ttorrent/TtorrentAdapter.java
  78. BIN
      app/src/main/res/drawable-hdpi/ab_bottom_solid_transdroid.9.png
  79. BIN
      app/src/main/res/drawable-hdpi/ab_bottom_solid_transdroid2.9.png
  80. BIN
      app/src/main/res/drawable-hdpi/ab_solid_transdroid.9.png
  81. BIN
      app/src/main/res/drawable-hdpi/ab_solid_transdroid2.9.png
  82. BIN
      app/src/main/res/drawable-hdpi/ab_stacked_solid_transdroid.9.png
  83. BIN
      app/src/main/res/drawable-hdpi/ab_stacked_solid_transdroid2.9.png
  84. BIN
      app/src/main/res/drawable-hdpi/ab_texture_tile_transdroid2.png
  85. BIN
      app/src/main/res/drawable-hdpi/ab_transparent_transdroid.9.png
  86. BIN
      app/src/main/res/drawable-hdpi/ab_transparent_transdroid2.9.png
  87. BIN
      app/src/main/res/drawable-hdpi/abc_list_focused_holo.9.png
  88. BIN
      app/src/main/res/drawable-hdpi/abc_list_longpressed_holo.9.png
  89. BIN
      app/src/main/res/drawable-hdpi/abc_list_pressed_holo_dark.9.png
  90. BIN
      app/src/main/res/drawable-hdpi/abc_list_pressed_holo_light.9.png
  91. BIN
      app/src/main/res/drawable-hdpi/abc_list_selector_disabled_holo_dark.9.png
  92. BIN
      app/src/main/res/drawable-hdpi/abc_list_selector_disabled_holo_light.9.png
  93. BIN
      app/src/main/res/drawable-hdpi/btn_cab_done_default_transdroid2.9.png
  94. BIN
      app/src/main/res/drawable-hdpi/btn_cab_done_focused_transdroid2.9.png
  95. BIN
      app/src/main/res/drawable-hdpi/btn_cab_done_pressed_transdroid2.9.png
  96. BIN
      app/src/main/res/drawable-hdpi/cab_background_bottom_transdroid2.9.png
  97. BIN
      app/src/main/res/drawable-hdpi/cab_background_top_transdroid2.9.png
  98. BIN
      app/src/main/res/drawable-hdpi/ic_action_add.png
  99. BIN
      app/src/main/res/drawable-hdpi/ic_action_barcode.png
  100. BIN
      app/src/main/res/drawable-hdpi/ic_action_copy.png
  101. Some files were not shown because too many files have changed in this diff Show More

2
README.md

@ -20,7 +20,7 @@ Please respect the coding standards for easier merging. master contains the curr @@ -20,7 +20,7 @@ Please respect the coding standards for easier merging. master contains the curr
Code structure
==============
Starting with version 2.3.0, Transdroid is developed in Android Studio, fully integrating with the Gradle build system. It is compiled against Android 4.4 (API level 19) and since version 2.2.0 supporting ICS (API level 15) and up only. To support lite (Transdrone, specially for the Play Store) and full (Transdroid) versions of the app, build flavours are defined in gradle, which contain version-specific resources. Dependencies are managed via Maven Central in the app's build.gradle file.
Starting with version 2.3.0, Transdroid is developed in Android Studio, fully integrating with the Gradle build system. It is (since version 2.5.0) compiled against Android 5.1 (API level 22) and (since version 2.2.0) supporting ICS (API level 15) and up only. To support lite (Transdrone, specially for the Play Store) and full (Transdroid) versions of the app, build flavours are defined in gradle, which contain version-specific resources. Dependencies are managed via JCentral in the app's build.gradle file.
Developed By
============

38
app/build.gradle

@ -1,29 +1,19 @@ @@ -1,29 +1,19 @@
apply plugin: 'com.android.application'
apply plugin: 'android-apt'
apply from: '../signing.gradle'
android {
compileSdkVersion 19
buildToolsVersion '20.0.0'
compileSdkVersion 22
buildToolsVersion '21.1.2'
defaultConfig {
minSdkVersion 15
targetSdkVersion 19
versionCode 217
versionName '2.3.0'
}
signingConfigs {
release {
storeFile STORE_FILE
storePassword STORE_PASSWORD
keyAlias KEY_ALIAS
keyPassword KEY_PASSWORD
}
targetSdkVersion 22
versionCode 221
versionName '2.5.1'
}
buildTypes {
release {
runProguard false
signingConfig signingConfigs.release
minifyEnabled false
}
}
productFlavors {
@ -43,19 +33,21 @@ android { @@ -43,19 +33,21 @@ android {
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'org.androidannotations:androidannotations-api:3.1'
compile 'org.androidannotations:androidannotations-api:3.2'
compile 'com.j256.ormlite:ormlite-core:4.48'
compile 'com.j256.ormlite:ormlite-android:4.48'
compile 'com.github.chrisbanes.actionbarpulltorefresh:library:0.8'
compile 'de.keyboardsurfer.android.widget:crouton:1.8.+'
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.+'
compile 'com.android.support:support-annotations:20.0.0'
apt "org.androidannotations:androidannotations:3.1"
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.3'
compile 'com.android.support:appcompat-v7:22.1.1'
compile 'com.android.support:support-annotations:22.1.1'
compile 'com.getbase:floatingactionbutton:1.8.0'
compile 'com.afollestad:material-dialogs:0.7.6.0'
compile 'com.nispok:snackbar:2.10.6'
apt 'org.androidannotations:androidannotations:3.2'
}
apt {
arguments {
androidManifestFile variant.processResources.manifestFile
androidManifestFile variant.outputs[0].processResources.manifestFile
resourcePackageName 'org.transdroid'
//logLevel 'INFO'
//logFile '/Users/erickok/Dev/transdroid/transdroid/app/build/aa-log.txt'

4
app/src/main/AndroidManifest.xml

@ -44,7 +44,7 @@ @@ -44,7 +44,7 @@
android:hardwareAccelerated="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@android:style/Theme.Holo" >
android:theme="@style/Theme.AppCompat" >
<!-- Main activities -->
<activity
@ -53,7 +53,7 @@ @@ -53,7 +53,7 @@
android:label="@string/app_name"
android:launchMode="singleTop"
android:theme="@style/TransdroidTheme"
android:uiOptions="splitActionBarWhenNarrow" >
android:windowSoftInputMode="stateHidden" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />

8
app/src/main/java/org/transdroid/core/app/settings/ApplicationSettings.java

@ -97,15 +97,15 @@ public class ApplicationSettings { @@ -97,15 +97,15 @@ public class ApplicationSettings {
* @return The server settings object, loaded from shared preferences
*/
public ServerSetting getServerSetting(int order) {
int max = getMaxNormalServer();
if (order <= max) {
int max = getMaxNormalServer() + 1;
if (order < max) {
return getNormalServerSetting(order);
}
for (SeedboxProvider provider : SeedboxProvider.values()) {
int offset = max;
max += provider.getSettings().getMaxSeedboxOrder(prefs) + 1;
if (order <= max) {
return provider.getSettings().getServerSetting(prefs, offset, order - offset - 1);
if (order < max) {
return provider.getSettings().getServerSetting(prefs, offset, order - offset);
}
}
return null;

5
app/src/main/java/org/transdroid/core/app/settings/ServerSetting.java

@ -259,6 +259,11 @@ public class ServerSetting implements SimpleListItem { @@ -259,6 +259,11 @@ public class ServerSetting implements SimpleListItem {
return false;
}
@Override
public String toString() {
return getUniqueIdentifier();
}
/**
* Returns the appropriate daemon adapter to which tasks can be executed, in accordance with this server's settings
* @param connectedToNetwork The name of the (wifi) network we are currently connected to, or null if this could not

2
app/src/main/java/org/transdroid/core/app/settings/SystemSettings.java

@ -76,7 +76,7 @@ public class SystemSettings { @@ -76,7 +76,7 @@ public class SystemSettings {
* @param lastChecked The date/time at which the {@link AppUpdateService} last checked the server for updates
*/
public void setLastCheckedForAppUpdates(Date lastChecked) {
prefs.edit().putLong("system_lastappupdatecheck", lastChecked == null ? -1L : lastChecked.getTime()).commit();
prefs.edit().putLong("system_lastappupdatecheck", lastChecked == null ? -1L : lastChecked.getTime()).apply();
}
}

126
app/src/main/java/org/transdroid/core/gui/DetailsActivity.java

@ -16,8 +16,15 @@ @@ -16,8 +16,15 @@
*/
package org.transdroid.core.gui;
import java.util.ArrayList;
import java.util.List;
import android.annotation.TargetApi;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.Toolbar;
import com.nispok.snackbar.Snackbar;
import com.nispok.snackbar.SnackbarManager;
import org.androidannotations.annotations.AfterViews;
import org.androidannotations.annotations.Background;
@ -29,10 +36,12 @@ import org.androidannotations.annotations.InstanceState; @@ -29,10 +36,12 @@ import org.androidannotations.annotations.InstanceState;
import org.androidannotations.annotations.OptionsItem;
import org.androidannotations.annotations.OptionsMenu;
import org.androidannotations.annotations.UiThread;
import org.androidannotations.annotations.ViewById;
import org.transdroid.R;
import org.transdroid.core.app.settings.*;
import org.transdroid.core.app.settings.ApplicationSettings;
import org.transdroid.core.app.settings.ServerSetting;
import org.transdroid.core.app.settings.SystemSettings_;
import org.transdroid.core.gui.lists.LocalTorrent;
import org.transdroid.core.gui.lists.NoProgressHeaderTransformer;
import org.transdroid.core.gui.log.Log;
import org.transdroid.core.gui.navigation.Label;
import org.transdroid.core.gui.navigation.NavigationHelper;
@ -64,27 +73,18 @@ import org.transdroid.daemon.task.SetTrackersTask; @@ -64,27 +73,18 @@ import org.transdroid.daemon.task.SetTrackersTask;
import org.transdroid.daemon.task.StartTask;
import org.transdroid.daemon.task.StopTask;
import uk.co.senab.actionbarpulltorefresh.library.PullToRefreshAttacher;
import uk.co.senab.actionbarpulltorefresh.library.PullToRefreshAttacher.OnRefreshListener;
import uk.co.senab.actionbarpulltorefresh.library.PullToRefreshAttacher.Options;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
import de.keyboardsurfer.android.widget.crouton.Crouton;
import java.util.ArrayList;
import java.util.List;
/**
* 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.
* 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 Activity implements TorrentTasksExecutor, RefreshableActivity {
@EActivity(R.layout.activity_details)
@OptionsMenu(R.menu.activity_details)
public class DetailsActivity extends ActionBarActivity implements TorrentTasksExecutor, RefreshableActivity {
@Extra
@InstanceState
@ -103,10 +103,11 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R @@ -103,10 +103,11 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R
@Bean
protected ApplicationSettings applicationSettings;
private IDaemonAdapter currentConnection = null;
private PullToRefreshAttacher pullToRefreshAttacher = null;
// Details view components
@FragmentById(resName = "torrentdetails_fragment")
@ViewById
protected Toolbar selectionToolbar;
@FragmentById(R.id.torrentdetails_fragment)
protected DetailsFragment fragmentDetails;
@Override
@ -114,7 +115,6 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R @@ -114,7 +115,6 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R
// Set the theme according to the user preference
if (SystemSettings_.getInstance_(this).useDarkTheme()) {
setTheme(R.style.TransdroidTheme_Dark);
getActionBar().setIcon(R.drawable.ic_activity_torrents);
}
super.onCreate(savedInstanceState);
}
@ -129,8 +129,9 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R @@ -129,8 +129,9 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R
}
// Simple action bar with up, torrent name as title and refresh button
getActionBar().setDisplayHomeAsUpEnabled(true);
getActionBar().setTitle(NavigationHelper.buildCondensedFontString(torrent.getName()));
setSupportActionBar(selectionToolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setTitle(NavigationHelper.buildCondensedFontString(torrent.getName()));
// Connect to the last used server
ServerSetting lastUsed = applicationSettings.getLastUsedServer();
@ -143,41 +144,13 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R @@ -143,41 +144,13 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R
}
@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();
}
/**
* Attaches some view (perhaps contained in a fragment) to this activity's pull to refresh support
* @param view The view to attach
*/
@Override
public void addRefreshableView(View view) {
if (pullToRefreshAttacher == null) {
// Still need to initialise the PullToRefreshAttacher
Options options = new PullToRefreshAttacher.Options();
options.headerTransformer = new NoProgressHeaderTransformer();
pullToRefreshAttacher = PullToRefreshAttacher.get(this, options);
}
pullToRefreshAttacher.addRefreshableView(view, new OnRefreshListener() {
@Override
public void onRefreshStarted(View view) {
// Just refresh the full screen, now that the user has pulled to refresh
pullToRefreshAttacher.setRefreshComplete();
refreshScreen();
}
});
}
@OptionsItem(resName = "action_refresh")
@OptionsItem(R.id.action_refresh)
public void refreshScreen() {
fragmentDetails.updateIsLoading(true, null);
refreshTorrent();
@ -189,8 +162,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R @@ -189,8 +162,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R
protected void refreshTorrent() {
DaemonTaskResult result = RetrieveTask.create(currentConnection).execute(log);
if (result instanceof RetrieveTaskSuccessResult) {
onTorrentsRetrieved(((RetrieveTaskSuccessResult) result).getTorrents(),
((RetrieveTaskSuccessResult) result).getLabels());
onTorrentsRetrieved(((RetrieveTaskSuccessResult) result).getTorrents(), ((RetrieveTaskSuccessResult) result).getLabels());
} else {
onCommunicationError((DaemonTaskFailureResult) result, true);
}
@ -198,8 +170,9 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R @@ -198,8 +170,9 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R
@Background
public void refreshTorrentDetails(Torrent torrent) {
if (!Daemon.supportsFineDetails(torrent.getDaemon()))
if (!Daemon.supportsFineDetails(torrent.getDaemon())) {
return;
}
DaemonTaskResult result = GetTorrentDetailsTask.create(currentConnection, torrent).execute(log);
if (result instanceof GetTorrentDetailsTaskSuccessResult) {
onTorrentDetailsRetrieved(torrent, ((GetTorrentDetailsTaskSuccessResult) result).getTorrentDetails());
@ -210,8 +183,9 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R @@ -210,8 +183,9 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R
@Background
public void refreshTorrentFiles(Torrent torrent) {
if (!Daemon.supportsFileListing(torrent.getDaemon()))
if (!Daemon.supportsFileListing(torrent.getDaemon())) {
return;
}
DaemonTaskResult result = GetFileListTask.create(currentConnection, torrent).execute(log);
if (result instanceof GetFileListTaskSuccessResult) {
onTorrentFilesRetrieved(torrent, ((GetFileListTaskSuccessResult) result).getFiles());
@ -274,8 +248,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R @@ -274,8 +248,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R
DaemonTaskResult result = RemoveTask.create(currentConnection, torrent, withData).execute(log);
if (result instanceof DaemonTaskSuccessResult) {
// Close the details activity (as the torrent is now removed)
closeActivity(getString(withData ? R.string.result_removed_with_data : R.string.result_removed,
torrent.getName()));
closeActivity(getString(withData ? R.string.result_removed_with_data : R.string.result_removed, torrent.getName()));
} else {
onCommunicationError((DaemonTaskFailureResult) result, false);
}
@ -283,19 +256,18 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R @@ -283,19 +256,18 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R
@UiThread
protected void closeActivity(String closeText) {
setResult(RESULT_OK,
new Intent().putExtra("torrent_removed", true).putExtra("affected_torrent", torrent));
setResult(RESULT_OK, new Intent().putExtra("torrent_removed", true).putExtra("affected_torrent", torrent));
finish();
if (closeText != null)
Toast.makeText(this, closeText, Toast.LENGTH_LONG).show();
if (closeText != null) {
SnackbarManager.show(Snackbar.with(this).text(closeText));
}
}
@Background
@Override
public void updateLabel(Torrent torrent, String newLabel) {
torrent.mimicNewLabel(newLabel);
DaemonTaskResult result = SetLabelTask.create(currentConnection, torrent, newLabel == null ? "" : newLabel)
.execute(log);
DaemonTaskResult result = SetLabelTask.create(currentConnection, torrent, newLabel == null ? "" : newLabel).execute(log);
if (result instanceof DaemonTaskSuccessResult) {
onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_labelset, newLabel));
} else {
@ -309,8 +281,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R @@ -309,8 +281,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R
torrent.mimicCheckingStatus();
DaemonTaskResult result = ForceRecheckTask.create(currentConnection, torrent).execute(log);
if (result instanceof DaemonTaskSuccessResult) {
onTaskSucceeded((DaemonTaskSuccessResult) result,
getString(R.string.result_recheckedstarted, torrent.getName()));
onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_recheckedstarted, torrent.getName()));
} else {
onCommunicationError((DaemonTaskFailureResult) result, false);
}
@ -341,8 +312,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R @@ -341,8 +312,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R
@Background
@Override
public void updatePriority(Torrent torrent, List<TorrentFile> files, Priority priority) {
DaemonTaskResult result = SetFilePriorityTask.create(currentConnection, torrent, priority,
new ArrayList<TorrentFile>(files)).execute(log);
DaemonTaskResult result = SetFilePriorityTask.create(currentConnection, torrent, priority, new ArrayList<>(files)).execute(log);
if (result instanceof DaemonTaskSuccessResult) {
onTaskSucceeded((DaemonTaskSuccessResult) result, getString(R.string.result_priotitiesset));
} else {
@ -353,12 +323,11 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R @@ -353,12 +323,11 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R
@UiThread
protected void onTaskSucceeded(DaemonTaskSuccessResult result, String successMessage) {
// Set the activity result so the calling activity knows it needs to update its view
setResult(RESULT_OK,
new Intent().putExtra("torrent_updated", true).putExtra("affected_torrent", torrent));
setResult(RESULT_OK, new Intent().putExtra("torrent_updated", true).putExtra("affected_torrent", torrent));
// Refresh the screen as well
refreshTorrent();
refreshTorrentDetails(torrent);
Crouton.showText(this, successMessage, NavigationHelper.CROUTON_INFO_STYLE);
SnackbarManager.show(Snackbar.with(this).text(successMessage));
}
@UiThread
@ -370,7 +339,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R @@ -370,7 +339,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R
@UiThread
protected void onTorrentFilesRetrieved(Torrent torrent, List<TorrentFile> torrentFiles) {
// Update the details fragment with the newly retrieved list of files
fragmentDetails.updateTorrentFiles(torrent, new ArrayList<TorrentFile>(torrentFiles));
fragmentDetails.updateTorrentFiles(torrent, new ArrayList<>(torrentFiles));
}
@UiThread
@ -378,8 +347,8 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R @@ -378,8 +347,8 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R
log.i(this, result.getException().toString());
String error = getString(LocalTorrent.getResourceForDaemonException(result.getException()));
fragmentDetails.updateIsLoading(false, isCritical ? error : null);
Crouton.showText(this, getString(LocalTorrent.getResourceForDaemonException(result.getException())),
NavigationHelper.CROUTON_ERROR_STYLE);
SnackbarManager.show(Snackbar.with(this).text(getString(LocalTorrent.getResourceForDaemonException(result.getException())))
.colorResource(R.color.red));
}
@UiThread
@ -387,8 +356,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R @@ -387,8 +356,7 @@ public class DetailsActivity extends Activity implements TorrentTasksExecutor, R
// Update the details fragment accordingly
fragmentDetails.updateIsLoading(false, null);
fragmentDetails.perhapsUpdateTorrent(torrents);
fragmentDetails.updateLabels(Label.convertToNavigationLabels(labels,
getResources().getString(R.string.labels_unlabeled)));
fragmentDetails.updateLabels(Label.convertToNavigationLabels(labels, getResources().getString(R.string.labels_unlabeled)));
}
}

351
app/src/main/java/org/transdroid/core/gui/DetailsFragment.java

@ -16,10 +16,28 @@ @@ -16,10 +16,28 @@
*/
package org.transdroid.core.gui;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import android.annotation.SuppressLint;
import android.app.Fragment;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.ActionMenuView;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AbsListView.MultiChoiceModeListener;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.nispok.snackbar.Snackbar;
import com.nispok.snackbar.SnackbarManager;
import com.nispok.snackbar.enums.SnackbarType;
import org.androidannotations.annotations.AfterViews;
import org.androidannotations.annotations.Click;
@ -27,13 +45,17 @@ import org.androidannotations.annotations.EFragment; @@ -27,13 +45,17 @@ import org.androidannotations.annotations.EFragment;
import org.androidannotations.annotations.InstanceState;
import org.androidannotations.annotations.ItemClick;
import org.androidannotations.annotations.OptionsItem;
import org.androidannotations.annotations.OptionsMenu;
import org.androidannotations.annotations.ViewById;
import org.transdroid.R;
import org.transdroid.core.app.settings.*;
import org.transdroid.core.app.settings.ServerSetting;
import org.transdroid.core.app.settings.SystemSettings_;
import org.transdroid.core.gui.lists.DetailsAdapter;
import org.transdroid.core.gui.lists.SimpleListItemAdapter;
import org.transdroid.core.gui.navigation.*;
import org.transdroid.core.gui.navigation.Label;
import org.transdroid.core.gui.navigation.NavigationHelper_;
import org.transdroid.core.gui.navigation.RefreshableActivity;
import org.transdroid.core.gui.navigation.SelectionManagerMode;
import org.transdroid.core.gui.navigation.SetLabelDialog;
import org.transdroid.core.gui.navigation.SetLabelDialog.OnLabelPickedListener;
import org.transdroid.core.gui.navigation.SetStorageLocationDialog;
import org.transdroid.core.gui.navigation.SetStorageLocationDialog.OnStorageLocationUpdatedListener;
@ -45,33 +67,19 @@ import org.transdroid.daemon.Torrent; @@ -45,33 +67,19 @@ import org.transdroid.daemon.Torrent;
import org.transdroid.daemon.TorrentDetails;
import org.transdroid.daemon.TorrentFile;
import android.annotation.SuppressLint;
import android.app.Fragment;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AbsListView.MultiChoiceModeListener;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import de.keyboardsurfer.android.widget.crouton.Crouton;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* 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.
* 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 Fragment implements OnTrackersUpdatedListener, OnLabelPickedListener,
OnStorageLocationUpdatedListener {
@EFragment(R.layout.fragment_details)
public class DetailsFragment extends Fragment implements OnTrackersUpdatedListener, OnLabelPickedListener, OnStorageLocationUpdatedListener {
// Local data
@InstanceState
@ -91,9 +99,15 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen @@ -91,9 +99,15 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen
private ServerSetting currentServerSettings = null;
// Views
@ViewById(resName = "details_container")
@ViewById
protected View detailsContainer;
@ViewById(resName = "details_list")
@ViewById(R.id.details_menu)
protected ActionMenuView detailsMenu;
@ViewById(R.id.contextual_menu)
protected ActionMenuView contextualMenu;
@ViewById
protected SwipeRefreshLayout swipeRefreshLayout;
@ViewById
protected ListView detailsList;
@ViewById
protected TextView emptyText, errorText;
@ -103,6 +117,9 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen @@ -103,6 +117,9 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen
@AfterViews
protected void init() {
// Inject menu options in the actions toolbar
setHasOptionsMenu(true);
// On large screens where this fragment is shown next to the torrents list, we show a continues grey vertical
// line to separate the lists visually
if (!NavigationHelper_.getInstance_(getActivity()).isSmallScreen()) {
@ -113,22 +130,33 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen @@ -113,22 +130,33 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen
}
}
createMenuOptions();
// 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 (getActivity() != null && getActivity() instanceof RefreshableActivity) {
((RefreshableActivity) getActivity()).addRefreshableView(detailsList);
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
((RefreshableActivity) getActivity()).refreshScreen();
swipeRefreshLayout.setRefreshing(false); // Use our custom indicator
}
});
}
// Restore the fragment state (on orientation changes et al.)
if (torrent != null)
if (torrent != null) {
updateTorrent(torrent);
if (torrentDetails != null)
}
if (torrentDetails != null) {
updateTorrentDetails(torrent, torrentDetails);
if (torrentFiles != null)
}
if (torrentFiles != null) {
updateTorrentFiles(torrent, torrentFiles);
}
}
@ -151,7 +179,7 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen @@ -151,7 +179,7 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen
errorText.setVisibility(View.GONE);
loadingProgress.setVisibility(View.GONE);
// Also update the available actions in the action bar
getActivity().invalidateOptionsMenu();
updateMenuOptions();
// Refresh the detailed statistics (errors) and list of files
torrentDetails = null;
torrentFiles = null;
@ -166,13 +194,14 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen @@ -166,13 +194,14 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen
*/
public void updateTorrentDetails(Torrent checkTorrent, TorrentDetails newTorrentDetails) {
// Check if these are actually the details of the torrent we are now showing
if (torrentId == null || !torrentId.equals(checkTorrent.getUniqueID()))
if (torrentId == null || !torrentId.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()));
((DetailsAdapter) detailsList.getAdapter())
.updateTrackers(SimpleListItemAdapter.SimpleStringItem.wrapStringsList(newTorrentDetails.getTrackers()));
((DetailsAdapter) detailsList.getAdapter())
.updateErrors(SimpleListItemAdapter.SimpleStringItem.wrapStringsList(newTorrentDetails.getErrors()));
}
/**
@ -182,22 +211,23 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen @@ -182,22 +211,23 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen
*/
public void updateTorrentFiles(Torrent checkTorrent, ArrayList<TorrentFile> newTorrentFiles) {
// Check if these are actually the details of the torrent we are now showing
if (torrentId == null || !torrentId.equals(checkTorrent.getUniqueID()))
if (torrentId == null || !torrentId.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.
* 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<Torrent> torrents) {
// Only try to update if we actually were showing a torrent
if (this.torrentId == null || torrents == null)
if (this.torrentId == null || torrents == null) {
return;
}
for (Torrent newTorrent : torrents) {
if (newTorrent.getUniqueID().equals(torrentId)) {
// Found, so we can update our data as well
@ -208,12 +238,12 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen @@ -208,12 +238,12 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen
}
/**
* Updates the locally maintained list of labels that are active on the server. Used in the label picking dialog and
* should be updated every time after the list of torrents was retrieved to keep it updated.
* Updates the locally maintained list of labels that are active on the server. Used in the label picking dialog and should be updated every time
* after the list of torrents was retrieved to keep it updated.
* @param currentLabels The list of known server labels
*/
public void updateLabels(ArrayList<Label> currentLabels) {
this.currentLabels = currentLabels == null ? null : new ArrayList<Label>(currentLabels);
this.currentLabels = currentLabels == null ? null : new ArrayList<>(currentLabels);
}
/**
@ -239,133 +269,178 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen @@ -239,133 +269,178 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen
this.isLoadingTorrent = isLoading;
this.hasCriticalError = connectionErrorMessage != null;
errorText.setText(connectionErrorMessage);
if (isLoading || hasCriticalError)
if (isLoading || hasCriticalError) {
clear();
}
}
@ItemClick(resName = "details_list")
protected void detailsListClicked(int position) {
detailsList.setItemChecked(position, false);
}
public void createMenuOptions() {
getActivity().getMenuInflater().inflate(R.menu.fragment_details, detailsMenu.getMenu());
detailsMenu.setOnMenuItemClickListener(new ActionMenuView.OnMenuItemClickListener() {
@Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
public boolean onMenuItemClick(MenuItem menuItem) {
switch (menuItem.getItemId()) {
case R.id.action_pause:
pauseTorrent();
return true;
case R.id.action_updatetrackers:
updateTrackers();
return true;
case R.id.action_start_forced:
startTorrentForced();
return true;
case R.id.action_stop:
stopTorrent();
return true;
case R.id.action_forcerecheck:
setForceRecheck();
return true;
case R.id.action_changelocation:
changeStorageLocation();
return true;
case R.id.action_start_default:
startTorrentDefault();
return true;
case R.id.action_remove:
removeTorrent();
return true;
case R.id.action_start_direct:
startTorrentDirect();
return true;
case R.id.action_setlabel:
setLabel();
return true;
case R.id.action_resume:
resumeTorrent();
return true;
}
return false;
}
});
}
private void updateMenuOptions() {
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_start_direct).setVisible(false);
menu.findItem(R.id.action_stop).setVisible(false);
menu.findItem(R.id.action_remove).setVisible(false);
menu.findItem(R.id.action_setlabel).setVisible(false);
menu.findItem(R.id.action_forcerecheck).setVisible(false);
menu.findItem(R.id.action_updatetrackers).setVisible(false);
menu.findItem(R.id.action_changelocation).setVisible(false);
detailsMenu.getMenu().findItem(R.id.action_resume).setVisible(false);
detailsMenu.getMenu().findItem(R.id.action_pause).setVisible(false);
detailsMenu.getMenu().findItem(R.id.action_start).setVisible(false);
detailsMenu.getMenu().findItem(R.id.action_start_direct).setVisible(false);
detailsMenu.getMenu().findItem(R.id.action_stop).setVisible(false);
detailsMenu.getMenu().findItem(R.id.action_remove).setVisible(false);
detailsMenu.getMenu().findItem(R.id.action_setlabel).setVisible(false);
detailsMenu.getMenu().findItem(R.id.action_forcerecheck).setVisible(false);
detailsMenu.getMenu().findItem(R.id.action_updatetrackers).setVisible(false);
detailsMenu.getMenu().findItem(R.id.action_changelocation).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());
detailsMenu.getMenu().findItem(R.id.action_resume).setVisible(torrent.canResume());
detailsMenu.getMenu().findItem(R.id.action_pause).setVisible(torrent.canPause());
boolean forcedStart = Daemon.supportsForcedStarting(torrent.getDaemon());
menu.findItem(R.id.action_start).setVisible(startStop && forcedStart && torrent.canStart());
menu.findItem(R.id.action_start_direct).setVisible(startStop && !forcedStart && torrent.canStart());
menu.findItem(R.id.action_stop).setVisible(startStop && torrent.canStop());
menu.findItem(R.id.action_remove).setVisible(true);
detailsMenu.getMenu().findItem(R.id.action_start).setVisible(startStop && forcedStart && torrent.canStart());
detailsMenu.getMenu().findItem(R.id.action_start_direct).setVisible(startStop && !forcedStart && torrent.canStart());
detailsMenu.getMenu().findItem(R.id.action_stop).setVisible(startStop && torrent.canStop());
detailsMenu.getMenu().findItem(R.id.action_remove).setVisible(true);
boolean setLabel = Daemon.supportsSetLabel(torrent.getDaemon());
menu.findItem(R.id.action_setlabel).setVisible(setLabel);
detailsMenu.getMenu().findItem(R.id.action_setlabel).setVisible(setLabel);
boolean forceRecheck = Daemon.supportsForceRecheck(torrent.getDaemon());
menu.findItem(R.id.action_forcerecheck).setVisible(forceRecheck);
detailsMenu.getMenu().findItem(R.id.action_forcerecheck).setVisible(forceRecheck);
boolean setTrackers = Daemon.supportsSetTrackers(torrent.getDaemon());
menu.findItem(R.id.action_updatetrackers).setVisible(setTrackers);
detailsMenu.getMenu().findItem(R.id.action_updatetrackers).setVisible(setTrackers);
boolean setLocation = Daemon.supportsSetDownloadLocation(torrent.getDaemon());
menu.findItem(R.id.action_changelocation).setVisible(setLocation);
detailsMenu.getMenu().findItem(R.id.action_changelocation).setVisible(setLocation);
}
@OptionsItem(resName = "action_resume")
@OptionsItem(R.id.action_resume)
protected void resumeTorrent() {
getTasksExecutor().resumeTorrent(torrent);
}
@OptionsItem(resName = "action_pause")
@OptionsItem(R.id.action_pause)
protected void pauseTorrent() {
getTasksExecutor().pauseTorrent(torrent);
}
@OptionsItem(resName = "action_start_direct")
@OptionsItem(R.id.action_start_direct)
protected void startTorrentDirect() {
getTasksExecutor().startTorrent(torrent, false);
}
@OptionsItem(resName = "action_start_default")
@OptionsItem(R.id.action_start_default)
protected void startTorrentDefault() {
getTasksExecutor().startTorrent(torrent, false);
}
@OptionsItem(resName = "action_start_forced")
@OptionsItem(R.id.action_start_forced)
protected void startTorrentForced() {
getTasksExecutor().startTorrent(torrent, true);
}
@OptionsItem(resName = "action_stop")
@OptionsItem(R.id.action_stop)
protected void stopTorrent() {
getTasksExecutor().stopTorrent(torrent);
}
@OptionsItem(resName = "action_remove")
protected void removeTorrentDefault() {
protected void removeTorrent() {
ConfirmRemoveDialog.startConfirmRemove((TorrentsActivity) getActivity(), Arrays.asList(torrent));
}
@OptionsItem(resName = "action_setlabel")
@OptionsItem(R.id.action_setlabel)
protected void setLabel() {
if (currentLabels != null)
new SetLabelDialog().setOnLabelPickedListener(this).setCurrentLabels(currentLabels)
.show(getFragmentManager(), "SetLabelDialog");
if (currentLabels != null) {
SetLabelDialog.show(getActivity(), this, currentLabels);
}
}
@OptionsItem(resName = "action_forcerecheck")
@OptionsItem(R.id.action_forcerecheck)
protected void setForceRecheck() {
getTasksExecutor().forceRecheckTorrent(torrent);
}
@OptionsItem(resName = "action_updatetrackers")
@OptionsItem(R.id.action_updatetrackers)
protected void updateTrackers() {
if (torrentDetails == null) {
Crouton.showText(getActivity(), R.string.error_stillloadingdetails, NavigationHelper.CROUTON_INFO_STYLE);
SnackbarManager.show(Snackbar.with(getActivity()).text(R.string.error_stillloadingdetails));
return;
}
new SetTrackersDialog().setOnTrackersUpdated(this).setCurrentTrackers(torrentDetails.getTrackersText())
.show(getFragmentManager(), "SetTrackersDialog");
SetTrackersDialog.show(getActivity(), this, torrentDetails.getTrackersText());
}
@OptionsItem(resName = "action_changelocation")
@OptionsItem(R.id.action_changelocation)
protected void changeStorageLocation() {
new SetStorageLocationDialog().setOnStorageLocationUpdated(this).setCurrentLocation(torrent.getLocationDir())
.show(getFragmentManager(), "SetStorageLocationDialog");
SetStorageLocationDialog.show(getActivity(), this, torrent.getLocationDir());
}
@Override
public void onLabelPicked(String newLabel) {
if (torrent == null)
if (torrent == null) {
return;
}
getTasksExecutor().updateLabel(torrent, newLabel);
}
@Override
public void onTrackersUpdated(List<String> updatedTrackers) {
if (torrent == null)
if (torrent == null) {
return;
}
getTasksExecutor().updateTrackers(torrent, updatedTrackers);
}
@Override
public void onStorageLocationUpdated(String newLocation) {
if (torrent == null)
if (torrent == null) {
return;
}
getTasksExecutor().updateLocation(torrent, newLocation);
}
@ -390,10 +465,21 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen @@ -390,10 +465,21 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen
SelectionManagerMode selectionManagerMode;
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
public boolean onCreateActionMode(final ActionMode mode, Menu menu) {
// Show contextual action bar to start/stop/remove/etc. torrents in batch mode
mode.getMenuInflater().inflate(R.menu.fragment_details_cab, menu);
selectionManagerMode = new SelectionManagerMode(detailsList, R.plurals.navigation_filesselected);
detailsMenu.setEnabled(false);
contextualMenu.setVisibility(View.VISIBLE);
contextualMenu.setOnMenuItemClickListener(new ActionMenuView.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
return onActionItemClicked(mode, menuItem);
}
});
contextualMenu.getMenu().clear();
getActivity().getMenuInflater().inflate(R.menu.fragment_details_cab_main, contextualMenu.getMenu());
Context themedContext = ((ActionBarActivity) getActivity()).getSupportActionBar().getThemedContext();
mode.getMenuInflater().inflate(R.menu.fragment_details_cab_secondary, menu);
selectionManagerMode = new SelectionManagerMode(themedContext, detailsList, R.plurals.navigation_filesselected);
selectionManagerMode.setOnlyCheckClass(TorrentFile.class);
selectionManagerMode.onCreateActionMode(mode, menu);
return true;
@ -401,21 +487,20 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen @@ -401,21 +487,20 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
selectionManagerMode.onPrepareActionMode(mode, menu);
// Pause autorefresh
if (getActivity() != null && getActivity() instanceof TorrentsActivity) {
((TorrentsActivity) getActivity()).stopRefresh = true;
((TorrentsActivity) getActivity()).stopAutoRefresh();
}
boolean filePaths =
currentServerSettings != null && Daemon.supportsFilePaths(currentServerSettings.getType());
menu.findItem(R.id.action_download).setVisible(filePaths);
boolean filePriorities = currentServerSettings != null &&
Daemon.supportsFilePrioritySetting(currentServerSettings.getType());
menu.findItem(R.id.action_priority_off).setVisible(filePriorities);
menu.findItem(R.id.action_priority_low).setVisible(filePriorities);
menu.findItem(R.id.action_priority_normal).setVisible(filePriorities);
menu.findItem(R.id.action_priority_high).setVisible(filePriorities);
return selectionManagerMode.onPrepareActionMode(mode, menu);
boolean filePaths = currentServerSettings != null && Daemon.supportsFilePaths(currentServerSettings.getType());
contextualMenu.getMenu().findItem(R.id.action_download).setVisible(filePaths);
boolean filePriorities = currentServerSettings != null && Daemon.supportsFilePrioritySetting(currentServerSettings.getType());
contextualMenu.getMenu().findItem(R.id.action_priority_off).setVisible(filePriorities);
contextualMenu.getMenu().findItem(R.id.action_priority_low).setVisible(filePriorities);
contextualMenu.getMenu().findItem(R.id.action_priority_normal).setVisible(filePriorities);
contextualMenu.getMenu().findItem(R.id.action_priority_high).setVisible(filePriorities);
return true;
}
@SuppressLint("SdCardPath")
@ -423,35 +508,36 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen @@ -423,35 +508,36 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
// Get checked torrents
List<TorrentFile> checked = new ArrayList<TorrentFile>();
List<TorrentFile> checked = new ArrayList<>();
for (int i = 0; i < detailsList.getCheckedItemPositions().size(); i++) {
if (detailsList.getCheckedItemPositions().valueAt(i)
&& i < detailsList.getAdapter().getCount()
&& detailsList.getAdapter().getItem(detailsList.getCheckedItemPositions().keyAt(i)) instanceof TorrentFile)
checked.add((TorrentFile) detailsList.getAdapter().getItem(
detailsList.getCheckedItemPositions().keyAt(i)));
if (detailsList.getCheckedItemPositions().valueAt(i) && i < detailsList.getAdapter().getCount() &&
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) {
if (checked.size() < 1 || currentServerSettings == null)
if (checked.size() < 1 || currentServerSettings == null) {
return true;
}
String urlBase = currentServerSettings.getFtpUrl();
if (urlBase == null || urlBase.equals(""))
if (urlBase == null || urlBase.equals("")) {
urlBase = "ftp://" + currentServerSettings.getAddress();
}
// Try using AndFTP intents
Intent andftpStart = new Intent(Intent.ACTION_PICK);
andftpStart.setDataAndType(Uri.parse(urlBase), "vnd.android.cursor.dir/lysesoft.andftp.uri");
andftpStart.putExtra("command_type", "download");
andftpStart.putExtra("ftp_pasv", "true");
if (Uri.parse(urlBase).getUserInfo() != null)
if (Uri.parse(urlBase).getUserInfo() != null) {
andftpStart.putExtra("ftp_username", Uri.parse(urlBase).getUserInfo());
else
} else {
andftpStart.putExtra("ftp_username", currentServerSettings.getUsername());
if (currentServerSettings.getFtpPassword() != null
&& !currentServerSettings.getFtpPassword().equals("")) {
}
if (currentServerSettings.getFtpPassword() != null && !currentServerSettings.getFtpPassword().equals("")) {
andftpStart.putExtra("ftp_password", currentServerSettings.getFtpPassword());
} else {
andftpStart.putExtra("ftp_password", currentServerSettings.getPassword());
@ -465,8 +551,9 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen @@ -465,8 +551,9 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen
// If the file is directly in the root, AndFTP fails if we supply the proper path (like
// /file.pdf)
// Work around this bug by removing the leading / if no further directories are used in the path
if (file.startsWith("/") && file.indexOf("/", 1) < 0)
if (file.startsWith("/") && file.indexOf("/", 1) < 0) {
file = file.substring(1);
}
andftpStart.putExtra("remote_file" + (f + 1), file);
}
}
@ -478,8 +565,7 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen @@ -478,8 +565,7 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen
// Try using a VIEW intent given an ftp:// scheme URI
String url = urlBase + checked.get(0).getFullPath();
Intent simpleStart = new Intent(Intent.ACTION_VIEW, Uri.parse(url))
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Intent simpleStart = new Intent(Intent.ACTION_VIEW, Uri.parse(url)).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (simpleStart.resolveActivity(getActivity().getPackageManager()) != null) {
startActivity(simpleStart);
mode.finish();
@ -487,8 +573,8 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen @@ -487,8 +573,8 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen
}
// No app is available that can handle FTP downloads
Crouton.showText(getActivity(), getString(R.string.error_noftpapp, url),
NavigationHelper.CROUTON_ERROR_STYLE);
SnackbarManager.show(Snackbar.with(getActivity()).text(getString(R.string.error_noftpapp, url)).type(SnackbarType.MULTI_LINE)
.colorResource(R.color.red));
mode.finish();
return true;
@ -496,24 +582,27 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen @@ -496,24 +582,27 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen
StringBuilder names = new StringBuilder();
for (int f = 0; f < checked.size(); f++) {
if (f != 0)
if (f != 0) {
names.append("\n");
}
names.append(checked.get(f).getName());
}
ClipboardManager clipboardManager = (ClipboardManager) getActivity().getSystemService(
Context.CLIPBOARD_SERVICE);
ClipboardManager clipboardManager = (ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
clipboardManager.setPrimaryClip(ClipData.newPlainText("Transdroid", names.toString()));
mode.finish();
return true;
} else {
Priority priority = Priority.Off;
if (itemId == R.id.action_priority_low)
if (itemId == R.id.action_priority_low) {
priority = Priority.Low;
if (itemId == R.id.action_priority_normal)
}
if (itemId == R.id.action_priority_normal) {
priority = Priority.Normal;
if (itemId == R.id.action_priority_high)
}
if (itemId == R.id.action_priority_high) {
priority = Priority.High;
}
getTasksExecutor().updatePriority(torrent, checked, priority);
mode.finish();
return true;
@ -533,6 +622,8 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen @@ -533,6 +622,8 @@ public class DetailsFragment extends Fragment implements OnTrackersUpdatedListen
((TorrentsActivity) getActivity()).startAutoRefresh();
}
selectionManagerMode.onDestroyActionMode(mode);
contextualMenu.setVisibility(View.GONE);
detailsMenu.setEnabled(true);
}
};

59
app/src/main/java/org/transdroid/core/gui/ServerSelectionView.java

@ -0,0 +1,59 @@ @@ -0,0 +1,59 @@
/*
* Copyright 2010-2013 Eric Kok et al.
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.transdroid.core.gui;
import android.content.Context;
import android.widget.RelativeLayout;
import android.widget.TextView;
import org.androidannotations.annotations.EViewGroup;
import org.androidannotations.annotations.ViewById;
import org.transdroid.R;
import org.transdroid.core.gui.navigation.NavigationFilter;
import org.transdroid.daemon.IDaemonAdapter;
@EViewGroup(R.layout.actionbar_serverselection)
public class ServerSelectionView extends RelativeLayout {
@ViewById
protected TextView filterText, serverText;
public ServerSelectionView(Context context) {
super(context);
}
public ServerSelectionView(TorrentsActivity activity) {
super(activity.torrentsToolbar.getContext());
}
/**
* Updates the name of the current connected server.
* @param currentServer The server currently connected to
*/
public void updateCurrentServer(IDaemonAdapter currentServer) {
serverText.setText(currentServer.getSettings().getName());
}
/**
* Updates the name of the selected filter.
* @param currentFilter The filter that is currently selected
*/
public void updateCurrentFilter(NavigationFilter currentFilter) {
filterText.setText(currentFilter.getName());
}
}

29
app/src/main/java/org/transdroid/core/gui/ServerStatusView.java

@ -16,24 +16,25 @@ @@ -16,24 +16,25 @@
*/
package org.transdroid.core.gui;
import java.util.List;
import android.content.Context;
import android.view.View;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.nispok.snackbar.Snackbar;
import com.nispok.snackbar.SnackbarManager;
import org.androidannotations.annotations.EViewGroup;
import org.androidannotations.annotations.ViewById;
import org.transdroid.R;
import org.transdroid.core.gui.navigation.NavigationHelper;
import org.transdroid.core.gui.navigation.SetTransferRatesDialog;
import org.transdroid.core.gui.navigation.SetTransferRatesDialog.OnRatesPickedListener;
import org.transdroid.daemon.Torrent;
import org.transdroid.daemon.util.FileSizeConverter;
import android.content.Context;
import android.view.View;
import android.widget.RelativeLayout;
import android.widget.TextView;
import de.keyboardsurfer.android.widget.crouton.Crouton;
import java.util.List;
@EViewGroup(resName = "actionbar_serverstatus")
@EViewGroup(R.layout.actionbar_serverstatus)
public class ServerStatusView extends RelativeLayout implements OnRatesPickedListener {
@ViewById
@ -57,7 +58,7 @@ public class ServerStatusView extends RelativeLayout implements OnRatesPickedLis @@ -57,7 +58,7 @@ public class ServerStatusView extends RelativeLayout implements OnRatesPickedLis
* @param dormantAsInactive Whether to treat dormant (0KB/s) torrent as inactive state torrents
* @param supportsSetTransferRates Whether the connected torrent client supports setting of max transfer speeds
*/
public void update(List<Torrent> torrents, boolean dormantAsInactive, boolean supportsSetTransferRates) {
public void updateStatus(List<Torrent> torrents, boolean dormantAsInactive, boolean supportsSetTransferRates) {
if (torrents == null) {
downcountText.setText(null);
@ -91,14 +92,16 @@ public class ServerStatusView extends RelativeLayout implements OnRatesPickedLis @@ -91,14 +92,16 @@ public class ServerStatusView extends RelativeLayout implements OnRatesPickedLis
upspeedText.setText(FileSizeConverter.getSize(upspeed) + "/s");
downcountSign.setVisibility(View.VISIBLE);
upcountSign.setVisibility(View.VISIBLE);
speedswrapperLayout.setOnClickListener(supportsSetTransferRates ? onStartDownPickerClicked : null);
if (supportsSetTransferRates)
speedswrapperLayout.setOnClickListener(onStartDownPickerClicked);
else
speedswrapperLayout.setBackgroundDrawable(null);
}
private OnClickListener onStartDownPickerClicked = new OnClickListener() {
public void onClick(View v) {
new SetTransferRatesDialog().setOnRatesPickedListener(ServerStatusView.this).show(
activity.getFragmentManager(), "SetTransferRatesDialog");
SetTransferRatesDialog.show(getContext(), ServerStatusView.this);
}
};
@ -114,7 +117,7 @@ public class ServerStatusView extends RelativeLayout implements OnRatesPickedLis @@ -114,7 +117,7 @@ public class ServerStatusView extends RelativeLayout implements OnRatesPickedLis
@Override
public void onInvalidNumber() {
Crouton.showText(activity, R.string.error_notanumber, NavigationHelper.CROUTON_ERROR_STYLE);
SnackbarManager.show(Snackbar.with(activity).text(R.string.error_notanumber).colorResource(R.color.red));
}
}

19
app/src/main/java/org/transdroid/core/gui/TorrentTasksExecutor.java

@ -16,28 +16,41 @@ @@ -16,28 +16,41 @@
*/
package org.transdroid.core.gui;
import java.util.List;
import android.support.v7.widget.ActionMenuView;
import android.support.v7.widget.Toolbar;
import org.transdroid.daemon.Priority;
import org.transdroid.daemon.Torrent;
import org.transdroid.daemon.TorrentFile;
import java.util.List;
/**
* Interface to be implemented by any activity that wants containing fragments to be able to load data and execute
* commands against a torrent server.
* Interface to be implemented by any activity that wants containing fragments to be able to load data and execute commands against a torrent server.
* @author Eric Kok
*/
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 forceRecheckTorrent(Torrent torrent);
void updateLabel(Torrent torrent, String newLabel);
void updateTrackers(Torrent torrent, List<String> newTrackers);
void updateLocation(Torrent torrent, String newLocation);
void refreshTorrentDetails(Torrent torrent);
void refreshTorrentFiles(Torrent torrent);
void updatePriority(Torrent torrent, List<TorrentFile> files, Priority priority);
}

660
app/src/main/java/org/transdroid/core/gui/TorrentsActivity.java

File diff suppressed because it is too large Load Diff

151
app/src/main/java/org/transdroid/core/gui/TorrentsFragment.java

@ -16,10 +16,22 @@ @@ -16,10 +16,22 @@
*/
package org.transdroid.core.gui;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.Locale;
import android.app.Fragment;
import android.content.Context;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.ActionMenuView;
import android.support.v7.widget.Toolbar;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AbsListView.MultiChoiceModeListener;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.getbase.floatingactionbutton.FloatingActionsMenu;
import org.androidannotations.annotations.AfterViews;
import org.androidannotations.annotations.Bean;
@ -31,7 +43,8 @@ import org.androidannotations.annotations.ViewById; @@ -31,7 +43,8 @@ import org.androidannotations.annotations.ViewById;
import org.transdroid.R;
import org.transdroid.core.app.settings.ApplicationSettings;
import org.transdroid.core.app.settings.SystemSettings;
import org.transdroid.core.gui.lists.*;
import org.transdroid.core.gui.lists.TorrentsAdapter;
import org.transdroid.core.gui.lists.TorrentsAdapter_;
import org.transdroid.core.gui.navigation.Label;
import org.transdroid.core.gui.navigation.NavigationFilter;
import org.transdroid.core.gui.navigation.RefreshableActivity;
@ -43,23 +56,17 @@ import org.transdroid.daemon.Torrent; @@ -43,23 +56,17 @@ import org.transdroid.daemon.Torrent;
import org.transdroid.daemon.TorrentsComparator;
import org.transdroid.daemon.TorrentsSortBy;
import android.app.Fragment;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AbsListView.MultiChoiceModeListener;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.Locale;
/**
* Fragment that shows a list of torrents that are active on the server. It supports sorting and filtering and can show
* connection progress and issues. However, actual task starting and execution and overall navigation elements are part
* of the containing activity, not this fragment.
* Fragment that shows a list of torrents that are active on the server. It supports sorting and filtering and can show connection progress and
* issues. However, actual task starting and execution and overall navigation elements are part of the containing activity, not this fragment.
* @author Eric Kok
*/
@EFragment(resName = "fragment_torrents")
@EFragment(R.layout.fragment_torrents)
public class TorrentsFragment extends Fragment implements OnLabelPickedListener {
// Local data
@ -91,7 +98,9 @@ public class TorrentsFragment extends Fragment implements OnLabelPickedListener @@ -91,7 +98,9 @@ public class TorrentsFragment extends Fragment implements OnLabelPickedListener
protected Daemon daemonType;
// Views
@ViewById(resName = "torrents_list")
@ViewById
protected SwipeRefreshLayout swipeRefreshLayout;
@ViewById
protected ListView torrentsList;
@ViewById
protected TextView emptyText;
@ -113,11 +122,18 @@ public class TorrentsFragment extends Fragment implements OnLabelPickedListener @@ -113,11 +122,18 @@ public class TorrentsFragment extends Fragment implements OnLabelPickedListener
torrentsList.setAdapter(TorrentsAdapter_.getInstance_(getActivity()));
torrentsList.setMultiChoiceModeListener(onTorrentsSelected);
torrentsList.setFastScrollEnabled(true);
if (torrents != null)
if (torrents != null) {
updateTorrents(torrents, currentLabels);
}
// Allow pulls on the list view to refresh the torrents
if (getActivity() != null && getActivity() instanceof RefreshableActivity) {
((RefreshableActivity) getActivity()).addRefreshableView(torrentsList);
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
((RefreshableActivity) getActivity()).refreshScreen();
swipeRefreshLayout.setRefreshing(false); // Use our custom indicator
}
});
}
nosettingsText.setText(getString(R.string.navigation_nosettings, getString(R.string.app_name)));
@ -149,8 +165,9 @@ public class TorrentsFragment extends Fragment implements OnLabelPickedListener @@ -149,8 +165,9 @@ public class TorrentsFragment extends Fragment implements OnLabelPickedListener
}
}
// In case it was an update, add the updated torrent object
if (!wasRemoved)
if (!wasRemoved) {
this.torrents.add(affected);
}
// Now refresh the screen
applyAllFilters();
}
@ -162,8 +179,9 @@ public class TorrentsFragment extends Fragment implements OnLabelPickedListener @@ -162,8 +179,9 @@ public class TorrentsFragment extends Fragment implements OnLabelPickedListener
*/
public void clear(boolean clearError, boolean clearFilter) {
this.torrents = null;
if (clearError)
if (clearError) {
this.connectionErrorMessage = null;
}
if (clearFilter) {
this.currentTextFilter = null;
this.currentNavigationFilter = null;
@ -172,8 +190,8 @@ public class TorrentsFragment extends Fragment implements OnLabelPickedListener @@ -172,8 +190,8 @@ public class TorrentsFragment extends Fragment implements OnLabelPickedListener
}
/**
* Stores the new sort order (for future refreshes) and sorts the current visible list. If the given new sort
* property equals the existing property, the list sort order is reversed instead.
* Stores the new sort order (for future refreshes) and sorts the current visible list. If the given new sort property equals the existing
* property, the list sort order is reversed instead.
* @param newSortOrder The sort order that the user selected.
*/
public void sortBy(TorrentsSortBy newSortOrder) {
@ -212,26 +230,26 @@ public class TorrentsFragment extends Fragment implements OnLabelPickedListener @@ -212,26 +230,26 @@ public class TorrentsFragment extends Fragment implements OnLabelPickedListener
}
// Filter the list of torrents to show according to navigation and text filters
ArrayList<Torrent> filteredTorrents = new ArrayList<Torrent>(torrents);
ArrayList<Torrent> filteredTorrents = new ArrayList<>(torrents);
if (currentNavigationFilter != null) {
// Remove torrents that do not match the selected navigation filter
for (Iterator<Torrent> torrentIter = filteredTorrents.iterator(); torrentIter.hasNext(); ) {
if (!currentNavigationFilter.matches(torrentIter.next(), systemSettings.treatDormantAsInactive()))
if (!currentNavigationFilter.matches(torrentIter.next(), systemSettings.treatDormantAsInactive())) {
torrentIter.remove();
}
}
}
if (currentTextFilter != null) {
// Remove torrent that do not contain the text filter string
// Remove torrents that do not contain the text filter string
for (Iterator<Torrent> torrentIter = filteredTorrents.iterator(); torrentIter.hasNext(); ) {
if (!torrentIter.next().getName().toLowerCase(Locale.getDefault())
.contains(currentTextFilter.toLowerCase(Locale.getDefault())))
if (!torrentIter.next().getName().toLowerCase(Locale.getDefault()).contains(currentTextFilter.toLowerCase(Locale.getDefault()))) {
torrentIter.remove();
}
}
}
// Sort the list of filtered torrents
Collections.sort(filteredTorrents, new TorrentsComparator(daemonType, this.currentSortOrder,
this.currentSortDescending));
Collections.sort(filteredTorrents, new TorrentsComparator(daemonType, this.currentSortOrder, this.currentSortDescending));
((TorrentsAdapter) torrentsList.getAdapter()).update(filteredTorrents);
updateViewVisibility();
@ -239,13 +257,32 @@ public class TorrentsFragment extends Fragment implements OnLabelPickedListener @@ -239,13 +257,32 @@ public class TorrentsFragment extends Fragment implements OnLabelPickedListener
private MultiChoiceModeListener onTorrentsSelected = new MultiChoiceModeListener() {
SelectionManagerMode selectionManagerMode;
private SelectionManagerMode selectionManagerMode;
private ActionMenuView actionsMenu;
private Toolbar actionsToolbar;
private FloatingActionsMenu addmenuButton;
@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_torrents_cab, menu);
selectionManagerMode = new SelectionManagerMode(torrentsList, R.plurals.navigation_torrentsselected);
public boolean onCreateActionMode(final ActionMode mode, Menu menu) {
// Show contextual action bars to start/stop/remove/etc. torrents in batch mode
if (actionsMenu == null) {
actionsMenu = ((TorrentsActivity) getActivity()).contextualMenu;
actionsToolbar = ((TorrentsActivity) getActivity()).actionsToolbar;
addmenuButton = ((TorrentsActivity) getActivity()).addmenuButton;
}
actionsToolbar.setEnabled(false);
actionsMenu.setVisibility(View.VISIBLE);
addmenuButton.setVisibility(View.GONE);
actionsMenu.setOnMenuItemClickListener(new ActionMenuView.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
return onActionItemClicked(mode, menuItem);
}
});
actionsMenu.getMenu().clear();
getActivity().getMenuInflater().inflate(R.menu.fragment_torrents_cab, actionsMenu.getMenu());
Context themedContext = ((ActionBarActivity) getActivity()).getSupportActionBar().getThemedContext();
selectionManagerMode = new SelectionManagerMode(themedContext, torrentsList, R.plurals.navigation_torrentsselected);
selectionManagerMode.onCreateActionMode(mode, menu);
return true;
}
@ -255,9 +292,9 @@ public class TorrentsFragment extends Fragment implements OnLabelPickedListener @@ -255,9 +292,9 @@ public class TorrentsFragment extends Fragment implements OnLabelPickedListener
selectionManagerMode.onPrepareActionMode(mode, menu);
// Hide/show options depending on the type of server we are connected to
if (daemonType != null) {
menu.findItem(R.id.action_start).setVisible(Daemon.supportsStoppingStarting(daemonType));
menu.findItem(R.id.action_stop).setVisible(Daemon.supportsStoppingStarting(daemonType));
menu.findItem(R.id.action_setlabel).setVisible(Daemon.supportsSetLabel(daemonType));
actionsMenu.getMenu().findItem(R.id.action_start).setVisible(Daemon.supportsStoppingStarting(daemonType));
actionsMenu.getMenu().findItem(R.id.action_stop).setVisible(Daemon.supportsStoppingStarting(daemonType));
actionsMenu.getMenu().findItem(R.id.action_setlabel).setVisible(Daemon.supportsSetLabel(daemonType));
}
// Pause autorefresh
if (getActivity() != null && getActivity() instanceof TorrentsActivity) {
@ -270,11 +307,11 @@ public class TorrentsFragment extends Fragment implements OnLabelPickedListener @@ -270,11 +307,11 @@ public class TorrentsFragment extends Fragment implements OnLabelPickedListener
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
// Get checked torrents
ArrayList<Torrent> checked = new ArrayList<Torrent>();
ArrayList<Torrent> checked = new ArrayList<>();
for (int i = 0; i < torrentsList.getCheckedItemPositions().size(); i++) {
if (torrentsList.getCheckedItemPositions().valueAt(i) && i < torrentsList.getAdapter().getCount())
checked.add((Torrent) torrentsList.getAdapter().getItem(
torrentsList.getCheckedItemPositions().keyAt(i)));
if (torrentsList.getCheckedItemPositions().valueAt(i) && i < torrentsList.getAdapter().getCount()) {
checked.add((Torrent) torrentsList.getAdapter().getItem(torrentsList.getCheckedItemPositions().keyAt(i)));
}
}
int itemId = item.getItemId();
@ -308,9 +345,9 @@ public class TorrentsFragment extends Fragment implements OnLabelPickedListener @@ -308,9 +345,9 @@ public class TorrentsFragment extends Fragment implements OnLabelPickedListener
return true;
} else if (itemId == R.id.action_setlabel) {
lastMultiSelectedTorrents = checked;
if (currentLabels != null)
new SetLabelDialog().setOnLabelPickedListener(TorrentsFragment.this).setCurrentLabels(currentLabels)
.show(getFragmentManager(), "SetLabelDialog");
if (currentLabels != null) {
SetLabelDialog.show(getActivity(), TorrentsFragment.this,currentLabels);
}
mode.finish();
return true;
} else {
@ -331,6 +368,9 @@ public class TorrentsFragment extends Fragment implements OnLabelPickedListener @@ -331,6 +368,9 @@ public class TorrentsFragment extends Fragment implements OnLabelPickedListener
((TorrentsActivity) getActivity()).startAutoRefresh();
}
selectionManagerMode.onDestroyActionMode(mode);
actionsMenu.setVisibility(View.GONE);
actionsToolbar.setEnabled(true);
addmenuButton.setVisibility(View.VISIBLE);
}
};
@ -351,7 +391,7 @@ public class TorrentsFragment extends Fragment implements OnLabelPickedListener @@ -351,7 +391,7 @@ public class TorrentsFragment extends Fragment implements OnLabelPickedListener
}
}
@ItemClick(resName = "torrents_list")
@ItemClick(R.id.torrents_list)
protected void torrentsListClicked(Torrent torrent) {
// Show the torrent details fragment
((TorrentsActivity) getActivity()).openDetails(torrent);
@ -365,8 +405,8 @@ public class TorrentsFragment extends Fragment implements OnLabelPickedListener @@ -365,8 +405,8 @@ public class TorrentsFragment extends Fragment implements OnLabelPickedListener
}
/**
* Updates the shown screen depending on whether we have a connection (so torrents can be shown) or not (in case we
* need to show a message suggesting help). This should only ever be called on the UI thread.
* Updates the shown screen depending on whether we have a connection (so torrents can be shown) or not (in case we need to show a message
* suggesting help). This should only ever be called on the UI thread.
* @param hasAConnection True if the user has servers configured and therefore has a connection that can be used
*/
public void updateConnectionStatus(boolean hasAConnection, Daemon daemonType) {
@ -378,6 +418,7 @@ public class TorrentsFragment extends Fragment implements OnLabelPickedListener @@ -378,6 +418,7 @@ public class TorrentsFragment extends Fragment implements OnLabelPickedListener
loadingProgress.setVisibility(View.GONE);
errorText.setVisibility(View.GONE);
nosettingsText.setVisibility(View.VISIBLE);
swipeRefreshLayout.setEnabled(false);
clear(true, true); // Indirectly also calls updateViewVisibility()
} else {
updateViewVisibility();
@ -385,8 +426,7 @@ public class TorrentsFragment extends Fragment implements OnLabelPickedListener @@ -385,8 +426,7 @@ public class TorrentsFragment extends Fragment implements OnLabelPickedListener
}
/**
* Updates the shown screen depending on whether the torrents are loading. This should only ever be called on the UI
* thread.
* Updates the shown screen depending on whether the torrents are loading. This should only ever be called on the UI thread.
* @param isLoading True if the list of torrents is (re)loading, false otherwise
*/
public void updateIsLoading(boolean isLoading) {
@ -399,10 +439,8 @@ public class TorrentsFragment extends Fragment implements OnLabelPickedListener @@ -399,10 +439,8 @@ public class TorrentsFragment extends Fragment implements OnLabelPickedListener
}
/**
* Updates the shown screen depending on whether a connection error occurred. This should only ever be called on the
* UI thread.
* @param connectionErrorMessage The error message from the last failed connection attempt, or null to clear the
* visible error text
* Updates the shown screen depending on whether a connection error occurred. This should only ever be called on the UI thread.
* @param connectionErrorMessage The error message from the last failed connection attempt, or null to clear the visible error text
*/
public void updateError(String connectionErrorMessage) {
this.connectionErrorMessage = connectionErrorMessage;
@ -425,6 +463,7 @@ public class TorrentsFragment extends Fragment implements OnLabelPickedListener @@ -425,6 +463,7 @@ public class TorrentsFragment extends Fragment implements OnLabelPickedListener
torrentsList.setVisibility(!hasError && !isLoading && !isEmpty ? View.VISIBLE : View.GONE);
loadingProgress.setVisibility(!hasError && isLoading ? View.VISIBLE : View.GONE);
emptyText.setVisibility(!hasError && !isLoading && isEmpty ? View.VISIBLE : View.GONE);
swipeRefreshLayout.setEnabled(true);
}
/**

17
app/src/main/java/org/transdroid/core/gui/lists/NoProgressHeaderTransformer.java

@ -1,17 +0,0 @@ @@ -1,17 +0,0 @@
package org.transdroid.core.gui.lists;
import org.transdroid.R;
import uk.co.senab.actionbarpulltorefresh.library.DefaultHeaderTransformer;
import android.app.Activity;
import android.view.View;
public class NoProgressHeaderTransformer extends DefaultHeaderTransformer {
@Override
public void onViewCreated(Activity activity, View headerView) {
super.onViewCreated(activity, headerView);
setProgressBarColor(activity.getResources().getColor(R.color.green));
}
}

12
app/src/main/java/org/transdroid/core/gui/lists/SimpleListItemView.java

@ -16,18 +16,19 @@ @@ -16,18 +16,19 @@
*/
package org.transdroid.core.gui.lists;
import org.androidannotations.annotations.EViewGroup;
import org.androidannotations.annotations.ViewById;
import android.content.Context;
import android.widget.FrameLayout;
import android.widget.TextView;
import org.androidannotations.annotations.EViewGroup;
import org.androidannotations.annotations.ViewById;
import org.transdroid.R;
/**
* View that represents some {@link SimpleListItem} object and simple prints out the text (in proper style)
* @author Eric Kok
*/
@EViewGroup(resName="list_item_simple")
@EViewGroup(R.layout.list_item_simple)
public class SimpleListItemView extends FrameLayout {
@ViewById
@ -39,8 +40,9 @@ public class SimpleListItemView extends FrameLayout { @@ -39,8 +40,9 @@ public class SimpleListItemView extends FrameLayout {
public void bind(SimpleListItem filterItem, int autoLinkMask) {
itemText.setText(filterItem.getName());
if (autoLinkMask > 0)
if (autoLinkMask > 0) {
itemText.setAutoLinkMask(autoLinkMask);
}
}
}

43
app/src/main/java/org/transdroid/core/gui/lists/TorrentDetailsView.java

@ -16,13 +16,6 @@ @@ -16,13 +16,6 @@
*/
package org.transdroid.core.gui.lists;
import org.androidannotations.annotations.EViewGroup;
import org.androidannotations.annotations.ViewById;
import org.transdroid.R;
import org.transdroid.daemon.Daemon;
import org.transdroid.daemon.Torrent;
import org.transdroid.daemon.util.FileSizeConverter;
import android.content.Context;
import android.text.TextUtils;
import android.text.format.DateUtils;
@ -30,16 +23,23 @@ import android.view.View; @@ -30,16 +23,23 @@ import android.view.View;
import android.widget.RelativeLayout;
import android.widget.TextView;
import org.androidannotations.annotations.EViewGroup;
import org.androidannotations.annotations.ViewById;
import org.transdroid.R;
import org.transdroid.daemon.Daemon;
import org.transdroid.daemon.Torrent;
import org.transdroid.daemon.util.FileSizeConverter;
/**
* Represents a group of views that show torrent status, sizes, speeds and other details.
* @author Eric Kok
*/
@EViewGroup(resName="fragment_details_header")
@EViewGroup(R.layout.fragment_details_header)
public class TorrentDetailsView extends RelativeLayout {
@ViewById
protected TextView labelText, dateaddedText, uploadedText, uploadedunitText, ratioText, upspeedText, seedersText,
downloadedunitText, downloadedText, totalsizeText, downspeedText, leechersText, statusText;
protected TextView labelText, dateaddedText, uploadedText, uploadedunitText, ratioText, upspeedText, seedersText, downloadedunitText,
downloadedText, totalsizeText, downspeedText, leechersText, statusText;
@ViewById
protected TorrentStatusLayout statusLayout;
@ -73,10 +73,9 @@ public class TorrentDetailsView extends RelativeLayout { @@ -73,10 +73,9 @@ public class TorrentDetailsView extends RelativeLayout {
// Set status texts
if (torrent.getDateAdded() != null) {
dateaddedText.setText(getResources().getString(
R.string.status_sincedate,
DateUtils.getRelativeDateTimeString(getContext(), torrent.getDateAdded().getTime(),
DateUtils.SECOND_IN_MILLIS, DateUtils.WEEK_IN_MILLIS, DateUtils.FORMAT_ABBREV_MONTH)));
dateaddedText.setText(getResources().getString(R.string.status_sincedate, DateUtils
.getRelativeDateTimeString(getContext(), torrent.getDateAdded().getTime(), DateUtils.SECOND_IN_MILLIS,
DateUtils.WEEK_IN_MILLIS, DateUtils.FORMAT_ABBREV_MONTH)));
dateaddedText.setVisibility(View.VISIBLE);
} else {
dateaddedText.setVisibility(View.INVISIBLE);
@ -85,24 +84,20 @@ public class TorrentDetailsView extends RelativeLayout { @@ -85,24 +84,20 @@ public class TorrentDetailsView extends RelativeLayout {
statusLayout.setStatus(torrent.getStatusCode());
statusText.setText(getResources().getString(R.string.status_status, local.getProgressStatusEta(getResources())));
ratioText.setText(getResources().getString(R.string.status_ratio, local.getRatioString()));
seedersText.setText(getResources().getString(R.string.status_seeders, torrent.getSeedersConnected(),
torrent.getSeedersKnown()));
leechersText.setText(getResources().getString(R.string.status_leechers, torrent.getLeechersConnected(),
torrent.getLeechersKnown()));
seedersText.setText(getResources().getString(R.string.status_seeders, torrent.getSeedersConnected(), torrent.getSeedersKnown()));
leechersText.setText(getResources().getString(R.string.status_leechers, torrent.getLeechersConnected(), torrent.getLeechersKnown()));
// TODO: Add field that displays torrent errors (as opposed to tracker errors)
// TODO: Add field that displays availability
// Sizes and speeds texts
totalsizeText.setText(getResources().getString(R.string.status_ofsize,
FileSizeConverter.getSize(torrent.getTotalSize())));
totalsizeText.setText(getResources().getString(R.string.status_ofsize, FileSizeConverter.getSize(torrent.getTotalSize())));
downloadedText.setText(FileSizeConverter.getSize(torrent.getDownloadedEver(), false));
downloadedunitText.setText(FileSizeConverter.getSizeUnit(torrent.getDownloadedEver()).toString());
uploadedText.setText(FileSizeConverter.getSize(torrent.getUploadedEver(), false));
uploadedunitText.setText(FileSizeConverter.getSizeUnit(torrent.getUploadedEver()).toString());
downspeedText.setText(getResources().getString(R.string.status_speed_down_details,
FileSizeConverter.getSize(torrent.getRateDownload()) + "/s"));
upspeedText.setText(getResources().getString(R.string.status_speed_up,
FileSizeConverter.getSize(torrent.getRateUpload()) + "/s"));
downspeedText
.setText(getResources().getString(R.string.status_speed_down_details, FileSizeConverter.getSize(torrent.getRateDownload()) + "/s"));
upspeedText.setText(getResources().getString(R.string.status_speed_up, FileSizeConverter.getSize(torrent.getRateUpload()) + "/s"));
}

9
app/src/main/java/org/transdroid/core/gui/lists/TorrentFileView.java

@ -16,18 +16,19 @@ @@ -16,18 +16,19 @@
*/
package org.transdroid.core.gui.lists;
import android.content.Context;
import android.widget.TextView;
import org.androidannotations.annotations.EViewGroup;
import org.androidannotations.annotations.ViewById;
import org.transdroid.R;
import org.transdroid.daemon.TorrentFile;
import android.content.Context;
import android.widget.TextView;
/**
* View that represents some {@link TorrentFile} object and show the file's name, status and priority
* @author Eric Kok
*/
@EViewGroup(resName="list_item_torrentfile")
@EViewGroup(R.layout.list_item_torrentfile)
public class TorrentFileView extends TorrentFilePriorityLayout {
@ViewById

12
app/src/main/java/org/transdroid/core/gui/lists/TorrentProgressBar.java

@ -82,12 +82,12 @@ public class TorrentProgressBar extends View { @@ -82,12 +82,12 @@ public class TorrentProgressBar extends View {
}
private void initPaints() {
notdonePaint.setColor(0xFFEEEEEE); // Light grey
inactiveDonePaint.setColor(0xFFA759D4); // Purple
inactivePaint.setColor(0xFF9E9E9E); // Grey
progressPaint.setColor(0xFF42A8FA); // Blue
donePaint.setColor(0xFF8ACC12); // Green
errorPaint.setColor(0xFFDE3939); // Red
notdonePaint.setColor(getResources().getColor(R.color.torrent_background));
inactiveDonePaint.setColor(getResources().getColor(R.color.torrent_paused));
inactivePaint.setColor(getResources().getColor(R.color.torrent_other));
progressPaint.setColor(getResources().getColor(R.color.torrent_downloading));
donePaint.setColor(getResources().getColor(R.color.torrent_seeding));
errorPaint.setColor(getResources().getColor(R.color.torrent_error));
}
@Override

2
app/src/main/java/org/transdroid/core/gui/lists/TorrentStatusLayout.java

@ -68,7 +68,7 @@ public class TorrentStatusLayout extends RelativeLayout { @@ -68,7 +68,7 @@ public class TorrentStatusLayout extends RelativeLayout {
/**
* Registers the status of the represented torrent and invalidates the view so the status colour will be updated
* accordingly.
* @param status
* @param status The updated torrent status to show
*/
public void setStatus(TorrentStatus status) {
this.status = status;

17
app/src/main/java/org/transdroid/core/gui/lists/TorrentView.java

@ -16,21 +16,22 @@ @@ -16,21 +16,22 @@
*/
package org.transdroid.core.gui.lists;
import org.androidannotations.annotations.EViewGroup;
import org.androidannotations.annotations.ViewById;
import org.transdroid.daemon.Torrent;
import org.transdroid.daemon.TorrentStatus;
import android.content.Context;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import org.androidannotations.annotations.EViewGroup;
import org.androidannotations.annotations.ViewById;
import org.transdroid.R;
import org.transdroid.daemon.Torrent;
import org.transdroid.daemon.TorrentStatus;
/**
* View that represents some {@link Torrent} object and displays progress, status, speeds, etc.
* @author Eric Kok
*/
@EViewGroup(resName = "list_item_torrent")
@EViewGroup(R.layout.list_item_torrent)
public class TorrentView extends TorrentStatusLayout {
@ViewById
@ -54,8 +55,8 @@ public class TorrentView extends TorrentStatusLayout { @@ -54,8 +55,8 @@ public class TorrentView extends TorrentStatusLayout {
priorityImage.setVisibility(View.INVISIBLE);
// Only show status bar, peers and speed fields if relevant, i.e. when downloading or actively seeding
if (torrent.getStatusCode() == TorrentStatus.Downloading
|| (torrent.getStatusCode() == TorrentStatus.Seeding && torrent.getRateUpload() > 0)) {
if (torrent.getStatusCode() == TorrentStatus.Downloading ||
(torrent.getStatusCode() == TorrentStatus.Seeding && torrent.getRateUpload() > 0)) {
torrentProgressbar.setVisibility(View.VISIBLE);
torrentProgressbar.setProgress((int) (torrent.getDownloadedPercentage() * 100));
torrentProgressbar.setActive(torrent.canPause());

16
app/src/main/java/org/transdroid/core/gui/lists/TorrentsAdapter.java

@ -16,16 +16,16 @@ @@ -16,16 +16,16 @@
*/
package org.transdroid.core.gui.lists;
import java.util.ArrayList;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import org.androidannotations.annotations.EBean;
import org.androidannotations.annotations.RootContext;
import org.transdroid.daemon.Torrent;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import java.util.ArrayList;
/**
* Adapter that contains a list of torrent objects to show.
@ -55,15 +55,17 @@ public class TorrentsAdapter extends BaseAdapter { @@ -55,15 +55,17 @@ public class TorrentsAdapter extends BaseAdapter {
@Override
public int getCount() {
if (torrents == null)
if (torrents == null) {
return 0;
}
return torrents.size();
}
@Override
public Torrent getItem(int position) {
if (torrents == null)
if (torrents == null) {
return null;
}
return torrents.get(position);
}

2
app/src/main/java/org/transdroid/core/gui/navigation/DialogHelper.java

@ -48,7 +48,7 @@ public class DialogHelper extends Activity { @@ -48,7 +48,7 @@ public class DialogHelper extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(dialog.getDialogLayoutId());
getActionBar().setDisplayHomeAsUpEnabled(true);
// TODO getActionBar().setDisplayHomeAsUpEnabled(true);
}
@Override

20
app/src/main/java/org/transdroid/core/gui/navigation/FilterListAdapter.java

@ -16,8 +16,8 @@ @@ -16,8 +16,8 @@
*/
package org.transdroid.core.gui.navigation;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.view.View;
import org.androidannotations.annotations.EBean;
import org.androidannotations.annotations.RootContext;
@ -28,12 +28,11 @@ import org.transdroid.core.gui.lists.SimpleListItem; @@ -28,12 +28,11 @@ import org.transdroid.core.gui.lists.SimpleListItem;
import org.transdroid.core.gui.lists.ViewHolderAdapter;
import org.transdroid.core.gui.navigation.StatusType.StatusTypeFilter;
import android.content.Context;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
/**
* List adapter that holds filter items, that is, servers, view types and labels. A header item is inserted where
* appropriate.
* List adapter that holds filter items, that is, servers, view types and labels. A header item is inserted where appropriate.
* @author Eric Kok
*/
@EBean
@ -54,8 +53,7 @@ public class FilterListAdapter extends MergeAdapter { @@ -54,8 +53,7 @@ public class FilterListAdapter extends MergeAdapter {
*/
public void updateServers(List<ServerSetting> servers) {
if (this.serverItems == null && servers != null) {
serverSeparator = new ViewHolderAdapter(FilterSeparatorView_.build(context).setText(
context.getString(R.string.navigation_servers)));
serverSeparator = new ViewHolderAdapter(FilterSeparatorView_.build(context).setText(context.getString(R.string.navigation_servers)));
serverSeparator.setViewVisibility(servers.isEmpty() ? View.GONE : View.VISIBLE);
addAdapter(serverSeparator);
this.serverItems = new FilterListItemAdapter(context, servers);
@ -76,8 +74,7 @@ public class FilterListAdapter extends MergeAdapter { @@ -76,8 +74,7 @@ public class FilterListAdapter extends MergeAdapter {
*/
public void updateStatusTypes(List<StatusTypeFilter> statusTypes) {
if (this.statusTypeItems == null && statusTypes != null) {
statusTypeSeparator = new ViewHolderAdapter(FilterSeparatorView_.build(context).setText(
context.getString(R.string.navigation_status)));
statusTypeSeparator = new ViewHolderAdapter(FilterSeparatorView_.build(context).setText(context.getString(R.string.navigation_status)));
statusTypeSeparator.setViewVisibility(statusTypes.isEmpty() ? View.GONE : View.VISIBLE);
addAdapter(statusTypeSeparator);
this.statusTypeItems = new FilterListItemAdapter(context, statusTypes);
@ -98,8 +95,7 @@ public class FilterListAdapter extends MergeAdapter { @@ -98,8 +95,7 @@ public class FilterListAdapter extends MergeAdapter {
*/
public void updateLabels(List<Label> labels) {
if (this.labelItems == null && labels != null) {
labelSeperator = new ViewHolderAdapter(FilterSeparatorView_.build(context).setText(
context.getString(R.string.navigation_labels)));
labelSeperator = new ViewHolderAdapter(FilterSeparatorView_.build(context).setText(context.getString(R.string.navigation_labels)));
labelSeperator.setViewVisibility(labels.isEmpty() ? View.GONE : View.VISIBLE);
addAdapter(labelSeperator);
this.labelItems = new FilterListItemAdapter(context, labels);

70
app/src/main/java/org/transdroid/core/gui/navigation/FilterListDropDownAdapter.java

@ -1,70 +0,0 @@ @@ -1,70 +0,0 @@
/*
* Copyright 2010-2013 Eric Kok et al.
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.transdroid.core.gui.navigation;
import org.androidannotations.annotations.EBean;
import org.transdroid.daemon.IDaemonAdapter;
import android.view.View;
import android.view.ViewGroup;
/**
* List adapter that holds filter items, that is, servers, view types and labels and is displayed as content to a
* Spinner instead of a ListView.
* @author Eric Kok
*/
@EBean
public class FilterListDropDownAdapter extends FilterListAdapter {
protected NavigationSelectionView navigationSelectionView = null;
private String currentServer = null;
private String currentFilter = null;
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// This returns the singleton navigation spinner view
if (navigationSelectionView == null) {
navigationSelectionView = NavigationSelectionView_.build(context);
}
navigationSelectionView.bind(currentServer, currentFilter);
return navigationSelectionView;
}
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
// This returns the item to show in the drop down list
return super.getView(position, convertView, parent);
}
public void updateCurrentFilter(NavigationFilter currentFilter) {
this.currentFilter = currentFilter.getName();
if (navigationSelectionView != null)
navigationSelectionView.bind(this.currentServer, this.currentFilter);
}
public void updateCurrentServer(IDaemonAdapter currentConnection) {
this.currentServer = currentConnection.getSettings().getName();
if (navigationSelectionView != null)
navigationSelectionView.bind(this.currentServer, this.currentFilter);
}
public void hideServersLabel() {
serverSeparator.setViewVisibility(View.GONE);
notifyDataSetInvalidated();
}
}

10
app/src/main/java/org/transdroid/core/gui/navigation/FilterListItemAdapter.java

@ -16,16 +16,16 @@ @@ -16,16 +16,16 @@
*/
package org.transdroid.core.gui.navigation;
import java.util.List;
import org.transdroid.core.gui.lists.SimpleListItem;
import org.transdroid.core.gui.lists.SimpleListItemView;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import org.transdroid.core.gui.lists.SimpleListItem;
import org.transdroid.core.gui.lists.SimpleListItemView;
import java.util.List;
public class FilterListItemAdapter extends BaseAdapter {
private final Context context;

11
app/src/main/java/org/transdroid/core/gui/navigation/FilterListItemView.java

@ -16,19 +16,20 @@ @@ -16,19 +16,20 @@
*/
package org.transdroid.core.gui.navigation;
import org.androidannotations.annotations.EViewGroup;
import org.androidannotations.annotations.ViewById;
import org.transdroid.core.gui.lists.SimpleListItem;
import android.content.Context;
import android.widget.FrameLayout;
import android.widget.TextView;
import org.androidannotations.annotations.EViewGroup;
import org.androidannotations.annotations.ViewById;
import org.transdroid.R;
import org.transdroid.core.gui.lists.SimpleListItem;
/**
* View that represents some {@link SimpleListItem} object specifically used to represent a navigation filter item.
* @author Eric Kok
*/
@EViewGroup(resName="list_item_filter")
@EViewGroup(R.layout.list_item_filter)
public class FilterListItemView extends FrameLayout {
@ViewById

12
app/src/main/java/org/transdroid/core/gui/navigation/FilterSeparatorView.java

@ -16,19 +16,20 @@ @@ -16,19 +16,20 @@
*/
package org.transdroid.core.gui.navigation;
import org.androidannotations.annotations.EViewGroup;
import org.androidannotations.annotations.ViewById;
import android.content.Context;
import android.widget.AbsListView;
import android.widget.FrameLayout;
import android.widget.TextView;
import org.androidannotations.annotations.EViewGroup;
import org.androidannotations.annotations.ViewById;
import org.transdroid.R;
/**
* A list item that shows a sub header or separator (in underlined Holo style).
* @author Eric Kok
*/
@EViewGroup(resName="list_item_separator")
@EViewGroup(R.layout.list_item_separator)
public class FilterSeparatorView extends FrameLayout {
protected String text;
@ -47,8 +48,7 @@ public class FilterSeparatorView extends FrameLayout { @@ -47,8 +48,7 @@ public class FilterSeparatorView extends FrameLayout {
*/
public FilterSeparatorView setText(String text) {
separatorText.setText(text);
setLayoutParams(new AbsListView.LayoutParams(AbsListView.LayoutParams.WRAP_CONTENT,
AbsListView.LayoutParams.WRAP_CONTENT));
setLayoutParams(new AbsListView.LayoutParams(AbsListView.LayoutParams.WRAP_CONTENT, AbsListView.LayoutParams.WRAP_CONTENT));
return this;
}

32
app/src/main/java/org/transdroid/core/gui/navigation/Label.java

@ -16,16 +16,16 @@ @@ -16,16 +16,16 @@
*/
package org.transdroid.core.gui.navigation;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import org.transdroid.core.gui.lists.SimpleListItem;
import org.transdroid.daemon.Torrent;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Represents some label that is active or available on the server.
@ -51,8 +51,9 @@ public class Label implements SimpleListItem, NavigationFilter, Comparable<Label @@ -51,8 +51,9 @@ public class Label implements SimpleListItem, NavigationFilter, Comparable<Label
@Override
public String getName() {
if (TextUtils.isEmpty(this.name))
if (TextUtils.isEmpty(this.name)) {
return unnamedLabelText;
}
return this.name;
}
@ -77,8 +78,9 @@ public class Label implements SimpleListItem, NavigationFilter, Comparable<Label @@ -77,8 +78,9 @@ public class Label implements SimpleListItem, NavigationFilter, Comparable<Label
*/
@Override
public boolean matches(Torrent torrent, boolean dormantAsInactive) {
if (isEmptyLabel)
if (isEmptyLabel) {
return TextUtils.isEmpty(torrent.getLabelName());
}
return torrent.getLabelName() != null && torrent.getLabelName().equals(name);
}
@ -88,23 +90,23 @@ public class Label implements SimpleListItem, NavigationFilter, Comparable<Label @@ -88,23 +90,23 @@ public class Label implements SimpleListItem, NavigationFilter, Comparable<Label
}
/**
* Converts a list of labels as retrieved from a server daemon into a list of labels that can be used in the UI as
* navigation filters.
* Converts a list of labels as retrieved from a server daemon into a list of labels that can be used in the UI as navigation filters.
* @param daemonLabels The raw list of labels as received from the server daemon adapter
* @param unnamedLabel The text to show for the empty label (i.e. the unnamed label)
* @return A label items that can be used in a filter list such as the action bar spinner
*/
public static ArrayList<Label> convertToNavigationLabels(List<org.transdroid.daemon.Label> daemonLabels,
String unnamedLabel) {
if (daemonLabels == null)
public static ArrayList<Label> convertToNavigationLabels(List<org.transdroid.daemon.Label> daemonLabels, String unnamedLabel) {
if (daemonLabels == null) {
return null;
ArrayList<Label> localLabels = new ArrayList<Label>();
}
ArrayList<Label> localLabels = new ArrayList<>();
unnamedLabelText = unnamedLabel;
localLabels.add(new Label(unnamedLabel, -1, true));
for (org.transdroid.daemon.Label label : daemonLabels) {
if (!TextUtils.isEmpty(label.getName()))
if (!TextUtils.isEmpty(label.getName())) {
localLabels.add(new Label(label));
}
}
Collections.sort(localLabels);
return localLabels;
}

14
app/src/main/java/org/transdroid/core/gui/navigation/NavigationFilter.java

@ -16,10 +16,10 @@ @@ -16,10 +16,10 @@
*/
package org.transdroid.core.gui.navigation;
import org.transdroid.daemon.Torrent;
import android.os.Parcelable;
import org.transdroid.daemon.Torrent;
/**
* Represents a filter, used in the app navigation, that can check if some torrent matches the user-set filter
* @author Eric Kok
@ -27,11 +27,10 @@ import android.os.Parcelable; @@ -27,11 +27,10 @@ import android.os.Parcelable;
public interface NavigationFilter extends Parcelable {
/**
* Implementations should check if the supplied torrent matches the filter; for example a label filter should return
* true if the torrent's label equals this items label name.
* Implementations should check if the supplied torrent matches the filter; for example a label filter should return true if the torrent's label
* equals this items label name.
* @param torrent The torrent to check for matches
* @param dormantAsInactive If true, dormant (0KB/s, so no data transfer) torrents are never actively downloading or
* seeding
* @param dormantAsInactive If true, dormant (0KB/s, so no data transfer) torrents are never actively downloading or seeding
* @return True if the torrent matches the filter and should be shown in the current screen, false otherwise
*/
boolean matches(Torrent torrent, boolean dormantAsInactive);
@ -43,8 +42,7 @@ public interface NavigationFilter extends Parcelable { @@ -43,8 +42,7 @@ public interface NavigationFilter extends Parcelable {
String getName();
/**
* Implementations should return a code that (within reasonable expectations) uniquely identifies it in the list of
* navigation filters
* Implementations should return a code that (within reasonable expectations) uniquely identifies it in the list of navigation filters
* @return The code to uniquely identify this specific navigation filter, such as the name with a class name prefix
*/
String getCode();

181
app/src/main/java/org/transdroid/core/gui/navigation/NavigationHelper.java

@ -18,15 +18,12 @@ package org.transdroid.core.gui.navigation; @@ -18,15 +18,12 @@ package org.transdroid.core.gui.navigation;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.TypefaceSpan;
import com.nostra13.universalimageloader.cache.disc.impl.ext.LruDiskCache;
import com.nostra13.universalimageloader.cache.disc.impl.ext.LruDiscCache;
import com.nostra13.universalimageloader.cache.disc.naming.Md5FileNameGenerator;
import com.nostra13.universalimageloader.cache.memory.impl.UsingFreqLimitedMemoryCache;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
@ -41,34 +38,82 @@ import org.transdroid.R; @@ -41,34 +38,82 @@ import org.transdroid.R;
import java.io.IOException;
import de.keyboardsurfer.android.widget.crouton.Crouton;
import de.keyboardsurfer.android.widget.crouton.Style;
/**
* Helper for activities to make navigation-related decisions, such as when a device can display a larger, tablet style
* layout or how to display errors.
* Helper for activities to make navigation-related decisions, such as when a device can display a larger, tablet style layout or how to display
* errors.
* @author Eric Kok
*/
@SuppressLint("ResourceAsColor")
@EBean
public class NavigationHelper {
private static ImageLoader imageCache;
@RootContext
protected Context context;
private Boolean inDebugMode;
private static ImageLoader imageCache;
/**
* Use with {@link Crouton#showText(android.app.Activity, int, Style)} (and variants) to display error messages.
* Converts a string into a {@link Spannable} that displays the string in the Roboto Condensed font
* @param string A plain text {@link String}
* @return A {@link Spannable} that can be applied to supporting views (such as the action bar title) so that the input string will be displayed
* using the Roboto Condensed font (if the OS has this)
*/
public static Style CROUTON_ERROR_STYLE =
new Style.Builder().setBackgroundColor(R.color.crouton_error).setTextSize(13).build();
public static SpannableString buildCondensedFontString(String string) {
if (string == null) {
return null;
}
SpannableString s = new SpannableString(string);
s.setSpan(new TypefaceSpan("sans-serif-condensed"), 0, s.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return s;
}
/**
* Use with {@link Crouton#showText(android.app.Activity, int, Style)} (and variants) to display info messages.
* Analyses a torrent http or magnet URI and tries to come up with a reasonable human-readable name.
* @param rawTorrentUri The raw http:// or magnet: link to the torrent
* @return A best-guess, reasonably long name for the linked torrent
*/
public static Style CROUTON_INFO_STYLE =
new Style.Builder().setBackgroundColor(R.color.crouton_info).setTextSize(13).build();
public static String extractNameFromUri(Uri rawTorrentUri) {
if (rawTorrentUri.getScheme() == null) {
// Probably an incorrect URI; just return the whole thing
return rawTorrentUri.toString();
}
if (rawTorrentUri.getScheme().equals("magnet")) {
// Magnet links might have a dn (display name) parameter
String dn = getQueryParameter(rawTorrentUri, "dn");
if (dn != null && !dn.equals("")) {
return dn;
}
// If not, try to return the hash that is specified as xt (exact topci)
String xt = getQueryParameter(rawTorrentUri, "xt");
if (xt != null && !xt.equals("")) {
return xt;
}
}
if (rawTorrentUri.isHierarchical()) {
String path = rawTorrentUri.getPath();
if (path != null) {
if (path.contains("/")) {
path = path.substring(path.lastIndexOf("/"));
}
return path;
}
}
// No idea what to do with this; return as is
return rawTorrentUri.toString();
}
private static String getQueryParameter(Uri uri, String parameter) {
int start = uri.toString().indexOf(parameter + "=");
if (start >= 0) {
int begin = start + (parameter + "=").length();
int end = uri.toString().indexOf("&", begin);
return uri.toString().substring(begin, end >= 0 ? end : uri.toString().length());
}
return null;
}
/**
* Returns (and initialises, if needed) an image cache that uses memory and (1MB) local storage.
@ -78,8 +123,9 @@ public class NavigationHelper { @@ -78,8 +123,9 @@ public class NavigationHelper {
if (imageCache == null) {
imageCache = ImageLoader.getInstance();
try {
LruDiskCache diskCache =
new LruDiskCache(context.getCacheDir(), null, new Md5FileNameGenerator(), 640000, 25);
// LruDiskCache diskCache =
// new LruDiskCache(context.getCacheDir(), null, new Md5FileNameGenerator(), 640000, 25);
LruDiscCache diskCache = new LruDiscCache(context.getCacheDir(), null, new Md5FileNameGenerator(), 640000, 25);
// @formatter:off
Builder imageCacheBuilder = new Builder(context)
.defaultDisplayImageOptions(
@ -100,24 +146,16 @@ public class NavigationHelper { @@ -100,24 +146,16 @@ public class NavigationHelper {
}
/**
* Whether any search-related UI components should be shown in the interface. At the moment returns false only if we
* run as Transdroid Lite version.
* @return True if search is enabled, false otherwise
* Returns the application name (like Transdroid) and version name (like 1.5.0), appended by the version code (like 180).
* @return The app name and version, such as 'Transdroid 1.5.0 (180)'
*/
public String getAppNameAndVersion() {
String appName = context.getString(R.string.app_name);
try {
PackageInfo m = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
return appName + " " + m.versionName + " (" + m.versionCode + ")";
} catch (NameNotFoundException e) {
return appName;
}
return context.getString(R.string.app_name) + " " + BuildConfig.VERSION_NAME + " (" + Integer.toString(BuildConfig.VERSION_CODE) + ")";
}
/**
* Returns whether the device is considered small (i.e. a phone) rather than large (i.e. a tablet). Can, for
* example, be used to determine if a dialog should be shown full screen. Currently is true if the device's smallest
* dimension is 500 dip.
* Returns whether the device is considered small (i.e. a phone) rather than large (i.e. a tablet). Can, for example, be used to determine if a
* dialog should be shown full screen. Currently is true if the device's smallest dimension is 500 dip.
* @return True if the app runs on a small device, false otherwise
*/
public boolean isSmallScreen() {
@ -125,8 +163,8 @@ public class NavigationHelper { @@ -125,8 +163,8 @@ public class NavigationHelper {
}
/**
* Whether any search-related UI components should be shown in the interface. At the moment returns false only if we
* run as Transdroid Lite version.
* Whether any search-related UI components should be shown in the interface. At the moment returns false only if we run as Transdroid Lite
* version.
* @return True if search is enabled, false otherwise
*/
public boolean enableSearchUi() {
@ -134,8 +172,7 @@ public class NavigationHelper { @@ -134,8 +172,7 @@ public class NavigationHelper {
}
/**
* Whether any RSS-related UI components should be shown in the interface. At the moment returns false only if we
* run as Transdroid Lite version.
* Whether any RSS-related UI components should be shown in the interface. At the moment returns false only if we run as Transdroid Lite version.
* @return True if search is enabled, false otherwise
*/
public boolean enableRssUi() {
@ -143,8 +180,8 @@ public class NavigationHelper { @@ -143,8 +180,8 @@ public class NavigationHelper {
}
/**
* Returns whether any seedbox-related components should be shown in the interface; specifically the option to add
* server settings via easy seedbox-specific screens.
* Returns whether any seedbox-related components should be shown in the interface; specifically the option to add server settings via easy
* seedbox-specific screens.
* @return True if seedbox settings should be shown, false otherwise
*/
public boolean enableSeedboxes() {
@ -153,75 +190,11 @@ public class NavigationHelper { @@ -153,75 +190,11 @@ public class NavigationHelper {
/**
* Whether the custom app update checker should be used to check for new app and search module versions.
* @return True if it should be checked against transdroid.org if there are app updates (as opposed to using the
* Play Store for updates, for example), false otherwise
* @return True if it should be checked against transdroid.org if there are app updates (as opposed to using the Play Store for updates, for
* example), false otherwise
*/
public boolean enableUpdateChecker() {
return context.getResources().getBoolean(R.bool.updatecheck_available);
}
/**
* Converts a string into a {@link Spannable} that displays the string in the Roboto Condensed font
* @param string A plain text {@link String}
* @return A {@link Spannable} that can be applied to supporting views (such as the action bar title) so that the
* input string will be displayed using the Roboto Condensed font (if the OS has this)
*/
public static SpannableString buildCondensedFontString(String string) {
if (string == null) {
return null;
}
SpannableString s = new SpannableString(string);
s.setSpan(new TypefaceSpan("sans-serif-condensed"), 0, s.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return s;
}
/**
* Analyses a torrent http or magnet URI and tries to come up with a reasonable human-readable name.
* @param rawTorrentUri The raw http:// or magnet: link to the torrent
* @return A best-guess, reasonably long name for the linked torrent
*/
public static String extractNameFromUri(Uri rawTorrentUri) {
if (rawTorrentUri.getScheme() == null) {
// Probably an incorrect URI; just return the whole thing
return rawTorrentUri.toString();
}
if (rawTorrentUri.getScheme().equals("magnet")) {
// Magnet links might have a dn (display name) parameter
String dn = getQueryParameter(rawTorrentUri, "dn");
if (dn != null && !dn.equals("")) {
return dn;
}
// If not, try to return the hash that is specified as xt (exact topci)
String xt = getQueryParameter(rawTorrentUri, "xt");
if (xt != null && !xt.equals("")) {
return xt;
}
}
if (rawTorrentUri.isHierarchical()) {
String path = rawTorrentUri.getPath();
if (path != null) {
if (path.contains("/")) {
path = path.substring(path.lastIndexOf("/"));
}
return path;
}
}
// No idea what to do with this; return as is
return rawTorrentUri.toString();
}
private static String getQueryParameter(Uri uri, String parameter) {
int start = uri.toString().indexOf(parameter + "=");
if (start >= 0) {
int begin = start + (parameter + "=").length();
int end = uri.toString().indexOf("&", begin);
return uri.toString().substring(begin, end >= 0 ? end : uri.toString().length());
}
return null;
}
}

52
app/src/main/java/org/transdroid/core/gui/navigation/NavigationSelectionView.java

@ -1,52 +0,0 @@ @@ -1,52 +0,0 @@
/*
* Copyright 2010-2013 Eric Kok et al.
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.transdroid.core.gui.navigation;
import org.androidannotations.annotations.EViewGroup;
import org.androidannotations.annotations.ViewById;
import android.content.Context;
import android.widget.LinearLayout;
import android.widget.TextView;
/**
* View that displays the user-selected server and display filter inside the action bar list navigation spinner
* @author Eric Kok
*/
@EViewGroup(resName="actionbar_navigation")
public class NavigationSelectionView extends LinearLayout {
@ViewById
protected TextView filterText;
@ViewById
protected TextView serverText;
public NavigationSelectionView(Context context) {
super(context);
}
/**
* Binds the names of the current connected server and selected filter to this navigation view.
* @param currentServer The name of the server currently connected to
* @param currentFilter The name of the filter that is currently selected
*/
public void bind(String currentServer, String currentFilter) {
serverText.setText(currentServer);
filterText.setText(currentFilter);
}
}

9
app/src/main/java/org/transdroid/core/gui/navigation/RefreshableActivity.java

@ -16,17 +16,12 @@ @@ -16,17 +16,12 @@
*/
package org.transdroid.core.gui.navigation;
import android.view.View;
/**
* Interface to be implemented by any activity that allows its content to be refreshed; fragments can ask for
* user-initiated refreshes.
* Interface to be implemented by any activity that allows its content to be refreshed; fragments can ask for user-initiated refreshes.
* @author Eric Kok
*/
public interface RefreshableActivity {
public void refreshScreen();
public void addRefreshableView(View view);
void refreshScreen();
}

14
app/src/main/java/org/transdroid/core/gui/navigation/SelectionManagerMode.java

@ -19,6 +19,7 @@ package org.transdroid.core.gui.navigation; @@ -19,6 +19,7 @@ package org.transdroid.core.gui.navigation;
import org.transdroid.core.gui.navigation.SelectionModificationSpinner.OnModificationActionSelectedListener;
import org.transdroid.daemon.Finishable;
import android.content.Context;
import android.util.SparseBooleanArray;
import android.view.ActionMode;
import android.view.Menu;
@ -35,18 +36,21 @@ import android.widget.ListView; @@ -35,18 +36,21 @@ import android.widget.ListView;
*/
public class SelectionManagerMode implements MultiChoiceModeListener, OnModificationActionSelectedListener {
private ListView managedList;
private int titleTemplateResource;
private final Context themedContext;
private final ListView managedList;
private final int titleTemplateResource;
private Class<?> onlyCheckClass = null;
/**
* Instantiates the helper by binding it to a specific {@link ListView} and providing the text resource to display
* as title in the spinner.
* @param themedContext The context which is associated with the correct theme to apply when inflating views, i.e. the toolbar context
* @param managedList The list to manage the selection for and execute selection action to
* @param titleTemplateResource The string resource id to show as the spinners title; the number of selected items
* will be supplied as numeric formatting argument
*/
public SelectionManagerMode(ListView managedList, int titleTemplateResource) {
public SelectionManagerMode(Context themedContext, ListView managedList, int titleTemplateResource) {
this.themedContext = themedContext;
this.managedList = managedList;
this.titleTemplateResource = titleTemplateResource;
}
@ -63,7 +67,7 @@ public class SelectionManagerMode implements MultiChoiceModeListener, OnModifica @@ -63,7 +67,7 @@ public class SelectionManagerMode implements MultiChoiceModeListener, OnModifica
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
// Allow modification of selection through a spinner
SelectionModificationSpinner selectionSpinner = new SelectionModificationSpinner(managedList.getContext());
SelectionModificationSpinner selectionSpinner = new SelectionModificationSpinner(themedContext);
selectionSpinner.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.MATCH_PARENT));
selectionSpinner.setOnModificationActionSelectedListener(this);
@ -85,7 +89,7 @@ public class SelectionManagerMode implements MultiChoiceModeListener, OnModifica @@ -85,7 +89,7 @@ public class SelectionManagerMode implements MultiChoiceModeListener, OnModifica
.getCheckedItemPositions().keyAt(i)))))
checkedCount++;
}
((SelectionModificationSpinner) mode.getCustomView()).updateTitle(managedList.getContext().getResources()
((SelectionModificationSpinner) mode.getCustomView()).updateTitle(themedContext.getResources()
.getQuantityString(titleTemplateResource, checkedCount, checkedCount));
}

117
app/src/main/java/org/transdroid/core/gui/navigation/SetLabelDialog.java

@ -16,109 +16,80 @@ @@ -16,109 +16,80 @@
*/
package org.transdroid.core.gui.navigation;
import java.security.InvalidParameterException;
import java.util.Iterator;
import java.util.List;
import org.transdroid.R;
import org.transdroid.core.gui.lists.SimpleListItem;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.os.Bundle;
import android.content.Context;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.EditText;
import android.widget.ListView;
import de.keyboardsurfer.android.widget.crouton.Crouton;
/**
* A dialog fragment that allows picking a label or entering a new label to set this new label to the torrent.
* @author Eric Kok
*/
public class SetLabelDialog extends DialogFragment {
import com.afollestad.materialdialogs.MaterialDialog;
import com.nispok.snackbar.Snackbar;
import com.nispok.snackbar.SnackbarManager;
import org.transdroid.R;
private OnLabelPickedListener onLabelPickedListener = null;
private List<? extends SimpleListItem> currentLabels = null;
import java.util.Iterator;
import java.util.List;
public SetLabelDialog() {
setRetainInstance(true);
}
public class SetLabelDialog {
/**
* Sets the callback for when the user is has picked a label for the target torrent.
* @param onLabelPickedListener The event listener to this dialog
* @return This dialog, for method chaining
* A dialog fragment that allows picking a label or entering a new label to set this new label to the torrent.
* @param context The activity context that opens (and owns) this dialog
* @param onLabelPickedListener The callback when a new label has been entered or picked by the user
* @param currentLabels The list of labels as currently exist on the server, to present as list for easy selection
*/
public SetLabelDialog setOnLabelPickedListener(OnLabelPickedListener onLabelPickedListener) {
this.onLabelPickedListener = onLabelPickedListener;
return this;
}
public static void show(final Context context, final OnLabelPickedListener onLabelPickedListener, List<Label> currentLabels) {
/**
* Sets the list of currently known labels as are active on the server. These are offered to the user to pick a new
* label for the target torrents.
* @param currentLabels The list of torrent labels
* @return This dialog, for method chaining
*/
public SetLabelDialog setCurrentLabels(List<Label> currentLabels) {
// Discard the empty label in this list before storing it locally
for (Iterator<Label> iter = currentLabels.iterator(); iter.hasNext(); ) {
if (iter.next().isEmptyLabel())
if (iter.next().isEmptyLabel()) {
iter.remove();
}
this.currentLabels = currentLabels;
return this;
}
final View setLabelLayout = LayoutInflater.from(context).inflate(R.layout.dialog_setlabel, null);
final ListView labelsList = (ListView) setLabelLayout.findViewById(R.id.labels_list);
final EditText newLabelEdit = (EditText) setLabelLayout.findViewById(R.id.newlabel_edit);
final MaterialDialog dialog = new MaterialDialog.Builder(context).customView(setLabelLayout, false).positiveText(R.string.status_update)
.neutralText(R.string.status_label_remove).negativeText(android.R.string.cancel).callback(new MaterialDialog.ButtonCallback() {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
if (onLabelPickedListener == null)
throw new InvalidParameterException(
"Please first set the callback listener using setOnLabelPickedListener before opening the dialog.");
if (currentLabels == null)
throw new InvalidParameterException(
"Please first set the list of currently known labels before opening the dialog, even if the list is empty.");
final View setlabelFrame = getActivity().getLayoutInflater().inflate(R.layout.dialog_setlabel, null, false);
final ListView labelsList = (ListView) setlabelFrame.findViewById(R.id.labels_list);
final EditText newlabelEdit = (EditText) setlabelFrame.findViewById(R.id.newlabel_edit);
public void onPositive(MaterialDialog dialog) {
// User should have provided a new label
if (TextUtils.isEmpty(newLabelEdit.getText())) {
SnackbarManager.show(Snackbar.with(context).text(R.string.error_notalabel).colorResource(R.color.red));
return;
}
onLabelPickedListener.onLabelPicked(newLabelEdit.getText().toString());
}
@Override
public void onNeutral(MaterialDialog dialog) {
onLabelPickedListener.onLabelPicked(null);
}
}).build();
if (currentLabels.size() == 0) {
// Hide the list (and its label) if there are no labels yet
setlabelFrame.findViewById(R.id.pick_label).setVisibility(View.GONE);
setlabelFrame.findViewById(R.id.line1).setVisibility(View.GONE);
setlabelFrame.findViewById(R.id.line2).setVisibility(View.GONE);
setLabelLayout.findViewById(R.id.pick_label).setVisibility(View.GONE);
labelsList.setVisibility(View.GONE);
} else {
labelsList.setAdapter(new FilterListItemAdapter(getActivity(), currentLabels));
labelsList.setAdapter(new FilterListItemAdapter(context, currentLabels));
labelsList.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
onLabelPickedListener.onLabelPicked(((Label) labelsList.getItemAtPosition(position)).getName());
dismiss();
dialog.dismiss();
}
});
}
return new AlertDialog.Builder(getActivity()).setView(setlabelFrame)
.setPositiveButton(R.string.status_update, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// User should have provided a new label
if (newlabelEdit.getText().toString().equals("")) {
Crouton.showText(getActivity(), R.string.error_notalabel,
NavigationHelper.CROUTON_ERROR_STYLE);
}
onLabelPickedListener.onLabelPicked(newlabelEdit.getText().toString());
}
}).setNeutralButton(R.string.status_label_remove, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
onLabelPickedListener.onLabelPicked(null);
}
}).setNegativeButton(android.R.string.cancel, null).show();
dialog.show();
}
public interface OnLabelPickedListener {

70
app/src/main/java/org/transdroid/core/gui/navigation/SetStorageLocationDialog.java

@ -16,73 +16,35 @@ @@ -16,73 +16,35 @@
*/
package org.transdroid.core.gui.navigation;
import java.security.InvalidParameterException;
import org.transdroid.R;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.os.Bundle;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
/**
* A dialog fragment that allows changing of the storage location by editing the path text directly.
* @author Eric Kok
*/
public class SetStorageLocationDialog extends DialogFragment {
private OnStorageLocationUpdatedListener onStorageLocationUpdatedListener = null;
private String currentLocation = null;
import com.afollestad.materialdialogs.MaterialDialog;
public SetStorageLocationDialog() {
setRetainInstance(true);
}
import org.transdroid.R;
/**
* Sets the callback for when the user is done updating the storage location.
* @param onStorageLocationUpdatedListener The event listener to this dialog
* @return This dialog, for method chaining
*/
public SetStorageLocationDialog setOnStorageLocationUpdated(
OnStorageLocationUpdatedListener onStorageLocationUpdatedListener) {
this.onStorageLocationUpdatedListener = onStorageLocationUpdatedListener;
return this;
}
public class SetStorageLocationDialog {
/**
* Sets the current storage location that will be available to the user to edit
* @param currentLocation The current storage location path as text
* @return This dialog, for method chaining
* A dialog fragment that allows changing of the storage location by editing the path text directly.
* @param context The activity context that opens (and owns) this dialog
* @param onStorageLocationUpdatedListener The callback for when the user is done updating the storage location
* @param currentLocation The current storage location that will be available to the user to edit
*/
public SetStorageLocationDialog setCurrentLocation(String currentLocation) {
this.currentLocation = currentLocation;
return this;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
if (currentLocation == null)
throw new InvalidParameterException(
"Please first set the current trackers text using setCurrentLocation before opening the dialog.");
if (onStorageLocationUpdatedListener == null)
throw new InvalidParameterException(
"Please first set the callback listener using setOnStorageLocationUpdated before opening the dialog.");
final View locationFrame = getActivity().getLayoutInflater().inflate(R.layout.dialog_storagelocation, null,
false);
final EditText locationText = (EditText) locationFrame.findViewById(R.id.location_edit);
public static void show(final Context context, final OnStorageLocationUpdatedListener onStorageLocationUpdatedListener, String currentLocation) {
View locationLayout = LayoutInflater.from(context).inflate(R.layout.dialog_storagelocation, null);
final EditText locationText = (EditText) locationLayout.findViewById(R.id.location_edit);
locationText.setText(currentLocation);
return new AlertDialog.Builder(getActivity()).setView(locationFrame)
.setPositiveButton(R.string.status_update, new OnClickListener() {
new MaterialDialog.Builder(context).customView(locationLayout, false).positiveText(R.string.status_update)
.negativeText(android.R.string.cancel).callback(new MaterialDialog.ButtonCallback() {
@Override
public void onClick(DialogInterface dialog, int which) {
public void onPositive(MaterialDialog dialog) {
// User is done editing and requested to update given the text input
onStorageLocationUpdatedListener.onStorageLocationUpdated(locationText.getText().toString());
}
}).setNegativeButton(android.R.string.cancel, null).show();
}).show();
}
public interface OnStorageLocationUpdatedListener {

73
app/src/main/java/org/transdroid/core/gui/navigation/SetTrackersDialog.java

@ -16,74 +16,39 @@ @@ -16,74 +16,39 @@
*/
package org.transdroid.core.gui.navigation;
import java.security.InvalidParameterException;
import java.util.Arrays;
import java.util.List;
import org.transdroid.R;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.os.Bundle;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
/**
* A dialog fragment that allows changing the trackers of a torrent by editing the text directly.
* @author Eric Kok
*/
public class SetTrackersDialog extends DialogFragment {
import com.afollestad.materialdialogs.MaterialDialog;
private OnTrackersUpdatedListener onTrackersUpdatedListener = null;
private String currentTrackers = null;
import org.transdroid.R;
public SetTrackersDialog() {
setRetainInstance(true);
}
import java.util.Arrays;
import java.util.List;
/**
* Sets the callback for when the user is done updating the trackers list.
* @param onTrackersUpdatedListener The event listener to this dialog
* @return This dialog, for method chaining
*/
public SetTrackersDialog setOnTrackersUpdated(OnTrackersUpdatedListener onTrackersUpdatedListener) {
this.onTrackersUpdatedListener = onTrackersUpdatedListener;
return this;
}
public class SetTrackersDialog extends DialogFragment {
/**
* Sets the current trackers text/list that will be available to the user to edit
* @param currentTrackers The current trackers for the target torrent
* @return This dialog, for method chaining
* A dialog fragment that allows changing the trackers of a torrent by editing the text directly.
* @param context The activity context that opens (and owns) this dialog
* @param onTrackersUpdatedListener The callback for when the user is done updating the trackers list
* @param currentTrackers The current trackers text/list that will be available to the user to edit
*/
public SetTrackersDialog setCurrentTrackers(String currentTrackers) {
this.currentTrackers = currentTrackers;
return this;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
if (currentTrackers == null)
throw new InvalidParameterException(
"Please first set the current trackers text using setCurrentTrackers before opening the dialog.");
if (onTrackersUpdatedListener == null)
throw new InvalidParameterException(
"Please first set the callback listener using setOnTrackersUpdated before opening the dialog.");
final View trackersFrame = getActivity().getLayoutInflater().inflate(R.layout.dialog_trackers, null, false);
final EditText trackersText = (EditText) trackersFrame.findViewById(R.id.trackers_edit);
public static void show(final Context context, final OnTrackersUpdatedListener onTrackersUpdatedListener, String currentTrackers) {
View trackersLayout = LayoutInflater.from(context).inflate(R.layout.dialog_trackers, null);
final EditText trackersText = (EditText) trackersLayout.findViewById(R.id.trackers_edit);
trackersText.setText(currentTrackers);
return new AlertDialog.Builder(getActivity()).setView(trackersFrame)
.setPositiveButton(R.string.status_update, new OnClickListener() {
new MaterialDialog.Builder(context).customView(trackersLayout, false).positiveText(R.string.status_update)
.negativeText(android.R.string.cancel).callback(new MaterialDialog.ButtonCallback() {
@Override
public void onClick(DialogInterface dialog, int which) {
public void onPositive(MaterialDialog dialog) {
// User is done editing and requested to update given the text input
onTrackersUpdatedListener.onTrackersUpdated(Arrays.asList(trackersText.getText().toString()
.split("\n")));
onTrackersUpdatedListener.onTrackersUpdated(Arrays.asList(trackersText.getText().toString().split("\n")));
}
}).setNegativeButton(android.R.string.cancel, null).show();
}).show();
}
public interface OnTrackersUpdatedListener {

94
app/src/main/java/org/transdroid/core/gui/navigation/SetTransferRatesDialog.java

@ -16,93 +16,64 @@ @@ -16,93 +16,64 @@
*/
package org.transdroid.core.gui.navigation;
import java.security.InvalidParameterException;
import org.transdroid.R;
import android.app.Dialog;
import android.app.DialogFragment;
import android.os.Bundle;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.widget.Button;
import android.widget.TextView;
/**
* A dialog fragment that allow picking of maximum download and upload transfer rates as well as the resetting of these
* values.
* @author Eric Kok
*/
public class SetTransferRatesDialog extends DialogFragment {
import com.afollestad.materialdialogs.MaterialDialog;
private OnRatesPickedListener onRatesPickedListener = null;
private TextView maxSpeedDown, maxSpeedUp;
import org.transdroid.R;
public SetTransferRatesDialog() {
setRetainInstance(true);
}
public class SetTransferRatesDialog {
/**
* Sets the callback for results in this dialog (with newly selected values or a reset).
* @param onRatesPickedListener The event listener to this dialog
* @return This dialog, for method chaining
* A dialog fragment that allow picking of maximum download and upload transfer rates as well as the resetting of these values.
* @param context The activity context that opens (and owns) this dialog
* @param onRatesPickedListener The callback for results in this dialog (with newly selected values or a reset)
*/
public SetTransferRatesDialog setOnRatesPickedListener(OnRatesPickedListener onRatesPickedListener) {
this.onRatesPickedListener = onRatesPickedListener;
return this;
}
public static void show(final Context context, final OnRatesPickedListener onRatesPickedListener) {
View transferRatesLayout = LayoutInflater.from(context).inflate(R.layout.dialog_transferrates, null);
final TextView maxSpeedDown = (TextView) transferRatesLayout.findViewById(R.id.maxspeeddown_text);
final TextView maxSpeedUp = (TextView) transferRatesLayout.findViewById(R.id.maxspeedup_text);
MaterialDialog dialog = new MaterialDialog.Builder(context).customView(transferRatesLayout, false).positiveText(R.string.status_update)
.neutralText(R.string.status_maxspeed_reset).negativeText(android.R.string.cancel).callback(new MaterialDialog.ButtonCallback() {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
if (onRatesPickedListener == null)
throw new InvalidParameterException(
"Please first set the callback listener using setOnRatesPickedListener before opening the dialog.");
final View transferRatesContent = getActivity().getLayoutInflater().inflate(R.layout.dialog_transferrates,
null, false);
maxSpeedDown = (TextView) transferRatesContent.findViewById(R.id.maxspeeddown_text);
maxSpeedUp = (TextView) transferRatesContent.findViewById(R.id.maxspeedup_text);
bindButtons(transferRatesContent, maxSpeedDown, R.id.down1Button, R.id.down2Button, R.id.down3Button,
R.id.down4Button, R.id.down5Button, R.id.down6Button, R.id.down7Button, R.id.down8Button,
R.id.down9Button, R.id.down0Button);
bindButtons(transferRatesContent, maxSpeedUp, R.id.up1Button, R.id.up2Button, R.id.up3Button, R.id.up4Button,
R.id.up5Button, R.id.up6Button, R.id.up7Button, R.id.up8Button, R.id.up9Button, R.id.up0Button);
((Button) transferRatesContent.findViewById(R.id.ok_button)).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
public void onPositive(MaterialDialog dialog) {
int maxDown = -1, maxUp = -1;
try {
maxDown = Integer.parseInt(maxSpeedDown.getText().toString());
maxUp = Integer.parseInt(maxSpeedUp.getText().toString());
} catch (NumberFormatException e) {
// Impossible as we only input via the number buttons
}
if (maxDown <= 0 || maxUp <= 0) {
onRatesPickedListener.onInvalidNumber();
return;
}
onRatesPickedListener.onRatesPicked(maxDown, maxUp);
dismiss();
}
});
((Button) transferRatesContent.findViewById(R.id.reset_button)).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
public void onNeutral(MaterialDialog dialog) {
onRatesPickedListener.resetRates();
dismiss();
}
});
((Button) transferRatesContent.findViewById(R.id.cancel_button)).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
dismiss();
}
});
Dialog dialog = new Dialog(getActivity());
dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
dialog.setContentView(transferRatesContent);
return dialog;
}).build();
bindButtons(dialog.getCustomView(), maxSpeedDown, R.id.down1Button, R.id.down2Button, R.id.down3Button, R.id.down4Button, R.id.down5Button,
R.id.down6Button, R.id.down7Button, R.id.down8Button, R.id.down9Button, R.id.down0Button);
bindButtons(dialog.getCustomView(), maxSpeedUp, R.id.up1Button, R.id.up2Button, R.id.up3Button, R.id.up4Button, R.id.up5Button,
R.id.up6Button, R.id.up7Button, R.id.up8Button, R.id.up9Button, R.id.up0Button);
dialog.show();
}
private void bindButtons(View transferRatesContent, View numberView, int... buttonResource) {
private static void bindButtons(View transferRatesContent, View numberView, int... buttonResource) {
for (int i : buttonResource) {
// Keep the relevant number as reference in the view tag and bind the click listerner
transferRatesContent.findViewById(i).setTag(numberView);
@ -110,14 +81,15 @@ public class SetTransferRatesDialog extends DialogFragment { @@ -110,14 +81,15 @@ public class SetTransferRatesDialog extends DialogFragment {
}
}
private android.view.View.OnClickListener onNumberClicked = new android.view.View.OnClickListener() {
private static OnClickListener onNumberClicked = new OnClickListener() {
@Override
public void onClick(View v) {
// Append the text contents of the button itself as text to the current number (as reference in the view's
// tag)
TextView numberView = (TextView) v.getTag();
if (numberView.getText().toString().equals(getString(R.string.status_maxspeed_novalue)))
if (numberView.getText().toString().equals(v.getContext().getString(R.string.status_maxspeed_novalue))) {
numberView.setText("");
}
numberView.setText(numberView.getText().toString() + ((Button) v).getText().toString());
}
};

23
app/src/main/java/org/transdroid/core/gui/rss/RssfeedLoader.java

@ -16,18 +16,18 @@ @@ -16,18 +16,18 @@
*/
package org.transdroid.core.gui.rss;
import org.transdroid.core.app.settings.RssfeedSetting;
import org.transdroid.core.rssparser.Channel;
import org.transdroid.core.rssparser.Item;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import org.transdroid.core.app.settings.RssfeedSetting;
import org.transdroid.core.rssparser.Channel;
import org.transdroid.core.rssparser.Item;
/**
* A container class that holds RSS feed settings and, after they have been retrieved, the contents as {@link Channel},
* the number of new items and an indication of a connection error.
* A container class that holds RSS feed settings and, after they have been retrieved, the contents as {@link Channel}, the number of new items and an
* indication of a connection error.
* @author Eric Kok
*/
public class RssfeedLoader {
@ -45,7 +45,7 @@ public class RssfeedLoader { @@ -45,7 +45,7 @@ public class RssfeedLoader {
this.channel = channel;
this.hasError = hasError;
if (channel == null || channel.getItems() == null || hasError) {
hasError = true;
this.hasError = true;
newCount = -1;
return;
}
@ -67,8 +67,7 @@ public class RssfeedLoader { @@ -67,8 +67,7 @@ public class RssfeedLoader {
}
});
for (Item item : items) {
if (item.getPubdate() == null || setting.getLastViewed() == null
|| item.getPubdate().after(setting.getLastViewed())) {
if (item.getPubdate() == null || setting.getLastViewed() == null || item.getPubdate().after(setting.getLastViewed())) {
newCount++;
item.setIsNew(true);
} else {
@ -79,12 +78,12 @@ public class RssfeedLoader { @@ -79,12 +78,12 @@ public class RssfeedLoader {
// Use the url of the last RSS item the last time the feed was viewed by the user to count new items
boolean isNew = true;
for (Item item : channel.getItems()) {
if (item.getTheLink() != null && setting.getLastViewedItemUrl() != null
&& item.getTheLink().equals(setting.getLastViewedItemUrl())) {
if (item.getTheLink() != null && setting.getLastViewedItemUrl() != null && item.getTheLink().equals(setting.getLastViewedItemUrl())) {
isNew = false;
}
if (isNew)
if (isNew) {
newCount++;
}
item.setIsNew(isNew);
}
}

24
app/src/main/java/org/transdroid/core/gui/rss/RssfeedView.java

@ -16,12 +16,6 @@ @@ -16,12 +16,6 @@
*/
package org.transdroid.core.gui.rss;
import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.EViewGroup;
import org.androidannotations.annotations.ViewById;
import org.transdroid.core.app.settings.RssfeedSetting;
import org.transdroid.core.gui.navigation.NavigationHelper;
import android.content.Context;
import android.view.View;
import android.widget.ImageView;
@ -29,15 +23,22 @@ import android.widget.LinearLayout; @@ -29,15 +23,22 @@ import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.EViewGroup;
import org.androidannotations.annotations.ViewById;
import org.transdroid.R;
import org.transdroid.core.app.settings.RssfeedSetting;
import org.transdroid.core.gui.navigation.NavigationHelper;
/**
* View that represents some {@link RssfeedSetting} object and displays name as well as loads a favicon for the feed's
* site and can load how many new items are available.
* View that represents some {@link RssfeedSetting} object and displays name as well as loads a favicon for the feed's site and can load how many new
* items are available.
* @author Eric Kok
*/
@EViewGroup(resName = "list_item_rssfeed")
@EViewGroup(R.layout.list_item_rssfeed)
public class RssfeedView extends LinearLayout {
private static final String GETFVO_URL = "http://g.etfv.co/%1$s";
private static final String GRABICON_URL = "http://grabicon.com/icon?origin=www.transdroid.org&domain=%1$s";
@Bean
protected NavigationHelper navigationHelper;
@ -70,8 +71,7 @@ public class RssfeedView extends LinearLayout { @@ -70,8 +71,7 @@ public class RssfeedView extends LinearLayout {
// Clear and then asynchronously load the RSS feed site' favicon
// Uses the g.etfv.co service to resolve the favicon of any feed URL
faviconImage.setImageDrawable(null);
navigationHelper.getImageCache().displayImage(String.format(GETFVO_URL, rssfeedLoader.getSetting().getUrl()),
faviconImage);
navigationHelper.getImageCache().displayImage(String.format(GRABICON_URL, rssfeedLoader.getSetting().getUrl()), faviconImage);
}

75
app/src/main/java/org/transdroid/core/gui/rss/RssfeedsActivity.java

@ -16,9 +16,17 @@ @@ -16,9 +16,17 @@
*/
package org.transdroid.core.gui.rss;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import android.annotation.TargetApi;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import com.nispok.snackbar.Snackbar;
import com.nispok.snackbar.SnackbarManager;
import org.androidannotations.annotations.AfterViews;
import org.androidannotations.annotations.Background;
@ -27,25 +35,23 @@ import org.androidannotations.annotations.EActivity; @@ -27,25 +35,23 @@ import org.androidannotations.annotations.EActivity;
import org.androidannotations.annotations.FragmentById;
import org.androidannotations.annotations.OptionsItem;
import org.androidannotations.annotations.UiThread;
import org.androidannotations.annotations.ViewById;
import org.transdroid.R;
import org.transdroid.core.app.settings.*;
import org.transdroid.core.gui.*;
import org.transdroid.core.app.settings.ApplicationSettings;
import org.transdroid.core.app.settings.RssfeedSetting;
import org.transdroid.core.app.settings.SystemSettings_;
import org.transdroid.core.gui.TorrentsActivity_;
import org.transdroid.core.gui.log.Log;
import org.transdroid.core.gui.navigation.NavigationHelper;
import org.transdroid.core.rssparser.Channel;
import org.transdroid.core.rssparser.RssParser;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.text.TextUtils;
import de.keyboardsurfer.android.widget.crouton.Crouton;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@EActivity(resName = "activity_rssfeeds")
public class RssfeedsActivity extends Activity {
@EActivity(R.layout.activity_rssfeeds)
public class RssfeedsActivity extends ActionBarActivity {
// Settings and local data
@Bean
@ -55,26 +61,27 @@ public class RssfeedsActivity extends Activity { @@ -55,26 +61,27 @@ public class RssfeedsActivity extends Activity {
protected List<RssfeedLoader> loaders;
// Contained feeds and items fragments
@FragmentById(resName = "rssfeeds_fragment")
@FragmentById(R.id.rssfeeds_fragment)
protected RssfeedsFragment fragmentFeeds;
@FragmentById(resName = "rssitems_fragment")
@FragmentById(R.id.rssitems_fragment)
protected RssitemsFragment fragmentItems;
@ViewById
protected Toolbar rssfeedsToolbar;
@Override
public void onCreate(Bundle savedInstanceState) {
// Set the theme according to the user preference
if (SystemSettings_.getInstance_(this).useDarkTheme()) {
setTheme(R.style.TransdroidTheme_Dark);
getActionBar().setIcon(R.drawable.ic_activity_torrents);
}
super.onCreate(savedInstanceState);
}
@AfterViews
protected void init() {
// Simple action bar with up button and correct title font
getActionBar().setDisplayHomeAsUpEnabled(true);
getActionBar().setTitle(NavigationHelper.buildCondensedFontString(getString(R.string.rss_feeds)));
setSupportActionBar(rssfeedsToolbar);
getSupportActionBar().setTitle(NavigationHelper.buildCondensedFontString(getString(R.string.rss_feeds)));
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@ -93,7 +100,7 @@ public class RssfeedsActivity extends Activity { @@ -93,7 +100,7 @@ public class RssfeedsActivity extends Activity {
* Reload the RSS feed settings and start loading all the feeds. To be called from contained fragments.
*/
public void refreshFeeds() {
loaders = new ArrayList<RssfeedLoader>();
loaders = new ArrayList<>();
// For each RSS feed setting the user created, start a loader that retrieved the RSS feed (via a background
// thread) and, on success, determines the new items in the feed
for (RssfeedSetting setting : applicationSettings.getRssfeedSettings()) {
@ -124,8 +131,7 @@ public class RssfeedsActivity extends Activity { @@ -124,8 +131,7 @@ public class RssfeedsActivity extends Activity {
}
/**
* Stores the retrieved RSS feed content channel into the loader and updates the RSS feed in the feeds list
* fragment.
* Stores the retrieved RSS feed content channel into the loader and updates the RSS feed in the feeds list fragment.
* @param loader The RSS feed loader that was executed
* @param channel The data that was retrieved, or null if it could not be parsed
* @param hasError True if a connection error occurred in the loading of the feed; false otherwise
@ -137,12 +143,10 @@ public class RssfeedsActivity extends Activity { @@ -137,12 +143,10 @@ public class RssfeedsActivity extends Activity {
}
/**
* Opens an RSS feed in the dedicated fragment (if there was space in the UI) or a new {@link RssitemsActivity}.
* Optionally this also registers in the user preferences that the feed was now viewed, so that in the future the
* new items can be properly marked.
* Opens an RSS feed in the dedicated fragment (if there was space in the UI) or a new {@link RssitemsActivity}. Optionally this also registers in
* the user preferences that the feed was now viewed, so that in the future the new items can be properly marked.
* @param loader The RSS feed loader (with settings and the loaded content channel) to show
* @param markAsViewedNow True if the user settings should be updated to reflect this feed's last viewed date; false
* otherwise
* @param markAsViewedNow True if the user settings should be updated to reflect this feed's last viewed date; false otherwise
*/
public void openRssfeed(RssfeedLoader loader, boolean markAsViewedNow) {
@ -153,8 +157,9 @@ public class RssfeedsActivity extends Activity { @@ -153,8 +157,9 @@ public class RssfeedsActivity extends Activity {
// be loaded until the RSS feeds screen in opened again.
if (!loader.hasError() && loader.getChannel() != null && markAsViewedNow) {
String lastViewedItemUrl = null;
if (loader.getChannel().getItems() != null && loader.getChannel().getItems().size() > 0)
if (loader.getChannel().getItems() != null && loader.getChannel().getItems().size() > 0) {
lastViewedItemUrl = loader.getChannel().getItems().get(0).getTheLink();
}
applicationSettings.setRssfeedLastViewer(loader.getSetting().getOrder(), new Date(), lastViewedItemUrl);
}
fragmentItems.update(loader.getChannel(), loader.hasError());
@ -163,11 +168,11 @@ public class RssfeedsActivity extends Activity { @@ -163,11 +168,11 @@ public class RssfeedsActivity extends Activity {
// Error message or not yet loaded? Show a toast message instead of opening the items activity
if (loader.hasError()) {
Crouton.showText(this, R.string.rss_error, NavigationHelper.CROUTON_INFO_STYLE);
SnackbarManager.show(Snackbar.with(this).text(R.string.rss_error).colorResource(R.color.red));
return;
}
if (loader.getChannel() == null || loader.getChannel().getItems().size() == 0) {
Crouton.showText(this, R.string.rss_notloaded, NavigationHelper.CROUTON_INFO_STYLE);
SnackbarManager.show(Snackbar.with(this).text(R.string.rss_notloaded).colorResource(R.color.red));
return;
}
@ -175,14 +180,16 @@ public class RssfeedsActivity extends Activity { @@ -175,14 +180,16 @@ public class RssfeedsActivity extends Activity {
// be loaded until the RSS feeds screen in opened again
if (markAsViewedNow) {
String lastViewedItemUrl = null;
if (loader.getChannel().getItems() != null && loader.getChannel().getItems().size() > 0)
if (loader.getChannel().getItems() != null && loader.getChannel().getItems().size() > 0) {
lastViewedItemUrl = loader.getChannel().getItems().get(0).getTheLink();
}
applicationSettings.setRssfeedLastViewer(loader.getSetting().getOrder(), new Date(), lastViewedItemUrl);
}
String name = loader.getChannel().getTitle();
if (TextUtils.isEmpty(name))
if (TextUtils.isEmpty(name)) {
name = loader.getSetting().getName();
}
if (TextUtils.isEmpty(name) && !TextUtils.isEmpty(loader.getSetting().getUrl())) {
name = Uri.parse(loader.getSetting().getUrl()).getHost();
}

16
app/src/main/java/org/transdroid/core/gui/rss/RssfeedsAdapter.java

@ -16,16 +16,16 @@ @@ -16,16 +16,16 @@
*/
package org.transdroid.core.gui.rss;
import java.util.List;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import org.androidannotations.annotations.EBean;
import org.androidannotations.annotations.RootContext;
import org.transdroid.core.app.settings.RssfeedSetting;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import java.util.List;
/**
* Adapter that contains a list of {@link RssfeedSetting}s, each with associated loaded RSS feed {@link org.transdroid.core.rssparser.Channel}.
@ -55,15 +55,17 @@ public class RssfeedsAdapter extends BaseAdapter { @@ -55,15 +55,17 @@ public class RssfeedsAdapter extends BaseAdapter {
@Override
public int getCount() {
if (loaders == null)
if (loaders == null) {
return 0;
}
return loaders.size();
}
@Override
public RssfeedLoader getItem(int position) {
if (loaders == null)
if (loaders == null) {
return null;
}
return loaders.get(position);
}

34
app/src/main/java/org/transdroid/core/gui/rss/RssfeedsFragment.java

@ -16,7 +16,12 @@ @@ -16,7 +16,12 @@
*/
package org.transdroid.core.gui.rss;
import java.util.List;
import android.app.Fragment;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ListView;
import android.widget.TextView;
import org.androidannotations.annotations.AfterViews;
import org.androidannotations.annotations.Bean;
@ -26,25 +31,20 @@ import org.androidannotations.annotations.OptionsItem; @@ -26,25 +31,20 @@ import org.androidannotations.annotations.OptionsItem;
import org.androidannotations.annotations.OptionsMenu;
import org.androidannotations.annotations.ViewById;
import org.transdroid.R;
import org.transdroid.core.gui.settings.*;
import org.transdroid.core.gui.settings.MainSettingsActivity_;
import android.app.Fragment;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ListView;
import android.widget.TextView;
import java.util.List;
/**
* Fragment lists the RSS feeds the user wants to monitor and, if room, the list of items in a feed in a right pane.
* @author Eric Kok
*/
@EFragment(resName = "fragment_rssfeeds")
@OptionsMenu(resName = "fragment_rssfeeds")
@EFragment(R.layout.fragment_rssfeeds)
@OptionsMenu(R.menu.fragment_rssfeeds)
public class RssfeedsFragment extends Fragment {
// Views
@ViewById(resName = "rssfeeds_list")
@ViewById(R.id.rssfeeds_list)
protected ListView feedsList;
@Bean
protected RssfeedsAdapter rssfeedsAdapter;
@ -67,22 +67,22 @@ public class RssfeedsFragment extends Fragment { @@ -67,22 +67,22 @@ public class RssfeedsFragment extends Fragment {
@Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
menu.findItem(R.id.action_settings).setShowAsAction(
rssfeedsAdapter == null || rssfeedsAdapter.getCount() == 0 ? MenuItem.SHOW_AS_ACTION_ALWAYS
: MenuItem.SHOW_AS_ACTION_NEVER);
boolean hasFeeds = rssfeedsAdapter != null && rssfeedsAdapter.getCount() > 0;
menu.findItem(R.id.action_refresh).setVisible(hasFeeds);
menu.findItem(R.id.action_settings).setShowAsAction(!hasFeeds ? MenuItem.SHOW_AS_ACTION_ALWAYS : MenuItem.SHOW_AS_ACTION_NEVER);
}
@OptionsItem(resName = "action_settings")
@OptionsItem(R.id.action_settings)
protected void openSettings() {
MainSettingsActivity_.intent(getActivity()).start();
}
@OptionsItem(resName = "action_refresh")
@OptionsItem(R.id.action_refresh)
protected void refreshScreen() {
((RssfeedsActivity) getActivity()).refreshFeeds();
}
@ItemClick(resName = "rssfeeds_list")
@ItemClick(R.id.rssfeeds_list)
protected void onFeedClicked(RssfeedLoader loader) {
((RssfeedsActivity) getActivity()).openRssfeed(loader, true);
}

13
app/src/main/java/org/transdroid/core/gui/rss/RssitemStatusLayout.java

@ -23,20 +23,21 @@ import android.graphics.RectF; @@ -23,20 +23,21 @@ import android.graphics.RectF;
import android.util.AttributeSet;
import android.widget.RelativeLayout;
import org.transdroid.R;
/**
* A relative layout that that is checkable (to be used in a contextual action bar) and shows a coloured bar in the far
* left indicating the view status, that is, if the item is new to the user or was viewed earlier.
* A relative layout that that is checkable (to be used in a contextual action bar) and shows a coloured bar in the far left indicating the view
* status, that is, if the item is new to the user or was viewed earlier.
* @author Eric Kok
*/
public class RssitemStatusLayout extends RelativeLayout {
private final float scale = getContext().getResources().getDisplayMetrics().density;
private final int WIDTH = (int) (6 * scale + 0.5f);
private Boolean isNew = null;
private final Paint oldPaint = new Paint();
private final Paint newPaint = new Paint();
private final RectF fullRect = new RectF();
private Boolean isNew = null;
public RssitemStatusLayout(Context context) {
super(context);
@ -51,8 +52,8 @@ public class RssitemStatusLayout extends RelativeLayout { @@ -51,8 +52,8 @@ public class RssitemStatusLayout extends RelativeLayout {
}
private void initPaints() {
oldPaint.setColor(0xFF9E9E9E); // Grey
newPaint.setColor(0xFF8ACC12); // Normal green
oldPaint.setColor(getResources().getColor(R.color.file_off)); // Grey
newPaint.setColor(getResources().getColor(R.color.file_normal)); // Normal green
}
public void setIsNew(Boolean isNew) {

15
app/src/main/java/org/transdroid/core/gui/rss/RssitemView.java

@ -16,19 +16,20 @@ @@ -16,19 +16,20 @@
*/
package org.transdroid.core.gui.rss;
import org.androidannotations.annotations.EViewGroup;
import org.androidannotations.annotations.ViewById;
import org.transdroid.core.rssparser.Item;
import android.content.Context;
import android.text.format.DateUtils;
import android.widget.TextView;
import org.androidannotations.annotations.EViewGroup;
import org.androidannotations.annotations.ViewById;
import org.transdroid.R;
import org.transdroid.core.rssparser.Item;
/**
* View that represents some {@link Item} object, which is a single item in some RSS feed.
* @author Eric Kok
*/
@EViewGroup(resName = "list_item_rssitem")
@EViewGroup(R.layout.list_item_rssitem)
public class RssitemView extends RssitemStatusLayout {
// Views
@ -42,8 +43,8 @@ public class RssitemView extends RssitemStatusLayout { @@ -42,8 +43,8 @@ public class RssitemView extends RssitemStatusLayout {
public void bind(Item rssitem) {
nameText.setText(rssitem.getTitle());
dateText.setText(rssitem.getPubdate() == null ? "" : DateUtils.getRelativeDateTimeString(getContext(), rssitem
.getPubdate().getTime(), DateUtils.SECOND_IN_MILLIS, DateUtils.WEEK_IN_MILLIS,
dateText.setText(rssitem.getPubdate() == null ? "" : DateUtils
.getRelativeDateTimeString(getContext(), rssitem.getPubdate().getTime(), DateUtils.SECOND_IN_MILLIS, DateUtils.WEEK_IN_MILLIS,
DateUtils.FORMAT_ABBREV_MONTH));
setIsNew(rssitem.isNew());

33
app/src/main/java/org/transdroid/core/gui/rss/RssitemsActivity.java

@ -16,40 +16,43 @@ @@ -16,40 +16,43 @@
*/
package org.transdroid.core.gui.rss;
import android.annotation.TargetApi;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.Toolbar;
import org.androidannotations.annotations.AfterViews;
import org.androidannotations.annotations.EActivity;
import org.androidannotations.annotations.Extra;
import org.androidannotations.annotations.FragmentById;
import org.androidannotations.annotations.OptionsItem;
import org.androidannotations.annotations.ViewById;
import org.transdroid.R;
import org.transdroid.core.app.settings.*;
import org.transdroid.core.gui.*;
import org.transdroid.core.app.settings.SystemSettings_;
import org.transdroid.core.gui.TorrentsActivity_;
import org.transdroid.core.gui.navigation.NavigationHelper;
import org.transdroid.core.rssparser.Channel;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
@EActivity(resName = "activity_rssitems")
public class RssitemsActivity extends Activity {
@EActivity(R.layout.activity_rssitems)
public class RssitemsActivity extends ActionBarActivity {
@Extra
protected Channel rssfeed = null;
@Extra
protected String rssfeedName;
@FragmentById(resName = "rssitems_fragment")
@FragmentById(R.id.rssitems_fragment)
protected RssitemsFragment fragmentItems;
@ViewById
protected Toolbar rssfeedsToolbar;
@Override
public void onCreate(Bundle savedInstanceState) {
// Set the theme according to the user preference
if (SystemSettings_.getInstance_(this).useDarkTheme()) {
setTheme(R.style.TransdroidTheme_Dark);
getActionBar().setIcon(R.drawable.ic_activity_torrents);
}
super.onCreate(savedInstanceState);
}
@ -63,9 +66,9 @@ public class RssitemsActivity extends Activity { @@ -63,9 +66,9 @@ public class RssitemsActivity extends Activity {
return;
}
// Simple action bar with up button and torrent name as title
getActionBar().setDisplayHomeAsUpEnabled(true);
getActionBar().setTitle(NavigationHelper.buildCondensedFontString(rssfeedName));
setSupportActionBar(rssfeedsToolbar);
getSupportActionBar().setTitle(NavigationHelper.buildCondensedFontString(rssfeedName));
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
// Get the intent extras and show them to the already loaded fragment
fragmentItems.update(rssfeed, false);

16
app/src/main/java/org/transdroid/core/gui/rss/RssitemsAdapter.java

@ -16,16 +16,16 @@ @@ -16,16 +16,16 @@
*/
package org.transdroid.core.gui.rss;
import org.androidannotations.annotations.EBean;
import org.androidannotations.annotations.RootContext;
import org.transdroid.core.rssparser.Channel;
import org.transdroid.core.rssparser.Item;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import org.androidannotations.annotations.EBean;
import org.androidannotations.annotations.RootContext;
import org.transdroid.core.rssparser.Channel;
import org.transdroid.core.rssparser.Item;
/**
* Adapter that contains a list of {@link Item}s in an RSS feed.
* @author Eric Kok
@ -54,15 +54,17 @@ public class RssitemsAdapter extends BaseAdapter { @@ -54,15 +54,17 @@ public class RssitemsAdapter extends BaseAdapter {
@Override
public int getCount() {
if (rssfeed == null)
if (rssfeed == null) {
return 0;
}
return rssfeed.getItems().size();
}
@Override
public Item getItem(int position) {
if (rssfeed == null)
if (rssfeed == null) {
return null;
}
return rssfeed.getItems().get(position);
}

33
app/src/main/java/org/transdroid/core/gui/rss/RssitemsFragment.java

@ -27,6 +27,7 @@ import android.content.Context; @@ -27,6 +27,7 @@ import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.text.TextUtils;
import android.view.ActionMode;
import android.view.Menu;
@ -37,6 +38,9 @@ import android.widget.ListView; @@ -37,6 +38,9 @@ import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.nispok.snackbar.Snackbar;
import com.nispok.snackbar.SnackbarManager;
import org.androidannotations.annotations.AfterViews;
import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.EFragment;
@ -44,23 +48,20 @@ import org.androidannotations.annotations.InstanceState; @@ -44,23 +48,20 @@ import org.androidannotations.annotations.InstanceState;
import org.androidannotations.annotations.ItemClick;
import org.androidannotations.annotations.ViewById;
import org.transdroid.R;
import org.transdroid.core.gui.*;
import org.transdroid.core.gui.navigation.NavigationHelper;
import org.transdroid.core.gui.TorrentsActivity_;
import org.transdroid.core.gui.navigation.SelectionManagerMode;
import org.transdroid.core.gui.search.*;
import org.transdroid.core.gui.search.SearchActivity_;
import org.transdroid.core.rssparser.Channel;
import org.transdroid.core.rssparser.Item;
import java.util.ArrayList;
import java.util.List;
import de.keyboardsurfer.android.widget.crouton.Crouton;
/**
* Fragment that lists the items in a specific RSS feed
* @author Eric Kok
*/
@EFragment(resName = "fragment_rssitems")
@EFragment(R.layout.fragment_rssitems)
public class RssitemsFragment extends Fragment {
@InstanceState
@ -69,7 +70,7 @@ public class RssitemsFragment extends Fragment { @@ -69,7 +70,7 @@ public class RssitemsFragment extends Fragment {
protected boolean hasError = false;
// Views
@ViewById(resName = "rssitems_list")
@ViewById(R.id.rssitems_list)
protected ListView rssitemsList;
private MultiChoiceModeListener onItemsSelected = new MultiChoiceModeListener() {
@ -79,7 +80,8 @@ public class RssitemsFragment extends Fragment { @@ -79,7 +80,8 @@ public class RssitemsFragment extends Fragment {
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
// Show contextual action bar to add items in batch mode
mode.getMenuInflater().inflate(R.menu.fragment_rssitems_cab, menu);
selectionManagerMode = new SelectionManagerMode(rssitemsList, R.plurals.rss_itemsselected);
Context themedContext = ((ActionBarActivity) getActivity()).getSupportActionBar().getThemedContext();
selectionManagerMode = new SelectionManagerMode(themedContext, rssitemsList, R.plurals.rss_itemsselected);
selectionManagerMode.onCreateActionMode(mode, menu);
return true;
}
@ -92,7 +94,7 @@ public class RssitemsFragment extends Fragment { @@ -92,7 +94,7 @@ public class RssitemsFragment extends Fragment {
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
// Get checked torrents
List<Item> checked = new ArrayList<Item>();
List<Item> checked = new ArrayList<>();
for (int i = 0; i < rssitemsList.getCheckedItemPositions().size(); i++) {
if (rssitemsList.getCheckedItemPositions().valueAt(i)) {
checked.add(rssitemsAdapter.getItem(rssitemsList.getCheckedItemPositions().keyAt(i)));
@ -126,8 +128,7 @@ public class RssitemsFragment extends Fragment { @@ -126,8 +128,7 @@ public class RssitemsFragment extends Fragment {
}
names.append(checked.get(f).getTitle());
}
ClipboardManager clipboardManager =
(ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
ClipboardManager clipboardManager = (ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
clipboardManager.setPrimaryClip(ClipData.newPlainText("Transdroid", names.toString()));
mode.finish();
return true;
@ -146,18 +147,15 @@ public class RssitemsFragment extends Fragment { @@ -146,18 +147,15 @@ public class RssitemsFragment extends Fragment {
return new AlertDialog.Builder(getActivity()).setMessage(first.getDescription())
.setPositiveButton(R.string.action_close, null).create();
}
;
}.show(getFragmentManager(), "RssItemDescription");
} else if (itemId == R.id.action_openwebsite) {
// Open the browser to show the website contained in the item's link tag
Toast.makeText(getActivity(), getString(R.string.search_openingdetails, first.getTitle()),
Toast.LENGTH_LONG).show();
Toast.makeText(getActivity(), getString(R.string.search_openingdetails, first.getTitle()), Toast.LENGTH_LONG).show();
if (!TextUtils.isEmpty(first.getLink())) {
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(first.getLink())));
} else {
// No URL was specified in the RSS feed item link tag (or no link tag was present)
Crouton.showText(getActivity(), R.string.error_no_link, NavigationHelper.CROUTON_ERROR_STYLE);
SnackbarManager.show(Snackbar.with(getActivity()).text(R.string.error_no_link).colorResource(R.color.red));
}
} else if (itemId == R.id.action_useassearch) {
// Use the RSS item title to start a new search (mimicking the search manager style)
@ -201,8 +199,7 @@ public class RssitemsFragment extends Fragment { @@ -201,8 +199,7 @@ public class RssitemsFragment extends Fragment {
/**
* Update the shown RSS items in the list.
* @param channel The loaded RSS content channel object
* @param hasError True if there were errors in loading the channel, in which case an error text is shown; false
* otherwise
* @param hasError True if there were errors in loading the channel, in which case an error text is shown; false otherwise
*/
public void update(Channel channel, boolean hasError) {
rssitemsAdapter.update(channel);

4
app/src/main/java/org/transdroid/core/gui/search/BarcodeHelper.java

@ -106,8 +106,8 @@ public class BarcodeHelper { @@ -106,8 +106,8 @@ public class BarcodeHelper {
* can be constructed for it
*/
public static String handleScanResult(int resultCode, Intent data, boolean supportsSearch) {
String contents = data.getStringExtra("SCAN_RESULT");
String formatName = data.getStringExtra("SCAN_RESULT_FORMAT");
String contents = data != null ? data.getStringExtra("SCAN_RESULT") : null;
String formatName = data != null ? data.getStringExtra("SCAN_RESULT_FORMAT") : null;
if (formatName != null && formatName.equals("QR_CODE")) {
// Scanned barcode was a QR code: return the contents directly
return contents;

69
app/src/main/java/org/transdroid/core/gui/search/SearchActivity.java

@ -17,15 +17,16 @@ @@ -17,15 +17,16 @@
package org.transdroid.core.gui.search;
import android.annotation.TargetApi;
import android.app.ActionBar;
import android.app.ActionBar.OnNavigationListener;
import android.app.Activity;
import android.app.SearchManager;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.SearchRecentSuggestions;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
@ -46,23 +47,26 @@ import org.androidannotations.annotations.ViewById; @@ -46,23 +47,26 @@ import org.androidannotations.annotations.ViewById;
import org.transdroid.R;
import org.transdroid.core.app.search.SearchHelper;
import org.transdroid.core.app.search.SearchSite;
import org.transdroid.core.app.settings.*;
import org.transdroid.core.gui.*;
import org.transdroid.core.app.settings.ApplicationSettings;
import org.transdroid.core.app.settings.SystemSettings_;
import org.transdroid.core.app.settings.WebsearchSetting;
import org.transdroid.core.gui.TorrentsActivity_;
import org.transdroid.core.gui.navigation.NavigationHelper;
import java.util.List;
/**
* An activity that shows search results to the user (after a query was supplied by the standard Android search manager)
* and either shows the list of search sites on the left (e.g. on tablets) or allows switching between search sites via
* the action bar spinner.
* An activity that shows search results to the user (after a query was supplied by the standard Android search manager) and either shows the list of
* search sites on the left (e.g. on tablets) or allows switching between search sites via the action bar spinner.
* @author Eric Kok
*/
@EActivity(resName = "activity_search")
@OptionsMenu(resName = "activity_search")
public class SearchActivity extends Activity implements OnNavigationListener {
@EActivity(R.layout.activity_search)
@OptionsMenu(R.menu.activity_search)
public class SearchActivity extends ActionBarActivity implements ActionBar.OnNavigationListener {
@FragmentById(resName = "searchresults_fragment")
@ViewById
protected Toolbar searchToolbar;
@FragmentById(R.id.searchresults_fragment)
protected SearchResultsFragment fragmentResults;
@ViewById
protected ListView searchsitesList;
@ -71,14 +75,11 @@ public class SearchActivity extends Activity implements OnNavigationListener { @@ -71,14 +75,11 @@ public class SearchActivity extends Activity implements OnNavigationListener {
@Bean
protected ApplicationSettings applicationSettings;
@Bean
protected NavigationHelper navigationHelper;
@Bean
protected SearchHelper searchHelper;
@SystemService
protected SearchManager searchManager;
private MenuItem searchMenu = null;
private SearchRecentSuggestions suggestions =
new SearchRecentSuggestions(this, SearchHistoryProvider.AUTHORITY, SearchHistoryProvider.MODE);
private SearchRecentSuggestions suggestions = new SearchRecentSuggestions(this, SearchHistoryProvider.AUTHORITY, SearchHistoryProvider.MODE);
private List<SearchSetting> searchSites;
private SearchSetting lastUsedSite;
@ -97,7 +98,6 @@ public class SearchActivity extends Activity implements OnNavigationListener { @@ -97,7 +98,6 @@ public class SearchActivity extends Activity implements OnNavigationListener {
// Set the theme according to the user preference
if (SystemSettings_.getInstance_(this).useDarkTheme()) {
setTheme(R.style.TransdroidTheme_Dark);
getActionBar().setIcon(R.drawable.ic_activity_torrents);
}
super.onCreate(savedInstanceState);
}
@ -105,6 +105,9 @@ public class SearchActivity extends Activity implements OnNavigationListener { @@ -105,6 +105,9 @@ public class SearchActivity extends Activity implements OnNavigationListener {
@AfterViews
protected void init() {
setSupportActionBar(searchToolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
// Get the user query, as coming from the standard SearchManager
handleIntent(getIntent());
@ -127,25 +130,26 @@ public class SearchActivity extends Activity implements OnNavigationListener { @@ -127,25 +130,26 @@ public class SearchActivity extends Activity implements OnNavigationListener {
}
// Allow site selection via list (on large screens) or action bar spinner
getActionBar().setDisplayHomeAsUpEnabled(true);
if (searchsitesList != null) {
// The current layout has a dedicated list view to select the search site
SearchSitesAdapter searchSitesAdapter = SearchSitesAdapter_.getInstance_(this);
searchSitesAdapter.update(searchSites);
searchsitesList.setAdapter(searchSitesAdapter);
searchsitesList.setOnItemClickListener(onSearchSiteClicked);
// Select the last used site; this also starts the search!
// Select the last used site and start the search
if (lastUsedPosition >= 0) {
searchsitesList.setItemChecked(lastUsedPosition, true);
lastUsedSite = searchSites.get(lastUsedPosition);
refreshSearch();
}
} else {
// Use the action bar spinner to select sites
getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
getActionBar().setDisplayShowTitleEnabled(false);
getActionBar().setListNavigationCallbacks(new SearchSettingsDropDownAdapter(this, searchSites), this);
getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
getSupportActionBar().setDisplayShowTitleEnabled(false);
getSupportActionBar().setListNavigationCallbacks(new SearchSettingsDropDownAdapter(searchToolbar.getContext(), searchSites), this);
// Select the last used site; this also starts the search!
if (lastUsedPosition >= 0) {
getActionBar().setSelectedNavigationItem(lastUsedPosition);
getSupportActionBar().setSelectedNavigationItem(lastUsedPosition);
}
}
@ -154,15 +158,15 @@ public class SearchActivity extends Activity implements OnNavigationListener { @@ -154,15 +158,15 @@ public class SearchActivity extends Activity implements OnNavigationListener {
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
if (navigationHelper.enableSearchUi()) {
// Add an expandable SearchView to the action bar
MenuItem item = menu.findItem(R.id.action_search);
final SearchView searchView = new SearchView(this);
final SearchView searchView = new SearchView(searchToolbar.getContext());
searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
searchView.setQueryRefinementEnabled(true);
item.setActionView(searchView);
//searchView.setIconified(false);
searchView.setIconifiedByDefault(false);
MenuItemCompat.setActionView(item, searchView);
searchMenu = item;
}
return true;
}
@ -253,7 +257,7 @@ public class SearchActivity extends Activity implements OnNavigationListener { @@ -253,7 +257,7 @@ public class SearchActivity extends Activity implements OnNavigationListener {
}
@OptionsItem(resName = "action_refresh")
@OptionsItem(R.id.action_refresh)
protected void refreshSearch() {
if (searchMenu != null) {
@ -265,8 +269,7 @@ public class SearchActivity extends Activity implements OnNavigationListener { @@ -265,8 +269,7 @@ public class SearchActivity extends Activity implements OnNavigationListener {
// Start a browser page directly to the requested search results
WebsearchSetting websearch = (WebsearchSetting) lastUsedSite;
startActivity(
new Intent(Intent.ACTION_VIEW, Uri.parse(websearch.getBaseUrl().replace("%s", lastUsedQuery))));
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(websearch.getBaseUrl().replace("%s", lastUsedQuery))));
finish();
} else if (lastUsedSite instanceof SearchSite) {
@ -274,15 +277,15 @@ public class SearchActivity extends Activity implements OnNavigationListener { @@ -274,15 +277,15 @@ public class SearchActivity extends Activity implements OnNavigationListener {
// Save the search site currently used to search for future usage
applicationSettings.setLastUsedSearchSite(lastUsedSite);
// Update the activity title (only shown on large devices)
getActionBar().setTitle(NavigationHelper.buildCondensedFontString(
getString(R.string.search_queryonsite, lastUsedQuery, lastUsedSite.getName())));
getSupportActionBar().setTitle(
NavigationHelper.buildCondensedFontString(getString(R.string.search_queryonsite, lastUsedQuery, lastUsedSite.getName())));
// Ask the results fragment to start a search for the specified query
fragmentResults.startSearch(lastUsedQuery, (SearchSite) lastUsedSite);
}
}
@OptionsItem(resName = "action_downloadsearch")
@OptionsItem(R.id.action_downloadsearch)
protected void downloadSearchModule() {
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.transdroid.org/latest-search")));
}

18
app/src/main/java/org/transdroid/core/gui/search/SearchResultsAdapter.java

@ -16,16 +16,16 @@ @@ -16,16 +16,16 @@
*/
package org.transdroid.core.gui.search;
import java.util.List;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import org.androidannotations.annotations.EBean;
import org.androidannotations.annotations.RootContext;
import org.transdroid.core.app.search.SearchResult;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import java.util.List;
/**
* Adapter that contains a list of {@link SearchResult}s.
@ -41,7 +41,7 @@ public class SearchResultsAdapter extends BaseAdapter { @@ -41,7 +41,7 @@ public class SearchResultsAdapter extends BaseAdapter {
/**
* Allows updating the search results, replacing the old data
* @param newRssfeeds The new list of search results
* @param results The new list of search results
*/
public void update(List<SearchResult> results) {
this.results = results;
@ -55,15 +55,17 @@ public class SearchResultsAdapter extends BaseAdapter { @@ -55,15 +55,17 @@ public class SearchResultsAdapter extends BaseAdapter {
@Override
public int getCount() {
if (results == null)
if (results == null) {
return 0;
}
return results.size();
}
@Override
public SearchResult getItem(int position) {
if (results == null)
if (results == null) {
return null;
}
return results.get(position);
}

172
app/src/main/java/org/transdroid/core/gui/search/SearchResultsFragment.java

@ -17,8 +17,11 @@ @@ -17,8 +17,11 @@
package org.transdroid.core.gui.search;
import android.app.Fragment;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.support.v7.app.ActionBarActivity;
import android.text.TextUtils;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuItem;
@ -29,6 +32,9 @@ import android.widget.ProgressBar; @@ -29,6 +32,9 @@ import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.nispok.snackbar.Snackbar;
import com.nispok.snackbar.SnackbarManager;
import org.androidannotations.annotations.AfterViews;
import org.androidannotations.annotations.Background;
import org.androidannotations.annotations.Bean;
@ -42,20 +48,19 @@ import org.transdroid.core.app.search.SearchHelper; @@ -42,20 +48,19 @@ import org.transdroid.core.app.search.SearchHelper;
import org.transdroid.core.app.search.SearchHelper.SearchSortOrder;
import org.transdroid.core.app.search.SearchResult;
import org.transdroid.core.app.search.SearchSite;
import org.transdroid.core.app.settings.*;
import org.transdroid.core.gui.*;
import org.transdroid.core.gui.navigation.*;
import org.transdroid.core.app.settings.SystemSettings_;
import org.transdroid.core.gui.TorrentsActivity_;
import org.transdroid.core.gui.navigation.NavigationHelper_;
import org.transdroid.core.gui.navigation.SelectionManagerMode;
import java.util.ArrayList;
import java.util.List;
import de.keyboardsurfer.android.widget.crouton.Crouton;
/**
* Fragment that lists the items in a specific RSS feed
* @author Eric Kok
*/
@EFragment(resName = "fragment_searchresults")
@EFragment(R.layout.fragment_searchresults)
public class SearchResultsFragment extends Fragment {
@InstanceState
@ -66,80 +71,8 @@ public class SearchResultsFragment extends Fragment { @@ -66,80 +71,8 @@ public class SearchResultsFragment extends Fragment {
protected SearchHelper searchHelper;
// Views
@ViewById(resName = "searchresults_list")
@ViewById(R.id.searchresults_list)
protected ListView resultsList;
private MultiChoiceModeListener onItemsSelected = new MultiChoiceModeListener() {
SelectionManagerMode selectionManagerMode;
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
// Show contextual action bar to add items in batch mode
mode.getMenuInflater().inflate(R.menu.fragment_searchresults_cab, menu);
selectionManagerMode = new SelectionManagerMode(resultsList, R.plurals.search_resutlsselected);
selectionManagerMode.onCreateActionMode(mode, menu);
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return selectionManagerMode.onPrepareActionMode(mode, menu);
}
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
// Get checked torrents
List<SearchResult> checked = new ArrayList<SearchResult>();
for (int i = 0; i < resultsList.getCheckedItemPositions().size(); i++) {
if (resultsList.getCheckedItemPositions().valueAt(i)) {
checked.add(resultsAdapter.getItem(resultsList.getCheckedItemPositions().keyAt(i)));
}
}
int itemId = item.getItemId();
if (itemId == R.id.action_addall) {
// Start an Intent that adds multiple items at once, by supplying the urls and titles as string array
// extras and setting the Intent action to ADD_MULTIPLE
Intent intent = new Intent("org.transdroid.ADD_MULTIPLE");
String[] urls = new String[checked.size()];
String[] titles = new String[checked.size()];
for (int i = 0; i < checked.size(); i++) {
urls[i] = checked.get(i).getTorrentUrl();
titles[i] = checked.get(i).getName();
}
intent.putExtra("TORRENT_URLS", urls);
intent.putExtra("TORRENT_TITLES", titles);
if (resultsSource != null) {
intent.putExtra("PRIVATE_SOURCE", resultsSource);
}
startActivity(intent);
mode.finish();
return true;
} else if (itemId == R.id.action_showdetails) {
SearchResult first = checked.get(0);
// Open the torrent's web page in the browser
if (checked.size() > 1) {
Toast.makeText(getActivity(), getString(R.string.search_openingdetails, first.getName()),
Toast.LENGTH_LONG).show();
}
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(first.getDetailsUrl())));
return true;
} else {
return false;
}
}
@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);
}
};
@Bean
protected SearchResultsAdapter resultsAdapter;
@ViewById
@ -196,10 +129,10 @@ public class SearchResultsFragment extends Fragment { @@ -196,10 +129,10 @@ public class SearchResultsFragment extends Fragment {
emptyText.setVisibility(View.GONE);
}
@ItemClick(resName = "searchresults_list")
@ItemClick(R.id.searchresults_list)
protected void onItemClicked(SearchResult item) {
if (item.getTorrentUrl() == null) {
Crouton.showText(getActivity(), R.string.error_notorrentfile, NavigationHelper.CROUTON_ERROR_STYLE);
SnackbarManager.show(Snackbar.with(getActivity()).text(R.string.error_notorrentfile).colorResource(R.color.red));
return;
}
// Don't broadcast this intent; we can safely assume this is intended for Transdroid only
@ -212,4 +145,81 @@ public class SearchResultsFragment extends Fragment { @@ -212,4 +145,81 @@ public class SearchResultsFragment extends Fragment {
startActivity(i);
}
private MultiChoiceModeListener onItemsSelected = new MultiChoiceModeListener() {
SelectionManagerMode selectionManagerMode;
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
// Show contextual action bar to add items in batch mode
mode.getMenuInflater().inflate(R.menu.fragment_searchresults_cab, menu);
Context themedContext = ((ActionBarActivity) getActivity()).getSupportActionBar().getThemedContext();
selectionManagerMode = new SelectionManagerMode(themedContext, resultsList, R.plurals.search_resutlsselected);
selectionManagerMode.onCreateActionMode(mode, menu);
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return selectionManagerMode.onPrepareActionMode(mode, menu);
}
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
// Get checked torrents
List<SearchResult> checked = new ArrayList<SearchResult>();
for (int i = 0; i < resultsList.getCheckedItemPositions().size(); i++) {
if (resultsList.getCheckedItemPositions().valueAt(i)) {
checked.add(resultsAdapter.getItem(resultsList.getCheckedItemPositions().keyAt(i)));
}
}
int itemId = item.getItemId();
if (itemId == R.id.action_addall) {
// Start an Intent that adds multiple items at once, by supplying the urls and titles as string array
// extras and setting the Intent action to ADD_MULTIPLE
Intent intent = new Intent("org.transdroid.ADD_MULTIPLE");
String[] urls = new String[checked.size()];
String[] titles = new String[checked.size()];
for (int i = 0; i < checked.size(); i++) {
urls[i] = checked.get(i).getTorrentUrl();
titles[i] = checked.get(i).getName();
}
intent.putExtra("TORRENT_URLS", urls);
intent.putExtra("TORRENT_TITLES", titles);
if (resultsSource != null) {
intent.putExtra("PRIVATE_SOURCE", resultsSource);
}
startActivity(intent);
mode.finish();
return true;
} else if (itemId == R.id.action_showdetails) {
SearchResult first = checked.get(0);
// Open the torrent's web page in the browser
if (checked.size() > 1) {
Toast.makeText(getActivity(), getString(R.string.search_openingdetails, first.getName()), Toast.LENGTH_LONG).show();
}
if (TextUtils.isEmpty(first.getDetailsUrl())) {
Toast.makeText(getActivity(), getString(R.string.error_invalid_url_form, first.getName()), Toast.LENGTH_LONG).show();
return false;
}
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(first.getDetailsUrl())));
return true;
} else {
return false;
}
}
@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);
}
};
}

9
app/src/main/java/org/transdroid/core/gui/search/SearchSettingSelectionView.java

@ -16,18 +16,19 @@ @@ -16,18 +16,19 @@
*/
package org.transdroid.core.gui.search;
import org.androidannotations.annotations.EViewGroup;
import org.androidannotations.annotations.ViewById;
import android.content.Context;
import android.widget.FrameLayout;
import android.widget.TextView;
import org.androidannotations.annotations.EViewGroup;
import org.androidannotations.annotations.ViewById;
import org.transdroid.R;
/**
* View that shows, as part of the action bar spinner, which {@link SearchSetting} is currently chosen.
* @author Eric Kok
*/
@EViewGroup(resName = "actionbar_searchsite")
@EViewGroup(R.layout.actionbar_searchsite)
public class SearchSettingSelectionView extends FrameLayout {
@ViewById

11
app/src/main/java/org/transdroid/core/gui/search/SearchSettingsDropDownAdapter.java

@ -16,18 +16,17 @@ @@ -16,18 +16,17 @@
*/
package org.transdroid.core.gui.search;
import java.util.List;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import org.transdroid.core.gui.lists.SimpleListItem;
import org.transdroid.core.gui.navigation.FilterListItemAdapter;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import java.util.List;
/**
* List adapter that holds search settings, that is, web searches and in-app search sites, displayed as content to a
* Spinner instead of a ListView.
* List adapter that holds search settings, that is, web searches and in-app search sites, displayed as content to a Spinner instead of a ListView.
* @author Eric Kok
*/
public class SearchSettingsDropDownAdapter extends FilterListItemAdapter {

20
app/src/main/java/org/transdroid/core/gui/search/SearchSiteView.java

@ -16,23 +16,24 @@ @@ -16,23 +16,24 @@
*/
package org.transdroid.core.gui.search;
import android.content.Context;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.EViewGroup;
import org.androidannotations.annotations.ViewById;
import org.transdroid.R;
import org.transdroid.core.app.settings.RssfeedSetting;
import org.transdroid.core.gui.navigation.NavigationHelper;
import android.content.Context;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
/**
* View that represents some {@link RssfeedSetting} object and displays name as well as loads a favicon for the feed's
* site and can load how many new items are available.
* View that represents some {@link RssfeedSetting} object and displays name as well as loads a favicon for the feed's site and can load how many new
* items are available.
* @author Eric Kok
*/
@EViewGroup(resName = "list_item_searchsite")
@EViewGroup(R.layout.list_item_searchsite)
public class SearchSiteView extends LinearLayout {
private static final String GETFVO_URL = "http://g.etfv.co/%1$s";
@ -57,8 +58,7 @@ public class SearchSiteView extends LinearLayout { @@ -57,8 +58,7 @@ public class SearchSiteView extends LinearLayout {
// Clear and then asynchronously load the site's favicon
// Uses the g.etfv.co service to resolve the favicon of any URL
faviconImage.setImageDrawable(null);
navigationHelper.getImageCache().displayImage(String.format(GETFVO_URL, rssfeedLoader.getBaseUrl()),
faviconImage);
navigationHelper.getImageCache().displayImage(String.format(GETFVO_URL, rssfeedLoader.getBaseUrl()), faviconImage);
}

16
app/src/main/java/org/transdroid/core/gui/search/SearchSitesAdapter.java

@ -16,17 +16,17 @@ @@ -16,17 +16,17 @@
*/
package org.transdroid.core.gui.search;
import java.util.List;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import org.androidannotations.annotations.EBean;
import org.androidannotations.annotations.RootContext;
import org.transdroid.core.app.search.SearchSite;
import org.transdroid.core.app.settings.WebsearchSetting;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import java.util.List;
/**
* Adapter that contains a list of {@link SearchSetting}s, either {@link SearchSite} or {@link WebsearchSetting}.
@ -56,15 +56,17 @@ public class SearchSitesAdapter extends BaseAdapter { @@ -56,15 +56,17 @@ public class SearchSitesAdapter extends BaseAdapter {
@Override
public int getCount() {
if (sites == null)
if (sites == null) {
return 0;
}
return sites.size();
}
@Override
public SearchSetting getItem(int position) {
if (sites == null)
if (sites == null) {
return null;
}
return sites.get(position);
}

57
app/src/main/java/org/transdroid/core/gui/search/UrlEntryDialog.java

@ -16,56 +16,43 @@ @@ -16,56 +16,43 @@
*/
package org.transdroid.core.gui.search;
import org.transdroid.core.gui.TorrentsActivity;
import org.transdroid.core.gui.navigation.NavigationHelper;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.AlertDialog;
import android.app.DialogFragment;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.net.Uri;
import android.os.Build;
import android.text.InputType;
import android.text.TextUtils;
import android.view.inputmethod.InputMethodManager;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
import com.afollestad.materialdialogs.MaterialDialog;
import org.transdroid.R;
import org.transdroid.core.gui.TorrentsActivity;
import org.transdroid.core.gui.navigation.NavigationHelper;
public class UrlEntryDialog {
/**
* Opens a dialog that allows entry of a single URL string, which (on confirmation) will be supplied to the calling
* activity's {@link TorrentsActivity#addTorrentByUrl(String, String) method}.
* Opens a dialog that allows entry of a single URL string, which (on confirmation) will be supplied to the calling activity's {@link
* TorrentsActivity#addTorrentByUrl(String, String) method}.
* @param activity The activity that opens (and owns) this dialog
*/
@SuppressLint("ValidFragment")
public static void startUrlEntry(final TorrentsActivity activity) {
new DialogFragment() {
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public android.app.Dialog onCreateDialog(android.os.Bundle savedInstanceState) {
final EditText urlInput = new EditText(activity);
urlInput.setInputType(InputType.TYPE_TEXT_VARIATION_URI);
if (android.os.Build.VERSION.SDK_INT >= 11) {
ClipboardManager clipboard = (ClipboardManager) activity
.getSystemService(Context.CLIPBOARD_SERVICE);
public static void show(final TorrentsActivity activity) {
View inputLayout = LayoutInflater.from(activity).inflate(R.layout.dialog_url, null);
final EditText urlEdit = (EditText) inputLayout.findViewById(R.id.url_edit);
ClipboardManager clipboard = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE);
if (clipboard.hasPrimaryClip() && clipboard.getPrimaryClip().getItemCount() > 0) {
CharSequence content = clipboard.getPrimaryClip().getItemAt(0).coerceToText(activity);
urlInput.setText(content);
}
urlEdit.setText(content);
}
((InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE)).toggleSoftInput(
InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY);
return new AlertDialog.Builder(activity).setView(urlInput)
.setPositiveButton(android.R.string.ok, new OnClickListener() {
new MaterialDialog.Builder(activity).customView(inputLayout, false).positiveText(android.R.string.ok).negativeText(android.R.string.cancel)
.callback(new MaterialDialog.ButtonCallback() {
@Override
public void onClick(DialogInterface dialog, int which) {
// Assume text entry box input as URL and treat the filename (after the last /) as title
String url = urlInput.getText().toString();
public void onPositive(MaterialDialog dialog) {
String url = urlEdit.getText().toString();
Uri uri = Uri.parse(url);
if (activity != null && !TextUtils.isEmpty(url)) {
if (!TextUtils.isEmpty(url)) {
String title = NavigationHelper.extractNameFromUri(uri);
if (uri.getScheme() != null && uri.getScheme().equals("magnet")) {
activity.addTorrentByMagnetUrl(url, title);
@ -74,9 +61,7 @@ public class UrlEntryDialog { @@ -74,9 +61,7 @@ public class UrlEntryDialog {
}
}
}
}).setNegativeButton(android.R.string.cancel, null).create();
};
}.show(activity.getFragmentManager(), "urlentry");
}).show();
}
}

4
app/src/main/java/org/transdroid/core/gui/settings/HelpSettingsActivity.java

@ -37,7 +37,7 @@ import android.preference.Preference.OnPreferenceClickListener; @@ -37,7 +37,7 @@ import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceActivity;
@EActivity
public class HelpSettingsActivity extends PreferenceActivity {
public class HelpSettingsActivity extends PreferenceCompatActivity {
protected static final int DIALOG_CHANGELOG = 0;
protected static final int DIALOG_ABOUT = 1;
@ -57,7 +57,7 @@ public class HelpSettingsActivity extends PreferenceActivity { @@ -57,7 +57,7 @@ public class HelpSettingsActivity extends PreferenceActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
// Just load the system-related preferences from XML
addPreferencesFromResource(R.xml.pref_help);

9
app/src/main/java/org/transdroid/core/gui/settings/KeyBoundPreferencesActivity.java

@ -28,7 +28,6 @@ import android.preference.CheckBoxPreference; @@ -28,7 +28,6 @@ import android.preference.CheckBoxPreference;
import android.preference.EditTextPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceManager;
import android.preference.PreferenceScreen;
import android.text.method.PasswordTransformationMethod;
@ -43,13 +42,13 @@ import android.text.method.PasswordTransformationMethod; @@ -43,13 +42,13 @@ import android.text.method.PasswordTransformationMethod;
* @author Eric Kok
*/
@EActivity
public abstract class KeyBoundPreferencesActivity extends PreferenceActivity {
public abstract class KeyBoundPreferencesActivity extends PreferenceCompatActivity {
@Extra
protected int key = -1;
private SharedPreferences sharedPrefs;
private Map<String, String> originalSummaries = new HashMap<String, String>();
private Map<String, String> originalSummaries = new HashMap<>();
/**
* Should be called during the activity {@link #onCreate(android.os.Bundle)} (but after super.onCreate(Bundle)) to
@ -78,14 +77,14 @@ public abstract class KeyBoundPreferencesActivity extends PreferenceActivity { @@ -78,14 +77,14 @@ public abstract class KeyBoundPreferencesActivity extends PreferenceActivity {
// Monitor preference changes
PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(
onPreferenceChangeListener);
};
}
protected void onPause() {
super.onPause();
// Stop monitoring preference changes
PreferenceManager.getDefaultSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(
onPreferenceChangeListener);
};
}
private OnSharedPreferenceChangeListener onPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
@Override

77
app/src/main/java/org/transdroid/core/gui/settings/MainSettingsActivity.java

@ -28,8 +28,6 @@ import android.os.Bundle; @@ -28,8 +28,6 @@ import android.os.Bundle;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceActivity;
import android.view.View;
import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.EActivity;
@ -41,9 +39,8 @@ import org.transdroid.core.app.settings.ApplicationSettings; @@ -41,9 +39,8 @@ import org.transdroid.core.app.settings.ApplicationSettings;
import org.transdroid.core.app.settings.RssfeedSetting;
import org.transdroid.core.app.settings.ServerSetting;
import org.transdroid.core.app.settings.WebsearchSetting;
import org.transdroid.core.gui.*;
import org.transdroid.core.gui.TorrentsActivity_;
import org.transdroid.core.gui.navigation.NavigationHelper;
import org.transdroid.core.gui.settings.OverflowPreference.OnOverflowClicked;
import org.transdroid.core.gui.settings.RssfeedPreference.OnRssfeedClickedListener;
import org.transdroid.core.gui.settings.ServerPreference.OnServerClickedListener;
import org.transdroid.core.gui.settings.WebsearchPreference.OnWebsearchClickedListener;
@ -55,21 +52,14 @@ import java.util.ArrayList; @@ -55,21 +52,14 @@ import java.util.ArrayList;
import java.util.List;
/**
* The main activity that provides access to all application settings. It shows the configured serves, web search sites
* and RSS feeds along with other general settings.
* The main activity that provides access to all application settings. It shows the configured serves, web search sites and RSS feeds along with other
* general settings.
* @author Eric Kok
*/
@EActivity
public class MainSettingsActivity extends PreferenceActivity {
public class MainSettingsActivity extends PreferenceCompatActivity {
protected static final int DIALOG_ADDSEEDBOX = 0;
private OnOverflowClicked onOverflowClicked = new OnOverflowClicked() {
@SuppressWarnings("deprecation")
@Override
public void onOverflowClicked(View overflowButton) {
showDialog(DIALOG_ADDSEEDBOX);
}
};
@Bean
protected NavigationHelper navigationHelper;
@Bean
@ -78,8 +68,12 @@ public class MainSettingsActivity extends PreferenceActivity { @@ -78,8 +68,12 @@ public class MainSettingsActivity extends PreferenceActivity {
protected SearchHelper searchHelper;
protected SharedPreferences prefs;
private OnPreferenceClickListener onAddServer = new OnPreferenceClickListener() {
@SuppressWarnings("deprecation")
@Override
public boolean onPreferenceClick(Preference preference) {
if (navigationHelper.enableSeedboxes())
showDialog(DIALOG_ADDSEEDBOX);
else
ServerSettingsActivity_.intent(MainSettingsActivity.this).start();
return true;
}
@ -130,8 +124,7 @@ public class MainSettingsActivity extends PreferenceActivity { @@ -130,8 +124,7 @@ public class MainSettingsActivity extends PreferenceActivity {
public void onSeedboxClicked(ServerSetting serverSetting, SeedboxProvider provider, int seedboxOffset) {
// NOTE: The seedboxOffset is the seedbox type-unique order that we need to supply uin the Extras bundle to
// edit this specific seedbox
startActivity(provider.getSettings().getSettingsActivityIntent(MainSettingsActivity.this)
.putExtra("key", seedboxOffset));
startActivity(provider.getSettings().getSettingsActivityIntent(MainSettingsActivity.this).putExtra("key", seedboxOffset));
}
};
private OnWebsearchClickedListener onWebsearchClicked = new OnWebsearchClickedListener() {
@ -150,8 +143,10 @@ public class MainSettingsActivity extends PreferenceActivity { @@ -150,8 +143,10 @@ public class MainSettingsActivity extends PreferenceActivity {
@Override
public void onClick(DialogInterface dialog, int which) {
// Start the configuration activity for this specific chosen seedbox
startActivity(
SeedboxProvider.values()[which].getSettings().getSettingsActivityIntent(MainSettingsActivity.this));
if (which == 0)
ServerSettingsActivity_.intent(MainSettingsActivity.this).start();
else
startActivity(SeedboxProvider.values()[which - 1].getSettings().getSettingsActivityIntent(MainSettingsActivity.this));
}
};
@ -166,7 +161,7 @@ public class MainSettingsActivity extends PreferenceActivity { @@ -166,7 +161,7 @@ public class MainSettingsActivity extends PreferenceActivity {
protected void onResume() {
super.onResume();
getActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
prefs = getPreferenceManager().getSharedPreferences();
if (getPreferenceScreen() != null) {
@ -178,13 +173,7 @@ public class MainSettingsActivity extends PreferenceActivity { @@ -178,13 +173,7 @@ public class MainSettingsActivity extends PreferenceActivity {
// Load the preference menu and attach actions
addPreferencesFromResource(R.xml.pref_main);
OverflowPreference addServerPrefernce = (OverflowPreference) findPreference("header_addserver");
addServerPrefernce.setOnPreferenceClickListener(onAddServer);
if (navigationHelper.enableSeedboxes()) {
addServerPrefernce.setOnOverflowClickedListener(onOverflowClicked);
} else {
addServerPrefernce.hideOverflowButton();
}
findPreference("header_addserver").setOnPreferenceClickListener(onAddServer);
if (enableSearchUi) {
findPreference("header_addwebsearch").setOnPreferenceClickListener(onAddWebsearch);
}
@ -196,8 +185,8 @@ public class MainSettingsActivity extends PreferenceActivity { @@ -196,8 +185,8 @@ public class MainSettingsActivity extends PreferenceActivity {
findPreference("header_help").setOnPreferenceClickListener(onHelpSettings);
// Keep a list of the server codes and names (for default server selection)
List<String> serverCodes = new ArrayList<String>();
List<String> serverNames = new ArrayList<String>();
List<String> serverCodes = new ArrayList<>();
List<String> serverNames = new ArrayList<>();
serverCodes.add(Integer.toString(ApplicationSettings.DEFAULTSERVER_LASTUSED));
serverCodes.add(Integer.toString(ApplicationSettings.DEFAULTSERVER_ASKONADD));
serverNames.add(getString(R.string.pref_defaultserver_lastused));
@ -206,8 +195,8 @@ public class MainSettingsActivity extends PreferenceActivity { @@ -206,8 +195,8 @@ public class MainSettingsActivity extends PreferenceActivity {
// Add existing servers
List<ServerSetting> servers = applicationSettings.getNormalServerSettings();
for (ServerSetting serverSetting : servers) {
getPreferenceScreen().addPreference(new ServerPreference(this).setServerSetting(serverSetting)
.setOnServerClickedListener(onServerClicked));
getPreferenceScreen()
.addPreference(new ServerPreference(this).setServerSetting(serverSetting).setOnServerClickedListener(onServerClicked));
if (serverSetting.getUniqueIdentifier() != null) {
serverCodes.add(Integer.toString(serverSetting.getOrder()));
serverNames.add(serverSetting.getName());
@ -219,8 +208,7 @@ public class MainSettingsActivity extends PreferenceActivity { @@ -219,8 +208,7 @@ public class MainSettingsActivity extends PreferenceActivity {
for (SeedboxProvider provider : SeedboxProvider.values()) {
int seedboxOffset = 0;
for (ServerSetting seedbox : provider.getSettings().getAllServerSettings(prefs, orderOffset)) {
getPreferenceScreen().addPreference(
new SeedboxPreference(this).setProvider(provider).setServerSetting(seedbox)
getPreferenceScreen().addPreference(new SeedboxPreference(this).setProvider(provider).setServerSetting(seedbox)
.setOnSeedboxClickedListener(onSeedboxClicked, seedboxOffset));
orderOffset++;
seedboxOffset++;
@ -242,8 +230,8 @@ public class MainSettingsActivity extends PreferenceActivity { @@ -242,8 +230,8 @@ public class MainSettingsActivity extends PreferenceActivity {
} else {
List<RssfeedSetting> rssfeeds = applicationSettings.getRssfeedSettings();
for (RssfeedSetting rssfeedSetting : rssfeeds) {
getPreferenceScreen().addPreference(new RssfeedPreference(this).setRssfeedSetting(rssfeedSetting)
.setOnRssfeedClickedListener(onRssfeedClicked));
getPreferenceScreen()
.addPreference(new RssfeedPreference(this).setRssfeedSetting(rssfeedSetting).setOnRssfeedClickedListener(onRssfeedClicked));
}
}
@ -256,8 +244,8 @@ public class MainSettingsActivity extends PreferenceActivity { @@ -256,8 +244,8 @@ public class MainSettingsActivity extends PreferenceActivity {
// Add existing websearch sites
List<WebsearchSetting> websearches = applicationSettings.getWebsearchSettings();
for (WebsearchSetting websearchSetting : websearches) {
getPreferenceScreen().addPreference(new WebsearchPreference(this).setWebsearchSetting(websearchSetting)
.setOnWebsearchClickedListener(onWebsearchClicked));
getPreferenceScreen().addPreference(
new WebsearchPreference(this).setWebsearchSetting(websearchSetting).setOnWebsearchClickedListener(onWebsearchClicked));
}
// Construct list of all available search sites, in-app and web
@ -265,10 +253,10 @@ public class MainSettingsActivity extends PreferenceActivity { @@ -265,10 +253,10 @@ public class MainSettingsActivity extends PreferenceActivity {
// Retrieve the available in-app search sites (using the Torrent Search package)
List<SearchSite> searchsites = searchHelper.getAvailableSites();
if (searchsites == null) {
searchsites = new ArrayList<SearchSite>();
searchsites = new ArrayList<>();
}
List<String> siteNames = new ArrayList<String>(websearches.size() + searchsites.size());
List<String> siteValues = new ArrayList<String>(websearches.size() + searchsites.size());
List<String> siteNames = new ArrayList<>(websearches.size() + searchsites.size());
List<String> siteValues = new ArrayList<>(websearches.size() + searchsites.size());
for (SearchSite searchSite : searchsites) {
siteNames.add(searchSite.getName());
siteValues.add(searchSite.getKey());
@ -295,14 +283,15 @@ public class MainSettingsActivity extends PreferenceActivity { @@ -295,14 +283,15 @@ public class MainSettingsActivity extends PreferenceActivity {
super.onBuildHeaders(target);
}
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case DIALOG_ADDSEEDBOX:
// Open dialog to pick one of the supported seedbox providers
String[] seedboxes = new String[SeedboxProvider.values().length];
for (int i = 0; i < seedboxes.length; i++) {
seedboxes[i] = getString(R.string.pref_seedbox_addseedbox,
SeedboxProvider.values()[i].getSettings().getName());
// Open dialog to pick one of the supported seedbox providers (or a normal server)
String[] seedboxes = new String[SeedboxProvider.values().length + 1];
seedboxes[0] = getString(R.string.pref_addserver_normal);
for (int i = 0; i < seedboxes.length - 1; i++) {
seedboxes[i + 1] = getString(R.string.pref_seedbox_addseedbox, SeedboxProvider.values()[i].getSettings().getName());
}
return new AlertDialog.Builder(this).setItems(seedboxes, onAddSeedbox).create();
}

20
app/src/main/java/org/transdroid/core/gui/settings/NotificationSettingsActivity.java

@ -16,24 +16,22 @@ @@ -16,24 +16,22 @@
*/
package org.transdroid.core.gui.settings;
import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.EActivity;
import org.androidannotations.annotations.OptionsItem;
import org.transdroid.R;
import org.transdroid.core.app.settings.NotificationSettings;
import org.transdroid.core.service.BootReceiver;
import android.annotation.TargetApi;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceActivity;
import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.EActivity;
import org.androidannotations.annotations.OptionsItem;
import org.transdroid.R;
import org.transdroid.core.app.settings.NotificationSettings;
import org.transdroid.core.service.BootReceiver;
@EActivity
public class NotificationSettingsActivity extends PreferenceActivity implements
OnSharedPreferenceChangeListener {
public class NotificationSettingsActivity extends PreferenceCompatActivity implements OnSharedPreferenceChangeListener {
@Bean
protected NotificationSettings notificationSettings;
@ -43,7 +41,7 @@ public class NotificationSettingsActivity extends PreferenceActivity implements @@ -43,7 +41,7 @@ public class NotificationSettingsActivity extends PreferenceActivity implements
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
// Load the notification-related preferences from XML and update availability thereof
addPreferencesFromResource(R.xml.pref_notifications);

117
app/src/main/java/org/transdroid/core/gui/settings/OverflowPreference.java

@ -1,117 +0,0 @@ @@ -1,117 +0,0 @@
package org.transdroid.core.gui.settings;
import org.transdroid.R;
import android.content.Context;
import android.preference.Preference;
import android.util.AttributeSet;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ImageButton;
/**
* A {@link Preference} item that shows an extra overflow button at the right side of the screen. The action attached to
* this button is set using {@link #setOnOverflowClickedListener(OnOverflowClicked)}. Normal clicks on this preference
* are handled in the standard way.
* @author Eric Kok
*/
public class OverflowPreference extends Preference {
private OnPreferenceClickListener onPreferenceClickListener = null;
private OnOverflowClicked onOverflowClickedListener = null;
private ImageButton overflowButton = null;
public OverflowPreference(Context context) {
super(context);
init();
}
public OverflowPreference(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public OverflowPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
@Override
protected View onCreateView(ViewGroup parent) {
View layout = super.onCreateView(parent);
// Since the Preference layout is now created, we can attach the proper click listeners
layout.setClickable(true);
layout.setFocusable(true);
// When setting the background drawable the padding on this Preference layout disappears, so add it again
int bottom = layout.getPaddingBottom();
int top = layout.getPaddingTop();
int right = layout.getPaddingRight();
int left = layout.getPaddingLeft();
layout.setBackgroundResource(R.drawable.selectable_background_holo_light);
layout.setPadding(left, top, right, bottom);
layout.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (onPreferenceClickListener != null)
onPreferenceClickListener.onPreferenceClick(OverflowPreference.this);
}
});
overflowButton = (ImageButton) layout.findViewById(R.id.overflow_button);
overflowButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (onOverflowClickedListener != null)
onOverflowClickedListener.onOverflowClicked(v);
}
});
return layout;
}
public void init() {
// Load an overflow-style image button as custom widget in the right of this Preference layout
setWidgetLayoutResource(R.layout.pref_withoverflow);
}
/**
* Hides the overflow button (on the right side of the Preference) from the UI.
*/
public void hideOverflowButton() {
if (overflowButton != null) {
overflowButton.setVisibility(View.GONE);
}
}
/**
* Shows (after hiding it) the overflow button (on the right side of the Preference) in the UI.
*/
public void showOverflowButton() {
if (overflowButton != null) {
overflowButton.setVisibility(View.VISIBLE);
}
}
@Override
public void setOnPreferenceClickListener(OnPreferenceClickListener onPreferenceClickListener) {
// Instead of the build-in list item click behaviour, we have to manually assign the click listener to this
// Preference item, as we stole the focus behaviour when we added a Button to the Preference layout
this.onPreferenceClickListener = onPreferenceClickListener;
}
/**
* Registers the listener for clicks on the overflow button contained in this preference.
* @param onOverflowClickedListener The overflow button click listener
*/
public void setOnOverflowClickedListener(OnOverflowClicked onOverflowClickedListener) {
this.onOverflowClickedListener = onOverflowClickedListener;
}
/**
* An interface to be implemented by any activity (or otherwise) that wants to handle events when the contained
* overflow button is clicked.
*/
public interface OnOverflowClicked {
public void onOverflowClicked(View overflowButton);
}
}

72
app/src/main/java/org/transdroid/core/gui/settings/PreferenceCompatActivity.java

@ -0,0 +1,72 @@ @@ -0,0 +1,72 @@
package org.transdroid.core.gui.settings;
import android.content.res.Configuration;
import android.os.Bundle;
import android.preference.PreferenceActivity;
import android.support.annotation.Nullable;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatCallback;
import android.support.v7.app.AppCompatDelegate;
import android.support.v7.view.ActionMode;
public class PreferenceCompatActivity extends PreferenceActivity implements AppCompatCallback {
private AppCompatDelegate acd;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
acd = AppCompatDelegate.create(this, this);
acd.onCreate(savedInstanceState);
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
acd.onPostCreate(savedInstanceState);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
acd.onConfigurationChanged(newConfig);
}
@Override
public void setTitle(CharSequence title) {
super.setTitle(title);
acd.setTitle(title);
}
@Override
protected void onStop() {
super.onStop();
acd.onStop();
}
@Override
protected void onDestroy() {
super.onDestroy();
acd.onDestroy();
}
public ActionBar getSupportActionBar() {
return acd.getSupportActionBar();
}
@Override
public void onSupportActionModeStarted(ActionMode actionMode) {
}
@Override
public void onSupportActionModeFinished(ActionMode actionMode) {
}
@Nullable
@Override
public ActionMode onWindowStartingSupportActionMode(ActionMode.Callback callback) {
return null;
}
}

7
app/src/main/java/org/transdroid/core/gui/settings/RssfeedSettingsActivity.java

@ -37,7 +37,7 @@ import android.os.Bundle; @@ -37,7 +37,7 @@ import android.os.Bundle;
* @author Eric Kok
*/
@EActivity
@OptionsMenu(resName = "activity_deleteableprefs")
@OptionsMenu(R.menu.activity_deleteableprefs)
public class RssfeedSettingsActivity extends KeyBoundPreferencesActivity {
private static final int DIALOG_CONFIRMREMOVE = 0;
@ -46,7 +46,7 @@ public class RssfeedSettingsActivity extends KeyBoundPreferencesActivity { @@ -46,7 +46,7 @@ public class RssfeedSettingsActivity extends KeyBoundPreferencesActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
// Load the raw preferences to show in this screen
init(R.xml.pref_rssfeed, ApplicationSettings_.getInstance_(this).getMaxRssfeed());
@ -65,11 +65,12 @@ public class RssfeedSettingsActivity extends KeyBoundPreferencesActivity { @@ -65,11 +65,12 @@ public class RssfeedSettingsActivity extends KeyBoundPreferencesActivity {
}
@SuppressWarnings("deprecation")
@OptionsItem(resName = "action_removesettings")
@OptionsItem(R.id.action_removesettings)
protected void removeSettings() {
showDialog(DIALOG_CONFIRMREMOVE);
}
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case DIALOG_CONFIRMREMOVE:

7
app/src/main/java/org/transdroid/core/gui/settings/ServerSettingsActivity.java

@ -41,7 +41,7 @@ import android.preference.PreferenceManager; @@ -41,7 +41,7 @@ import android.preference.PreferenceManager;
* @author Eric Kok
*/
@EActivity
@OptionsMenu(resName = "activity_deleteableprefs")
@OptionsMenu(R.menu.activity_deleteableprefs)
public class ServerSettingsActivity extends KeyBoundPreferencesActivity {
private static final int DIALOG_CONFIRMREMOVE = 0;
@ -52,7 +52,7 @@ public class ServerSettingsActivity extends KeyBoundPreferencesActivity { @@ -52,7 +52,7 @@ public class ServerSettingsActivity extends KeyBoundPreferencesActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
// Load the raw preferences to show in this screen
init(R.xml.pref_server, ApplicationSettings_.getInstance_(this).getMaxNormalServer());
@ -89,11 +89,12 @@ public class ServerSettingsActivity extends KeyBoundPreferencesActivity { @@ -89,11 +89,12 @@ public class ServerSettingsActivity extends KeyBoundPreferencesActivity {
}
@SuppressWarnings("deprecation")
@OptionsItem(resName = "action_removesettings")
@OptionsItem(R.id.action_removesettings)
protected void removeSettings() {
showDialog(DIALOG_CONFIRMREMOVE);
}
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case DIALOG_CONFIRMREMOVE:

49
app/src/main/java/org/transdroid/core/gui/settings/SystemSettingsActivity.java

@ -32,6 +32,9 @@ import android.preference.PreferenceActivity; @@ -32,6 +32,9 @@ import android.preference.PreferenceActivity;
import android.preference.PreferenceManager;
import android.text.TextUtils;
import com.nispok.snackbar.Snackbar;
import com.nispok.snackbar.SnackbarManager;
import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.EActivity;
import org.androidannotations.annotations.OnActivityResult;
@ -49,10 +52,8 @@ import org.transdroid.core.service.BootReceiver; @@ -49,10 +52,8 @@ import org.transdroid.core.service.BootReceiver;
import java.io.FileNotFoundException;
import java.io.IOException;
import de.keyboardsurfer.android.widget.crouton.Crouton;
@EActivity
public class SystemSettingsActivity extends PreferenceActivity {
public class SystemSettingsActivity extends PreferenceCompatActivity {
protected static final int DIALOG_IMPORTSETTINGS = 0;
private OnPreferenceClickListener onImportSettingsClick = new OnPreferenceClickListener() {
@ -95,8 +96,7 @@ public class SystemSettingsActivity extends PreferenceActivity { @@ -95,8 +96,7 @@ public class SystemSettingsActivity extends PreferenceActivity {
@Override
public boolean onPreferenceClick(Preference preference) {
SearchHistoryProvider.clearHistory(getApplicationContext());
Crouton.showText(SystemSettingsActivity.this, R.string.pref_clearsearch_success,
NavigationHelper.CROUTON_INFO_STYLE);
SnackbarManager.show(Snackbar.with(SystemSettingsActivity.this).text(R.string.pref_clearsearch_success));
return true;
}
};
@ -106,15 +106,13 @@ public class SystemSettingsActivity extends PreferenceActivity { @@ -106,15 +106,13 @@ public class SystemSettingsActivity extends PreferenceActivity {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(SystemSettingsActivity.this);
try {
settingsPersistence.importSettingsFromFile(prefs, SettingsPersistence.DEFAULT_SETTINGS_FILE);
Crouton.showText(SystemSettingsActivity.this, R.string.pref_import_success,
NavigationHelper.CROUTON_INFO_STYLE);
SnackbarManager.show(Snackbar.with(SystemSettingsActivity.this).text(R.string.pref_import_success));
} catch (FileNotFoundException e) {
Crouton.showText(SystemSettingsActivity.this, R.string.error_file_not_found,
NavigationHelper.CROUTON_ERROR_STYLE);
SnackbarManager
.show(Snackbar.with(SystemSettingsActivity.this).text(R.string.error_file_not_found).colorResource(R.color.red));
} catch (JSONException e) {
Crouton.showText(SystemSettingsActivity.this,
getString(R.string.error_no_valid_settings_file, getString(R.string.app_name)),
NavigationHelper.CROUTON_ERROR_STYLE);
SnackbarManager.show(Snackbar.with(SystemSettingsActivity.this)
.text(getString(R.string.error_no_valid_settings_file, getString(R.string.app_name))).colorResource(R.color.red));
}
}
};
@ -130,14 +128,10 @@ public class SystemSettingsActivity extends PreferenceActivity { @@ -130,14 +128,10 @@ public class SystemSettingsActivity extends PreferenceActivity {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(SystemSettingsActivity.this);
try {
settingsPersistence.exportSettingsToFile(prefs, SettingsPersistence.DEFAULT_SETTINGS_FILE);
Crouton.showText(SystemSettingsActivity.this, R.string.pref_export_success,
NavigationHelper.CROUTON_INFO_STYLE);
} catch (JSONException e) {
Crouton.showText(SystemSettingsActivity.this, R.string.error_cant_write_settings_file,
NavigationHelper.CROUTON_ERROR_STYLE);
} catch (IOException e) {
Crouton.showText(SystemSettingsActivity.this, R.string.error_cant_write_settings_file,
NavigationHelper.CROUTON_ERROR_STYLE);
SnackbarManager.show(Snackbar.with(SystemSettingsActivity.this).text(R.string.pref_export_success));
} catch (JSONException | IOException e) {
SnackbarManager.show(Snackbar.with(SystemSettingsActivity.this).text(R.string.error_cant_write_settings_file)
.colorResource(R.color.red));
}
}
};
@ -149,8 +143,8 @@ public class SystemSettingsActivity extends PreferenceActivity { @@ -149,8 +143,8 @@ public class SystemSettingsActivity extends PreferenceActivity {
String settings = settingsPersistence.exportSettingsAsString(prefs);
BarcodeHelper.shareContentBarcode(SystemSettingsActivity.this, settings);
} catch (JSONException e) {
Crouton.showText(SystemSettingsActivity.this, R.string.error_cant_write_settings_file,
NavigationHelper.CROUTON_ERROR_STYLE);
SnackbarManager.show(Snackbar.with(SystemSettingsActivity.this).text(R.string.error_cant_write_settings_file)
.colorResource(R.color.red));
}
}
};
@ -160,7 +154,7 @@ public class SystemSettingsActivity extends PreferenceActivity { @@ -160,7 +154,7 @@ public class SystemSettingsActivity extends PreferenceActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
// Just load the system-related preferences from XML
addPreferencesFromResource(R.xml.pref_system);
@ -185,17 +179,18 @@ public class SystemSettingsActivity extends PreferenceActivity { @@ -185,17 +179,18 @@ public class SystemSettingsActivity extends PreferenceActivity {
@OnActivityResult(BarcodeHelper.ACTIVITY_BARCODE_QRSETTINGS)
public void onQrCodeScanned(int resultCode, Intent data) {
// We should have received Intent extras with the QR-decoded data representing Transdroid settings
if (data == null || !data.hasExtra("SCAN_RESULT"))
return; // Cancelled scan; ignore
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(SystemSettingsActivity.this);
String contents = data.getStringExtra("SCAN_RESULT");
String formatName = data.getStringExtra("SCAN_RESULT_FORMAT");
if (formatName != null && formatName.equals("QR_CODE") && !TextUtils.isEmpty(contents)) {
try {
settingsPersistence.importSettingsAsString(prefs, contents);
Crouton.showText(SystemSettingsActivity.this, R.string.pref_import_success,
NavigationHelper.CROUTON_INFO_STYLE);
SnackbarManager.show(Snackbar.with(SystemSettingsActivity.this).text(R.string.pref_import_success));
} catch (JSONException e) {
Crouton.showText(SystemSettingsActivity.this, R.string.error_file_not_found,
NavigationHelper.CROUTON_ERROR_STYLE);
SnackbarManager
.show(Snackbar.with(SystemSettingsActivity.this).text(R.string.error_file_not_found).colorResource(R.color.red));
}
}
}

6
app/src/main/java/org/transdroid/core/gui/settings/WebsearchPreference.java

@ -16,11 +16,11 @@ @@ -16,11 +16,11 @@
*/
package org.transdroid.core.gui.settings;
import org.transdroid.core.app.settings.WebsearchSetting;
import android.content.Context;
import android.preference.Preference;
import org.transdroid.core.app.settings.WebsearchSetting;
/**
* Represents a {@link WebsearchSetting} in a preferences screen.
* @author Eric Kok
@ -70,7 +70,7 @@ public class WebsearchPreference extends Preference { @@ -70,7 +70,7 @@ public class WebsearchPreference extends Preference {
};
public interface OnWebsearchClickedListener {
public void onWebsearchClicked(WebsearchSetting serverSetting);
void onWebsearchClicked(WebsearchSetting serverSetting);
}
}

7
app/src/main/java/org/transdroid/core/gui/settings/WebsearchSettingsActivity.java

@ -37,7 +37,7 @@ import android.os.Bundle; @@ -37,7 +37,7 @@ import android.os.Bundle;
* @author Eric Kok
*/
@EActivity
@OptionsMenu(resName="activity_deleteableprefs")
@OptionsMenu(R.menu.activity_deleteableprefs)
public class WebsearchSettingsActivity extends KeyBoundPreferencesActivity {
private static final int DIALOG_CONFIRMREMOVE = 0;
@ -46,7 +46,7 @@ public class WebsearchSettingsActivity extends KeyBoundPreferencesActivity { @@ -46,7 +46,7 @@ public class WebsearchSettingsActivity extends KeyBoundPreferencesActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
// Load the raw preferences to show in this screen
init(R.xml.pref_websearch, ApplicationSettings_.getInstance_(this).getMaxWebsearch());
@ -63,11 +63,12 @@ public class WebsearchSettingsActivity extends KeyBoundPreferencesActivity { @@ -63,11 +63,12 @@ public class WebsearchSettingsActivity extends KeyBoundPreferencesActivity {
}
@SuppressWarnings("deprecation")
@OptionsItem(resName = "action_removesettings")
@OptionsItem(R.id.action_removesettings)
protected void removeSettings() {
showDialog(DIALOG_CONFIRMREMOVE);
}
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case DIALOG_CONFIRMREMOVE:

2
app/src/main/java/org/transdroid/core/seedbox/SeedstuffSettingsActivity.java

@ -42,7 +42,7 @@ public class SeedstuffSettingsActivity extends KeyBoundPreferencesActivity { @@ -42,7 +42,7 @@ public class SeedstuffSettingsActivity extends KeyBoundPreferencesActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
// Load the raw preferences to show in this screen
init(R.xml.pref_seedbox_seedstuff,

2
app/src/main/java/org/transdroid/core/seedbox/XirvikDediSettingsActivity.java

@ -42,7 +42,7 @@ public class XirvikDediSettingsActivity extends KeyBoundPreferencesActivity { @@ -42,7 +42,7 @@ public class XirvikDediSettingsActivity extends KeyBoundPreferencesActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
// Load the raw preferences to show in this screen
init(R.xml.pref_seedbox_xirvikdedi,

2
app/src/main/java/org/transdroid/core/seedbox/XirvikSemiSettingsActivity.java

@ -42,7 +42,7 @@ public class XirvikSemiSettingsActivity extends KeyBoundPreferencesActivity { @@ -42,7 +42,7 @@ public class XirvikSemiSettingsActivity extends KeyBoundPreferencesActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
// Load the raw preferences to show in this screen
init(R.xml.pref_seedbox_xirviksemi,

48
app/src/main/java/org/transdroid/core/seedbox/XirvikSharedSettingsActivity.java

@ -16,7 +16,18 @@ @@ -16,7 +16,18 @@
*/
package org.transdroid.core.seedbox;
import java.io.InputStream;
import android.annotation.TargetApi;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.preference.EditTextPreference;
import android.preference.PreferenceManager;
import com.nispok.snackbar.Snackbar;
import com.nispok.snackbar.SnackbarManager;
import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.EActivity;
@ -27,25 +38,15 @@ import org.apache.http.client.methods.HttpGet; @@ -27,25 +38,15 @@ import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.transdroid.R;
import org.transdroid.core.gui.log.Log;
import org.transdroid.core.gui.navigation.NavigationHelper;
import org.transdroid.core.gui.settings.KeyBoundPreferencesActivity;
import org.transdroid.core.gui.settings.*;
import org.transdroid.core.gui.settings.MainSettingsActivity_;
import org.transdroid.daemon.util.HttpHelper;
import android.annotation.TargetApi;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.preference.EditTextPreference;
import android.preference.PreferenceManager;
import de.keyboardsurfer.android.widget.crouton.Crouton;
import java.io.InputStream;
/**
* Activity that allows for the configuration of a Xirvik shared seedbox. The key can be supplied to update an existing
* server setting instead of creating a new one.
* Activity that allows for the configuration of a Xirvik shared seedbox. The key can be supplied to update an existing server setting instead of
* creating a new one.
* @author Eric Kok
*/
@EActivity
@ -59,12 +60,11 @@ public class XirvikSharedSettingsActivity extends KeyBoundPreferencesActivity { @@ -59,12 +60,11 @@ public class XirvikSharedSettingsActivity extends KeyBoundPreferencesActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
// Load the raw preferences to show in this screen
init(R.xml.pref_seedbox_xirvikshared,
SeedboxProvider.XirvikShared.getSettings().getMaxSeedboxOrder(
PreferenceManager.getDefaultSharedPreferences(this)));
SeedboxProvider.XirvikShared.getSettings().getMaxSeedboxOrder(PreferenceManager.getDefaultSharedPreferences(this)));
initTextPreference("seedbox_xirvikshared_name");
initTextPreference("seedbox_xirvikshared_server");
initTextPreference("seedbox_xirvikshared_user");
@ -82,15 +82,14 @@ public class XirvikSharedSettingsActivity extends KeyBoundPreferencesActivity { @@ -82,15 +82,14 @@ public class XirvikSharedSettingsActivity extends KeyBoundPreferencesActivity {
try {
// When the shared server settings change, we also have to update the RPC mount point to use
SharedPreferences prefs = PreferenceManager
.getDefaultSharedPreferences(XirvikSharedSettingsActivity.this);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(XirvikSharedSettingsActivity.this);
String server = prefs.getString("seedbox_xirvikshared_server_" + key, null);
String user = prefs.getString("seedbox_xirvikshared_user_" + key, null);
String pass = prefs.getString("seedbox_xirvikshared_pass_" + key, null);
// Retrieve the RPC mount point setting from the server itself
DefaultHttpClient httpclient = HttpHelper.createStandardHttpClient(true, user, pass, true, null,
HttpHelper.DEFAULT_CONNECTION_TIMEOUT, server, 443);
DefaultHttpClient httpclient =
HttpHelper.createStandardHttpClient(true, user, pass, true, null, HttpHelper.DEFAULT_CONNECTION_TIMEOUT, server, 443);
String url = "https://" + server + ":443/browsers_addons/transdroid_autoconf.txt";
HttpResponse request = httpclient.execute(new HttpGet(url));
InputStream stream = request.getEntity().getContent();
@ -103,8 +102,7 @@ public class XirvikSharedSettingsActivity extends KeyBoundPreferencesActivity { @@ -103,8 +102,7 @@ public class XirvikSharedSettingsActivity extends KeyBoundPreferencesActivity {
} catch (Exception e) {
log.d(XirvikSharedSettingsActivity.this,
"Could not retrieve the Xirvik shared seedbox RPC mount point setting: " + e.toString());
log.d(XirvikSharedSettingsActivity.this, "Could not retrieve the Xirvik shared seedbox RPC mount point setting: " + e.toString());
return null;
}
@ -123,7 +121,7 @@ public class XirvikSharedSettingsActivity extends KeyBoundPreferencesActivity { @@ -123,7 +121,7 @@ public class XirvikSharedSettingsActivity extends KeyBoundPreferencesActivity {
Editor edit = PreferenceManager.getDefaultSharedPreferences(XirvikSharedSettingsActivity.this).edit();
EditTextPreference pref = (EditTextPreference) findPreference("seedbox_xirvikshared_rpc_" + key);
if (result == null) {
Crouton.showText(this, R.string.pref_seedbox_xirviknofolder, NavigationHelper.CROUTON_ERROR_STYLE);
SnackbarManager.show(Snackbar.with(this).text(R.string.pref_seedbox_xirviknofolder).colorResource(R.color.red));
edit.remove("seedbox_xirvikshared_rpc_" + key);
pref.setSummary("");
} else {

10
app/src/main/java/org/transdroid/core/service/ConnectivityHelper.java

@ -32,16 +32,8 @@ public class ConnectivityHelper { @@ -32,16 +32,8 @@ public class ConnectivityHelper {
@SystemService
protected WifiManager wifiManager;
public ConnectivityHelper(Context context) {
}
@SuppressWarnings("deprecation")
public boolean shouldPerformBackgroundActions() {
// First check the old background data setting (this will always be true for ICS+)
if (!connectivityManager.getBackgroundDataSetting())
return false;
// Still good? Check the current active network instead
// Check the current active network whether we are connected
return connectivityManager.getActiveNetworkInfo() != null
&& connectivityManager.getActiveNetworkInfo().isConnected();
}

10
app/src/main/java/org/transdroid/core/widget/ListWidgetConfigActivity.java

@ -17,12 +17,12 @@ @@ -17,12 +17,12 @@
package org.transdroid.core.widget;
import android.annotation.TargetApi;
import android.app.ActionBar;
import android.app.Activity;
import android.appwidget.AppWidgetManager;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.CheckBox;
@ -66,7 +66,7 @@ import java.util.List; @@ -66,7 +66,7 @@ import java.util.List;
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@EActivity(resName = "activity_widgetconfig")
public class ListWidgetConfigActivity extends Activity {
public class ListWidgetConfigActivity extends ActionBarActivity {
// Views and adapters
@ViewById
@ -182,11 +182,11 @@ public class ListWidgetConfigActivity extends Activity { @@ -182,11 +182,11 @@ public class ListWidgetConfigActivity extends Activity {
// Set up action bar with a done button
// Inspired by NoNonsenseNotes's ListWidgetConfig.java (Apache License, Version 2.0)
getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM,
getSupportActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM,
ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_SHOW_TITLE);
View doneButtonFrame = getLayoutInflater().inflate(R.layout.actionbar_donebutton, null);
doneButtonFrame.findViewById(R.id.actionbar_done).setOnClickListener(doneClicked);
getActionBar().setCustomView(doneButtonFrame);
getSupportActionBar().setCustomView(doneButtonFrame);
}

21
app/src/main/java/org/transdroid/daemon/Daemon.java

@ -29,6 +29,7 @@ import org.transdroid.daemon.Rtorrent.RtorrentAdapter; @@ -29,6 +29,7 @@ import org.transdroid.daemon.Rtorrent.RtorrentAdapter;
import org.transdroid.daemon.Synology.SynologyAdapter;
import org.transdroid.daemon.Tfb4rt.Tfb4rtAdapter;
import org.transdroid.daemon.Transmission.TransmissionAdapter;
import org.transdroid.daemon.Ttorrent.TtorrentAdapter;
import org.transdroid.daemon.Utorrent.UtorrentAdapter;
import org.transdroid.daemon.Vuze.VuzeAdapter;
@ -95,6 +96,11 @@ public enum Daemon { @@ -95,6 +96,11 @@ public enum Daemon {
return new Tfb4rtAdapter(settings);
}
},
tTorrent {
public IDaemonAdapter createAdapter(DaemonSettings settings) {
return new TtorrentAdapter(settings);
}
},
Synology {
public IDaemonAdapter createAdapter(DaemonSettings settings) {
return new SynologyAdapter(settings);
@ -157,6 +163,8 @@ public enum Daemon { @@ -157,6 +163,8 @@ public enum Daemon {
return "daemon_synology";
case Tfb4rt:
return "daemon_tfb4rt";
case tTorrent:
return "daemon_ttorrent";
case Transmission:
return "daemon_transmission";
case uTorrent:
@ -216,6 +224,9 @@ public enum Daemon { @@ -216,6 +224,9 @@ public enum Daemon {
if (daemonCode.equals("daemon_tfb4rt")) {
return Tfb4rt;
}
if (daemonCode.equals("daemon_ttorrent")) {
return tTorrent;
}
if (daemonCode.equals("daemon_transmission")) {
return Transmission;
}
@ -265,6 +276,8 @@ public enum Daemon { @@ -265,6 +276,8 @@ public enum Daemon {
return 6884;
case Aria2:
return 6800;
case tTorrent:
return 1080;
}
return 8080;
}
@ -278,7 +291,7 @@ public enum Daemon { @@ -278,7 +291,7 @@ public enum Daemon {
}
public static boolean supportsFileListing(Daemon type) {
return type == Synology || type == Transmission || type == uTorrent || type == BitTorrent || type == KTorrent || type == Deluge || type == rTorrent || type == Vuze || type == DLinkRouterBT || type == Bitflu || type == qBittorrent || type == BuffaloNas || type == BitComet || type == Aria2 || type == Dummy;
return type == Synology || type == Transmission || type == uTorrent || type == BitTorrent || type == KTorrent || type == Deluge || type == rTorrent || type == Vuze || type == DLinkRouterBT || type == Bitflu || type == qBittorrent || type == BuffaloNas || type == BitComet || type == Aria2 || type == tTorrent || type == Dummy;
}
public static boolean supportsFineDetails(Daemon type) {
@ -315,15 +328,15 @@ public enum Daemon { @@ -315,15 +328,15 @@ public enum Daemon {
}
public static boolean supportsAddByMagnetUrl(Daemon type) {
return type == uTorrent || type == BitTorrent || type == Transmission || type == Synology || type == Deluge || type == Bitflu || type == KTorrent || type == rTorrent || type == qBittorrent || type == BitComet || type == Aria2 || type == Dummy;
return type == uTorrent || type == BitTorrent || type == Transmission || type == Synology || type == Deluge || type == Bitflu || type == KTorrent || type == rTorrent || type == qBittorrent || type == BitComet || type == Aria2 || type == tTorrent || type == Dummy;
}
public static boolean supportsRemoveWithData(Daemon type) {
return type == uTorrent || type == Vuze || type == Transmission || type == Deluge || type == BitTorrent || type == Tfb4rt || type == DLinkRouterBT || type == Bitflu || type == qBittorrent || type == BuffaloNas || type == BitComet || type == rTorrent || type == Aria2 || type == Dummy;
return type == uTorrent || type == Vuze || type == Transmission || type == Deluge || type == BitTorrent || type == Tfb4rt || type == DLinkRouterBT || type == Bitflu || type == qBittorrent || type == BuffaloNas || type == BitComet || type == rTorrent || type == Aria2 || type == tTorrent || type == Dummy;
}
public static boolean supportsFilePrioritySetting(Daemon type) {
return type == BitTorrent || type == uTorrent || type == Transmission || type == KTorrent || type == rTorrent || type == Vuze || type == Deluge || type == qBittorrent || type == Dummy;
return type == BitTorrent || type == uTorrent || type == Transmission || type == KTorrent || type == rTorrent || type == Vuze || type == Deluge || type == qBittorrent || type == tTorrent || type == Dummy;
}
public static boolean supportsDateAdded(Daemon type) {

192
app/src/main/java/org/transdroid/daemon/Qbittorrent/QbittorrentAdapter.java

@ -17,20 +17,16 @@ @@ -17,20 +17,16 @@
*/
package org.transdroid.daemon.Qbittorrent;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.android.internalcopy.http.multipart.FilePart;
import com.android.internalcopy.http.multipart.MultipartEntity;
import com.android.internalcopy.http.multipart.Part;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
@ -40,6 +36,7 @@ import org.json.JSONObject; @@ -40,6 +36,7 @@ import org.json.JSONObject;
import org.transdroid.core.gui.log.Log;
import org.transdroid.daemon.Daemon;
import org.transdroid.daemon.DaemonException;
import org.transdroid.daemon.DaemonException.ExceptionType;
import org.transdroid.daemon.DaemonSettings;
import org.transdroid.daemon.IDaemonAdapter;
import org.transdroid.daemon.Priority;
@ -47,7 +44,6 @@ import org.transdroid.daemon.Torrent; @@ -47,7 +44,6 @@ import org.transdroid.daemon.Torrent;
import org.transdroid.daemon.TorrentDetails;
import org.transdroid.daemon.TorrentFile;
import org.transdroid.daemon.TorrentStatus;
import org.transdroid.daemon.DaemonException.ExceptionType;
import org.transdroid.daemon.task.AddByFileTask;
import org.transdroid.daemon.task.AddByMagnetUrlTask;
import org.transdroid.daemon.task.AddByUrlTask;
@ -65,9 +61,15 @@ import org.transdroid.daemon.task.RetrieveTaskSuccessResult; @@ -65,9 +61,15 @@ import org.transdroid.daemon.task.RetrieveTaskSuccessResult;
import org.transdroid.daemon.task.SetFilePriorityTask;
import org.transdroid.daemon.task.SetTransferRatesTask;
import org.transdroid.daemon.util.HttpHelper;
import com.android.internalcopy.http.multipart.FilePart;
import com.android.internalcopy.http.multipart.MultipartEntity;
import com.android.internalcopy.http.multipart.Part;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* The daemon adapter for the qBittorrent torrent client.
@ -80,26 +82,47 @@ public class QbittorrentAdapter implements IDaemonAdapter { @@ -80,26 +82,47 @@ public class QbittorrentAdapter implements IDaemonAdapter {
private DaemonSettings settings;
private DefaultHttpClient httpclient;
private int version = -1;
private int apiVersion = -1;
public QbittorrentAdapter(DaemonSettings settings) {
this.settings = settings;
}
private synchronized void ensureVersion(Log log) throws DaemonException {
if (version > 0)
// Still need to retrieve the API and qBittorrent version numbers from the server?
if (version > 0 && apiVersion > 0)
return;
// We still need to retrieve the version number from the server
// Do this by getting the web interface about page and trying to parse the version number
// Format is something like 'qBittorrent v2.9.7 (Web UI)'
try {
// The API version is only supported since qBittorrent 3.2, so otherwise we assume version 1
try {
String apiVerText = makeRequest(log, "/version/api");
apiVersion = Integer.parseInt(apiVerText.trim());
} catch (DaemonException | NumberFormatException e) {
apiVersion = 1;
}
log.d(LOG_NAME, "qBittorrent API version is " + apiVersion);
// The qBittorent version is only supported since 3.2; for earlier versions we parse the about dialog and parse it
String versionText = "";
if (apiVersion > 1) {
// Format is something like 'v3.2.0'
versionText = makeRequest(log, "/version/qbittorrent").substring(1);
} else {
// Format is something like 'qBittorrent v2.9.7 (Web UI)' or 'qBittorrent v3.0.0-alpha5 (Web UI)'
String about = makeRequest(log, "/about.html");
String aboutStartText = "qBittorrent v";
String aboutEndText = " (Web UI)";
int aboutStart = about.indexOf(aboutStartText);
int aboutEnd = about.indexOf(aboutEndText);
try {
if (aboutStart >= 0 && aboutEnd > aboutStart) {
versionText = about.substring(aboutStart + aboutStartText.length(), aboutEnd);
}
}
// String found: now parse a version like 2.9.7 as a number like 20907 (allowing 10 places for each .)
String[] parts = about.substring(aboutStart + aboutStartText.length(), aboutEnd).split("\\.");
String[] parts = versionText.split("\\.");
if (parts.length > 0) {
version = Integer.parseInt(parts[0]) * 100 * 100;
if (parts.length > 1) {
@ -122,11 +145,47 @@ public class QbittorrentAdapter implements IDaemonAdapter { @@ -122,11 +145,47 @@ public class QbittorrentAdapter implements IDaemonAdapter {
}
}
}
}
} catch (NumberFormatException e) {
}
} catch (Exception e) {
// Unable to establish version number; assume an old version by setting it to version 1
version = 10000;
apiVersion = 1;
}
}
private synchronized void ensureAuthenticated(Log log) throws DaemonException {
// API changed in 3.2.0, login is now handled by its own request, which provides you a cookie.
// If we don't have that cookie, let's try and get it.
if (apiVersion < 2) {
return;
}
// Have we already authenticated? Check if we have the cookie that we need
List<Cookie> cookies = httpclient.getCookieStore().getCookies();
for (Cookie c : cookies) {
if (c.getName().equals("SID")) {
// And here it is! Okay, no need authenticate again.
return;
}
}
makeRequest(log, "/login", new BasicNameValuePair("username", settings.getUsername()),
new BasicNameValuePair("password", settings.getPassword()));
// The HttpClient will automatically remember the cookie for us, no need to parse it out.
// However, we would like to see if authentication was successful or not...
cookies = httpclient.getCookieStore().getCookies();
for (Cookie c : cookies) {
if (c.getName().equals("SID")) {
// Good. Let's get out of here.
return;
}
}
// No cookie found, we didn't authenticate.
throw new DaemonException(ExceptionType.AuthenticationFailure, "Server rejected our login");
}
@Override
@ -134,26 +193,37 @@ public class QbittorrentAdapter implements IDaemonAdapter { @@ -134,26 +193,37 @@ public class QbittorrentAdapter implements IDaemonAdapter {
try {
ensureVersion(log);
ensureAuthenticated(log);
switch (task.getMethod()) {
case Retrieve:
String path;
if (version >= 30200) {
path = "/query/torrents";
} else if (version >= 30000) {
path = "/json/torrents";
} else {
path = "/json/events";
}
// Request all torrents from server
JSONArray result = new JSONArray(makeRequest(log, version >= 30000 ? "/json/torrents" : "/json/events"));
JSONArray result = new JSONArray(makeRequest(log, path));
return new RetrieveTaskSuccessResult((RetrieveTask) task, parseJsonTorrents(result), null);
case GetTorrentDetails:
// Request tracker and error details for a specific teacher
String mhash = task.getTargetTorrent().getUniqueID();
JSONArray messages = new JSONArray(makeRequest(log, "/json/propertiesTrackers/" + mhash));
return new GetTorrentDetailsTaskSuccessResult((GetTorrentDetailsTask) task,
parseJsonTorrentDetails(messages));
JSONArray messages =
new JSONArray(makeRequest(log, (version >= 30200 ? "/query/propertiesTrackers/" : "/json/propertiesTrackers/") + mhash));
return new GetTorrentDetailsTaskSuccessResult((GetTorrentDetailsTask) task, parseJsonTorrentDetails(messages));
case GetFileList:
// Request files listing for a specific torrent
String fhash = task.getTargetTorrent().getUniqueID();
JSONArray files = new JSONArray(makeRequest(log, "/json/propertiesFiles/" + fhash));
JSONArray files =
new JSONArray(makeRequest(log, (version >= 30200 ? "/query/propertiesFiles/" : "/json/propertiesFiles/") + fhash));
return new GetFileListTaskSuccessResult((GetFileListTask) task, parseJsonFiles(files));
case AddByFile:
@ -223,9 +293,8 @@ public class QbittorrentAdapter implements IDaemonAdapter { @@ -223,9 +293,8 @@ public class QbittorrentAdapter implements IDaemonAdapter {
}
// We have to make a separate request per file, it seems
for (TorrentFile file : setPrio.getForFiles()) {
makeRequest(log, "/command/setFilePrio", new BasicNameValuePair("hash", task.getTargetTorrent()
.getUniqueID()), new BasicNameValuePair("id", file.getKey()), new BasicNameValuePair(
"priority", newPrio));
makeRequest(log, "/command/setFilePrio", new BasicNameValuePair("hash", task.getTargetTorrent().getUniqueID()),
new BasicNameValuePair("id", file.getKey()), new BasicNameValuePair("priority", newPrio));
}
return new DaemonTaskSuccessResult(task);
@ -241,13 +310,12 @@ public class QbittorrentAdapter implements IDaemonAdapter { @@ -241,13 +310,12 @@ public class QbittorrentAdapter implements IDaemonAdapter {
JSONObject prefs = new JSONObject(makeRequest(log, "/json/preferences"));
prefs.put("dl_limit", dl);
prefs.put("up_limit", ul);
makeRequest(log, "/command/setPreferences",
new BasicNameValuePair("json", URLEncoder.encode(prefs.toString(), HTTP.UTF_8)));
makeRequest(log, "/command/setPreferences", new BasicNameValuePair("json", URLEncoder.encode(prefs.toString(), HTTP.UTF_8)));
return new DaemonTaskSuccessResult(task);
default:
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.MethodUnsupported,
task.getMethod() + " is not supported by " + getType()));
return new DaemonTaskFailureResult(task,
new DaemonException(ExceptionType.MethodUnsupported, task.getMethod() + " is not supported by " + getType()));
}
} catch (JSONException e) {
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.ParsingFailed, e.toString()));
@ -264,7 +332,7 @@ public class QbittorrentAdapter implements IDaemonAdapter { @@ -264,7 +332,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
// Setup request using POST
HttpPost httppost = new HttpPost(buildWebUIUrl(path));
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
List<NameValuePair> nvps = new ArrayList<>();
Collections.addAll(nvps, params);
httppost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
return makeWebRequest(httppost, log);
@ -347,8 +415,8 @@ public class QbittorrentAdapter implements IDaemonAdapter { @@ -347,8 +415,8 @@ public class QbittorrentAdapter implements IDaemonAdapter {
private TorrentDetails parseJsonTorrentDetails(JSONArray messages) throws JSONException {
ArrayList<String> trackers = new ArrayList<String>();
ArrayList<String> errors = new ArrayList<String>();
ArrayList<String> trackers = new ArrayList<>();
ArrayList<String> errors = new ArrayList<>();
// Parse response
if (messages.length() > 0) {
@ -369,15 +437,37 @@ public class QbittorrentAdapter implements IDaemonAdapter { @@ -369,15 +437,37 @@ public class QbittorrentAdapter implements IDaemonAdapter {
private ArrayList<Torrent> parseJsonTorrents(JSONArray response) throws JSONException {
// Parse response
ArrayList<Torrent> torrents = new ArrayList<Torrent>();
ArrayList<Torrent> torrents = new ArrayList<>();
for (int i = 0; i < response.length(); i++) {
JSONObject tor = response.getJSONObject(i);
int leechers[] = parsePeers(tor.getString("num_leechs"));
int seeders[] = parsePeers(tor.getString("num_seeds"));
long size = parseSize(tor.getString("size"));
double ratio = parseRatio(tor.getString("ratio"));
double progress = tor.getDouble("progress");
int dlspeed = parseSpeed(tor.getString("dlspeed"));
int leechers[];
int seeders[];
double ratio;
long size;
int dlspeed;
int upspeed;
if (apiVersion >= 2) {
leechers = new int[2];
leechers[0] = tor.getInt("num_leechs");
leechers[1] = tor.getInt("num_complete") + tor.getInt("num_incomplete");
seeders = new int[2];
seeders[0] = tor.getInt("num_seeds");
seeders[1] = tor.getInt("num_complete");
size = tor.getLong("size");
ratio = tor.getDouble("ratio");
dlspeed = tor.getInt("dlspeed");
upspeed = tor.getInt("upspeed");
} else {
leechers = parsePeers(tor.getString("num_leechs"));
seeders = parsePeers(tor.getString("num_seeds"));
size = parseSize(tor.getString("size"));
ratio = parseRatio(tor.getString("ratio"));
dlspeed = parseSpeed(tor.getString("dlspeed"));
upspeed = parseSpeed(tor.getString("upspeed"));
}
long eta = -1L;
if (dlspeed > 0)
eta = (long) (size - (size * progress)) / dlspeed;
@ -391,7 +481,7 @@ public class QbittorrentAdapter implements IDaemonAdapter { @@ -391,7 +481,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
parseStatus(tor.getString("state")),
null,
dlspeed,
parseSpeed(tor.getString("upspeed")),
upspeed,
seeders[0],
seeders[1],
leechers[0],
@ -454,8 +544,7 @@ public class QbittorrentAdapter implements IDaemonAdapter { @@ -454,8 +544,7 @@ public class QbittorrentAdapter implements IDaemonAdapter {
// In some situations it it just a "6" string
String[] parts = seeds.split(" ");
if (parts.length > 1) {
return new int[] { Integer.parseInt(parts[0]),
Integer.parseInt(parts[1].substring(1, parts[1].length() - 1)) };
return new int[]{Integer.parseInt(parts[0]), Integer.parseInt(parts[1].substring(1, parts[1].length() - 1))};
}
return new int[]{Integer.parseInt(parts[0]), Integer.parseInt(parts[0])};
}
@ -525,9 +614,16 @@ public class QbittorrentAdapter implements IDaemonAdapter { @@ -525,9 +614,16 @@ public class QbittorrentAdapter implements IDaemonAdapter {
ArrayList<TorrentFile> torrentfiles = new ArrayList<TorrentFile>();
for (int i = 0; i < response.length(); i++) {
JSONObject file = response.getJSONObject(i);
long size = parseSize(file.getString("size"));
torrentfiles.add(new TorrentFile("" + i, file.getString("name"), null, null, size, (long) (size * file
.getDouble("progress")), parsePriority(file.getInt("priority"))));
long size;
if (apiVersion >= 2) {
size = file.getLong("size");
} else {
size = parseSize(file.getString("size"));
}
torrentfiles.add(new TorrentFile("" + i, file.getString("name"), null, null, size, (long) (size * file.getDouble("progress")),
parsePriority(file.getInt("priority"))));
}
// Return the list

106
app/src/main/java/org/transdroid/daemon/Rtorrent/RtorrentAdapter.java

@ -82,6 +82,7 @@ public class RtorrentAdapter implements IDaemonAdapter { @@ -82,6 +82,7 @@ public class RtorrentAdapter implements IDaemonAdapter {
private DaemonSettings settings;
private XMLRPCClient rpcclient;
private List<Label> lastKnownLabels = null;
private Integer version = null;
public RtorrentAdapter(DaemonSettings settings) {
this.settings = settings;
@ -91,36 +92,46 @@ public class RtorrentAdapter implements IDaemonAdapter { @@ -91,36 +92,46 @@ public class RtorrentAdapter implements IDaemonAdapter {
public DaemonTaskResult executeTask(Log log, DaemonTask task) {
try {
// Ensure a version number is know to switch to the right methods
if (version == null) {
try {
Object versionObject = makeRtorrentCall(log, "system.client_version", new String[0]);
String[] versionRaw = versionObject.toString().split("\\.");
version = (Integer.parseInt(versionRaw[0]) * 10000) + (Integer.parseInt(versionRaw[1]) * 100) + Integer.parseInt(versionRaw[2]);
} catch (Exception e) {
version = 10000;
}
}
switch (task.getMethod()) {
case Retrieve:
// @formatter:off
Object result = makeRtorrentCall(log,"d.multicall",
new String[] { "main",
"d.get_hash=",
"d.get_name=",
"d.get_state=",
"d.get_down_rate=",
"d.get_up_rate=",
"d.get_peers_connected=",
"d.get_peers_not_connected=",
"d.get_peers_accounted=",
"d.get_bytes_done=",
"d.get_up_total=",
"d.get_size_bytes=",
"d.get_creation_date=",
"d.get_left_bytes=",
"d.get_complete=",
Object result = makeRtorrentCall(log, "d.multicall2",
new String[] { "", "main",
"d.hash=",
"d.name=",
"d.state=",
"d.down.rate=",
"d.up.rate=",
"d.peers_connected=",
"d.peers_not_connected=",
"d.peers_accounted=",
"d.bytes_done=",
"d.up.total=",
"d.size_bytes=",
"d.creation_date=",
"d.left_bytes=",
"d.complete=",
"d.is_active=",
"d.is_hash_checking=",
"d.get_base_path=",
"d.get_base_filename=",
"d.get_message=",
"d.get_custom=addtime",
"d.get_custom=seedingtime",
"d.get_custom1=",
"d.get_peers_complete=",
"d.get_peers_accounted=" });
"d.base_path=",
"d.base_filename=",
"d.message=",
"d.custom=addtime",
"d.custom=seedingtime",
"d.custom1=",
"d.peers_complete=",
"d.peers_accounted=" });
// @formatter:on
return new RetrieveTaskSuccessResult((RetrieveTask) task, onTorrentsRetrieved(result),
lastKnownLabels);
@ -131,7 +142,7 @@ public class RtorrentAdapter implements IDaemonAdapter { @@ -131,7 +142,7 @@ public class RtorrentAdapter implements IDaemonAdapter {
Object dresult = makeRtorrentCall(log,"t.multicall", new String[] {
task.getTargetTorrent().getUniqueID(),
"",
"t.get_url=" });
"t.url=" });
// @formatter:on
return new GetTorrentDetailsTaskSuccessResult((GetTorrentDetailsTask) task,
onTorrentDetailsRetrieved(log, dresult));
@ -142,13 +153,13 @@ public class RtorrentAdapter implements IDaemonAdapter { @@ -142,13 +153,13 @@ public class RtorrentAdapter implements IDaemonAdapter {
Object fresult = makeRtorrentCall(log,"f.multicall", new String[] {
task.getTargetTorrent().getUniqueID(),
"",
"f.get_path=",
"f.get_size_bytes=",
"f.get_priority=",
"f.get_completed_chunks=",
"f.get_size_chunks=",
"f.get_priority=",
"f.get_frozen_path=" });
"f.path=",
"f.size_bytes=",
"f.priority=",
"f.completed_chunks=",
"f.size_chunks=",
"f.priority=",
"f.frozen_path=" });
// @formatter:on
return new GetFileListTaskSuccessResult((GetFileListTask) task,
onTorrentFilesRetrieved(fresult, task.getTargetTorrent()));
@ -167,22 +178,35 @@ public class RtorrentAdapter implements IDaemonAdapter { @@ -167,22 +178,35 @@ public class RtorrentAdapter implements IDaemonAdapter {
byte[] bytes = baos.toByteArray();
int size = (int) file.length() * 2;
final int XMLRPC_EXTRA_PADDING = 1280;
if (version >= 907) {
makeRtorrentCall(log, "network.xmlrpc.size_limit.set", new Object[]{size + XMLRPC_EXTRA_PADDING});
makeRtorrentCall(log, "load.raw_start", new Object[]{bytes});
} else {
makeRtorrentCall(log, "set_xmlrpc_size_limit", new Object[]{size + XMLRPC_EXTRA_PADDING});
makeRtorrentCall(log, "load_raw_start", new Object[]{bytes});
}
return new DaemonTaskSuccessResult(task);
case AddByUrl:
// Request to add a torrent by URL
String url = ((AddByUrlTask) task).getUrl();
if (version >= 907) {
makeRtorrentCall(log, "load.start", new String[]{"", url});
} else {
makeRtorrentCall(log, "load_start", new String[]{url});
}
return new DaemonTaskSuccessResult(task);
case AddByMagnetUrl:
// Request to add a magnet link by URL
String magnet = ((AddByMagnetUrlTask) task).getUrl();
if (version >= 907) {
makeRtorrentCall(log, "load.start", new String[]{"", magnet});
} else {
makeRtorrentCall(log, "load_start", new String[]{magnet});
}
return new DaemonTaskSuccessResult(task);
case Remove:
@ -190,7 +214,7 @@ public class RtorrentAdapter implements IDaemonAdapter { @@ -190,7 +214,7 @@ public class RtorrentAdapter implements IDaemonAdapter {
// Remove a torrent
RemoveTask removeTask = (RemoveTask) task;
if (removeTask.includingData()) {
makeRtorrentCall(log, "d.set_custom5",
makeRtorrentCall(log, "d.custom5.set",
new String[]{task.getTargetTorrent().getUniqueID(), "1"});
}
makeRtorrentCall(log, "d.erase", new String[]{task.getTargetTorrent().getUniqueID()});
@ -205,7 +229,7 @@ public class RtorrentAdapter implements IDaemonAdapter { @@ -205,7 +229,7 @@ public class RtorrentAdapter implements IDaemonAdapter {
case PauseAll:
// Resume all torrents
makeRtorrentCall(log, "d.multicall", new String[]{"main", "d.pause="});
makeRtorrentCall(log, "d.multicall2", new String[]{"","main", "d.pause="});
return new DaemonTaskSuccessResult(task);
case Resume:
@ -217,7 +241,7 @@ public class RtorrentAdapter implements IDaemonAdapter { @@ -217,7 +241,7 @@ public class RtorrentAdapter implements IDaemonAdapter {
case ResumeAll:
// Resume all torrents
makeRtorrentCall(log, "d.multicall", new String[]{"main", "d.resume="});
makeRtorrentCall(log, "d.multicall2", new String[]{"", "main", "d.resume="});
return new DaemonTaskSuccessResult(task);
case Stop:
@ -229,7 +253,7 @@ public class RtorrentAdapter implements IDaemonAdapter { @@ -229,7 +253,7 @@ public class RtorrentAdapter implements IDaemonAdapter {
case StopAll:
// Stop all torrents
makeRtorrentCall(log, "d.multicall", new String[]{"main", "d.stop="});
makeRtorrentCall(log, "d.multicall2", new String[]{"", "main", "d.stop="});
return new DaemonTaskSuccessResult(task);
case Start:
@ -241,7 +265,7 @@ public class RtorrentAdapter implements IDaemonAdapter { @@ -241,7 +265,7 @@ public class RtorrentAdapter implements IDaemonAdapter {
case StartAll:
// Start all torrents
makeRtorrentCall(log, "d.multicall", new String[]{"main", "d.start="});
makeRtorrentCall(log, "d.multicall2", new String[]{"", "main", "d.start="});
return new DaemonTaskSuccessResult(task);
case SetFilePriorities:
@ -251,7 +275,7 @@ public class RtorrentAdapter implements IDaemonAdapter { @@ -251,7 +275,7 @@ public class RtorrentAdapter implements IDaemonAdapter {
String newPriority = "" + convertPriority(prioTask.getNewPriority());
// One at a time; rTorrent doesn't seem to support a multicall on a selective number of files
for (TorrentFile forFile : prioTask.getForFiles()) {
makeRtorrentCall(log, "f.set_priority",
makeRtorrentCall(log, "f.priority.set",
new String[]{task.getTargetTorrent().getUniqueID() + ":f" + forFile.getKey(),
newPriority});
}
@ -261,16 +285,16 @@ public class RtorrentAdapter implements IDaemonAdapter { @@ -261,16 +285,16 @@ public class RtorrentAdapter implements IDaemonAdapter {
// Request to set the maximum transfer rates
SetTransferRatesTask ratesTask = (SetTransferRatesTask) task;
makeRtorrentCall(log, "set_download_rate", new String[]{(ratesTask.getDownloadRate() == null ? "0" :
makeRtorrentCall(log, "throttle.global_down.max_rate.set", new String[]{"",(ratesTask.getDownloadRate() == null ? "0" :
ratesTask.getDownloadRate().toString() + "k")});
makeRtorrentCall(log, "set_upload_rate", new String[]{
makeRtorrentCall(log, "throttle.global_up.max_rate.set", new String[]{"",
(ratesTask.getUploadRate() == null ? "0" : ratesTask.getUploadRate().toString() + "k")});
return new DaemonTaskSuccessResult(task);
case SetLabel:
SetLabelTask labelTask = (SetLabelTask) task;
makeRtorrentCall(log, "d.set_custom1",
makeRtorrentCall(log, "d.custom1.set",
new String[]{task.getTargetTorrent().getUniqueID(), labelTask.getNewLabel()});
return new DaemonTaskSuccessResult(task);

450
app/src/main/java/org/transdroid/daemon/Ttorrent/TtorrentAdapter.java

@ -0,0 +1,450 @@ @@ -0,0 +1,450 @@
/*
* This file is part of Transdroid <http://www.transdroid.org>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
package org.transdroid.daemon.Ttorrent;
import com.android.internalcopy.http.multipart.FilePart;
import com.android.internalcopy.http.multipart.MultipartEntity;
import com.android.internalcopy.http.multipart.Part;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.transdroid.core.gui.log.Log;
import org.transdroid.daemon.Daemon;
import org.transdroid.daemon.DaemonException;
import org.transdroid.daemon.DaemonException.ExceptionType;
import org.transdroid.daemon.DaemonSettings;
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.TorrentStatus;
import org.transdroid.daemon.task.AddByFileTask;
import org.transdroid.daemon.task.AddByMagnetUrlTask;
import org.transdroid.daemon.task.AddByUrlTask;
import org.transdroid.daemon.task.DaemonTask;
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.RemoveTask;
import org.transdroid.daemon.task.RetrieveTask;
import org.transdroid.daemon.task.RetrieveTaskSuccessResult;
import org.transdroid.daemon.task.SetFilePriorityTask;
import org.transdroid.daemon.task.SetTransferRatesTask;
import org.transdroid.daemon.util.HttpHelper;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* The daemon adapter for the tTorrent Android torrent client.
* @author erickok
*/
public class TtorrentAdapter implements IDaemonAdapter {
private static final String LOG_NAME = "tTorrent daemon";
private DaemonSettings settings;
private DefaultHttpClient httpclient;
public TtorrentAdapter(DaemonSettings settings) {
this.settings = settings;
}
@Override
public DaemonTaskResult executeTask(Log log, DaemonTask task) {
try {
switch (task.getMethod()) {
case Retrieve:
// Request all torrents from server
JSONArray result = new JSONArray(makeRequest(log, "/json/events"));
return new RetrieveTaskSuccessResult((RetrieveTask) task, parseJsonTorrents(result), null);
case GetTorrentDetails:
// Request tracker and error details for a specific teacher
String mhash = task.getTargetTorrent().getUniqueID();
JSONArray messages =
new JSONArray(makeRequest(log, "/json/propertiesTrackers/" + mhash));
return new GetTorrentDetailsTaskSuccessResult((GetTorrentDetailsTask) task, parseJsonTorrentDetails(messages));
case GetFileList:
// Request files listing for a specific torrent
String fhash = task.getTargetTorrent().getUniqueID();
JSONArray files =
new JSONArray(makeRequest(log, "/json/propertiesFiles/" + fhash));
return new GetFileListTaskSuccessResult((GetFileListTask) task, parseJsonFiles(files));
case AddByFile:
// Upload a local .torrent file
String ufile = ((AddByFileTask) task).getFile();
makeUploadRequest("/command/upload", ufile, log);
return new DaemonTaskSuccessResult(task);
case AddByUrl:
// Request to add a torrent by URL
String url = ((AddByUrlTask) task).getUrl();
makeRequest(log, "/command/download", new BasicNameValuePair("urls", url));
return new DaemonTaskSuccessResult(task);
case AddByMagnetUrl:
// Request to add a magnet link by URL
String magnet = ((AddByMagnetUrlTask) task).getUrl();
makeRequest(log, "/command/download", new BasicNameValuePair("urls", magnet));
return new DaemonTaskSuccessResult(task);
case Remove:
// Remove a torrent
RemoveTask removeTask = (RemoveTask) task;
makeRequest(log, (removeTask.includingData() ? "/command/deletePerm" : "/command/delete"),
new BasicNameValuePair("hashes", removeTask.getTargetTorrent().getUniqueID()));
return new DaemonTaskSuccessResult(task);
case Pause:
// Pause a torrent
makeRequest(log, "/command/pause", new BasicNameValuePair("hash", task.getTargetTorrent().getUniqueID()));
return new DaemonTaskSuccessResult(task);
case PauseAll:
// Resume all torrents
makeRequest(log, "/command/pauseall");
return new DaemonTaskSuccessResult(task);
case Resume:
// Resume a torrent
makeRequest(log, "/command/resume", new BasicNameValuePair("hash", task.getTargetTorrent().getUniqueID()));
return new DaemonTaskSuccessResult(task);
case ResumeAll:
// Resume all torrents
makeRequest(log, "/command/resumeall");
return new DaemonTaskSuccessResult(task);
case SetFilePriorities:
// Update the priorities to a set of files
SetFilePriorityTask setPrio = (SetFilePriorityTask) task;
String newPrio = "0";
if (setPrio.getNewPriority() == Priority.Low) {
newPrio = "1";
} else if (setPrio.getNewPriority() == Priority.Normal) {
newPrio = "2";
} else if (setPrio.getNewPriority() == Priority.High) {
newPrio = "7";
}
// We have to make a separate request per file, it seems
for (TorrentFile file : setPrio.getForFiles()) {
makeRequest(log, "/command/setFilePrio", new BasicNameValuePair("hash", task.getTargetTorrent().getUniqueID()),
new BasicNameValuePair("id", file.getKey()), new BasicNameValuePair("priority", newPrio));
}
return new DaemonTaskSuccessResult(task);
default:
return new DaemonTaskFailureResult(task,
new DaemonException(ExceptionType.MethodUnsupported, task.getMethod() + " is not supported by " + getType()));
}
} catch (JSONException e) {
return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.ParsingFailed, e.toString()));
} catch (DaemonException e) {
return new DaemonTaskFailureResult(task, e);
}
}
private String makeRequest(Log log, String path, NameValuePair... params) throws DaemonException {
try {
// Setup request using POST
HttpPost httppost = new HttpPost(buildWebUIUrl(path));
List<NameValuePair> nvps = new ArrayList<>();
Collections.addAll(nvps, params);
httppost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
return makeWebRequest(httppost, log);
} catch (UnsupportedEncodingException e) {
throw new DaemonException(ExceptionType.ConnectionError, e.toString());
}
}
private String makeUploadRequest(String path, String file, Log log) throws DaemonException {
try {
// Setup request using POST
HttpPost httppost = new HttpPost(buildWebUIUrl(path));
File upload = new File(URI.create(file));
Part[] parts = {new FilePart("torrentfile", upload)};
httppost.setEntity(new MultipartEntity(parts, httppost.getParams()));
return makeWebRequest(httppost, log);
} catch (FileNotFoundException e) {
throw new DaemonException(ExceptionType.FileAccessError, e.toString());
}
}
private String makeWebRequest(HttpPost httppost, Log log) throws DaemonException {
try {
// Initialise the HTTP client
if (httpclient == null) {
initialise();
}
// Execute
HttpResponse response = httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
if (entity != null) {
// Read JSON response
java.io.InputStream instream = entity.getContent();
String result = HttpHelper.convertStreamToString(instream);
instream.close();
// Return raw result
return result;
}
log.d(LOG_NAME, "Error: No entity in HTTP response");
throw new DaemonException(ExceptionType.UnexpectedResponse, "No HTTP entity object in response.");
} catch (Exception e) {
log.d(LOG_NAME, "Error: " + e.toString());
throw new DaemonException(ExceptionType.ConnectionError, e.toString());
}
}
/**
* Instantiates an HTTP client with proper credentials that can be used for all tTorrent requests.
* @throws DaemonException On conflicting or missing settings
*/
private void initialise() throws DaemonException {
httpclient = HttpHelper.createStandardHttpClient(settings, true);
}
/**
* Build the URL of the web UI request from the user settings
* @return The URL to request
*/
private String buildWebUIUrl(String path) {
return (settings.getSsl() ? "https://" : "http://") + settings.getAddress() + ":" + settings.getPort() + path;
}
private TorrentDetails parseJsonTorrentDetails(JSONArray messages) throws JSONException {
ArrayList<String> trackers = new ArrayList<>();
ArrayList<String> errors = new ArrayList<>();
// Parse response
if (messages.length() > 0) {
for (int i = 0; i < messages.length(); i++) {
JSONObject tor = messages.getJSONObject(i);
trackers.add(tor.getString("url"));
String msg = tor.getString("msg");
if (msg != null && !msg.equals(""))
errors.add(msg);
}
}
// Return the list
return new TorrentDetails(trackers, errors);
}
private ArrayList<Torrent> parseJsonTorrents(JSONArray response) throws JSONException {
// Parse response
ArrayList<Torrent> torrents = new ArrayList<>();
for (int i = 0; i < response.length(); i++) {
JSONObject tor = response.getJSONObject(i);
double progress = tor.getDouble("progress");
int leechers[] = parsePeers(tor.getString("num_leechs"));
int seeders[] = parsePeers(tor.getString("num_seeds"));
long size = parseSize(tor.getString("size"));
double ratio = parseRatio(tor.getString("ratio"));
int dlspeed = (int) parseSize(tor.getString("dlspeed"));
int upspeed = (int) parseSize(tor.getString("upspeed"));
long eta = -1L;
if (dlspeed > 0)
eta = (long) (size - (size * progress)) / dlspeed;
// @formatter:off
torrents.add(new Torrent(
(long) i,
tor.getString("hash"),
tor.getString("name"),
parseStatus(tor.getString("state")),
null,
dlspeed,
upspeed,
seeders[0],
seeders[1],
leechers[0],
leechers[1],
(int) eta,
(long) (size * progress),
(long) (size * ratio),
size,
(float) progress,
0f,
null,
null,
null,
null,
settings.getType()));
// @formatter:on
}
// Return the list
return torrents;
}
private double parseRatio(String string) {
// Ratio is given in "1.5" string format
try {
return Double.parseDouble(string);
} catch (Exception e) {
return 0D;
}
}
private long parseSize(String string) {
if (string.equals("Unknown"))
return -1;
// Sizes are given in "1562690683 B"-like string format
String[] parts = string.split(" ");
try {
return Long.parseLong(parts[0]);
} catch (Exception e) {
return -1L;
}
}
private int[] parsePeers(String seeds) {
// Peers (seeders or leechers) are defined in a string like "num_seeds":"66 (27)" but we are also compatible with the old
// "num_seeds":"66 (27)" format
String[] parts = seeds.split(" ");
if (parts.length > 1) {
return new int[]{Integer.parseInt(parts[0]), Integer.parseInt(parts[1].substring(1, parts[1].length() - 1))};
}
return new int[]{Integer.parseInt(parts[0]), Integer.parseInt(parts[0])};
}
private TorrentStatus parseStatus(String state) {
// Status is given as a descriptive string
if (state.equals("downloading")) {
return TorrentStatus.Downloading;
} else if (state.equals("uploading")) {
return TorrentStatus.Seeding;
} else if (state.equals("pausedDL")) {
return TorrentStatus.Paused;
} else if (state.equals("pausedUL")) {
return TorrentStatus.Paused;
} else if (state.equals("stalledUP")) {
return TorrentStatus.Seeding;
} else if (state.equals("stalledDL")) {
return TorrentStatus.Downloading;
} else if (state.equals("checkingUP")) {
return TorrentStatus.Checking;
} else if (state.equals("checkingDL")) {
return TorrentStatus.Checking;
} else if (state.equals("queuedDL")) {
return TorrentStatus.Queued;
} else if (state.equals("queuedUL")) {
return TorrentStatus.Queued;
}
return TorrentStatus.Unknown;
}
private ArrayList<TorrentFile> parseJsonFiles(JSONArray response) throws JSONException {
// Parse response
ArrayList<TorrentFile> torrentfiles = new ArrayList<>();
for (int i = 0; i < response.length(); i++) {
JSONObject file = response.getJSONObject(i);
long size = parseSize(file.getString("size"));
torrentfiles.add(new TorrentFile("" + i, file.getString("name"), null, null, size, (long) (size * file.getDouble("progress")),
parsePriority(file.getInt("priority"))));
}
// Return the list
return torrentfiles;
}
private Priority parsePriority(int priority) {
// Priority is an integer
// Actually 1 = Normal, 2 = High, 7 = Maximum, but adjust this to Transdroid values
if (priority == 0) {
return Priority.Off;
} else if (priority == 1) {
return Priority.Low;
} else if (priority == 2) {
return Priority.Normal;
}
return Priority.High;
}
@Override
public Daemon getType() {
return settings.getType();
}
@Override
public DaemonSettings getSettings() {
return this.settings;
}
}

BIN
app/src/main/res/drawable-hdpi/ab_bottom_solid_transdroid.9.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 205 B

BIN
app/src/main/res/drawable-hdpi/ab_bottom_solid_transdroid2.9.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 189 B

BIN
app/src/main/res/drawable-hdpi/ab_solid_transdroid.9.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 213 B

BIN
app/src/main/res/drawable-hdpi/ab_solid_transdroid2.9.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 192 B

BIN
app/src/main/res/drawable-hdpi/ab_stacked_solid_transdroid.9.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 199 B

BIN
app/src/main/res/drawable-hdpi/ab_stacked_solid_transdroid2.9.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 199 B

BIN
app/src/main/res/drawable-hdpi/ab_texture_tile_transdroid2.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 B

BIN
app/src/main/res/drawable-hdpi/ab_transparent_transdroid.9.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 205 B

BIN
app/src/main/res/drawable-hdpi/ab_transparent_transdroid2.9.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 204 B

BIN
app/src/main/res/drawable-hdpi/abc_list_focused_holo.9.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 191 B

BIN
app/src/main/res/drawable-hdpi/abc_list_longpressed_holo.9.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 B

BIN
app/src/main/res/drawable-hdpi/abc_list_pressed_holo_dark.9.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 159 B

BIN
app/src/main/res/drawable-hdpi/abc_list_pressed_holo_light.9.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 159 B

BIN
app/src/main/res/drawable-hdpi/abc_list_selector_disabled_holo_dark.9.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 189 B

BIN
app/src/main/res/drawable-hdpi/abc_list_selector_disabled_holo_light.9.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 189 B

BIN
app/src/main/res/drawable-hdpi/btn_cab_done_default_transdroid2.9.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 B

BIN
app/src/main/res/drawable-hdpi/btn_cab_done_focused_transdroid2.9.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 B

BIN
app/src/main/res/drawable-hdpi/btn_cab_done_pressed_transdroid2.9.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 B

BIN
app/src/main/res/drawable-hdpi/cab_background_bottom_transdroid2.9.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 202 B

BIN
app/src/main/res/drawable-hdpi/cab_background_top_transdroid2.9.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 199 B

BIN
app/src/main/res/drawable-hdpi/ic_action_add.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 B

BIN
app/src/main/res/drawable-hdpi/ic_action_barcode.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
app/src/main/res/drawable-hdpi/ic_action_copy.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 B

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

Loading…
Cancel
Save