package com.door43.translationstudio; import android.app.Activity; import android.app.AlertDialog; import android.app.Application; import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.Environment; import android.preference.PreferenceManager; import android.support.multidex.MultiDex; import android.text.TextUtils; import android.view.Gravity; import android.view.View; import android.widget.Toast; import com.door43.tools.reporting.GlobalExceptionHandler; import com.door43.tools.reporting.Logger; import com.door43.translationstudio.dialogs.CustomAlertDialog; import com.door43.translationstudio.service.BackupService; import com.door43.util.DummyDialogListener; import com.jcraft.jsch.JSch; import com.jcraft.jsch.KeyPair; import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; /** * Custom application class so we can effectively handle state accross activities and other classes * TODO: we are slowly stripping everything out of this class and placing it into the AppContext class */ public class MainApplication extends Application { private Activity mCurrentActivity = null; private Toast mToast = null; // private ProjectManager mProjectManager; private ProgressDialog mProgressDialog; public static final String PREFERENCES_TAG = "com.door43.translationstudio"; private ImageLoader mImageLoader; // private Activity mCurrentDialogActivity; // private Map<String, ArrayList<String>> mNotificationsMap = new HashMap<String, ArrayList<String>>(); // private static Typeface mTranslationTypeface; // private static String mSelectedTypeface = ""; private static Activity mMainActivity; // private Term mSelectedKeyTerm; private boolean mShowImportantTerms; public static final String STACKTRACE_DIR = "crashes"; private boolean mClosingProgressDialog = false; @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); MultiDex.install(this); } @Override public void onCreate() { super.onCreate(); // initialize basic functions with link to main application new AppContext(this); File dir = new File(AppContext.getPublicDirectory(), STACKTRACE_DIR); GlobalExceptionHandler.register(dir); // configure logger int minLogLevel = Integer.parseInt(getUserPreferences().getString(SettingsActivity.KEY_PREF_LOGGING_LEVEL, getResources().getString(R.string.pref_default_logging_level))); configureLogger(minLogLevel); // initialize default settings // NOTE: make sure to add any new preference files here in order to have their default values properly loaded. PreferenceManager.setDefaultValues(this, R.xml.general_preferences, false); PreferenceManager.setDefaultValues(this, R.xml.server_preferences, false); PreferenceManager.setDefaultValues(this, R.xml.sharing_preferences, false); PreferenceManager.setDefaultValues(this, R.xml.advanced_preferences, false); // begins the backup manager service Intent backupIntent = new Intent(this, BackupService.class); startService(backupIntent); } public void configureLogger(int minLogLevel) { Logger.configure(new File(AppContext.getPublicDirectory(), "log.txt"), Logger.Level.getLevel(minLogLevel)); } // /** // * Sets the main activity that can be used for displaying dialogs. // * @param activity // */ // public static void setMainActivity(Activity activity) { // mMainActivity = activity; // } /** * Checks if the app should always share resources. * @return */ public boolean alwaysShare() { return getUserPreferences().getBoolean(SettingsActivity.KEY_PREF_ALWAYS_SHARE, Boolean.parseBoolean(getResources().getString(R.string.pref_default_always_share))); } /** * Moves an asset into the cache directory and returns a file reference to it * @param path * @return */ public File getAssetAsFile(String path) { // TODO: we probably don't want to do this for everything. // think about changing this up a bit. // TODO: we need to figure out when the clear out these cached files. Probably just on version bumps. File cacheFile = new File(getCacheDir(), "assets/" + path); if(!cacheFile.exists()) { cacheFile.getParentFile().mkdirs(); try { InputStream is = getAssets().open(path); try { FileOutputStream outputStream = new FileOutputStream(cacheFile); try { byte[] buf = new byte[1024]; int len; while ((len = is.read(buf)) > 0) { outputStream.write(buf, 0, len); } } finally { outputStream.close(); } } finally { is.close(); } } catch (IOException e) { return null; } } return cacheFile; } /** * Sends a new local notification */ // public void sendNotification(int notificationId, int titleResourceId, String message) { // // keep track of all the notifications // ArrayList<String> notifications; // if(mNotificationsMap.containsKey(""+notificationId)) { // notifications = mNotificationsMap.get(""+notificationId); // } else { // // add new notification group // notifications = new ArrayList<String>(); // mNotificationsMap.put("" + notificationId, notifications); // } // // // build notification // notifications.add(message); // NotificationCompat.Builder mBuilder = // new NotificationCompat.Builder(this) // .setSmallIcon(R.drawable.ic_stat_notify_msg) // .setContentTitle(getResourceSlugs().getString(titleResourceId)) // .setContentText(message) // .setAutoCancel(true) // .setNumber(notifications.size()); // // // build big notification // NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle(); // inboxStyle.setBigContentTitle(getResourceSlugs().getString(titleResourceId)); // for (String event:notifications) { // inboxStyle.addLine(event); // } // mBuilder.setStyle(inboxStyle); // // // issue notification // NotificationManager mNotifyMgr = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE); // if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { // mNotifyMgr.notify(notificationId, mBuilder.build()); // } else { // mNotifyMgr.notify(notificationId, mBuilder.getNotification()); // } // } // /** // * Sets the current activity so we can access it throughout the app. // * @param mCurrentActivity // */ // public void setCurrentActivity(Activity mCurrentActivity) { // this.mCurrentActivity = mCurrentActivity; // } /** * Returns the currently active activity * @return */ public Activity getCurrentActivity() { return mCurrentActivity; } /** * Generates and returns the image loader * @return */ public ImageLoader getImageLoader() { if(mImageLoader == null) { ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(this).build(); mImageLoader = ImageLoader.getInstance(); mImageLoader.init(config); } return mImageLoader; } /** * Displays a standard toast message in the ui * @param message */ public void showToastMessage(final String message) { showToastMessage(message, Toast.LENGTH_LONG); } /** * Displays a standard toast message in the ui. * If a toast message is currently visible it will be replaced. * @param message The message to display to the user * @param duration */ public void showToastMessage(final String message, final int duration) { if(mCurrentActivity != null) { mCurrentActivity.runOnUiThread(new Runnable() { public void run() { if(mToast != null) { mToast.cancel(); } mToast = Toast.makeText(mCurrentActivity, message, duration); mToast.setGravity(Gravity.TOP, 0, 0); mToast.show(); } }); } } /** * Checks if this apk was installed from the playstore or sideloaded * @return */ public boolean isStoreVersion() { String installer = getPackageManager().getInstallerPackageName(getPackageName()); return !TextUtils.isEmpty(installer); } /** * Cancels any toast message that is currently being displayed. */ public void closeToastMessage() { if(mCurrentActivity != null) { mCurrentActivity.runOnUiThread(new Runnable() { @Override public void run() { if(mToast != null) mToast.cancel(); } }); } } public void showToastMessage(int resId, int duration) { showToastMessage(getString(resId), duration); } public void showToastMessage(int resId) { showToastMessage(getString(resId)); } // public void showMessageDialog(int title, int msg, int positiveBtn, DialogInterface.OnClickListener positiveListenerr) { // showMessageDialog(title, getString(msg), positiveBtn, R.string.title_cancel, positiveListenerr, new DummyDialogListener()); // } // public void showMessageDialog(int title, String msg, int positiveBtn, DialogInterface.OnClickListener positiveListenerr) { // showMessageDialog(title, msg, positiveBtn, R.string.title_cancel, positiveListenerr, new DummyDialogListener()); // } // /** // * // * @param title // * @param msg // * @param positiveBtn // * @param negativeBtn // * @param positiveListener // * @param negativeListener // */ // public void showMessageDialog(int title, String msg, int positiveBtn, int negativeBtn, DialogInterface.OnClickListener positiveListener, DialogInterface.OnClickListener negativeListener) { // AlertDialog.Builder builder = new AlertDialog.Builder(this); // builder.setTitle(title).setMessage(msg) // .setPositiveButton(positiveBtn, positiveListener) // .setNegativeButton(negativeBtn, negativeListener).show(); // } // public void showMessageDialog(int title, int msg, int positiveBtn, int negativeBtn, DialogInterface.OnClickListener positiveListener, DialogInterface.OnClickListener negativeListener) { // showMessageDialog(title, getResources().getString(msg), positiveBtn, negativeBtn, positiveListener, negativeListener); // } public void showMessageDialog(int title, String msg) { showMessageDialog(getResources().getString(title), msg); } public void showMessageDialog(String title, String msg) { CustomAlertDialog.Create(this.getCurrentActivity()) .setTitle(title).setMessage(msg).setPositiveButton(R.string.label_ok, null).show("ShowMsg"); } // public void showMessageDialog(int title, int msg) { // showMessageDialog(title, getResources().getString(msg)); // } /** * Displays a message dialog to the user with a detailed view * @param title * @param msg * @param details */ @Deprecated public void showMessageDialogDetails(final int title, int msg, final String details) { CustomAlertDialog.Create(this.getCurrentActivity()) .setTitle(title).setMessage(msg).setPositiveButton(R.string.label_ok, null) .setNeutralButton(R.string.label_details, new View.OnClickListener() { @Override public void onClick(View v) { showMessageDialog(title, details); } }).show("ShowMsgDetail"); } public void showException(Throwable t) { showToastMessage(t.getMessage()); Logger.e(this.getClass().getName(), "non-critical exception", t); } public void showException(Throwable t, int res) { showToastMessage(res); Logger.e(this.getClass().getName(), "non-critical exception", t); } /** * Displays a progress dialog * TODO: Hack alert! we are using a quick fix to avoid leaked windows while showing dialogs from async tasks. Right now we are only using this for the upload manager. * @param message the message to display in the dialog */ public void showProgressDialog(final String message) { getCurrentActivity().runOnUiThread(new Runnable() { @Override public void run() { if (mMainActivity != null && !mClosingProgressDialog) { if (mProgressDialog == null) { // was using: getCurrentActivity() closeProgressDialog(); mProgressDialog = new ProgressDialog(mMainActivity); // was using: getCurrentActivity() } mProgressDialog.setMessage(message); if (!mProgressDialog.isShowing()) { mProgressDialog.show(); } } else if (mProgressDialog != null) { mProgressDialog.dismiss(); } mClosingProgressDialog = false; } }); } /** * Displays a progress dialog * @param res the resource id of the text to display */ public void showProgressDialog(int res) { showProgressDialog(getResources().getString(res)); } /** * Closes the current progress dialog. * You probably want to make sure not to call this twice unnessesarily */ public void closeProgressDialog() { if (mProgressDialog != null && mProgressDialog.isShowing()) { try { mProgressDialog.dismiss(); } catch (Exception e) { showToastMessage(e.getMessage()); } } } /** * Checks if the ssh keys have already been generated * @return */ public boolean hasSSHKeys() { File keysDir = getKeysFolder(); File privFile = new File(keysDir.getAbsolutePath()+"/id_rsa"); File pubFile = new File(keysDir.getAbsolutePath()+"/id_rsa.pub"); return privFile.exists() && pubFile.exists(); } /** * Returns the directory in which the ssh keys are stored * @return */ public File getKeysFolder() { File folder = new File(getFilesDir() + "/" + getResources().getString(R.string.keys_dir) + "/"); if(!folder.exists()) { folder.mkdir(); } return folder; } /** * Returns the public key file * @return */ public File getPublicKey() { File keysDir = getKeysFolder(); return new File(keysDir.getAbsolutePath()+"/id_rsa.pub"); } /** * Returns the private key file * @return */ public File getPrivateKey() { File keysDir = getKeysFolder(); return new File(keysDir.getAbsolutePath()+"/id_rsa"); } /** * Generates a new RSA key pair for use with ssh */ public void generateSSHKeys() { JSch jsch = new JSch(); int type = KeyPair.RSA; File keysDir = getKeysFolder(); String privateKeyPath = keysDir.getAbsolutePath() + "/id_rsa"; String publicKeyPath = keysDir.getAbsolutePath() + "/id_rsa.pub"; try{ KeyPair kpair=KeyPair.genKeyPair(jsch, type); new File(privateKeyPath).createNewFile(); kpair.writePrivateKey(privateKeyPath); new File(publicKeyPath).createNewFile(); kpair.writePublicKey(publicKeyPath, AppContext.udid()); kpair.dispose(); } catch(Exception e){ showException(e); } } /** * Checks if the client has sent it's ssh key to the server * @return * @deprecated we will always try to push first and register if it fails */ // public boolean hasRegisteredKeys() { // SharedPreferences settings = getSharedPreferences(PREFERENCES_TAG, MODE_PRIVATE); // return settings.getBoolean("has_registered_with_server", false); // } /** * Sets whether the client has sent it's ssh key to the server * @param hasRegistered * @deprecated */ // public void setHasRegisteredKeys(Boolean hasRegistered) { // SharedPreferences settings = getSharedPreferences(PREFERENCES_TAG, MODE_PRIVATE); // SharedPreferences.Editor editor = settings.edit(); // editor.putBoolean("has_registered_with_server", hasRegistered); // editor.apply(); // } /** * Checks if the app should opperate as if this is the first time it has opened. * @return */ public boolean shouldShowWelcome() { SharedPreferences settings = getSharedPreferences(PREFERENCES_TAG, MODE_PRIVATE); return settings.getBoolean("show_welcome", true); } /** * Sets whether the app should operate as if this is the first time it has opened. * @param shouldWelcome */ public void setShouldShowWelcome(Boolean shouldWelcome) { SharedPreferences settings = getSharedPreferences(PREFERENCES_TAG, MODE_PRIVATE); SharedPreferences.Editor editor = settings.edit(); editor.putBoolean("show_welcome", shouldWelcome); editor.apply(); } /** * Checks if the client has accepted the terms of use * @return */ // public boolean hasAcceptedTerms() { // SharedPreferences settings = getSharedPreferences(PREFERENCES_TAG, MODE_PRIVATE); // int termsVersion = getResources().getInteger(R.integer.terms_of_use_version); // return settings.getBoolean("has_accepted_terms_v"+termsVersion, false); // } /** * Sets whether the client has accepted the terms of use. * @param hasAcceptedTerms */ // public void setHasAcceptedTerms(Boolean hasAcceptedTerms) { // SharedPreferences settings = getSharedPreferences(PREFERENCES_TAG, MODE_PRIVATE); // SharedPreferences.Editor editor = settings.edit(); // int termsVersion = getResources().getInteger(R.integer.terms_of_use_version); // editor.putBoolean("has_accepted_terms_v"+termsVersion, hasAcceptedTerms); // editor.apply(); // } /** * Returns an instance of the user preferences. * This is just the default shared preferences * @return */ public SharedPreferences getUserPreferences() { return PreferenceManager.getDefaultSharedPreferences(this); } /** * Checks if we have internet * @return */ public boolean isNetworkAvailable() { ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); return activeNetwork != null && activeNetwork.isConnectedOrConnecting(); } /** * Sets the currently selected key term in the resources pane. * We store this here so selections persist between orientation changes * @param term */ // public void setSelectedKeyTerm(Term term) { // mSelectedKeyTerm = term; // } /** * Returns the curently selected key term * @return */ // public Term getSelectedKeyTerm() { // return mSelectedKeyTerm; // } /** * Check if the important terms should be shown * @return */ // public boolean getShowImportantTerms() { // return mShowImportantTerms; // } /** * Sets if the resources pane should display the important terms. * This is needed to persist selection between orientation change * @param showImportantTerms */ // public void setShowImportantTerms(boolean showImportantTerms) { // this.mShowImportantTerms = showImportantTerms; // } /** * Returns the directory where temporary indexes are stored * @return */ // public File getCacheIndexDir() { // return new File(getCacheDir(), "index"); // } /** * Returns the directory where indexes are stored * @return */ // public File getIndexDir() { // return new File(getFilesDir(), "index"); // } }