package com.door43.translationstudio; import android.app.AlertDialog; import android.app.Fragment; import android.app.FragmentTransaction; import android.app.ProgressDialog; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.support.design.widget.Snackbar; import android.support.v4.content.FileProvider; import android.view.View; import android.widget.AdapterView; import android.widget.ListView; import android.widget.TextView; import com.door43.tools.reporting.Logger; import com.door43.translationstudio.core.ImportUsfm; import com.door43.translationstudio.core.Project; import com.door43.translationstudio.core.Resource; import com.door43.translationstudio.core.SourceTranslation; import com.door43.translationstudio.core.Util; import com.door43.translationstudio.dialogs.CustomAlertDialog; import com.door43.translationstudio.dialogs.ErrorLogDialog; import com.door43.translationstudio.newui.BaseActivity; import com.door43.translationstudio.newui.ImportUsfmActivity; import com.door43.translationstudio.tasks.GetLibraryUpdatesTask; import com.door43.translationstudio.tasks.DownloadAllProjectsTask; import com.door43.translationstudio.util.ToolAdapter; import com.door43.translationstudio.util.ToolItem; import com.door43.util.StringUtilities; import com.door43.util.tasks.ManagedTask; import com.door43.util.tasks.TaskManager; import com.door43.util.tasks.ThreadableUI; import com.door43.widget.ViewUtil; import org.apache.commons.io.IOUtils; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.text.NumberFormat; import java.util.ArrayList; public class DeveloperToolsActivity extends BaseActivity implements ManagedTask.OnProgressListener, ManagedTask.OnFinishedListener, DialogInterface.OnCancelListener { public static final String INDEX_CHUNK_MARKERS_TASK_ID = "index_chunk_markers"; private static final String TASK_PREP_FORCE_DOWNLOAD_ALL_PROJECTS = "prep_force_download_all_projects"; public static final String TAG = DeveloperToolsActivity.class.getSimpleName(); private ArrayList<ToolItem> mDeveloperTools = new ArrayList<>(); private ToolAdapter mAdapter; private String mVersionName; private String mVersionCode; private String TASK_FORCE_DOWNLOAD_ALL_PROJECTS = "force_download_all_projects"; private ProgressDialog mDownloadProgressDialog; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_developer_tools); getSupportActionBar().setDisplayHomeAsUpEnabled(true); TextView versionText = (TextView)findViewById(R.id.appVersionText); TextView buildText = (TextView)findViewById(R.id.appBuildNumberText); TextView udidText = (TextView)findViewById(R.id.deviceUDIDText); // display app version PackageInfo pInfo; try { pInfo = getPackageManager().getPackageInfo(getPackageName(), 0); mVersionName = pInfo.versionName; versionText.setText(String.format(getResources().getString(R.string.app_version_name), pInfo.versionName)); mVersionCode = pInfo.versionCode+""; buildText.setText(String.format(getResources().getString(R.string.app_version_code) ,pInfo.versionCode)); } catch (PackageManager.NameNotFoundException e) { Logger.e(this.getClass().getName(), "failed to get package name", e); } // display device id udidText.setText(String.format(getResources().getString(R.string.app_udid), AppContext.udid())); // set up copy handlers versionText.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { StringUtilities.copyToClipboard(DeveloperToolsActivity.this, mVersionName); AppContext.context().showToastMessage(R.string.copied_to_clipboard); } }); buildText.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { StringUtilities.copyToClipboard(DeveloperToolsActivity.this, mVersionCode); AppContext.context().showToastMessage(R.string.copied_to_clipboard); } }); udidText.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { StringUtilities.copyToClipboard(DeveloperToolsActivity.this, AppContext.udid()); AppContext.context().showToastMessage(R.string.copied_to_clipboard); } }); // set up developer tools ListView list = (ListView)findViewById(R.id.developerToolsListView); mAdapter = new ToolAdapter(mDeveloperTools, this); list.setAdapter(mAdapter); list.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) { ToolItem tool = mAdapter.getItem(i); if (tool.isEnabled()) { tool.getAction().run(); } } }); // load tools mDeveloperTools.add(new ToolItem(getResources().getString(R.string.regenerate_keys), getResources().getString(R.string.regenerate_keys_description), 0, new ToolItem.ToolAction() { @Override public void run() { final ProgressDialog dialog = new ProgressDialog(DeveloperToolsActivity.this); dialog.setMessage(getResources().getString(R.string.loading)); dialog.setCancelable(false); dialog.setCanceledOnTouchOutside(false); dialog.show(); final Handler handle = new Handler(Looper.getMainLooper()); new ThreadableUI(DeveloperToolsActivity.this) { @Override public void onStop() { handle.post(new Runnable() { @Override public void run() { dialog.dismiss(); } }); } @Override public void run() { AppContext.context().generateSSHKeys(); } @Override public void onPostExecute() { dialog.dismiss(); AppContext.context().showToastMessage(R.string.success); } }.start(); } })); mDeveloperTools.add(new ToolItem(getResources().getString(R.string.read_debugging_log), getResources().getString(R.string.read_debugging_log_description), 0, new ToolItem.ToolAction() { @Override public void run() { ErrorLogDialog dialog = new ErrorLogDialog(); FragmentTransaction ft = getFragmentManager().beginTransaction(); Fragment prev = getFragmentManager().findFragmentByTag("dialog"); if (prev != null) { ft.remove(prev); } ft.addToBackStack(null); dialog.show(ft, "dialog"); } })); mDeveloperTools.add(new ToolItem("Expire local resources", "Resets the local date modified on content to allow manually updating", 0, new ToolItem.ToolAction() { @Override public void run() { AppContext.getLibrary().setExpired(); Snackbar snack = Snackbar.make(findViewById(android.R.id.content), "The resources have been expired", Snackbar.LENGTH_LONG); ViewUtil.setSnackBarTextColor(snack, getResources().getColor(R.color.light_primary_text)); snack.show(); } })); mDeveloperTools.add(new ToolItem(getResources().getString(R.string.force_update_projects), getResources().getString(R.string.force_update_projects_description), 0, new ToolItem.ToolAction() { @Override public void run() { CustomAlertDialog.Create(DeveloperToolsActivity.this) .setTitle(R.string.action_download_all) .setMessage(R.string.download_confirmation) .setIcon(R.drawable.icon_update_cloud_dark) .setPositiveButton(R.string.yes, new View.OnClickListener() { @Override public void onClick(View v) { // create new prep task GetLibraryUpdatesTask task = new GetLibraryUpdatesTask(); task.addOnProgressListener(DeveloperToolsActivity.this); task.addOnFinishedListener(DeveloperToolsActivity.this); TaskManager.addTask(task, GetLibraryUpdatesTask.TASK_ID); } }) .setNegativeButton(R.string.no, null) .show("DownloadAll"); } })); mDeveloperTools.add(new ToolItem("Index tA", "Indexes the bundled tA json", 0, new ToolItem.ToolAction() { @Override public void run() { ThreadableUI thread = new ThreadableUI(DeveloperToolsActivity.this) { @Override public void onStop() { } @Override public void run() { // TRICKY: for now tA is only in english Project[] projects = AppContext.getLibrary().getProjects("en"); String catalog = null; try { catalog = Util.readStream(getAssets().open("ta.json")); } catch (IOException e) { e.printStackTrace(); } if(catalog != null) { for (Project p : projects) { Resource[] resources = AppContext.getLibrary().getResources(p.getId(), "en"); for (Resource r : resources) { SourceTranslation sourceTranslation = SourceTranslation.simple(p.getId(), "en", r.getId()); AppContext.getLibrary().manuallyIndexTranslationAcademy(sourceTranslation, catalog); } } } } @Override public void onPostExecute() { CustomAlertDialog.Create(DeveloperToolsActivity.this) .setTitle(R.string.success) .setMessage("tA has been indexed") .setNeutralButton(R.string.dismiss, null) .show("ta-index-success"); } }; thread.start(); Snackbar snack = Snackbar.make(findViewById(android.R.id.content), "indexing tA...", Snackbar.LENGTH_LONG); ViewUtil.setSnackBarTextColor(snack, getResources().getColor(R.color.light_primary_text)); snack.show(); } })); mDeveloperTools.add(new ToolItem("Index Chunk Markers", "Injects the chunk marker catalog url into the database and runs the update check", 0, new ToolItem.ToolAction() { @Override public void run() { // manually inject chunk marker details into db AppContext.getLibrary().manuallyInjectChunkMarkerCatalogUrl(); // run update check to index the chunk markers GetLibraryUpdatesTask task = new GetLibraryUpdatesTask(); task.addOnProgressListener(DeveloperToolsActivity.this); task.addOnFinishedListener(DeveloperToolsActivity.this); TaskManager.addTask(task, INDEX_CHUNK_MARKERS_TASK_ID); // ThreadableUI thread = new ThreadableUI(DeveloperToolsActivity.this) { // @Override // public void onStop() { // // } // // @Override // public void run() { // // manually inject chunk marker details into db // // // run update check to index the chunk markers // GetLibraryUpdatesTask task = new GetLibraryUpdatesTask(); // task.addOnProgressListener(DeveloperToolsActivity.this); // task.addOnFinishedListener(DeveloperToolsActivity.this); // TaskManager.addTask(task, "just_check_for_updates"); // } // // @Override // public void onPostExecute() { // CustomAlertDialog.Create(DeveloperToolsActivity.this) // .setTitle(R.string.success) // .setMessage("chunk markers has been indexed") // .setNeutralButton(R.string.dismiss, null) // .show("chunk-index-success"); // } // }; // thread.start(); // Snackbar snack = Snackbar.make(findViewById(android.R.id.content), "indexing chunk markers...", Snackbar.LENGTH_LONG); // ViewUtil.setSnackBarTextColor(snack, getResources().getColor(R.color.light_primary_text)); // snack.show(); } })); mDeveloperTools.add(new ToolItem(getResources().getString(R.string.export_source), getResources().getString(R.string.export_source_description), 0, new ToolItem.ToolAction() { @Override public void run() { final Handler handle = new Handler(Looper.getMainLooper()); // final Project[] projects = AppContext.projectManager().getProjectSlugs(); final ProgressDialog dialog = new ProgressDialog(DeveloperToolsActivity.this); dialog.setCancelable(false); dialog.setCanceledOnTouchOutside(false); dialog.setIndeterminate(true); dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); // dialog.setMax(projects.length); dialog.setMessage(getResources().getString(R.string.exporting)); // TODO: this should be placed inside of a task instead. final ThreadableUI thread = new ThreadableUI(DeveloperToolsActivity.this) { private File archive; @Override public void onStop() { } @Override public void run() { // TODO: add progress listener archive = AppContext.getLibrary().export(new File(getCacheDir(), "sharing/")); } @Override public void onPostExecute() { if(archive != null && archive.exists()) { new AlertDialog.Builder(DeveloperToolsActivity.this) .setTitle(R.string.success) .setIcon(R.drawable.ic_done_black_24dp) .setMessage(R.string.source_export_complete) .setPositiveButton(R.string.menu_share, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { Uri u = FileProvider.getUriForFile(DeveloperToolsActivity.this, "com.door43.translationstudio.fileprovider", archive); Intent intent = new Intent(Intent.ACTION_SEND); intent.setType("application/zip"); intent.putExtra(Intent.EXTRA_STREAM, u); startActivity(Intent.createChooser(intent, "Email:")); } }) .show(); } dialog.dismiss(); } }; dialog.show(); thread.start(); } })); mDeveloperTools.add(new ToolItem(getResources().getString(R.string.simulate_crash), getResources().getString(R.string.simulate_crash_description), 0, new ToolItem.ToolAction() { @Override public void run() { int killme = 1/0; } })); mDeveloperTools.add(new ToolItem("Delete Library", "Completely deletes the library and all of it's indexes", R.drawable.ic_delete_black_24dp, new ToolItem.ToolAction() { @Override public void run() { AppContext.getLibrary().delete(); Snackbar snack = Snackbar.make(findViewById(android.R.id.content), "The library content was deleted", Snackbar.LENGTH_LONG); ViewUtil.setSnackBarTextColor(snack, getResources().getColor(R.color.light_primary_text)); snack.show(); } })); if(savedInstanceState != null) { connectDownloadAllTask(); } } /** * Connects to an existing task */ public void connectDownloadAllTask() { DownloadAllProjectsTask downloadAllTask = (DownloadAllProjectsTask)TaskManager.getTask(DownloadAllProjectsTask.TASK_ID); GetLibraryUpdatesTask getUpdatesTask = (GetLibraryUpdatesTask)TaskManager.getTask(GetLibraryUpdatesTask.TASK_ID); if(getUpdatesTask == null) { getUpdatesTask = (GetLibraryUpdatesTask)TaskManager.getTask(INDEX_CHUNK_MARKERS_TASK_ID); } if(downloadAllTask != null) { downloadAllTask.addOnProgressListener(this); downloadAllTask.addOnFinishedListener(this); } if(getUpdatesTask != null) { getUpdatesTask.addOnProgressListener(this); getUpdatesTask.addOnFinishedListener(this); } } @Override public void onFinished(final ManagedTask task) { TaskManager.clearTask(task); if(task instanceof GetLibraryUpdatesTask) { if(task.getTaskId().equals(INDEX_CHUNK_MARKERS_TASK_ID)) { if(mDownloadProgressDialog != null && mDownloadProgressDialog.isShowing()) { mDownloadProgressDialog.dismiss(); } mDownloadProgressDialog = null; if(!task.isCanceled()) { CustomAlertDialog.Create(DeveloperToolsActivity.this) .setTitle(R.string.success) .setIcon(R.drawable.ic_done_black_24dp) .setMessage("Chunk Markers have been indexed") .setCancelableChainable(false) .setPositiveButton(R.string.label_ok, null) .show("Success"); } } else { DownloadAllProjectsTask newTask = new DownloadAllProjectsTask(); newTask.addOnProgressListener(this); newTask.addOnFinishedListener(this); TaskManager.addTask(newTask, DownloadAllProjectsTask.TASK_ID); } } if(task instanceof DownloadAllProjectsTask) { Handler hand = new Handler(Looper.getMainLooper()); // display success hand.post(new Runnable() { @Override public void run() { if (mDownloadProgressDialog != null && mDownloadProgressDialog.isShowing()) { mDownloadProgressDialog.dismiss(); } mDownloadProgressDialog = null; if (!task.isCanceled()) { // the download is complete CustomAlertDialog.Create(DeveloperToolsActivity.this) .setTitle(R.string.success) .setIcon(R.drawable.ic_done_black_24dp) .setMessage(R.string.download_complete) .setCancelableChainable(false) .setPositiveButton(R.string.label_ok, null) .show("Success"); } } }); } } @Override public void onProgress(final ManagedTask task, final double progress, final String message, final boolean secondary) { if(!task.isFinished()) { Handler hand = new Handler(Looper.getMainLooper()); hand.post(new Runnable() { @Override public void run() { if (task.isFinished()) { // because we start on a new thread we need to make sure the task didn't finish while this thread was starting. if (mDownloadProgressDialog != null) { mDownloadProgressDialog.dismiss(); } return; } if (mDownloadProgressDialog == null) { mDownloadProgressDialog = new ProgressDialog(DeveloperToolsActivity.this); mDownloadProgressDialog.setCancelable(false); // TODO: need to update the download method to support cancelling mDownloadProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); mDownloadProgressDialog.setCanceledOnTouchOutside(false); // mProgressDialog.setOnCancelListener(ServerLibraryActivity.this); mDownloadProgressDialog.setIcon(R.drawable.ic_cloud_download_black_24dp); if(task instanceof GetLibraryUpdatesTask) { mDownloadProgressDialog.setTitle(getResources().getString(R.string.checking_for_updates)); } else if(task instanceof DownloadAllProjectsTask) { mDownloadProgressDialog.setTitle(getResources().getString(R.string.downloading)); } mDownloadProgressDialog.setMessage(""); } mDownloadProgressDialog.setMax(task.maxProgress()); if (!mDownloadProgressDialog.isShowing()) { mDownloadProgressDialog.show(); } if (progress == -1) { mDownloadProgressDialog.setIndeterminate(true); mDownloadProgressDialog.setProgress(mDownloadProgressDialog.getMax()); mDownloadProgressDialog.setProgressNumberFormat(null); mDownloadProgressDialog.setProgressPercentFormat(null); } else { mDownloadProgressDialog.setIndeterminate(false); if(secondary) { mDownloadProgressDialog.setSecondaryProgress((int) progress); } else { mDownloadProgressDialog.setProgress((int) progress); } mDownloadProgressDialog.setProgressNumberFormat("%1d/%2d"); mDownloadProgressDialog.setProgressPercentFormat(NumberFormat.getPercentInstance()); } if (task instanceof DownloadAllProjectsTask && !message.isEmpty()) { mDownloadProgressDialog.setMessage(String.format(getResources().getString(R.string.downloading_project), message)); } else { mDownloadProgressDialog.setMessage(""); } } }); } } @Override public void onCancel(DialogInterface dialogInterface) { // the download dialog was canceled mDownloadProgressDialog = null; DownloadAllProjectsTask task = (DownloadAllProjectsTask) TaskManager.getTask(TASK_FORCE_DOWNLOAD_ALL_PROJECTS); if(task != null) { TaskManager.cancelTask(task); } } }