package com.github.andlyticsproject; import android.support.v7.app.AppCompatActivity; import android.app.AlertDialog; import android.app.Dialog; import android.app.DialogFragment; import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.CheckBox; import android.widget.ListView; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; import com.github.andlyticsproject.io.ImportService; import com.github.andlyticsproject.io.ServiceException; import com.github.andlyticsproject.io.StatsCsvReaderWriter; import com.github.andlyticsproject.util.DetachableAsyncTask; import com.github.andlyticsproject.util.FileUtils; import com.github.andlyticsproject.util.Utils; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; public class ImportActivity extends AppCompatActivity { private static final String TAG = ImportActivity.class.getSimpleName(); public static final int TAG_IMAGE_REF = R.id.tag_mainlist_image_reference; private static final String EXTRA_IMPORT_FILENAMES = "importFilenames"; private ImportListAdapter adapter; private LayoutInflater layoutInflater; private ArrayList<String> importFilenames = new ArrayList<String>(); private String accountName; private ListView listView; private LoadImportDialogTask loadTask; @SuppressWarnings("unchecked") @Override public void onCreate(Bundle state) { super.onCreate(state); setContentView(R.layout.import_stats); setProgressBarIndeterminateVisibility(false); layoutInflater = getLayoutInflater(); accountName = getAccountName(); getSupportActionBar().setSubtitle(accountName); if (state != null) { importFilenames = (ArrayList<String>) state.getSerializable(EXTRA_IMPORT_FILENAMES); } setupViews(); if (Intent.ACTION_VIEW.equals(getIntent().getAction())) { Uri data = getIntent().getData(); if (data == null) { Toast.makeText(this, getString(R.string.stats_file_not_specified_error), Toast.LENGTH_LONG).show(); finish(); } if (getLastNonConfigurationInstance() != null) { loadTask = (LoadImportDialogTask) getLastNonConfigurationInstance(); loadTask.attach(this); setFilenames(loadTask.getFilenames()); } else { loadTask = new LoadImportDialogTask(this, accountName); Utils.execute(loadTask, data); } } else { Log.w(TAG, "Don't know how to handle this action: " + getIntent().getAction()); finish(); } } @Override protected void onSaveInstanceState(Bundle state) { super.onSaveInstanceState(state); state.putSerializable(EXTRA_IMPORT_FILENAMES, importFilenames); } @Override public Object onRetainCustomNonConfigurationInstance() { return loadTask == null ? null : loadTask.detach(); } private void setupViews() { View closeButton = (View) this.findViewById(R.id.import_dialog_close_button); closeButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { finish(); } }); View importButton = (View) this.findViewById(R.id.import_dialog_import_button); importButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (importFilenames.isEmpty()) { Toast.makeText(ImportActivity.this, getString(R.string.import_no_app), Toast.LENGTH_LONG).show(); return; } ConfirmImportDialogFragment.newInstance(adapter.getCount()).show( getFragmentManager(), "confirmImportDialog"); } }); listView = (ListView) this.findViewById(R.id.list_view_id); listView.addHeaderView(layoutInflater.inflate(R.layout.import_list_header, null), null, false); setFilenames(new ArrayList<String>()); } void setFilenames(List<String> filenames) { adapter = new ImportListAdapter(filenames); listView.setAdapter(adapter); } private String getAccountName() { String ownerAccount = StatsCsvReaderWriter.getAccountNameForExport(new File(getIntent() .getData().getPath()).getName()); if (ownerAccount == null) { // fall back to value from preferences // XXX should we give a choice instead? ownerAccount = DeveloperAccountManager.getInstance(this).getSelectedDeveloperAccount() .getName(); } return ownerAccount; } private void startImport() { Intent intent = new Intent(ImportActivity.this, ImportService.class); intent.setData(getIntent().getData()); intent.putExtra(ImportService.FILE_NAMES, importFilenames.toArray(new String[importFilenames.size()])); intent.putExtra(ImportService.ACCOUNT_NAME, accountName); startService(intent); finish(); } public static class ConfirmImportDialogFragment extends DialogFragment { public static final String ARG_NUM_APPS = "numApps"; public static ConfirmImportDialogFragment newInstance(int numExistingApps) { ConfirmImportDialogFragment frag = new ConfirmImportDialogFragment(); Bundle args = new Bundle(); args.putInt(ARG_NUM_APPS, numExistingApps); frag.setArguments(args); return frag; } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { final int numExistingApps = getArguments().getInt(ARG_NUM_APPS); return new AlertDialog.Builder(getActivity()) .setIcon(android.R.drawable.ic_dialog_alert) .setTitle(R.string.import_confirm_dialog_title) .setMessage( getResources().getString(R.string.import_confirm_dialog_message, numExistingApps)) .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { ((ImportActivity) getActivity()).startImport(); } }) .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { dismiss(); } }).create(); } } private static class LoadImportDialogTask extends DetachableAsyncTask<Uri, Void, Boolean, ImportActivity> { ContentAdapter db; String accountName; LoadImportDialogTask(ImportActivity parent, String accountName) { super(parent); this.accountName = accountName; db = ContentAdapter.getInstance(parent.getApplication()); } private List<String> filenames = new ArrayList<String>(); private Uri zipFileUri; @Override protected void onPreExecute() { activity.setProgressBarIndeterminateVisibility(true); } @Override protected Boolean doInBackground(Uri... params) { if (activity == null) { return false; } zipFileUri = params[0]; List<String> pacakgeNames = db.getPackagesForAccount(accountName); try { String zipFilePath = zipFileUri.getPath(); File tempZip = null; try { if (!zipFileUri.getScheme().equalsIgnoreCase("file")) { tempZip = copyToTempFile(); zipFilePath = tempZip.getAbsolutePath(); } filenames = StatsCsvReaderWriter.getImportFileNamesFromZip( activity.getAccountName(), pacakgeNames, zipFilePath); } catch (IOException e) { Log.e(TAG, "Error reading import zip file: " + e.getMessage()); return false; } finally { if (tempZip != null) { tempZip.delete(); } } return true; } catch (ServiceException e) { Log.e(TAG, "Error reading import zip file: " + e.getMessage()); return false; } } private File copyToTempFile() throws IOException { File tempZip = File.createTempFile("andlytics-import", ".zip"); FileOutputStream out = new FileOutputStream(tempZip); byte[] zipBytes = FileUtils.readFromUri(activity, zipFileUri); out.write(zipBytes); out.flush(); out.close(); return tempZip; } @Override protected void onPostExecute(Boolean result) { if (activity == null) { return; } activity.setProgressBarIndeterminateVisibility(false); if (!activity.isFinishing()) { if (result) { activity.setFilenames(filenames); } else { Toast.makeText(activity, activity.getString(R.string.import_no_sdcard_or_file), Toast.LENGTH_LONG).show(); activity.finish(); } } } List<String> getFilenames() { return filenames; } } class ImportListAdapter extends BaseAdapter { List<String> files; ImportListAdapter(List<String> files) { this.files = files; } @Override public int getCount() { return files.size(); } @Override public String getItem(int position) { return files.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { convertView = layoutInflater.inflate(R.layout.import_list_item, null); holder = new ViewHolder(); holder.name = (TextView) convertView.findViewById(R.id.import_file_name); holder.row = (RelativeLayout) convertView.findViewById(R.id.import_app_row); holder.checkbox = (CheckBox) convertView.findViewById(R.id.import_file_checkbox); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } final String fileName = getItem(position); holder.name.setText(StatsCsvReaderWriter.getPackageName(fileName)); holder.checkbox.setChecked(importFilenames.contains(fileName)); holder.row.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { CheckBox checkbox = ((CheckBox) (((ViewGroup) v) .findViewById(R.id.import_file_checkbox))); checkbox.setChecked(!checkbox.isChecked()); if (checkbox.isChecked()) { importFilenames.add(fileName); } else { importFilenames.remove(fileName); } } }); holder.checkbox.setTag(fileName); holder.checkbox.setOnClickListener(new CheckBox.OnClickListener() { @Override public void onClick(View v) { boolean isChecked = ((CheckBox) v).isChecked(); if (isChecked) { importFilenames.add(fileName); } else { importFilenames.remove(fileName); } } }); return convertView; } private class ViewHolder { public RelativeLayout row; public TextView name; public CheckBox checkbox; } } }