Browse Source

Added background services for app updates, RSS feeds and server checks for new and finished torrents.

pull/11/head
Eric Kok 12 years ago
parent
commit
97c2589632
  1. 4
      core/AndroidManifest.xml
  2. BIN
      core/res/drawable-hdpi-v11/ic_stat_notification.png
  3. BIN
      core/res/drawable-hdpi/ic_stat_notification.png
  4. BIN
      core/res/drawable-mdpi-v11/ic_stat_notification.png
  5. BIN
      core/res/drawable-mdpi/ic_stat_notification.png
  6. BIN
      core/res/drawable-xhdpi-v11/ic_stat_notification.png
  7. BIN
      core/res/drawable-xhdpi/ic_stat_notification.png
  8. BIN
      core/res/drawable-xxhdpi-v11/ic_stat_notification.png
  9. BIN
      core/res/drawable-xxhdpi/ic_stat_notification.png
  10. 4
      core/res/values/changelog.xml
  11. 30
      core/res/values/strings.xml
  12. 28
      core/src/org/transdroid/core/app/settings/ApplicationSettings.java
  13. 9
      core/src/org/transdroid/core/app/settings/NotificationSettings.java
  14. 17
      core/src/org/transdroid/core/app/settings/ServerSetting.java
  15. 12
      core/src/org/transdroid/core/gui/TorrentsActivity.java
  16. 2
      core/src/org/transdroid/core/gui/log/Log.java
  17. 43
      core/src/org/transdroid/core/gui/settings/NotificationSettingsActivity.java
  18. 16
      core/src/org/transdroid/core/gui/settings/SystemSettingsActivity.java
  19. 34
      core/src/org/transdroid/core/service/AlarmReceiver.java
  20. 136
      core/src/org/transdroid/core/service/AppUpdateService.java
  21. 88
      core/src/org/transdroid/core/service/BootReceiver.java
  22. 29
      core/src/org/transdroid/core/service/ConnectivityHelper.java
  23. 107
      core/src/org/transdroid/core/service/RssCheckerService.java
  24. 183
      core/src/org/transdroid/core/service/ServerCheckerService.java
  25. 54
      full/AndroidManifest.xml
  26. 23
      lite/AndroidManifest.xml

4
core/AndroidManifest.xml

@ -1,8 +1,8 @@ @@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.transdroid.core"
android:versionCode="2"
android:versionName="2.0-alpha2" >
android:versionCode="3"
android:versionName="2.0-alpha3" >
<uses-sdk
android:minSdkVersion="7"

BIN
core/res/drawable-hdpi-v11/ic_stat_notification.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 942 B

BIN
core/res/drawable-hdpi/ic_stat_notification.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
core/res/drawable-mdpi-v11/ic_stat_notification.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 578 B

BIN
core/res/drawable-mdpi/ic_stat_notification.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 632 B

BIN
core/res/drawable-xhdpi-v11/ic_stat_notification.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
core/res/drawable-xhdpi/ic_stat_notification.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
core/res/drawable-xxhdpi-v11/ic_stat_notification.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
core/res/drawable-xxhdpi/ic_stat_notification.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

4
core/res/values/changelog.xml

@ -4,6 +4,8 @@ @@ -4,6 +4,8 @@
Transdroid 2.0.0-alpha3\n
- Added torrent search\n
- Setting of max. up/down speeds\n
- Update trackers and set labels (where supported)\n
- Background RSS and server checker services\n
\n
Transdroid 2.0.0-alpha2\n
- Fixed Transmission adapter folder setting\n
@ -16,4 +18,4 @@ Transdroid 2.0.0-alpha1\n @@ -16,4 +18,4 @@ Transdroid 2.0.0-alpha1\n
\n
Older changes: http://www.transdroid.org/about/changelog/
</string>
</resources>
</resources>

30
core/res/values/strings.xml

@ -100,15 +100,28 @@ @@ -100,15 +100,28 @@
<string name="status_maxspeed_unit">KB/S</string>
<string name="status_maxspeed_reset">Reset</string>
<string name="status_update">Update</string>
<string name="status_label_pick">PICK A LABEL</string>
<string name="status_label_new">NEW LABEL</string>
<string name="status_label_remove">Remove label</string>
<string name="status_label_hint">E.g. movies or linux</string>
<plurals name="status_service_added">
<item quantity="one">New torrent added</item>
<item quantity="many">%1$s new torrents added</item>
</plurals>
<plurals name="status_service_finished">
<item quantity="one">Torrent is finished</item>
<item quantity="many">%1$s torrents are finished</item>
</plurals>
<plurals name="status_service_addedfinished">
<item quantity="one">%1$s added, %2$s finished torrent</item>
<item quantity="many">%1$s added, %2$s finished torrents</item>
</plurals>
<string name="status_service_andothers">%1$s and others</string>
<string name="labels_showall">All labels</string>
<string name="labels_unlabeled">Unlabeled</string>
<string name="labels_newlabel">New label</string>
<string name="labels_no_support">Setting a label is not supported by your client</string>
<string name="status_label_pick">PICK A LABEL</string>
<string name="status_label_new">NEW LABEL</string>
<string name="status_label_remove">Remove label</string>
<string name="status_label_hint">E.g. movies or linux</string>
<string name="result_added">%1$s added (refreshing)</string>
<string name="result_removed">%1$s removed</string>
@ -153,6 +166,11 @@ @@ -153,6 +166,11 @@
<item quantity="one">%1$d item selected</item>
<item quantity="other">%1$d items selected</item>
</plurals>
<plurals name="rss_service_new">
<item quantity="one">New RSS feed torrent available</item>
<item quantity="many">%1$s new RSS feed torrents</item>
</plurals>
<string name="rss_service_newfor">New torrents for %1$s</string>
<string name="pref_servers">Servers</string>
<string name="pref_addserver">Add new server</string>
@ -320,6 +338,10 @@ @@ -320,6 +338,10 @@
<string name="error_notanumber">Please enter a positive number</string>
<string name="error_notalabel">Please enter a valid label or pick from the list</string>
<string name="update_app_newversion">New Transdroid version available</string>
<string name="update_search_newversion">New Transdroid search module available</string>
<string name="update_updateto">You can now update to %1$s</string>
<string name="system_name" translatable="false">Transdroid</string>
<string name="system_developer" translatable="false">\u00A9 Eric Kok, 2312 development</string>
<string name="system_license" translatable="false">Published under GNU General Public License v3</string>

28
core/src/org/transdroid/core/app/settings/ApplicationSettings.java

@ -9,6 +9,8 @@ import org.androidannotations.annotations.Bean; @@ -9,6 +9,8 @@ import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.EBean;
import org.androidannotations.annotations.EBean.Scope;
import org.androidannotations.annotations.RootContext;
import org.json.JSONArray;
import org.json.JSONException;
import org.transdroid.core.app.search.SearchHelper;
import org.transdroid.core.app.search.SearchSite;
import org.transdroid.core.gui.search.SearchSetting;
@ -460,4 +462,30 @@ public class ApplicationSettings { @@ -460,4 +462,30 @@ public class ApplicationSettings {
prefs.edit().putString("header_setsearchsite", site.getKey()).commit();
}
/**
* Returns the statistics of this server as it was last seen by the background server checker service.
* @param server The server for which to retrieved the statistics from the stored preferences
* @return A JSON array of JSON objects, each which represent a since torrent
*/
public JSONArray getServerLastStats(ServerSetting server) {
String lastStats = prefs.getString(server.getUniqueIdentifier(), null);
if (lastStats == null)
return null;
try {
return new JSONArray(lastStats);
} catch (JSONException e) {
return null;
}
}
/**
* Stores the now-last seen statistics of the supplied server by the background server checker service to the
* internal stored preferences.
* @param server The server to which the statistics apply to
* @param lastStats A JSON array of JSON objects that each represent a single seen torrent
*/
public void setServerLastStats(ServerSetting server, JSONArray lastStats) {
prefs.edit().putString(server.getUniqueIdentifier(), lastStats.toString()).commit();
}
}

9
core/src/org/transdroid/core/app/settings/NotificationSettings.java

@ -71,6 +71,15 @@ public class NotificationSettings { @@ -71,6 +71,15 @@ public class NotificationSettings {
return prefs.getBoolean("notifications_vibrate", false);
}
/**
* Returns the default vibrate pattern to use if the user enabled notification vibrations; check
* {@link #shouldVibrate()},
* @return A unique pattern for vibrations in Transdroid
*/
public long[] getDefaultVibratePattern() {
return new long[]{100, 100, 200, 300, 400, 700}; // Unique pattern?
}
private int getRawLedColour() {
return prefs.getInt("notifications_ledcolour", -1);
}

17
core/src/org/transdroid/core/app/settings/ServerSetting.java

@ -189,6 +189,10 @@ public class ServerSetting implements SimpleListItem { @@ -189,6 +189,10 @@ public class ServerSetting implements SimpleListItem {
return this.key;
}
/**
* Returns a string that the user can use to identify the server by internal settings (rather than the name).
* @return A human-readable identifier in the form [https://]username@address:port/folder
*/
public String getHumanReadableIdentifier() {
if (isAutoGenerated) {
// Hide the 'implementation details'; just give the username and server
@ -201,6 +205,19 @@ public class ServerSetting implements SimpleListItem { @@ -201,6 +205,19 @@ public class ServerSetting implements SimpleListItem {
+ (Daemon.supportsCustomFolder(getType()) && getFolder() != null ? getFolder() : "");
}
/**
* Returns a string that acts as a unique identifier for this server, non-depending on the internal storage
* order/index. THis may be used to store additional details about this server elsewhere. It may change if the user
* changes server settings, but not with name or notification settings.
* @return A unique identifying string, based primarily on the configured address, port number, SSL settings and
* user name; returns null if the server is not yet fully identifiable (during configuration, for example)
*/
public String getUniqueIdentifier() {
if (getType() == null || getAddress() == null || getAddress().equals(""))
return null;
return getType().toString() + "|" + getHumanReadableIdentifier();
}
@Override
public boolean equals(Object o) {
if (o instanceof ServerSetting) {

12
core/src/org/transdroid/core/gui/TorrentsActivity.java

@ -38,6 +38,7 @@ import org.transdroid.core.gui.search.BarcodeHelper; @@ -38,6 +38,7 @@ import org.transdroid.core.gui.search.BarcodeHelper;
import org.transdroid.core.gui.search.FilePickerHelper;
import org.transdroid.core.gui.search.UrlEntryDialog;
import org.transdroid.core.gui.settings.*;
import org.transdroid.core.service.BootReceiver;
import org.transdroid.daemon.Daemon;
import org.transdroid.daemon.IDaemonAdapter;
import org.transdroid.daemon.Priority;
@ -185,12 +186,17 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi @@ -185,12 +186,17 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi
// Log messages from the server daemons using our singleton logger
DLog.setLogger(Log_.getInstance_(this));
// Connect to the last used server
// Connect to the last used server or a server that was supplied in the starting intent
ServerSetting lastUsed = applicationSettings.getLastUsedServer();
if (lastUsed == null) {
// No server settings yet;
return;
}
if (getIntent().getExtras() == null && getIntent().hasExtra("org.transdroid.START_SERVER")) {
lastUsed = applicationSettings.getServerSetting(getIntent().getExtras().getInt(
"org.transdroid.START_SERVER"));
}
// Set this as selection in the action bar spinner; we can use the server setting key since we have stable ids
getSupportActionBar().setSelectedNavigationItem(lastUsed.getOrder() + 1);
skipNextOnNavigationItemSelectedCall = true;
@ -200,6 +206,10 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi @@ -200,6 +206,10 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi
currentConnection = lastUsed.createServerAdapter();
handleStartIntent();
}
// Start the alarms for the background services, if needed
BootReceiver.startBackgroundServices(getApplicationContext(), false);
BootReceiver.startAppUpdatesService(getApplicationContext());
}

2
core/src/org/transdroid/core/gui/log/Log.java

@ -37,7 +37,7 @@ public class Log implements ITLogger { @@ -37,7 +37,7 @@ public class Log implements ITLogger {
}
protected void log(String logName, int priority, String message) {
if (!navigationHelper.inDebugMode())
if (navigationHelper.inDebugMode())
android.util.Log.println(priority, LOG_NAME, message);
try {
// Store this log message to the database

43
core/src/org/transdroid/core/gui/settings/NotificationSettingsActivity.java

@ -4,31 +4,50 @@ import org.androidannotations.annotations.Bean; @@ -4,31 +4,50 @@ import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.EActivity;
import org.androidannotations.annotations.OptionsItem;
import org.transdroid.core.R;
import org.transdroid.core.app.settings.ApplicationSettings;
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 com.actionbarsherlock.app.SherlockPreferenceActivity;
@EActivity
public class NotificationSettingsActivity extends SherlockPreferenceActivity {
public class NotificationSettingsActivity extends SherlockPreferenceActivity implements
OnSharedPreferenceChangeListener {
@Bean
protected ApplicationSettings applicationSettings;
protected NotificationSettings notificationSettings;
@SuppressWarnings("deprecation")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
// Just load the notification-related preferences from XML
addPreferencesFromResource(R.xml.pref_notifications);
}
@SuppressWarnings("deprecation")
@Override
protected void onResume() {
super.onResume();
// Start/stop the background service appropriately
getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
}
@SuppressWarnings("deprecation")
@Override
protected void onPause() {
super.onPause();
getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@ -37,4 +56,16 @@ public class NotificationSettingsActivity extends SherlockPreferenceActivity { @@ -37,4 +56,16 @@ public class NotificationSettingsActivity extends SherlockPreferenceActivity {
MainSettingsActivity_.intent(this).flags(Intent.FLAG_ACTIVITY_CLEAR_TOP).start();
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (!notificationSettings.isEnabled()) {
// Disabled background notifications; disable the alarms that start the service
BootReceiver.cancelBackgroundServices(getApplicationContext());
}
// (Re-)enable the alarms for the background services
BootReceiver.startBackgroundServices(getApplicationContext(), true);
}
}

16
core/src/org/transdroid/core/gui/settings/SystemSettingsActivity.java

@ -13,6 +13,7 @@ import org.transdroid.core.app.settings.SettingsPersistence; @@ -13,6 +13,7 @@ import org.transdroid.core.app.settings.SettingsPersistence;
import org.transdroid.core.gui.log.ErrorLogSender;
import org.transdroid.core.gui.navigation.DialogHelper;
import org.transdroid.core.gui.navigation.NavigationHelper;
import org.transdroid.core.service.BootReceiver;
import android.annotation.TargetApi;
import android.app.AlertDialog;
@ -24,6 +25,7 @@ import android.content.SharedPreferences; @@ -24,6 +25,7 @@ import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceManager;
@ -58,7 +60,8 @@ public class SystemSettingsActivity extends SherlockPreferenceActivity { @@ -58,7 +60,8 @@ public class SystemSettingsActivity extends SherlockPreferenceActivity {
// Just load the system-related preferences from XML
addPreferencesFromResource(R.xml.pref_system);
// Handle outgoing links
// Handle outgoing links and preference changes
findPreference("system_checkupdates").setOnPreferenceClickListener(onCheckUpdatesClick);
findPreference("system_sendlog").setOnPreferenceClickListener(onSendLogClick);
findPreference("system_installhelp").setOnPreferenceClickListener(onInstallHelpClick);
findPreference("system_changelog").setOnPreferenceClickListener(onChangeLogClick);
@ -73,6 +76,17 @@ public class SystemSettingsActivity extends SherlockPreferenceActivity { @@ -73,6 +76,17 @@ public class SystemSettingsActivity extends SherlockPreferenceActivity {
MainSettingsActivity_.intent(this).flags(Intent.FLAG_ACTIVITY_CLEAR_TOP).start();
}
private OnPreferenceClickListener onCheckUpdatesClick = new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
if (((CheckBoxPreference) preference).isChecked())
BootReceiver.startAppUpdatesService(getApplicationContext());
else
BootReceiver.cancelAppUpdates(getApplicationContext());
return true;
}
};
private OnPreferenceClickListener onSendLogClick = new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {

34
core/src/org/transdroid/core/service/AlarmReceiver.java

@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
package org.transdroid.core.service;
import org.androidannotations.annotations.EReceiver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
/**
* Acts simply as an intermediary to start the appropriate background service when an alarm goes off.
* @author Eric Kok
*/
@EReceiver
public class AlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
switch (intent.getIntExtra("service", -1)) {
case BootReceiver.ALARM_SERVERCHECKER:
context.startService(new Intent(context, ServerCheckerService_.class));
break;
case BootReceiver.ALARM_RSSCHECKER:
context.startService(new Intent(context, RssCheckerService_.class));
break;
case BootReceiver.ALARM_APPUPDATES:
context.startService(new Intent(context, AppUpdateService_.class));
break;
default:
// No valid service start ID
break;
}
}
}

136
core/src/org/transdroid/core/service/AppUpdateService.java

@ -0,0 +1,136 @@ @@ -0,0 +1,136 @@
package org.transdroid.core.service;
import java.io.IOException;
import java.io.InputStream;
import java.util.Random;
import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.EService;
import org.androidannotations.annotations.SystemService;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.AbstractHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.transdroid.core.R;
import org.transdroid.core.app.settings.NotificationSettings;
import org.transdroid.core.app.settings.SystemSettings;
import org.transdroid.core.gui.log.Log;
import org.transdroid.daemon.util.HttpHelper;
import android.app.IntentService;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationCompat.Builder;
@EService
public class AppUpdateService extends IntentService {
private static final String LATEST_URL_APP = "http://www.transdroid.org/update/latest-app.php";
private static final String LATEST_URL_SEARCH = "http://www.transdroid.org/update/latest-search.php";
private static final String DOWNLOAD_URL_APP = "http://www.transdroid.org/latest";
private static final String DOWNLOAD_URL_SEARCH = "http://www.transdroid.org/latest-search";
@Bean
protected ConnectivityHelper connectivityHelper;
@Bean
protected SystemSettings systemSettings;
@Bean
protected NotificationSettings notificationSettings;
@SystemService
protected NotificationManager notificationManager;
public AppUpdateService() {
super("AppUpdateService");
}
@Override
protected void onHandleIntent(Intent intent) {
if (!connectivityHelper.shouldPerformActions() || !systemSettings.checkForUpdates()) {
Log.d(this,
"Skip the app update service, as background data is disabled, the service is disabled or we are not connected.");
return;
}
DefaultHttpClient httpclient = new DefaultHttpClient();
Random random = new Random();
try {
// Retrieve what is the latest released app and search module versions
String[] app = retrieveLatestVersion(httpclient, LATEST_URL_APP);
String[] search = retrieveLatestVersion(httpclient, LATEST_URL_SEARCH);
int appVersion = Integer.parseInt(app[0].trim());
int searchVersion = Integer.parseInt(search[0].trim());
// New version of the app?
try {
PackageInfo appPackage = getPackageManager().getPackageInfo(getPackageName(), 0);
if (appPackage.versionCode < appVersion) {
// New version available! Notify the user.
newNotification(getString(R.string.update_app_newversion),
getString(R.string.update_app_newversion),
getString(R.string.update_updateto, app[1].trim()),
DOWNLOAD_URL_APP + "?" + Integer.toString(random.nextInt()), 90000);
}
} catch (NameNotFoundException e) {
// Not installed... this can never happen since this Service is part of the app itself
}
// New version of the search module?
try {
PackageInfo searchPackage = getPackageManager().getPackageInfo("org.transdroid.search", 0);
if (searchPackage.versionCode < searchVersion) {
// New version available! Notify the user.
newNotification(getString(R.string.update_search_newversion),
getString(R.string.update_search_newversion),
getString(R.string.update_updateto, search[1].trim()),
DOWNLOAD_URL_SEARCH + "?" + Integer.toString(random.nextInt()), 90001);
}
} catch (NameNotFoundException e) {
// The search module isn't installed yet at all; ignore and wait for the user to manually
// install it (when the first search is initiated)
}
} catch (Exception e) {
// Cannot check right now for some reason; log and ignore
Log.d(this, "Cannot retrieve latest app or search module version code from the site: " + e.toString());
}
}
/**
* Retrieves the latest version number of the app or search module by checking an online text file that looks like
* '160|1.1.15' for version code 160 and version name 1.1.15.
* @param httpclient An already instantiated HTTP client
* @param url The URL of the the text file that contains the current latest version code and name
* @return A string array with two elements: the version code and the version number
* @throws ClientProtocolException Thrown when the provided URL is invalid
* @throws IOException Thrown when the last version information could not be retrieved
*/
private String[] retrieveLatestVersion(AbstractHttpClient httpclient, String url) throws ClientProtocolException,
IOException {
HttpResponse request = httpclient.execute(new HttpGet(url));
InputStream stream = request.getEntity().getContent();
String appVersion[] = HttpHelper.convertStreamToString(stream).split("\\|");
stream.close();
return appVersion;
}
private void newNotification(String ticker, String title, String text, String downloadUrl, int notifyID) {
PendingIntent pi = PendingIntent.getActivity(this, notifyID,
new Intent(Intent.ACTION_VIEW, Uri.parse(downloadUrl)), Intent.FLAG_ACTIVITY_NEW_TASK);
Builder builder = new NotificationCompat.Builder(this).setSmallIcon(R.drawable.ic_stat_notification)
.setTicker(ticker).setContentTitle(title).setContentText(text)
.setLights(notificationSettings.getDesiredLedColour(), 600, 1000)
.setSound(notificationSettings.getSound()).setAutoCancel(true).setContentIntent(pi);
notificationManager.notify(notifyID, builder.build());
}
}

88
core/src/org/transdroid/core/service/BootReceiver.java

@ -0,0 +1,88 @@ @@ -0,0 +1,88 @@
package org.transdroid.core.service;
import org.transdroid.core.app.settings.NotificationSettings;
import org.transdroid.core.app.settings.NotificationSettings_;
import org.transdroid.core.app.settings.SystemSettings;
import org.transdroid.core.app.settings.SystemSettings_;
import org.transdroid.core.gui.log.Log;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.SystemClock;
/**
* Receives the intent that the device has been started in order to set up proper alarms for all background services.
* @author Eric Kok
*/
public class BootReceiver extends BroadcastReceiver {
public static final int ALARM_SERVERCHECKER = 0;
public static final int ALARM_RSSCHECKER = 1;
public static final int ALARM_APPUPDATES = 2;
public static PendingIntent piServerChecker = null, piRssChecker = null, piAppUpdates = null;
@Override
public void onReceive(Context context, Intent intent) {
startBackgroundServices(context, false);
startAppUpdatesService(context);
}
public static void startBackgroundServices(Context context, boolean forceReload) {
NotificationSettings notificationSettings = NotificationSettings_.getInstance_(context);
AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
if (notificationSettings.isEnabled() && (forceReload || (piServerChecker == null && piRssChecker == null))) {
Log.d(context, "Boot signal received, starting server and rss checker background services");
// Schedule repeating alarms, with the first being (somewhat) in 1 second from now
piServerChecker = PendingIntent.getBroadcast(context, ALARM_SERVERCHECKER, new Intent(context,
AlarmReceiver_.class).putExtra("service", ALARM_SERVERCHECKER), 0);
piRssChecker = PendingIntent.getBroadcast(context, ALARM_RSSCHECKER, new Intent(context,
AlarmReceiver_.class).putExtra("service", ALARM_RSSCHECKER), 0);
alarms.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + 1000,
notificationSettings.getInvervalInMilliseconds(), piServerChecker);
alarms.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + 1000,
notificationSettings.getInvervalInMilliseconds(), piRssChecker);
}
}
public static void startAppUpdatesService(Context context) {
SystemSettings systemSettings = SystemSettings_.getInstance_(context);
AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
if (systemSettings.checkForUpdates() && piAppUpdates == null) {
Log.d(context, "Boot signal received, starting app update checker service");
// Schedule a daily, with the first being (somewhat) in 1 second from now
piAppUpdates = PendingIntent.getBroadcast(context, ALARM_APPUPDATES, new Intent(context,
AlarmReceiver_.class).putExtra("service", ALARM_APPUPDATES), 0);
alarms.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + 1000,
AlarmManager.INTERVAL_DAY, piAppUpdates);
}
}
public static void cancelBackgroundServices(Context context) {
AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
if (piServerChecker != null) {
alarms.cancel(piServerChecker);
piServerChecker = null;
}
if (piRssChecker != null) {
alarms.cancel(piRssChecker);
piRssChecker = null;
}
}
public static void cancelAppUpdates(Context context) {
if (piAppUpdates != null) {
AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarms.cancel(piAppUpdates);
piAppUpdates = null;
}
}
}

29
core/src/org/transdroid/core/service/ConnectivityHelper.java

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
package org.transdroid.core.service;
import org.androidannotations.annotations.EBean;
import org.androidannotations.annotations.SystemService;
import org.androidannotations.annotations.EBean.Scope;
import android.content.Context;
import android.net.ConnectivityManager;
@EBean(scope = Scope.Singleton)
public class ConnectivityHelper {
@SystemService
protected ConnectivityManager connectivityManager;
public ConnectivityHelper(Context context) {
}
@SuppressWarnings("deprecation")
public boolean shouldPerformActions() {
// 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
return connectivityManager.getActiveNetworkInfo().isConnected();
}
}

107
core/src/org/transdroid/core/service/RssCheckerService.java

@ -0,0 +1,107 @@ @@ -0,0 +1,107 @@
package org.transdroid.core.service;
import java.util.LinkedHashSet;
import java.util.Set;
import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.EService;
import org.androidannotations.annotations.SystemService;
import org.transdroid.core.R;
import org.transdroid.core.app.settings.ApplicationSettings;
import org.transdroid.core.app.settings.NotificationSettings;
import org.transdroid.core.app.settings.RssfeedSetting;
import org.transdroid.core.gui.log.Log;
import org.transdroid.core.gui.rss.RssfeedsActivity_;
import org.transdroid.core.rssparser.Item;
import org.transdroid.core.rssparser.RssParser;
import org.transdroid.daemon.util.Collections2;
import android.app.IntentService;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationCompat.Builder;
/**
* A background service that checks all user-configured RSS feeds for new items.
* @author Eric Kok
*/
@EService
public class RssCheckerService extends IntentService {
@Bean
protected ConnectivityHelper connectivityHelper;
@Bean
protected NotificationSettings notificationSettings;
@Bean
protected ApplicationSettings applicationSettings;
@SystemService
protected NotificationManager notificationManager;
public RssCheckerService() {
super("RssCheckerService");
}
@Override
protected void onHandleIntent(Intent intent) {
if (!connectivityHelper.shouldPerformActions() || !notificationSettings.isEnabled()) {
Log.d(this,
"Skip the RSS checker service, as background data is disabled, the service is disabled or we are not connected.");
return;
}
// Check every RSS feed for new items
int unread = 0;
Set<String> hasUnread = new LinkedHashSet<String>();
for (RssfeedSetting feed : applicationSettings.getRssfeedSettings()) {
try {
Log.d(this, "Try to parse " + feed.getName() + " (" + feed.getUrl() + ")");
RssParser parser = new RssParser(feed.getUrl());
parser.parse();
if (parser.getChannel() == null)
continue;
// Find the last item that is newer than the last viewed date
for (Item item : parser.getChannel().getItems()) {
if (item.getPubdate() != null && item.getPubdate().before(feed.getLastViewed())) {
break;
} else {
unread++;
if (!hasUnread.contains(feed.getName()))
hasUnread.add(feed.getName());
}
}
Log.d(this, feed.getName() + " has " + (hasUnread.contains(feed.getName()) ? "" : "no ")
+ "unread items");
} catch (Exception e) {
// Ignore RSS feeds that could not be retrieved or parsed
}
}
if (unread == 0) {
// No new items; just exit
return;
}
// Provide a notification, since there are new RSS items
PendingIntent pi = PendingIntent.getActivity(this, 80000, new Intent(this, RssfeedsActivity_.class),
Intent.FLAG_ACTIVITY_NEW_TASK);
String title = getResources().getQuantityString(R.plurals.rss_service_new, unread, Integer.toString(unread));
String forString = Collections2.joinString(hasUnread, ", ");
Builder builder = new NotificationCompat.Builder(this).setSmallIcon(R.drawable.ic_stat_notification)
.setTicker(title).setContentTitle(title)
.setContentText(getString(R.string.rss_service_newfor, forString)).setNumber(unread)
.setLights(notificationSettings.getDesiredLedColour(), 600, 1000)
.setSound(notificationSettings.getSound()).setAutoCancel(true).setContentIntent(pi);
if (notificationSettings.shouldVibrate())
builder.setVibrate(notificationSettings.getDefaultVibratePattern());
notificationManager.notify(80001, builder.build());
}
}

183
core/src/org/transdroid/core/service/ServerCheckerService.java

@ -0,0 +1,183 @@ @@ -0,0 +1,183 @@
package org.transdroid.core.service;
import java.util.ArrayList;
import java.util.List;
import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.EService;
import org.androidannotations.annotations.SystemService;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.transdroid.core.R;
import org.transdroid.core.app.settings.ApplicationSettings;
import org.transdroid.core.app.settings.NotificationSettings;
import org.transdroid.core.app.settings.ServerSetting;
import org.transdroid.core.gui.TorrentsActivity_;
import org.transdroid.core.gui.log.Log;
import org.transdroid.daemon.IDaemonAdapter;
import org.transdroid.daemon.Torrent;
import org.transdroid.daemon.task.DaemonTaskResult;
import org.transdroid.daemon.task.RetrieveTask;
import org.transdroid.daemon.task.RetrieveTaskSuccessResult;
import org.transdroid.daemon.util.Collections2;
import android.app.IntentService;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationCompat.Builder;
import android.support.v4.app.NotificationCompat.InboxStyle;
/**
* A background service that checks all user-configured servers (if so desired) for new and finished torrents.
* @author Eric Kok
*/
@EService
public class ServerCheckerService extends IntentService {
@Bean
protected ConnectivityHelper connectivityHelper;
@Bean
protected NotificationSettings notificationSettings;
@Bean
protected ApplicationSettings applicationSettings;
@SystemService
protected NotificationManager notificationManager;
public ServerCheckerService() {
super("ServerCheckerService");
}
@Override
protected void onHandleIntent(Intent intent) {
if (!connectivityHelper.shouldPerformActions() || !notificationSettings.isEnabled()) {
Log.d(this,
"Skip the server checker service, as background data is disabled, the service is disabled or we are not connected.");
return;
}
int notifyBase = 10000;
for (ServerSetting server : applicationSettings.getServerSettings()) {
// No need to check if the server is not properly configured or none of the two types of notifications are
// enabled by the user for this specific server
if (server.getType() == null || server.getAddress() == null || server.getAddress().equals("")
|| !(server.shouldAlarmOnFinishedDownload() || server.shouldAlarmOnNewTorrent()))
return;
// Get the statistics for the last time we checked this server
JSONArray lastStats = applicationSettings.getServerLastStats(server);
// Synchronously retrieve torrents listing
IDaemonAdapter adapter = server.createServerAdapter();
DaemonTaskResult result = RetrieveTask.create(adapter).execute();
if (!(result instanceof RetrieveTaskSuccessResult)) {
// Cannot retrieve torrents at this time
return;
}
List<Torrent> retrieved = ((RetrieveTaskSuccessResult) result).getTorrents();
Log.d(this, server.getName() + ": Retrieved torrent listing");
// Check for differences between the last and the current stats
JSONArray currentStats = new JSONArray();
List<Torrent> newTorrents = new ArrayList<Torrent>();
List<Torrent> doneTorrents = new ArrayList<Torrent>();
for (Torrent torrent : retrieved) {
// Remember this torrent for the next time
try {
currentStats.put(new JSONObject().put("id", torrent.getUniqueID()).put("done",
torrent.getPartDone() == 1F));
} catch (JSONException e) {
// Can't build the JSON object; this should not happen and we can safely ignore it
}
// See if this torrent was done the last time we checked
if (lastStats != null) {
Boolean wasDone = findLastDoneStat(lastStats, torrent);
if (server.shouldAlarmOnNewTorrent() && wasDone == null) {
// This torrent wasn't present earlier
newTorrents.add(torrent);
continue;
}
if (server.shouldAlarmOnFinishedDownload() && torrent.getPartDone() == 1F && !wasDone)
// This torrent is now done, but wasn't before
doneTorrents.add(torrent);
}
}
// Store the now-current statistics on torrents for the next time we check this server
applicationSettings.setServerLastStats(server, currentStats);
// Notify on new and now-done torrents for this server
Log.d(this, server.getName() + ": " + newTorrents.size() + " new torrents, " + doneTorrents.size()
+ " newly finished torrents.");
Intent i = new Intent(this, TorrentsActivity_.class);
i.putExtra("org.transdroid.START_SERVER", server.getOrder());
// Should start the main activity directly into this server
PendingIntent pi = PendingIntent.getActivity(this, notifyBase + server.getOrder(), i,
Intent.FLAG_ACTIVITY_NEW_TASK);
ArrayList<Torrent> affectedTorrents = new ArrayList<Torrent>(newTorrents.size() + doneTorrents.size());
affectedTorrents.addAll(newTorrents);
affectedTorrents.addAll(doneTorrents);
String title, forString = Collections2.joinString(affectedTorrents, ", ");
if (newTorrents.size() > 0 && doneTorrents.size() > 0) {
// Note: use the 'one' plural iif 1 new torrent was added and 1 was newly finished
title = getResources().getQuantityString(R.plurals.status_service_finished,
newTorrents.size() + doneTorrents.size() == 2 ? 1 : 2, Integer.toString(newTorrents.size()),
Integer.toString(doneTorrents.size()));
} else if (newTorrents.size() > 0) {
title = getResources().getQuantityString(R.plurals.status_service_added, newTorrents.size(),
Integer.toString(newTorrents.size()));
} else if (doneTorrents.size() > 0) {
title = getResources().getQuantityString(R.plurals.status_service_finished, doneTorrents.size(),
Integer.toString(doneTorrents.size()));
} else {
// No notification to show
continue;
}
// Build the basic notification
Builder builder = new NotificationCompat.Builder(this).setSmallIcon(R.drawable.ic_stat_notification)
.setTicker(title).setContentTitle(title).setContentText(forString)
.setNumber(affectedTorrents.size())
.setLights(notificationSettings.getDesiredLedColour(), 600, 1000)
.setSound(notificationSettings.getSound()).setAutoCancel(true).setContentIntent(pi);
if (notificationSettings.shouldVibrate())
builder.setVibrate(notificationSettings.getDefaultVibratePattern());
// Add at most 5 lines with the affected torrents
InboxStyle inbox = new NotificationCompat.InboxStyle(builder);
if (affectedTorrents.size() < 6) {
for (Torrent affectedTorrent : affectedTorrents) {
inbox.addLine(affectedTorrent.getName());
}
} else {
for (int j = 0; j < 4; j++) {
inbox.addLine(affectedTorrents.get(j).getName());
}
inbox.addLine(getString(R.string.status_service_andothers, affectedTorrents.get(5).getName()));
}
notificationManager.notify(notifyBase + server.getOrder(), inbox.build());
}
}
private Boolean findLastDoneStat(JSONArray lastStats, Torrent torrent) {
for (int i = 0; i < lastStats.length(); i++) {
try {
if (lastStats.getJSONObject(i).getString("id").equals(torrent.getUniqueID()))
return lastStats.getJSONObject(i).getBoolean("done");
} catch (JSONException e) {
return null;
}
}
return null;
}
}

54
full/AndroidManifest.xml

@ -1,8 +1,8 @@ @@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.transdroid.full"
android:versionCode="2"
android:versionName="2.0-alpha2" >
android:versionCode="3"
android:versionName="2.0-alpha3" >
<uses-sdk
android:minSdkVersion="7"
@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
android:xlargeScreens="true" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
@ -26,10 +27,10 @@ @@ -26,10 +27,10 @@
<application
android:allowBackup="true"
android:hardwareAccelerated="true"
android:icon="@drawable/ic_launcher"
android:label="@string/system_name"
android:theme="@style/Theme.Sherlock"
android:hardwareAccelerated="true" >
android:theme="@style/Theme.Sherlock" >
<!-- Main activities -->
<activity
@ -57,8 +58,8 @@ @@ -57,8 +58,8 @@
<category android:name="android.intent.category.BROWSABLE" />
<data
android:mimeType="application/x-bittorrent"
android:host="*"
android:mimeType="application/x-bittorrent"
android:scheme="http" />
</intent-filter>
<intent-filter>
@ -68,8 +69,8 @@ @@ -68,8 +69,8 @@
<category android:name="android.intent.category.BROWSABLE" />
<data
android:pathPattern=".*\\.torrent"
android:host="*"
android:pathPattern=".*\\.torrent"
android:scheme="http" />
</intent-filter>
<intent-filter>
@ -79,8 +80,8 @@ @@ -79,8 +80,8 @@
<category android:name="android.intent.category.BROWSABLE" />
<data
android:mimeType="application/x-bittorrent"
android:host="*"
android:mimeType="application/x-bittorrent"
android:scheme="https" />
</intent-filter>
<intent-filter>
@ -90,8 +91,8 @@ @@ -90,8 +91,8 @@
<category android:name="android.intent.category.BROWSABLE" />
<data
android:pathPattern=".*\\.torrent"
android:host="*"
android:pathPattern=".*\\.torrent"
android:scheme="https" />
</intent-filter>
<intent-filter>
@ -101,8 +102,8 @@ @@ -101,8 +102,8 @@
<category android:name="android.intent.category.BROWSABLE" />
<data
android:mimeType="application/x-bittorrent"
android:host="*"
android:mimeType="application/x-bittorrent"
android:scheme="file" />
</intent-filter>
<intent-filter>
@ -112,8 +113,8 @@ @@ -112,8 +113,8 @@
<category android:name="android.intent.category.BROWSABLE" />
<data
android:pathPattern=".*\\.torrent"
android:host="*"
android:pathPattern=".*\\.torrent"
android:scheme="file" />
</intent-filter>
<intent-filter>
@ -123,8 +124,8 @@ @@ -123,8 +124,8 @@
<category android:name="android.intent.category.BROWSABLE" />
<data
android:mimeType="application/x-bittorrent"
android:host="*"
android:mimeType="application/x-bittorrent"
android:scheme="content" />
</intent-filter>
<intent-filter>
@ -134,8 +135,8 @@ @@ -134,8 +135,8 @@
<category android:name="android.intent.category.BROWSABLE" />
<data
android:pathPattern=".*\\.torrent"
android:host="*"
android:pathPattern=".*\\.torrent"
android:scheme="content" />
</intent-filter>
<intent-filter>
@ -146,6 +147,7 @@ @@ -146,6 +147,7 @@
<data android:scheme="magnet" />
</intent-filter>
<meta-data
android:name="android.app.default_searchable"
android:value="org.transdroid.core.gui.search.SearchActivity_" />
@ -170,8 +172,8 @@ @@ -170,8 +172,8 @@
android:name="org.transdroid.core.gui.search.SearchActivity_"
android:icon="@drawable/ic_launcher"
android:label="@string/search_torrentsearch"
android:theme="@style/TransdroidTheme"
android:launchMode="singleTask" >
android:launchMode="singleTask"
android:theme="@style/TransdroidTheme" >
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
@ -192,11 +194,12 @@ @@ -192,11 +194,12 @@
android:name="android.app.default_searchable"
android:value="org.transdroid.core.gui.search.SearchActivity_" />
</activity>
<provider
android:name="org.transdroid.core.gui.search.TorrentSearchHistoryProvider"
android:authorities="org.transdroid.core.gui.search.TorrentSearchHistoryProvider"
android:exported="false" />
<provider
android:name="org.transdroid.core.gui.search.TorrentSearchHistoryProvider"
android:authorities="org.transdroid.core.gui.search.TorrentSearchHistoryProvider"
android:exported="false" />
<!-- RSS -->
<activity
android:name="org.transdroid.core.gui.rss.RssfeedsActivity_"
@ -207,6 +210,19 @@ @@ -207,6 +210,19 @@
android:label="@string/rss_feeds"
android:theme="@style/TransdroidTheme" />
<!-- Background service -->
<service android:name="org.transdroid.core.service.ServerCheckerService_" />
<service android:name="org.transdroid.core.service.RssCheckerService_" />
<service android:name="org.transdroid.core.service.AppUpdateService_" />
<receiver android:name="org.transdroid.core.service.AlarmReceiver_" />
<receiver android:name="org.transdroid.core.service.BootReceiver_" >
<intent-filter>
<action
android:name="android.intent.action.BOOT_COMPLETED"
android:value="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
</application>
</manifest>

23
lite/AndroidManifest.xml

@ -1,8 +1,8 @@ @@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.transdroid.lite"
android:versionCode="2"
android:versionName="2.0-alpha2" >
android:versionCode="3"
android:versionName="2.0-alpha3" >
<uses-sdk
android:minSdkVersion="7"
@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
android:xlargeScreens="true" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
@ -26,10 +27,10 @@ @@ -26,10 +27,10 @@
<application
android:allowBackup="true"
android:hardwareAccelerated="true"
android:icon="@drawable/ic_launcher"
android:label="@string/system_name"
android:theme="@style/Theme.Sherlock"
android:hardwareAccelerated="true" >
android:theme="@style/Theme.Sherlock" >
<!-- Main activities -->
<activity
@ -153,6 +154,20 @@ @@ -153,6 +154,20 @@
<activity android:name="org.transdroid.core.gui.settings.NotificationSettingsActivity_" />
<activity android:name="org.transdroid.core.gui.settings.SystemSettingsActivity_" />
<activity android:name="org.transdroid.core.gui.navigation.DialogHelper_" />
<!-- Background service -->
<service android:name="org.transdroid.core.service.ServerCheckerService_" />
<service android:name="org.transdroid.core.service.RssCheckerService_" />
<service android:name="org.transdroid.core.service.AppUpdateService_" />
<receiver android:name="org.transdroid.core.service.AlarmReceiver_" />
<receiver android:name="org.transdroid.core.service.BootReceiver_" >
<intent-filter>
<action
android:name="android.intent.action.BOOT_COMPLETED"
android:value="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
</application>
</manifest>
Loading…
Cancel
Save