Browse Source

Added support for importing/exporting settings via QR code. Fixes #178.

pull/187/head
Eric Kok 10 years ago
parent
commit
219b2b71bc
  1. 30
      app/src/main/java/org/transdroid/core/app/settings/ApplicationSettings.java
  2. 91
      app/src/main/java/org/transdroid/core/app/settings/SettingsPersistence.java
  3. 4
      app/src/main/java/org/transdroid/core/gui/TorrentsActivity.java
  4. 66
      app/src/main/java/org/transdroid/core/gui/search/BarcodeHelper.java
  5. 230
      app/src/main/java/org/transdroid/core/gui/settings/SystemSettingsActivity.java
  6. 4
      app/src/main/res/values/changelog.xml
  7. 4
      app/src/main/res/values/strings.xml

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

@ -258,7 +258,7 @@ public class ApplicationSettings {
edit.putInt("header_defaultserver", --order); edit.putInt("header_defaultserver", --order);
} }
edit.commit(); edit.apply();
} }
@ -352,7 +352,7 @@ public class ApplicationSettings {
* @param order The key identifying the specific server * @param order The key identifying the specific server
*/ */
public void setLastUsedServerKey(int order) { public void setLastUsedServerKey(int order) {
prefs.edit().putInt("system_lastusedserver", order).commit(); prefs.edit().putInt("system_lastusedserver", order).apply();
} }
/** /**
@ -367,10 +367,10 @@ public class ApplicationSettings {
/** /**
* Registers some navigation filter as being the last used by the user * Registers some navigation filter as being the last used by the user
* @param server The navigation filter that the user last used in the interface * @param filter The navigation filter that the user last used in the interface
*/ */
public void setLastUsedNavigationFilter(NavigationFilter filter) { public void setLastUsedNavigationFilter(NavigationFilter filter) {
prefs.edit().putString("system_lastusedfilter", filter.getCode()).commit(); prefs.edit().putString("system_lastusedfilter", filter.getCode()).apply();
} }
/** /**
@ -432,7 +432,7 @@ public class ApplicationSettings {
edit.remove("websearch_name_" + max); edit.remove("websearch_name_" + max);
edit.remove("websearch_baseurl_" + max); edit.remove("websearch_baseurl_" + max);
edit.remove("websearch_cookies_" + max); edit.remove("websearch_cookies_" + max);
edit.commit(); edit.apply();
} }
@ -503,7 +503,7 @@ public class ApplicationSettings {
edit.remove("rssfeed_reqauth_" + max); edit.remove("rssfeed_reqauth_" + max);
edit.remove("rssfeed_alarmnew_" + max); edit.remove("rssfeed_alarmnew_" + max);
edit.remove("rssfeed_lastviewed_" + max); edit.remove("rssfeed_lastviewed_" + max);
edit.commit(); edit.apply();
} }
@ -522,7 +522,7 @@ public class ApplicationSettings {
Editor edit = prefs.edit(); Editor edit = prefs.edit();
edit.putLong("rssfeed_lastviewed_" + order, lastViewed.getTime()); edit.putLong("rssfeed_lastviewed_" + order, lastViewed.getTime());
edit.putString("rssfeed_lastvieweditemurl_" + order, lastViewedItemUrl); edit.putString("rssfeed_lastvieweditemurl_" + order, lastViewedItemUrl);
edit.commit(); edit.apply();
} }
/** /**
@ -531,8 +531,10 @@ public class ApplicationSettings {
* @param currentSortAscending The sort order direction that was last used * @param currentSortAscending The sort order direction that was last used
*/ */
public void setLastUsedSortOrder(TorrentsSortBy currentSortOrder, boolean currentSortAscending) { public void setLastUsedSortOrder(TorrentsSortBy currentSortOrder, boolean currentSortAscending) {
prefs.edit().putInt("system_lastusedsortorder", currentSortOrder.getCode()).commit(); Editor edit = prefs.edit();
prefs.edit().putBoolean("system_lastusedsortdirection", currentSortAscending).commit(); edit.putInt("system_lastusedsortorder", currentSortOrder.getCode());
edit.putBoolean("system_lastusedsortdirection", currentSortAscending);
edit.apply();
} }
/** /**
@ -631,10 +633,10 @@ public class ApplicationSettings {
/** /**
* Registers the unique key of some web search or in-app search site as being last used by the user * Registers the unique key of some web search or in-app search site as being last used by the user
* @param order The key identifying the specific server * @param site The site settings to register as being last used
*/ */
public void setLastUsedSearchSite(SearchSetting site) { public void setLastUsedSearchSite(SearchSetting site) {
prefs.edit().putString("header_setsearchsite", site.getKey()).commit(); prefs.edit().putString("header_setsearchsite", site.getKey()).apply();
} }
/** /**
@ -660,7 +662,7 @@ public class ApplicationSettings {
* @param lastStats A JSON array of JSON objects that each represent a single seen torrent * @param lastStats A JSON array of JSON objects that each represent a single seen torrent
*/ */
public void setServerLastStats(ServerSetting server, JSONArray lastStats) { public void setServerLastStats(ServerSetting server, JSONArray lastStats) {
prefs.edit().putString(server.getUniqueIdentifier(), lastStats.toString()).commit(); prefs.edit().putString(server.getUniqueIdentifier(), lastStats.toString()).apply();
} }
/** /**
@ -699,7 +701,7 @@ public class ApplicationSettings {
edit.putBoolean("widget_reverse_" + appWidgetId, settings.shouldReserveSort()); edit.putBoolean("widget_reverse_" + appWidgetId, settings.shouldReserveSort());
edit.putBoolean("widget_showstatus_" + appWidgetId, settings.shouldShowStatusView()); edit.putBoolean("widget_showstatus_" + appWidgetId, settings.shouldShowStatusView());
edit.putBoolean("widget_darktheme_" + appWidgetId, settings.shouldUseDarkTheme()); edit.putBoolean("widget_darktheme_" + appWidgetId, settings.shouldUseDarkTheme());
edit.commit(); edit.apply();
} }
/** /**
@ -714,7 +716,7 @@ public class ApplicationSettings {
edit.remove("widget_reverse_" + appWidgetId); edit.remove("widget_reverse_" + appWidgetId);
edit.remove("widget_showstatus_" + appWidgetId); edit.remove("widget_showstatus_" + appWidgetId);
edit.remove("widget_darktheme_" + appWidgetId); edit.remove("widget_darktheme_" + appWidgetId);
edit.commit(); edit.apply();
} }
} }

91
app/src/main/java/org/transdroid/core/app/settings/SettingsPersistence.java

@ -52,21 +52,37 @@ public class SettingsPersistence {
public static final String DEFAULT_SETTINGS_FILENAME = "/settings.json"; public static final String DEFAULT_SETTINGS_FILENAME = "/settings.json";
public static final File DEFAULT_SETTINGS_FILE = new File(DEFAULT_SETTINGS_DIR + DEFAULT_SETTINGS_FILENAME); public static final File DEFAULT_SETTINGS_FILE = new File(DEFAULT_SETTINGS_DIR + DEFAULT_SETTINGS_FILENAME);
/**
* Reads the server, web searches, RSS feed, background service and system settings from a JSON-encoded String, such as when read via a QR code.
* @param prefs The application-global preferences object to write settings to
* @param contents The JSON-encoded settings as raw String
* @throws JSONException Thrown when the file did not contain valid JSON content
*/
public void importSettingsAsString(SharedPreferences prefs, String contents) throws JSONException {
importSettings(prefs, new JSONObject(contents));
}
/** /**
* Synchronously reads the server, web searches, RSS feed, background service and system settings from a file in * Synchronously reads the server, web searches, RSS feed, background service and system settings from a file in
* JSON format. * JSON format.
* @param settingsFile The local file to write the settings to * @param prefs The application-global preferences object to write settings to
* @throws FileNotFoundException Thrown when the settings file doesn't exist or couln't be read * @param settingsFile The local file to read the settings from
* @throws FileNotFoundException Thrown when the settings file doesn't exist or couldn't be read
* @throws JSONException Thrown when the file did not contain valid JSON content * @throws JSONException Thrown when the file did not contain valid JSON content
*/ */
public void importSettings(SharedPreferences prefs, File settingsFile) throws FileNotFoundException, public void importSettingsFromFile(SharedPreferences prefs, File settingsFile) throws FileNotFoundException,
JSONException { JSONException {
Editor editor = prefs.edit();
// Read the settings file
String raw = HttpHelper.convertStreamToString(new FileInputStream(settingsFile)); String raw = HttpHelper.convertStreamToString(new FileInputStream(settingsFile));
JSONObject json = new JSONObject(raw); importSettings(prefs, new JSONObject(raw));
}
public void importSettings(SharedPreferences prefs, JSONObject json) throws JSONException {
Editor editor = prefs.edit();
// Import servers // Import servers
if (json.has("servers")) { if (json.has("servers")) {
@ -96,7 +112,7 @@ public class SettingsPersistence {
if (server.has("folder")) if (server.has("folder"))
editor.putString("server_folder_" + postfix, server.getString("folder")); editor.putString("server_folder_" + postfix, server.getString("folder"));
if (server.has("use_auth")) if (server.has("use_auth"))
editor.putBoolean("server_useauth_" + postfix, server.getBoolean("use_auth")); editor.putBoolean("server_disableauth_" + postfix, !server.getBoolean("use_auth"));
if (server.has("username")) if (server.has("username"))
editor.putString("server_user_" + postfix, server.getString("username")); editor.putString("server_user_" + postfix, server.getString("username"));
if (server.has("password")) if (server.has("password"))
@ -132,6 +148,8 @@ public class SettingsPersistence {
editor.putString("websearch_name_" + postfix, site.getString("name")); editor.putString("websearch_name_" + postfix, site.getString("name"));
if (site.has("url")) if (site.has("url"))
editor.putString("websearch_baseurl_" + postfix, site.getString("url")); editor.putString("websearch_baseurl_" + postfix, site.getString("url"));
if (site.has("cookies"))
editor.putString("websearch_cookies_" + postfix, site.getString("cookies"));
} }
} }
@ -149,6 +167,8 @@ public class SettingsPersistence {
editor.putString("rssfeed_url_" + postfix, feed.getString("url")); editor.putString("rssfeed_url_" + postfix, feed.getString("url"));
if (feed.has("needs_auth")) if (feed.has("needs_auth"))
editor.putBoolean("rssfeed_reqauth_" + postfix, feed.getBoolean("needs_auth")); editor.putBoolean("rssfeed_reqauth_" + postfix, feed.getBoolean("needs_auth"));
if (feed.has("new_item_alarm"))
editor.putBoolean("rssfeed_alarmnew_" + postfix, feed.getBoolean("new_item_alarm"));
if (feed.has("last_seen")) if (feed.has("last_seen"))
editor.putString("rssfeed_lastnew_" + postfix, feed.getString("last_seen")); editor.putString("rssfeed_lastnew_" + postfix, feed.getString("last_seen"));
@ -168,24 +188,54 @@ public class SettingsPersistence {
editor.putInt("notifications_ledcolour", json.getInt("alarm_ledcolour")); editor.putInt("notifications_ledcolour", json.getInt("alarm_ledcolour"));
if (json.has("alarm_adwnotifications")) if (json.has("alarm_adwnotifications"))
editor.putBoolean("notifications_adwnotify", json.getBoolean("alarm_adwnotifications")); editor.putBoolean("notifications_adwnotify", json.getBoolean("alarm_adwnotifications"));
if (json.has("system_dormantasinactive"))
editor.putBoolean("system_dormantasinactive", json.getBoolean("system_dormantasinactive"));
if (json.has("system_autorefresh"))
editor.putString("system_autorefresh", json.getString("system_autorefresh"));
if (json.has("system_checkupdates")) if (json.has("system_checkupdates"))
editor.putBoolean("system_checkupdates", json.getBoolean("system_checkupdates")); editor.putBoolean("system_checkupdates", json.getBoolean("system_checkupdates"));
if (json.has("system_usedarktheme")) if (json.has("system_usedarktheme"))
editor.putBoolean("system_usedarktheme", json.getBoolean("system_usedarktheme")); editor.putBoolean("system_usedarktheme", json.getBoolean("system_usedarktheme"));
editor.commit(); editor.apply();
}
/**
* Returns encoded server, web searches, RSS feed, background service and system settings as a JSON data object structure, serialized to a String.
* @param prefs The application-global preferences object to read settings from
* @throws JSONException Thrown when the JSON content could not be constructed properly
*/
public String exportSettingsAsString(SharedPreferences prefs) throws JSONException {
return exportSettings(prefs).toString();
} }
/** /**
* Synchronously writes the server, web searches, RSS feed, background service and system settings to a file in JSON * Synchronously writes the server, web searches, RSS feed, background service and system settings to a file in JSON
* format. * format.
* @param prefs The application-global preferences object to write settings to * @param prefs The application-global preferences object to read settings from
* @param settingsFile The local file to read the settings from * @param settingsFile The local file to read the settings from
* @throws JSONException Thrown when the JSON content could not be constructed properly * @throws JSONException Thrown when the JSON content could not be constructed properly
* @throws IOException Thrown when the settings file could not be created or written to * @throws IOException Thrown when the settings file could not be created or written to
*/ */
public void exportSettings(SharedPreferences prefs, File settingsFile) throws JSONException, IOException { public void exportSettingsToFile(SharedPreferences prefs, File settingsFile) throws JSONException, IOException {
JSONObject json = exportSettings(prefs);
// Serialise the JSON object to a file
if (settingsFile.exists()) {
settingsFile.delete();
}
settingsFile.getParentFile().mkdirs();
settingsFile.createNewFile();
FileWriter writer = new FileWriter(settingsFile);
writer.write(json.toString(2));
writer.flush();
writer.close();
}
private JSONObject exportSettings(SharedPreferences prefs) throws JSONException {
// Create a single JSON object that will contain all settings // Create a single JSON object that will contain all settings
JSONObject json = new JSONObject(); JSONObject json = new JSONObject();
@ -207,7 +257,7 @@ public class SettingsPersistence {
server.put("ssl_accept_all", prefs.getBoolean("server_ssltrustall_" + postfixi, false)); server.put("ssl_accept_all", prefs.getBoolean("server_ssltrustall_" + postfixi, false));
server.put("ssl_trust_key", prefs.getString("server_ssltrustkey_" + postfixi, null)); server.put("ssl_trust_key", prefs.getString("server_ssltrustkey_" + postfixi, null));
server.put("folder", prefs.getString("server_folder_" + postfixi, null)); server.put("folder", prefs.getString("server_folder_" + postfixi, null));
server.put("use_auth", prefs.getBoolean("server_useauth_" + postfixi, true)); server.put("use_auth", !prefs.getBoolean("server_disableauth_" + postfixi, false));
server.put("username", prefs.getString("server_user_" + postfixi, null)); server.put("username", prefs.getString("server_user_" + postfixi, null));
server.put("password", prefs.getString("server_pass_" + postfixi, null)); server.put("password", prefs.getString("server_pass_" + postfixi, null));
server.put("extra_password", prefs.getString("server_extrapass_" + postfixi, null)); server.put("extra_password", prefs.getString("server_extrapass_" + postfixi, null));
@ -234,6 +284,7 @@ public class SettingsPersistence {
JSONObject site = new JSONObject(); JSONObject site = new JSONObject();
site.put("name", prefs.getString("websearch_name_" + postfixj, null)); site.put("name", prefs.getString("websearch_name_" + postfixj, null));
site.put("url", prefs.getString("websearch_baseurl_" + postfixj, null)); site.put("url", prefs.getString("websearch_baseurl_" + postfixj, null));
site.put("cookies", prefs.getString("websearch_cookies_" + postfixj, null));
sites.put(site); sites.put(site);
j++; j++;
@ -251,6 +302,7 @@ public class SettingsPersistence {
feed.put("name", prefs.getString("rssfeed_name_" + postfixk, null)); feed.put("name", prefs.getString("rssfeed_name_" + postfixk, null));
feed.put("url", prefs.getString("rssfeed_url_" + postfixk, null)); feed.put("url", prefs.getString("rssfeed_url_" + postfixk, null));
feed.put("needs_auth", prefs.getBoolean("rssfeed_reqauth_" + postfixk, false)); feed.put("needs_auth", prefs.getBoolean("rssfeed_reqauth_" + postfixk, false));
feed.put("new_item_alarm", prefs.getBoolean("rssfeed_alarmnew_" + postfixk, false));
feed.put("last_seen", prefs.getString("rssfeed_lastnew_" + postfixk, null)); feed.put("last_seen", prefs.getString("rssfeed_lastnew_" + postfixk, null));
feeds.put(feed); feeds.put(feed);
@ -266,19 +318,12 @@ public class SettingsPersistence {
json.put("alarm_vibrate", prefs.getBoolean("notifications_vibrate", false)); json.put("alarm_vibrate", prefs.getBoolean("notifications_vibrate", false));
json.put("alarm_ledcolour", prefs.getInt("notifications_ledcolour", -1)); json.put("alarm_ledcolour", prefs.getInt("notifications_ledcolour", -1));
json.put("alarm_adwnotifications", prefs.getBoolean("notifications_adwnotify", false)); json.put("alarm_adwnotifications", prefs.getBoolean("notifications_adwnotify", false));
json.put("system_checkupdates", prefs.getBoolean("system_checkupdates", true)); json.put("system_dormantasinactive", prefs.getBoolean("system_dormantasinactive", false));
json.put("system_autorefresh", prefs.getString("system_autorefresh", "0"));
json.put("system_usedarktheme", prefs.getBoolean("system_usedarktheme", false)); json.put("system_usedarktheme", prefs.getBoolean("system_usedarktheme", false));
json.put("system_checkupdates", prefs.getBoolean("system_checkupdates", true));
// Serialise the JSON object to a file return json;
if (settingsFile.exists()) {
settingsFile.delete();
}
settingsFile.getParentFile().mkdirs();
settingsFile.createNewFile();
FileWriter writer = new FileWriter(settingsFile);
writer.write(json.toString(2));
writer.flush();
writer.close();
} }

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

@ -712,11 +712,11 @@ public class TorrentsActivity extends Activity implements OnNavigationListener,
@OptionsItem(resName = "action_add_frombarcode") @OptionsItem(resName = "action_add_frombarcode")
protected void startBarcodeScanner() { protected void startBarcodeScanner() {
BarcodeHelper.startBarcodeScanner(this); BarcodeHelper.startBarcodeScanner(this, BarcodeHelper.ACTIVITY_BARCODE_ADDTORRENT);
} }
@Background @Background
@OnActivityResult(BarcodeHelper.ACTIVITY_BARCODE) @OnActivityResult(BarcodeHelper.ACTIVITY_BARCODE_ADDTORRENT)
public void onBarcodeScanned(int resultCode, Intent data) { public void onBarcodeScanned(int resultCode, Intent data) {
// We receive from the helper either a URL (as string) or a query we can start a search for // We receive from the helper either a URL (as string) or a query we can start a search for
String query = BarcodeHelper.handleScanResult(resultCode, data); String query = BarcodeHelper.handleScanResult(resultCode, data);

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

@ -16,9 +16,6 @@
*/ */
package org.transdroid.core.gui.search; package org.transdroid.core.gui.search;
import org.transdroid.R;
import org.transdroid.core.app.search.GoogleWebSearchBarcodeResolver;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
@ -29,23 +26,53 @@ import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.text.TextUtils; import android.text.TextUtils;
import org.transdroid.R;
import org.transdroid.core.app.search.GoogleWebSearchBarcodeResolver;
public class BarcodeHelper { public class BarcodeHelper {
public static final int ACTIVITY_BARCODE = 0x0000c0de; // A 'random' ID to identify scan intents public static final int ACTIVITY_BARCODE_ADDTORRENT = 0x0000c0de;
// A 'random' ID to identify torrent adding scan intents
public static final int ACTIVITY_BARCODE_QRSETTINGS = 0x0000c0df;
// A 'random' ID to identify QR-encoded settings scan intents
public static final Uri SCANNER_MARKET_URI = Uri.parse("market://search?q=pname:com.google.zxing.client.android"); public static final Uri SCANNER_MARKET_URI = Uri.parse("market://search?q=pname:com.google.zxing.client.android");
/** /**
* Call this to start a bar code scanner intent. The calling activity will receive an Intent result with ID * Call this to start a bar code scanner intent. The calling activity will receive an Intent result with ID {@link
* {@link #ACTIVITY_BARCODE}. From there {@link #handleScanResult(int, Intent)} should be called to parse the result * #ACTIVITY_BARCODE_ADDTORRENT} or {@link #ACTIVITY_BARCODE_QRSETTINGS}. From there {@link #handleScanResult(int,
* into a search query. * Intent)} can be called to parse the result into a search query, in case of {@link #ACTIVITY_BARCODE_ADDTORRENT}
* scans.
* @param activity The calling activity, to which the result is returned or a dialog is bound that asks to install
* the bar code scanner
* @param requestCode {@link #ACTIVITY_BARCODE_ADDTORRENT} or {@link #ACTIVITY_BARCODE_QRSETTINGS
*/
public static void startBarcodeScanner(final Activity activity, int requestCode) {
// Start a bar code scanner that can handle the SCAN intent (specifically ZXing)
startBarcodeIntent(activity, new Intent("com.google.zxing.client.android.SCAN"), requestCode);
}
/**
* Call this to share content encoded in a QR code, specially used to share settings. The calling activity will
* receive an Intent result with ID {@link #ACTIVITY_BARCODE_QRSETTINGS}. From there the returned intent will
* contain the data as SCAN_RESULT String extra.
* @param activity The calling activity, to which the result is returned or a dialog is bound that asks to install * @param activity The calling activity, to which the result is returned or a dialog is bound that asks to install
* the bar code scanner * the bar code scanner
* @param content The content to share, that is, the raw data (Transdroid settings encoded as JSON data structure)
* to share as QR code
*/ */
public static void shareContentBarcode(final Activity activity, final String content) {
// Start a bar code encoded that can handle the ENCODE intent (specifically ZXing)
Intent encodeIntent = new Intent("com.google.zxing.client.android.ENCODE");
encodeIntent.putExtra("ENCODE_TYPE", "TEXT_TYPE");
encodeIntent.putExtra("ENCODE_DATA", content);
encodeIntent.putExtra("ENCODE_SHOW_CONTENTS", false);
startBarcodeIntent(activity, encodeIntent, -1);
}
@SuppressLint("ValidFragment") @SuppressLint("ValidFragment")
public static void startBarcodeScanner(final Activity activity) { private static void startBarcodeIntent(final Activity activity, final Intent intent, int requestCode) {
try { try {
// Start a bar code scanner that can handle the SCAN intent (specifically ZXing) activity.startActivityForResult(intent, requestCode);
activity.startActivityForResult(new Intent("com.google.zxing.client.android.SCAN"), ACTIVITY_BARCODE);
} catch (Exception e) { } catch (Exception e) {
// Can't start the bar code scanner, for example with a SecurityException or when ZXing is not present // Can't start the bar code scanner, for example with a SecurityException or when ZXing is not present
new DialogFragment() { new DialogFragment() {
@ -55,22 +82,26 @@ public class BarcodeHelper {
.setPositiveButton(android.R.string.yes, new OnClickListener() { .setPositiveButton(android.R.string.yes, new OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
if (activity != null) if (activity != null) {
activity.startActivity(new Intent(Intent.ACTION_VIEW, SCANNER_MARKET_URI)); activity.startActivity(new Intent(Intent.ACTION_VIEW, SCANNER_MARKET_URI));
}
} }
}).setNegativeButton(android.R.string.no, null).create(); }).setNegativeButton(android.R.string.no, null).create();
}; }
;
}.show(activity.getFragmentManager(), "installscanner"); }.show(activity.getFragmentManager(), "installscanner");
} }
} }
/** /**
* The activity that called {@link #startBarcodeScanner(Activity)} should call this after the scan * The activity that called {@link #startBarcodeScanner(android.app.Activity, int)} with {@link
* result was returned. This will parse the scan data and return a query search query appropriate to the bar code. * #ACTIVITY_BARCODE_ADDTORRENT} should call this after the scan result was returned. This will parse the scan data
* and return a query search query appropriate to the bar code.
* @param resultCode The raw result code as returned by the bar code scanner * @param resultCode The raw result code as returned by the bar code scanner
* @param data The raw data as returned from the bar code scanner * @param data The raw data as returned from the bar code scanner
* @return A String that can be used as new search query, or null if the bar code could not be scanned or no query * @return A String that can be used as new search query, or null if the bar code could not be scanned or no query
* can be constructed for it * can be constructed for it
*/ */
public static String handleScanResult(int resultCode, Intent data) { public static String handleScanResult(int resultCode, Intent data) {
String contents = data.getStringExtra("SCAN_RESULT"); String contents = data.getStringExtra("SCAN_RESULT");
@ -79,8 +110,9 @@ public class BarcodeHelper {
// Scanned barcode was a QR code: return the contents directly // Scanned barcode was a QR code: return the contents directly
return contents; return contents;
} else { } else {
if (TextUtils.isEmpty(contents)) if (TextUtils.isEmpty(contents)) {
return null; return null;
}
// Get a meaningful search query based on a Google Search product lookup // Get a meaningful search query based on a Google Search product lookup
return GoogleWebSearchBarcodeResolver.resolveBarcode(contents); return GoogleWebSearchBarcodeResolver.resolveBarcode(contents);
} }

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

@ -16,21 +16,6 @@
*/ */
package org.transdroid.core.gui.settings; package org.transdroid.core.gui.settings;
import java.io.FileNotFoundException;
import java.io.IOException;
import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.EActivity;
import org.androidannotations.annotations.OptionsItem;
import org.json.JSONException;
import org.transdroid.R;
import org.transdroid.core.app.settings.ApplicationSettings;
import org.transdroid.core.app.settings.SettingsPersistence;
import org.transdroid.core.gui.log.ErrorLogSender;
import org.transdroid.core.gui.navigation.NavigationHelper;
import org.transdroid.core.gui.search.SearchHistoryProvider;
import org.transdroid.core.service.BootReceiver;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.Dialog; import android.app.Dialog;
@ -45,14 +30,48 @@ import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener; import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceActivity; import android.preference.PreferenceActivity;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.text.TextUtils;
import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.EActivity;
import org.androidannotations.annotations.OnActivityResult;
import org.androidannotations.annotations.OptionsItem;
import org.json.JSONException;
import org.transdroid.R;
import org.transdroid.core.app.settings.ApplicationSettings;
import org.transdroid.core.app.settings.SettingsPersistence;
import org.transdroid.core.gui.log.ErrorLogSender;
import org.transdroid.core.gui.navigation.NavigationHelper;
import org.transdroid.core.gui.search.BarcodeHelper;
import org.transdroid.core.gui.search.SearchHistoryProvider;
import org.transdroid.core.service.BootReceiver;
import java.io.FileNotFoundException;
import java.io.IOException;
import de.keyboardsurfer.android.widget.crouton.Crouton; import de.keyboardsurfer.android.widget.crouton.Crouton;
@EActivity @EActivity
public class SystemSettingsActivity extends PreferenceActivity { public class SystemSettingsActivity extends PreferenceActivity {
protected static final int DIALOG_IMPORTSETTINGS = 0; protected static final int DIALOG_IMPORTSETTINGS = 0;
private OnPreferenceClickListener onImportSettingsClick = new OnPreferenceClickListener() {
@SuppressWarnings("deprecation")
@Override
public boolean onPreferenceClick(Preference preference) {
showDialog(DIALOG_IMPORTSETTINGS);
return true;
}
};
protected static final int DIALOG_EXPORTSETTINGS = 1; protected static final int DIALOG_EXPORTSETTINGS = 1;
private OnPreferenceClickListener onExportSettingsClick = new OnPreferenceClickListener() {
@SuppressWarnings("deprecation")
@Override
public boolean onPreferenceClick(Preference preference) {
showDialog(DIALOG_EXPORTSETTINGS);
return true;
}
};
@Bean @Bean
protected NavigationHelper navigationHelper; protected NavigationHelper navigationHelper;
@Bean @Bean
@ -61,6 +80,80 @@ public class SystemSettingsActivity extends PreferenceActivity {
protected ErrorLogSender errorLogSender; protected ErrorLogSender errorLogSender;
@Bean @Bean
protected SettingsPersistence settingsPersistence; protected SettingsPersistence settingsPersistence;
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 onClearSearchClick = new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
SearchHistoryProvider.clearHistory(getApplicationContext());
Crouton.showText(SystemSettingsActivity.this, R.string.pref_clearsearch_success,
NavigationHelper.CROUTON_INFO_STYLE);
return true;
}
};
private OnClickListener importSettingsFromFile = new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
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);
} catch (FileNotFoundException e) {
Crouton.showText(SystemSettingsActivity.this, R.string.error_file_not_found,
NavigationHelper.CROUTON_ERROR_STYLE);
} catch (JSONException e) {
Crouton.showText(SystemSettingsActivity.this,
getString(R.string.error_no_valid_settings_file, getString(R.string.app_name)),
NavigationHelper.CROUTON_ERROR_STYLE);
}
}
};
private OnClickListener importSettingsFromQr = new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
BarcodeHelper.startBarcodeScanner(SystemSettingsActivity.this, BarcodeHelper.ACTIVITY_BARCODE_QRSETTINGS);
}
};
private OnClickListener exportSettingsToFile = new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
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);
}
}
};
private OnClickListener exportSettingsToQr = new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(SystemSettingsActivity.this);
try {
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);
}
}
};
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@Override @Override
@ -89,108 +182,53 @@ public class SystemSettingsActivity extends PreferenceActivity {
MainSettingsActivity_.intent(this).flags(Intent.FLAG_ACTIVITY_CLEAR_TOP).start(); MainSettingsActivity_.intent(this).flags(Intent.FLAG_ACTIVITY_CLEAR_TOP).start();
} }
private OnPreferenceClickListener onCheckUpdatesClick = new OnPreferenceClickListener() { @OnActivityResult(BarcodeHelper.ACTIVITY_BARCODE_QRSETTINGS)
@Override public void onQrCodeScanned(int resultCode, Intent data) {
public boolean onPreferenceClick(Preference preference) { // We should have received Intent extras with the QR-decoded data representing Transdroid settings
if (((CheckBoxPreference) preference).isChecked()) SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(SystemSettingsActivity.this);
BootReceiver.startAppUpdatesService(getApplicationContext()); String contents = data.getStringExtra("SCAN_RESULT");
else String formatName = data.getStringExtra("SCAN_RESULT_FORMAT");
BootReceiver.cancelAppUpdates(getApplicationContext()); if (formatName != null && formatName.equals("QR_CODE") && !TextUtils.isEmpty(contents)) {
return true; try {
} settingsPersistence.importSettingsAsString(prefs, contents);
}; Crouton.showText(SystemSettingsActivity.this, R.string.pref_import_success,
NavigationHelper.CROUTON_INFO_STYLE);
private OnPreferenceClickListener onImportSettingsClick = new OnPreferenceClickListener() { } catch (JSONException e) {
@SuppressWarnings("deprecation") Crouton.showText(SystemSettingsActivity.this, R.string.error_file_not_found,
@Override NavigationHelper.CROUTON_ERROR_STYLE);
public boolean onPreferenceClick(Preference preference) { }
showDialog(DIALOG_IMPORTSETTINGS);
return true;
}
};
private OnPreferenceClickListener onExportSettingsClick = new OnPreferenceClickListener() {
@SuppressWarnings("deprecation")
@Override
public boolean onPreferenceClick(Preference preference) {
showDialog(DIALOG_EXPORTSETTINGS);
return true;
}
};
private OnPreferenceClickListener onClearSearchClick = new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
SearchHistoryProvider.clearHistory(getApplicationContext());
Crouton.showText(SystemSettingsActivity.this, R.string.pref_clearsearch_success,
NavigationHelper.CROUTON_INFO_STYLE);
return true;
} }
}; }
@SuppressWarnings("deprecation")
protected Dialog onCreateDialog(int id) { protected Dialog onCreateDialog(int id) {
switch (id) { switch (id) {
case DIALOG_IMPORTSETTINGS: case DIALOG_IMPORTSETTINGS:
// @formatter:off // @formatter:off
return new AlertDialog.Builder(this) return new AlertDialog.Builder(this)
.setMessage( .setMessage(
getString( getString(
R.string.pref_import_dialog, R.string.pref_import_dialog,
getString(R.string.app_name), getString(R.string.app_name),
SettingsPersistence.DEFAULT_SETTINGS_FILE.toString())) SettingsPersistence.DEFAULT_SETTINGS_FILE.toString()))
.setPositiveButton(android.R.string.ok, importSettings) .setPositiveButton(R.string.pref_import_fromfile, importSettingsFromFile)
.setNeutralButton(R.string.pref_import_fromqr, importSettingsFromQr)
.setNegativeButton(android.R.string.cancel, null).create(); .setNegativeButton(android.R.string.cancel, null).create();
// @formatter:on // @formatter:on
case DIALOG_EXPORTSETTINGS: case DIALOG_EXPORTSETTINGS:
// @formatter:off // @formatter:off
return new AlertDialog.Builder(this) return new AlertDialog.Builder(this)
.setMessage( .setMessage(
getString( getString(
R.string.pref_export_dialog, R.string.pref_export_dialog,
getString(R.string.app_name), getString(R.string.app_name),
SettingsPersistence.DEFAULT_SETTINGS_FILE.toString())) SettingsPersistence.DEFAULT_SETTINGS_FILE.toString()))
.setPositiveButton(android.R.string.ok, exportSettings) .setPositiveButton(R.string.pref_export_tofile, exportSettingsToFile)
.setNeutralButton(R.string.pref_export_toqr, exportSettingsToQr)
.setNegativeButton(android.R.string.cancel, null).create(); .setNegativeButton(android.R.string.cancel, null).create();
// @formatter:on // @formatter:on
} }
return null; return null;
} }
private OnClickListener importSettings = new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(SystemSettingsActivity.this);
try {
settingsPersistence.importSettings(prefs, SettingsPersistence.DEFAULT_SETTINGS_FILE);
Crouton.showText(SystemSettingsActivity.this, R.string.pref_import_success,
NavigationHelper.CROUTON_INFO_STYLE);
} catch (FileNotFoundException e) {
Crouton.showText(SystemSettingsActivity.this, R.string.error_file_not_found,
NavigationHelper.CROUTON_ERROR_STYLE);
} catch (JSONException e) {
Crouton.showText(SystemSettingsActivity.this,
getString(R.string.error_no_valid_settings_file, getString(R.string.app_name)),
NavigationHelper.CROUTON_ERROR_STYLE);
}
}
};
private OnClickListener exportSettings = new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(SystemSettingsActivity.this);
try {
settingsPersistence.exportSettings(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);
}
}
};
} }

4
app/src/main/res/values/changelog.xml

@ -21,8 +21,10 @@ Transdroid 2.3.0\n
- Aria2 support\n - Aria2 support\n
- Improved SSL support with TLS 1.1/1.2 and SNI hostname verification\n - Improved SSL support with TLS 1.1/1.2 and SNI hostname verification\n
- Fixed server checker when one is unavailable\n - Fixed server checker when one is unavailable\n
- Fixed Deluge magnet links from Chrome\n - Fixed magnet links from Chrome\n
- Enable RSS notifications per feed\n - Enable RSS notifications per feed\n
- Improved bug logging\n
- Import/export settings using QR code\n
\n \n
Transdroid 2.2.0\n Transdroid 2.2.0\n
- Dropped legacy Android support; Android 4.0.3 is the new minimum\n - Dropped legacy Android support; Android 4.0.3 is the new minimum\n

4
app/src/main/res/values/strings.xml

@ -321,9 +321,13 @@
<string name="pref_clearsearch_success">Search history is cleared</string> <string name="pref_clearsearch_success">Search history is cleared</string>
<string name="pref_import">Import settings</string> <string name="pref_import">Import settings</string>
<string name="pref_import_dialog">%1$s will try to import server, web search, RSS and system settings from: %2$s</string> <string name="pref_import_dialog">%1$s will try to import server, web search, RSS and system settings from: %2$s</string>
<string name="pref_import_fromfile">Use file</string>
<string name="pref_import_fromqr">Use QR code</string>
<string name="pref_import_success">Settings successfully imported</string> <string name="pref_import_success">Settings successfully imported</string>
<string name="pref_export">Export settings</string> <string name="pref_export">Export settings</string>
<string name="pref_export_dialog">%1$s will export server (including passwords), web search, RSS and system settings to the following plain text JSON file: %2$s</string> <string name="pref_export_dialog">%1$s will export server (including passwords), web search, RSS and system settings to the following plain text JSON file: %2$s</string>
<string name="pref_export_tofile">To file</string>
<string name="pref_export_toqr">To QR code</string>
<string name="pref_export_success">Settings successfully exported</string> <string name="pref_export_success">Settings successfully exported</string>
<string name="pref_help">Transdroid help</string> <string name="pref_help">Transdroid help</string>

Loading…
Cancel
Save