/** * Copyright (C) 2013 by Raphael Michel under the MIT license: * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and * associated documentation files (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, publish, distribute, * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all copies or * substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package de.geeksfactory.opacclient; import android.app.Activity; import android.app.Application; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.AssetManager; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; import android.os.Bundle; import android.preference.PreferenceManager; import android.support.annotation.NonNull; import android.support.v4.app.ActivityCompat; import android.util.Log; import com.commonsware.cwac.wakeful.WakefulIntentService; import org.acra.ACRA; import org.acra.ACRAConfiguration; import org.acra.annotation.ReportsCrashes; import org.joda.time.DateTime; import org.json.JSONException; import org.json.JSONObject; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import de.geeksfactory.opacclient.apis.OpacApi; import de.geeksfactory.opacclient.frontend.AccountListActivity; import de.geeksfactory.opacclient.frontend.LibraryListActivity; import de.geeksfactory.opacclient.frontend.MainActivity; import de.geeksfactory.opacclient.frontend.MainPreferenceActivity; import de.geeksfactory.opacclient.frontend.SearchResultListActivity; import de.geeksfactory.opacclient.i18n.AndroidStringProvider; import de.geeksfactory.opacclient.networking.AndroidHttpClientFactory; import de.geeksfactory.opacclient.objects.Account; import de.geeksfactory.opacclient.objects.Library; import de.geeksfactory.opacclient.reminder.SyncAccountAlarmListener; import de.geeksfactory.opacclient.searchfields.SearchField; import de.geeksfactory.opacclient.searchfields.SearchQuery; import de.geeksfactory.opacclient.storage.AccountDataSource; import de.geeksfactory.opacclient.storage.PreferenceDataSource; import de.geeksfactory.opacclient.storage.StarContentProvider; import de.geeksfactory.opacclient.utils.DebugTools; import de.geeksfactory.opacclient.utils.ErrorReporter; import de.geeksfactory.opacclient.utils.Utils; import de.geeksfactory.opacclient.webservice.LibraryConfigUpdateService; import de.geeksfactory.opacclient.webservice.UpdateHandler; import de.geeksfactory.opacclient.webservice.WebserviceReportHandler; @ReportsCrashes(mailTo = "info@opacapp.de", mode = org.acra.ReportingInteractionMode.NOTIFICATION, sendReportsInDevMode = false, resToastText = R.string.crash_toast_text) public class OpacClient extends Application { public static final String PREF_SELECTED_ACCOUNT = "selectedAccount"; public static final String PREF_HOME_BRANCH_PREFIX = "homeBranch_"; public static final String ASSETS_BIBSDIR = "bibs"; public static int NOTIF_ID = 1; public static int BROADCAST_REMINDER = 2; public static Context context; public static String versionName = "unknown"; private static OpacClient instance; public final boolean SLIDING_MENU = true; private final Uri STAR_PROVIDER_STAR_URI = StarContentProvider.STAR_URI; protected Account account; protected OpacApi api; protected Library library; protected String currentLang; private SharedPreferences sp; public OpacClient() { super(); instance = this; } public static Context getEmergencyContext() { return instance.getApplicationContext(); } public static Bundle queryToBundle(List<SearchQuery> query) { if (query == null) { return null; } Bundle b = new Bundle(); for (SearchQuery q : query) { try { b.putString(q.getSearchField().toJSON().toString(), q.getValue()); } catch (JSONException e) { e.printStackTrace(); } } return b; } // public Class getSearchResultsActivityClass() { // return SearchResultsActivity.class; // } public static List<SearchQuery> bundleToQuery(Bundle bundle) { if (bundle == null) { return null; } List<SearchQuery> query = new ArrayList<>(); for (String e : bundle.keySet()) { try { query.add(new SearchQuery(SearchField .fromJSON(new JSONObject(e)), bundle.getString(e))); } catch (JSONException e1) { e1.printStackTrace(); } } return query; } public static Bundle mapToBundle(Map<String, String> map) { if (map == null) { return null; } Bundle b = new Bundle(); for (Entry<String, String> e : map.entrySet()) { b.putString(e.getKey(), e.getValue()); } return b; } public static Map<String, String> bundleToMap(Bundle bundle) { if (bundle == null) { return null; } Map<String, String> map = new HashMap<>(); for (String e : bundle.keySet()) { map.put(e, (String) bundle.get(e)); } return map; } public Uri getStarProviderStarUri() { return STAR_PROVIDER_STAR_URI; } public void addFirstAccount(Activity activity) { Intent intent = new Intent(activity, LibraryListActivity.class); intent.putExtra(LibraryListActivity.EXTRA_WELCOME, true); activity.startActivity(intent); activity.finish(); } public void startSearch(Activity caller, List<SearchQuery> query) { startSearch(caller, query, null); } public void startSearch(Activity caller, List<SearchQuery> query, Bundle bundle) { Intent myIntent = new Intent(caller, SearchResultListActivity.class); myIntent.putExtra("query", queryToBundle(query)); ActivityCompat.startActivity(caller, myIntent, bundle); } public void startVolumeSearch(Activity caller, Map<String, String> query) { Intent myIntent = new Intent(caller, SearchResultListActivity.class); myIntent.putExtra("volumeQuery", mapToBundle(query)); caller.startActivity(myIntent); } public boolean isOnline() { ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = connMgr.getActiveNetworkInfo(); return (networkInfo != null && networkInfo.isConnected()); } public OpacApi getNewApi(Library lib) throws LibraryRemovedException { if (!lib.isActive()) throw new LibraryRemovedException(); currentLang = getResources().getConfiguration().locale.getLanguage(); return OpacApiFactory .create(lib, new AndroidStringProvider(), new AndroidHttpClientFactory(), currentLang, new WebserviceReportHandler()); } private OpacApi initApi(Library lib) throws LibraryRemovedException { api = getNewApi(lib); return api; } public void resetCache() { account = null; api = null; library = null; } public OpacApi getApi() throws LibraryRemovedException { if (account != null && api != null) { if (sp.getLong(PREF_SELECTED_ACCOUNT, 0) == account.getId() && getResources().getConfiguration().locale.getLanguage() .equals(currentLang)) { return api; } } api = initApi(getLibrary()); return api; } public Account getAccount() { if (account != null) { if (sp.getLong(PREF_SELECTED_ACCOUNT, 0) == account.getId()) { return account; } } AccountDataSource data = new AccountDataSource(this); account = data.getAccount(sp.getLong(PREF_SELECTED_ACCOUNT, 0)); return account; } public void setAccount(long id) { sp.edit().putLong(OpacClient.PREF_SELECTED_ACCOUNT, id).commit(); resetCache(); if (getLibrary() != null && !BuildConfig.DEBUG) { ACRA.getErrorReporter().putCustomData("library", getLibrary().getIdent()); } } public Library getLibrary(String ident) throws IOException, JSONException { String filename = ident + ".json"; String json; File filesDir = getLibrariesDir(); PreferenceDataSource prefs = getPreferenceDataSource(); if (fileExists(filesDir, filename) && ( !prefs.hasBundledConfiguration() || prefs.getLastLibraryConfigUpdateVersion() == BuildConfig.VERSION_CODE)) { // only use files if they were downloaded using the current app version json = Utils.readStreamToString(openFile(filesDir, filename)); } else { json = Utils.readStreamToString(getAssets().open(ASSETS_BIBSDIR + "/" + filename)); } return Library.fromJSON(ident, new JSONObject(json)); } @NonNull PreferenceDataSource getPreferenceDataSource() { return new PreferenceDataSource(this); } InputStream openFile(File filesDir, String filename) throws FileNotFoundException { File file = new File(filesDir, filename); return new FileInputStream(file); } boolean fileExists(File filesDir, String filename) { File file = new File(filesDir, filename); return file.exists(); } @NonNull protected File getFile(File filesDir, String filename) { return new File(filesDir, filename); } @NonNull protected File getLibrariesDir() { File filesDir = new File(getFilesDir(), LibraryConfigUpdateService.LIBRARIES_DIR); filesDir.mkdirs(); return filesDir; } public Library getLibrary() { if (getAccount() == null) { return null; } if (account != null && library != null) { if (sp.getLong(PREF_SELECTED_ACCOUNT, 0) == account.getId()) { return library; } } try { library = getLibrary(getAccount().getLibrary()); } catch (IOException e) { e.printStackTrace(); } catch (JSONException e) { ErrorReporter.handleException(e); } return library; } @SuppressWarnings("UnusedDeclaration") // ProgressCallback should not be required public List<Library> getLibraries() throws IOException { return getLibraries(null); } public List<Library> getLibraries(ProgressCallback callback) throws IOException { AssetManager assets = getAssets(); String[] files = assets.list(ASSETS_BIBSDIR); String[] additionalFiles = getLibrariesDir().list(); Set<String> allFiles = new HashSet<>(); Collections.addAll(allFiles, files); Collections.addAll(allFiles, additionalFiles); int num = allFiles.size(); List<Library> libs = new ArrayList<>(); int i = 0; for (String file : allFiles) { try { Library lib = getLibrary(file.substring(0, file.length() - ".json".length())); if ((!lib.getApi().equals("test") || BuildConfig.DEBUG) && lib.isActive()) { libs.add(lib); } } catch (JSONException e) { Log.w("JSON library files", "Failed parsing library " + file); e.printStackTrace(); } if (callback != null && i % 10 == 0 && i > 0) { // reporting progress for every 10 loaded files should be enough callback.publishProgress(((double) i) / num); } i++; } return libs; } public void toPrefs(Activity activity) { Intent intent = new Intent(activity, MainPreferenceActivity.class); activity.startActivity(intent); } @Override public void onCreate() { super.onCreate(); sp = PreferenceManager.getDefaultSharedPreferences(this); if (!BuildConfig.DEBUG) { ACRAConfiguration config = ACRA.getNewDefaultConfig(this); config.setResToastText(R.string.crash_toast_text); config.setResDialogText(R.string.crash_dialog_text); config.setResToastText(R.string.crash_toast_text); config.setResNotifTickerText(R.string.crash_notif_ticker_text); config.setResNotifTitle(R.string.crash_notif_title); config.setResNotifText(R.string.crash_notif_text); config.setResNotifIcon(android.R.drawable.stat_notify_error); config.setResDialogText(R.string.crash_dialog_text); ACRA.init(this, config); if (getLibrary() != null) { ACRA.getErrorReporter().putCustomData("library", getLibrary().getIdent()); } DateTime lastUpdate = new PreferenceDataSource(getApplicationContext()) .getLastLibraryConfigUpdate(); ACRA.getErrorReporter().putCustomData("data_version", lastUpdate != null ? lastUpdate.toString() : "null"); } DebugTools.init(this); OpacClient.context = getApplicationContext(); try { OpacClient.versionName = getPackageManager().getPackageInfo( getPackageName(), 0).versionName; } catch (NameNotFoundException e) { e.printStackTrace(); } // Schedule alarms WakefulIntentService.scheduleAlarms(new SyncAccountAlarmListener(), this); } public boolean getSlidingMenuEnabled() { return SLIDING_MENU; } public Class<?> getMainActivity() { return MainActivity.class; } public void openAccountList(Activity ctx) { Intent intent = new Intent(ctx, AccountListActivity.class); ctx.startActivity(intent); } public interface ProgressCallback { public void publishProgress(double progress); } public static class LibraryRemovedException extends Exception { } public UpdateHandler getUpdateHandler() { return new UpdateHandler(); } }