Java tutorial
/* * Copyright (C) 2014-2016 Andrew Gunnerson <andrewgunnerson@gmail.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.github.chenxiaolong.dualbootpatcher.switcher; import android.app.Activity; import android.app.Fragment; import android.app.LoaderManager.LoaderCallbacks; import android.content.AsyncTaskLoader; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.Loader; import android.content.ServiceConnection; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.provider.DocumentFile; import android.support.v7.widget.CardView; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView.ViewHolder; import android.support.v7.widget.helper.ItemTouchHelper; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; import com.github.chenxiaolong.dualbootpatcher.Constants; import com.github.chenxiaolong.dualbootpatcher.FileUtils; import com.github.chenxiaolong.dualbootpatcher.FileUtils.UriMetadata; import com.github.chenxiaolong.dualbootpatcher.R; import com.github.chenxiaolong.dualbootpatcher.RomUtils; import com.github.chenxiaolong.dualbootpatcher.RomUtils.RomInformation; import com.github.chenxiaolong.dualbootpatcher.ThreadPoolService.ThreadPoolServiceBinder; import com.github.chenxiaolong.dualbootpatcher.dialogs.FirstUseDialog; import com.github.chenxiaolong.dualbootpatcher.dialogs.FirstUseDialog.FirstUseDialogListener; import com.github.chenxiaolong.dualbootpatcher.dialogs.GenericConfirmDialog; import com.github.chenxiaolong.dualbootpatcher.dialogs.GenericProgressDialog; import com.github.chenxiaolong.dualbootpatcher.dialogs.GenericSingleChoiceDialog; import com.github.chenxiaolong.dualbootpatcher.dialogs.GenericSingleChoiceDialog.GenericSingleChoiceDialogListener; import com.github.chenxiaolong.dualbootpatcher.socket.MbtoolConnection; import com.github.chenxiaolong.dualbootpatcher.socket.MbtoolUtils; import com.github.chenxiaolong.dualbootpatcher.socket.MbtoolUtils.Feature; import com.github.chenxiaolong.dualbootpatcher.socket.interfaces.MbtoolInterface; import com.github.chenxiaolong.dualbootpatcher.switcher.BackupRestoreTargetsSelectionDialog.BackupRestoreTargetsSelectionDialogListener; import com.github.chenxiaolong.dualbootpatcher.switcher.ChangeInstallLocationDialog.ChangeInstallLocationDialogListener; import com.github.chenxiaolong.dualbootpatcher.switcher.InAppFlashingFragment.LoaderResult; import com.github.chenxiaolong.dualbootpatcher.switcher.NamedSlotIdInputDialog.NamedSlotIdInputDialogListener; import com.github.chenxiaolong.dualbootpatcher.switcher.RomIdSelectionDialog.RomIdSelectionDialogListener; import com.github.chenxiaolong.dualbootpatcher.switcher.RomIdSelectionDialog.RomIdType; import com.github.chenxiaolong.dualbootpatcher.switcher.SwitcherUtils.VerificationResult; import com.github.chenxiaolong.dualbootpatcher.switcher.actions.BackupRestoreParams; import com.github.chenxiaolong.dualbootpatcher.switcher.actions.BackupRestoreParams.Action; import com.github.chenxiaolong.dualbootpatcher.switcher.actions.MbtoolAction; import com.github.chenxiaolong.dualbootpatcher.switcher.actions.MbtoolAction.Type; import com.github.chenxiaolong.dualbootpatcher.switcher.actions.RomInstallerParams; import com.github.chenxiaolong.dualbootpatcher.switcher.service.VerifyZipTask.VerifyZipTaskListener; import com.github.chenxiaolong.dualbootpatcher.views.DragSwipeItemTouchCallback; import com.github.chenxiaolong.dualbootpatcher.views.DragSwipeItemTouchCallback.OnItemMovedOrDismissedListener; import com.github.clans.fab.FloatingActionButton; import com.github.clans.fab.FloatingActionMenu; import org.apache.commons.io.IOUtils; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; public class InAppFlashingFragment extends Fragment implements FirstUseDialogListener, RomIdSelectionDialogListener, NamedSlotIdInputDialogListener, ChangeInstallLocationDialogListener, GenericSingleChoiceDialogListener, BackupRestoreTargetsSelectionDialogListener, LoaderCallbacks<LoaderResult>, ServiceConnection, OnItemMovedOrDismissedListener { private static final int PERFORM_ACTIONS = 1234; private static final String EXTRA_PENDING_ACTIONS = "pending_actions"; private static final String EXTRA_SELECTED_URI = "selected_uri"; private static final String EXTRA_SELECTED_URI_FILE_NAME = "selected_uri_file_name"; private static final String EXTRA_SELECTED_BACKUP_DIR_URI = "selected_backup_uri"; private static final String EXTRA_SELECTED_BACKUP_NAME = "selected_backup_name"; private static final String EXTRA_SELECTED_BACKUP_TARGETS = "selected_backup_targets"; private static final String EXTRA_SELECTED_ROM_ID = "selected_rom_id"; private static final String EXTRA_ZIP_ROM_ID = "zip_rom_id"; private static final String EXTRA_ADD_TYPE = "add_type"; private static final String EXTRA_TASK_ID_VERIFY_ZIP = "task_id_verify_zip"; private static final String EXTRA_QUERYING_METADATA = "querying_metadata"; private static final String PREF_SHOW_FIRST_USE_DIALOG = "zip_flashing_first_use_show_dialog"; /** Request code for file picker (used in {@link #onActivityResult(int, int, Intent)}) */ private static final int ACTIVITY_REQUEST_FILE = 1000; private static final String PROGRESS_DIALOG_VERIFY_ZIP = InAppFlashingFragment.class.getCanonicalName() + ".progress.verify_zip"; private static final String PROGRESS_DIALOG_QUERYING_METADATA = InAppFlashingFragment.class.getCanonicalName() + ".progress.querying_metadata"; private static final String CONFIRM_DIALOG_FIRST_USE = InAppFlashingFragment.class.getCanonicalName() + ".confirm.first_use"; private static final String CONFIRM_DIALOG_INSTALL_LOCATION = InAppFlashingFragment.class.getCanonicalName() + ".confirm.install_location"; private static final String CONFIRM_DIALOG_NAMED_SLOT_ID = InAppFlashingFragment.class.getCanonicalName() + ".confirm.named_slot_id"; private static final String CONFIRM_DIALOG_ROM_ID = InAppFlashingFragment.class.getCanonicalName() + ".confirm.rom_id"; private static final String CONFIRM_DIALOG_ERROR = InAppFlashingFragment.class.getCanonicalName() + ".confirm.error"; private static final String CONFIRM_DIALOG_SELECT_BACKUP = InAppFlashingFragment.class.getCanonicalName() + ".confirm.select_backup"; private static final String CONFIRM_DIALOG_SELECT_TARGETS = InAppFlashingFragment.class.getCanonicalName() + ".confirm.select_targets"; private OnReadyStateChangedListener mActivityCallback; private SharedPreferences mPrefs; private Uri mSelectedUri; private String mSelectedUriFileName; private Uri mSelectedBackupDirUri; private String mSelectedBackupName; private String[] mSelectedBackupTargets; private String mSelectedRomId; private String mCurrentRomId; private String mZipRomId; private Type mAddType; private ProgressBar mProgressBar; private ArrayList<MbtoolAction> mPendingActions = new ArrayList<>(); private PendingActionCardAdapter mAdapter; private ArrayList<RomInformation> mBuiltinRoms = new ArrayList<>(); private boolean mVerifyZipOnServiceConnected; private int mTaskIdVerifyZip = -1; /** Task IDs to remove */ private ArrayList<Integer> mTaskIdsToRemove = new ArrayList<>(); /** Switcher service */ private SwitcherService mService; /** Callback for events from the service */ private final SwitcherEventCallback mCallback = new SwitcherEventCallback(); /** Handler for processing events from the service on the UI thread */ private final Handler mHandler = new Handler(Looper.getMainLooper()); /** Whether we're querying the URI metadata */ private boolean mQueryingMetadata; /** Task for querying the metadata of URIs */ private GetUriMetadataTask mQueryMetadataTask; public interface OnReadyStateChangedListener { void onReady(boolean ready); void onFinished(); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_in_app_flashing, container, false); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); if (savedInstanceState != null) { ArrayList<MbtoolAction> savedActions = savedInstanceState.getParcelableArrayList(EXTRA_PENDING_ACTIONS); mPendingActions.addAll(savedActions); } mProgressBar = (ProgressBar) getActivity().findViewById(R.id.card_list_loading); RecyclerView cardListView = (RecyclerView) getActivity().findViewById(R.id.card_list); mAdapter = new PendingActionCardAdapter(getActivity(), mPendingActions); cardListView.setHasFixedSize(true); cardListView.setAdapter(mAdapter); DragSwipeItemTouchCallback itemTouchCallback = new DragSwipeItemTouchCallback(this); ItemTouchHelper itemTouchHelper = new ItemTouchHelper(itemTouchCallback); itemTouchHelper.attachToRecyclerView(cardListView); LinearLayoutManager llm = new LinearLayoutManager(getActivity()); llm.setOrientation(LinearLayoutManager.VERTICAL); cardListView.setLayoutManager(llm); final FloatingActionMenu fabMenu = (FloatingActionMenu) getActivity().findViewById(R.id.fab_add_item_menu); FloatingActionButton fabAddPatchedFile = (FloatingActionButton) getActivity() .findViewById(R.id.fab_add_patched_file); FloatingActionButton fabAddBackup = (FloatingActionButton) getActivity().findViewById(R.id.fab_add_backup); fabAddPatchedFile.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { addPatchedFile(); fabMenu.close(true); } }); fabAddBackup.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { addBackup(); fabMenu.close(true); } }); if (savedInstanceState != null) { mSelectedUri = savedInstanceState.getParcelable(EXTRA_SELECTED_URI); mSelectedUriFileName = savedInstanceState.getString(EXTRA_SELECTED_URI_FILE_NAME); mSelectedBackupDirUri = savedInstanceState.getParcelable(EXTRA_SELECTED_BACKUP_DIR_URI); mSelectedBackupName = savedInstanceState.getString(EXTRA_SELECTED_BACKUP_NAME); mSelectedBackupTargets = savedInstanceState.getStringArray(EXTRA_SELECTED_BACKUP_TARGETS); mSelectedRomId = savedInstanceState.getString(EXTRA_SELECTED_ROM_ID); mZipRomId = savedInstanceState.getString(EXTRA_ZIP_ROM_ID); mAddType = (Type) savedInstanceState.getSerializable(EXTRA_ADD_TYPE); mTaskIdVerifyZip = savedInstanceState.getInt(EXTRA_TASK_ID_VERIFY_ZIP); mQueryingMetadata = savedInstanceState.getBoolean(EXTRA_QUERYING_METADATA); } try { mActivityCallback = (OnReadyStateChangedListener) getActivity(); } catch (ClassCastException e) { throw new ClassCastException(getActivity().toString() + " must implement OnReadyStateChangedListener"); } mActivityCallback.onReady(!mPendingActions.isEmpty()); mPrefs = getActivity().getSharedPreferences("settings", 0); if (savedInstanceState == null) { boolean shouldShow = mPrefs.getBoolean(PREF_SHOW_FIRST_USE_DIALOG, true); if (shouldShow) { FirstUseDialog d = FirstUseDialog.newInstance(this, R.string.in_app_flashing_title, R.string.in_app_flashing_dialog_first_use); d.show(getFragmentManager(), CONFIRM_DIALOG_FIRST_USE); } } getActivity().getLoaderManager().initLoader(0, null, this); } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putParcelableArrayList(EXTRA_PENDING_ACTIONS, mPendingActions); outState.putParcelable(EXTRA_SELECTED_URI, mSelectedUri); outState.putString(EXTRA_SELECTED_URI_FILE_NAME, mSelectedUriFileName); outState.putParcelable(EXTRA_SELECTED_BACKUP_DIR_URI, mSelectedBackupDirUri); outState.putString(EXTRA_SELECTED_BACKUP_NAME, mSelectedBackupName); outState.putStringArray(EXTRA_SELECTED_BACKUP_TARGETS, mSelectedBackupTargets); outState.putString(EXTRA_SELECTED_ROM_ID, mSelectedRomId); outState.putString(EXTRA_ZIP_ROM_ID, mZipRomId); outState.putSerializable(EXTRA_ADD_TYPE, mAddType); outState.putInt(EXTRA_TASK_ID_VERIFY_ZIP, mTaskIdVerifyZip); outState.putBoolean(EXTRA_QUERYING_METADATA, mQueryingMetadata); } @Override public void onStart() { super.onStart(); // Start and bind to the service Intent intent = new Intent(getActivity(), SwitcherService.class); getActivity().bindService(intent, this, Context.BIND_AUTO_CREATE); getActivity().startService(intent); } @Override public void onStop() { super.onStop(); // Cancel metadata query task cancelQueryUriMetadata(); // If we connected to the service and registered the callback, now we unregister it if (mService != null) { if (mTaskIdVerifyZip >= 0) { mService.removeCallback(mTaskIdVerifyZip, mCallback); } } // Unbind from our service getActivity().unbindService(this); mService = null; // At this point, the mCallback will not get called anymore by the service. Now we just need // to remove all pending Runnables that were posted to mHandler. mHandler.removeCallbacksAndMessages(null); } @Override public void onServiceConnected(ComponentName name, IBinder service) { // Save a reference to the service so we can interact with it ThreadPoolServiceBinder binder = (ThreadPoolServiceBinder) service; mService = (SwitcherService) binder.getService(); // Remove old task IDs for (int taskId : mTaskIdsToRemove) { mService.removeCachedTask(taskId); } mTaskIdsToRemove.clear(); if (mTaskIdVerifyZip >= 0) { mService.addCallback(mTaskIdVerifyZip, mCallback); } if (mVerifyZipOnServiceConnected) { mVerifyZipOnServiceConnected = false; verifyZip(); } if (mQueryingMetadata) { queryUriMetadata(); } } @Override public void onServiceDisconnected(ComponentName name) { mService = null; } private void removeCachedTaskId(int taskId) { if (mService != null) { mService.removeCachedTask(taskId); } else { mTaskIdsToRemove.add(taskId); } } @Override public Loader<LoaderResult> onCreateLoader(int id, Bundle args) { return new BuiltinRomsLoader(getActivity()); } @Override public void onLoadFinished(Loader<LoaderResult> loader, LoaderResult result) { mBuiltinRoms.clear(); if (result.builtinRoms != null) { Collections.addAll(mBuiltinRoms, result.builtinRoms); } if (result.currentRom != null) { mCurrentRomId = result.currentRom.getId(); } mProgressBar.setVisibility(View.GONE); } @Override public void onLoaderReset(Loader<LoaderResult> loader) { } @Nullable private static String[] getDirectories(Context context, Uri uri) { final ArrayList<String> filenames = new ArrayList<>(); DocumentFile directory = FileUtils.getDocumentFile(context, uri); DocumentFile[] files = directory.listFiles(); if (files == null) { return null; } for (DocumentFile file : files) { if (file.isDirectory()) { filenames.add(file.getName()); } } Collections.sort(filenames); return filenames.toArray(new String[filenames.size()]); } private void addPatchedFile() { mAddType = Type.ROM_INSTALLER; // Show file chooser Intent intent = FileUtils.getFileOpenIntent(getActivity(), "*/*"); startActivityForResult(intent, ACTIVITY_REQUEST_FILE); } private void addBackup() { mSelectedBackupDirUri = Uri.parse(mPrefs.getString(Constants.Preferences.BACKUP_DIRECTORY_URI, Constants.Defaults.BACKUP_DIRECTORY_URI)); String[] backupNames = getDirectories(getActivity(), mSelectedBackupDirUri); if (backupNames == null || backupNames.length == 0) { Toast.makeText(getActivity(), R.string.in_app_flashing_no_backups_available, Toast.LENGTH_LONG).show(); } else { mAddType = Type.BACKUP_RESTORE; GenericSingleChoiceDialog.Builder builder = new GenericSingleChoiceDialog.Builder(); builder.message(R.string.in_app_flashing_select_backup_dialog_desc); builder.positive(R.string.ok); builder.negative(R.string.cancel); builder.choices(backupNames); builder.buildFromFragment(CONFIRM_DIALOG_SELECT_BACKUP, this).show(getFragmentManager(), CONFIRM_DIALOG_SELECT_BACKUP); } } private void onVerifiedZip(String romId, VerificationResult result) { removeCachedTaskId(mTaskIdVerifyZip); mTaskIdVerifyZip = -1; GenericProgressDialog dialog = (GenericProgressDialog) getFragmentManager() .findFragmentByTag(PROGRESS_DIALOG_VERIFY_ZIP); if (dialog != null) { dialog.dismiss(); } mZipRomId = romId; if (result == VerificationResult.NO_ERROR) { if (mZipRomId != null) { ChangeInstallLocationDialog cild = ChangeInstallLocationDialog.newInstance(this, mZipRomId); cild.show(getFragmentManager(), CONFIRM_DIALOG_INSTALL_LOCATION); } else { showRomIdSelectionDialog(); } } else { String error; switch (result) { case ERROR_ZIP_NOT_FOUND: error = getString(R.string.in_app_flashing_error_zip_not_found); break; case ERROR_ZIP_READ_FAIL: error = getString(R.string.in_app_flashing_error_zip_read_fail); break; case ERROR_NOT_MULTIBOOT: error = getString(R.string.in_app_flashing_error_not_multiboot); break; case ERROR_VERSION_TOO_OLD: error = String.format(getString(R.string.in_app_flashing_error_version_too_old), MbtoolUtils.getMinimumRequiredVersion(Feature.IN_APP_INSTALLATION)); break; default: throw new IllegalStateException("Invalid verification result ID"); } GenericConfirmDialog.Builder builder = new GenericConfirmDialog.Builder(); builder.message(error); builder.buttonText(R.string.ok); builder.build().show(getFragmentManager(), CONFIRM_DIALOG_ERROR); } } /** * Called after the input URI's metadata has been retrieved * * @param metadata URI metadata * * @see #queryUriMetadata() */ private void onQueriedMetadata(@NonNull UriMetadata metadata) { GenericProgressDialog dialog = (GenericProgressDialog) getFragmentManager() .findFragmentByTag(PROGRESS_DIALOG_QUERYING_METADATA); if (dialog != null) { dialog.dismiss(); } mSelectedUriFileName = metadata.displayName; if (mService != null) { verifyZip(); } else { mVerifyZipOnServiceConnected = true; } } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case PERFORM_ACTIONS: mActivityCallback.onFinished(); break; case ACTIVITY_REQUEST_FILE: if (data != null && resultCode == Activity.RESULT_OK) { mSelectedUri = data.getData(); GenericProgressDialog.Builder builder = new GenericProgressDialog.Builder(); builder.title(R.string.in_app_flashing_dialog_verifying_zip); builder.message(R.string.please_wait); builder.build().show(getFragmentManager(), PROGRESS_DIALOG_VERIFY_ZIP); queryUriMetadata(); } break; } super.onActivityResult(requestCode, resultCode, data); } private void verifyZip() { mTaskIdVerifyZip = mService.verifyZip(mSelectedUri); mService.addCallback(mTaskIdVerifyZip, mCallback); mService.enqueueTaskId(mTaskIdVerifyZip); } /** * Query the metadata for the input file * * After the user selects an input file, this function is called to start the task of retrieving * the file's name and size. Once the information has been retrieved, * {@link #onQueriedMetadata(UriMetadata)} is called. * * @see #onQueriedMetadata(UriMetadata) */ private void queryUriMetadata() { if (mQueryMetadataTask != null) { throw new IllegalStateException("Already querying metadata!"); } mQueryMetadataTask = new GetUriMetadataTask(); mQueryMetadataTask.execute(mSelectedUri); // Show progress dialog. Dialog may already exist if a configuration change occurred during // the query (and thus, this function is called again in onReady()). GenericProgressDialog dialog = (GenericProgressDialog) getFragmentManager() .findFragmentByTag(PROGRESS_DIALOG_QUERYING_METADATA); if (dialog == null) { GenericProgressDialog.Builder builder = new GenericProgressDialog.Builder(); builder.message(R.string.please_wait); dialog = builder.build(); dialog.show(getFragmentManager(), PROGRESS_DIALOG_QUERYING_METADATA); } } /** * Cancel task for querying the input URI metadata * * This function is a no-op if there is no such task. * * @see #onStop() */ private void cancelQueryUriMetadata() { if (mQueryMetadataTask != null) { mQueryMetadataTask.cancel(true); mQueryMetadataTask = null; } } @Override public void onConfirmFirstUse(boolean dontShowAgain) { Editor e = mPrefs.edit(); e.putBoolean(PREF_SHOW_FIRST_USE_DIALOG, !dontShowAgain); e.apply(); } @Override public void onConfirmSingleChoice(@Nullable String tag, int index, String text) { mSelectedBackupName = text; // Adk for restore targets BackupRestoreTargetsSelectionDialog d = BackupRestoreTargetsSelectionDialog.newInstanceFromFragment(this, Action.RESTORE); d.show(getFragmentManager(), CONFIRM_DIALOG_SELECT_TARGETS); } @Override public void onSelectedBackupRestoreTargets(String[] targets) { mSelectedBackupTargets = targets; // Ask for ROM ID showRomIdSelectionDialog(); } @Override public void onSelectedRomId(RomIdType type, String romId) { switch (type) { case BUILT_IN_ROM_ID: mSelectedRomId = romId; onHaveRomId(); break; case NAMED_DATA_SLOT: { NamedSlotIdInputDialog d = NamedSlotIdInputDialog.newInstance(this, NamedSlotIdInputDialog.DATA_SLOT); d.show(getFragmentManager(), CONFIRM_DIALOG_NAMED_SLOT_ID); break; } case NAMED_EXTSD_SLOT: { NamedSlotIdInputDialog d = NamedSlotIdInputDialog.newInstance(this, NamedSlotIdInputDialog.EXTSD_SLOT); d.show(getFragmentManager(), CONFIRM_DIALOG_NAMED_SLOT_ID); break; } } } @Override public void onSelectedNamedSlotRomId(String romId) { mSelectedRomId = romId; onHaveRomId(); } @Override public void onChangeInstallLocationClicked(boolean changeInstallLocation) { if (changeInstallLocation) { showRomIdSelectionDialog(); } else { mSelectedRomId = mZipRomId; onHaveRomId(); } } @Override public void onItemMoved(int fromPosition, int toPosition) { Collections.swap(mPendingActions, fromPosition, toPosition); mAdapter.notifyItemMoved(fromPosition, toPosition); } @Override public void onItemDismissed(int position) { mPendingActions.remove(position); mAdapter.notifyItemRemoved(position); if (mPendingActions.isEmpty()) { mActivityCallback.onReady(false); } } private void onHaveRomId() { if (mSelectedRomId.equals(mCurrentRomId)) { GenericConfirmDialog.Builder builder = new GenericConfirmDialog.Builder(); builder.message(R.string.in_app_flashing_error_no_overwrite_rom); builder.buttonText(R.string.ok); builder.build().show(getFragmentManager(), CONFIRM_DIALOG_ERROR); } else { mActivityCallback.onReady(true); MbtoolAction action = null; if (mAddType == Type.ROM_INSTALLER) { RomInstallerParams params = new RomInstallerParams(mSelectedUri, mSelectedUriFileName, mSelectedRomId); action = new MbtoolAction(params); } else if (mAddType == Type.BACKUP_RESTORE) { BackupRestoreParams params = new BackupRestoreParams(Action.RESTORE, mSelectedRomId, mSelectedBackupTargets, mSelectedBackupName, mSelectedBackupDirUri, false); action = new MbtoolAction(params); } if (action != null) { mPendingActions.add(action); mAdapter.notifyItemInserted(mPendingActions.size() - 1); } } } private void showRomIdSelectionDialog() { RomIdSelectionDialog dialog = RomIdSelectionDialog.newInstance(this, mBuiltinRoms); dialog.show(getFragmentManager(), CONFIRM_DIALOG_ROM_ID); } public void onActionBarCheckItemClicked() { Intent intent = new Intent(getActivity(), MbtoolTaskOutputActivity.class); intent.putExtra(MbtoolTaskOutputFragment.PARAM_ACTIONS, getPendingActions()); startActivityForResult(intent, PERFORM_ACTIONS); } @NonNull private MbtoolAction[] getPendingActions() { return mPendingActions.toArray(new MbtoolAction[mPendingActions.size()]); } private static class BuiltinRomsLoader extends AsyncTaskLoader<LoaderResult> { private LoaderResult mResult; public BuiltinRomsLoader(Context context) { super(context); onContentChanged(); } @Override protected void onStartLoading() { if (mResult != null) { deliverResult(mResult); } else if (takeContentChanged()) { forceLoad(); } } @Override public LoaderResult loadInBackground() { RomInformation currentRom = null; MbtoolConnection conn = null; try { conn = new MbtoolConnection(getContext()); MbtoolInterface iface = conn.getInterface(); currentRom = RomUtils.getCurrentRom(getContext(), iface); } catch (Exception e) { // Ignore } finally { IOUtils.closeQuietly(conn); } mResult = new LoaderResult(); mResult.builtinRoms = RomUtils.getBuiltinRoms(getContext()); mResult.currentRom = currentRom; return mResult; } } protected static class LoaderResult { RomInformation[] builtinRoms; RomInformation currentRom; } private static class PendingActionViewHolder extends ViewHolder { CardView vCard; TextView vTitle; TextView vSubtitle1; TextView vSubtitle2; TextView vSubtitle3; public PendingActionViewHolder(View itemView) { super(itemView); vCard = (CardView) itemView; vTitle = (TextView) itemView.findViewById(R.id.action_title); vSubtitle1 = (TextView) itemView.findViewById(R.id.action_subtitle1); vSubtitle2 = (TextView) itemView.findViewById(R.id.action_subtitle2); vSubtitle3 = (TextView) itemView.findViewById(R.id.action_subtitle3); } } private static class PendingActionCardAdapter extends RecyclerView.Adapter<PendingActionViewHolder> { private Context mContext; private List<MbtoolAction> mItems; public PendingActionCardAdapter(Context context, List<MbtoolAction> items) { mContext = context; mItems = items; } @Override public PendingActionViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_v7_pending_action, parent, false); return new PendingActionViewHolder(view); } @Override public void onBindViewHolder(PendingActionViewHolder holder, int position) { MbtoolAction pa = mItems.get(position); switch (pa.getType()) { case ROM_INSTALLER: { RomInstallerParams params = pa.getRomInstallerParams(); holder.vTitle.setText(R.string.in_app_flashing_action_flash_file); holder.vSubtitle1 .setText(mContext.getString(R.string.in_app_flashing_filename, params.getDisplayName())); holder.vSubtitle2.setText(mContext.getString(R.string.in_app_flashing_location, params.getRomId())); holder.vSubtitle3.setVisibility(View.GONE); break; } case BACKUP_RESTORE: { BackupRestoreParams params = pa.getBackupRestoreParams(); holder.vTitle.setText(R.string.in_app_flashing_action_restore_backup); holder.vSubtitle1 .setText(mContext.getString(R.string.in_app_flashing_backup_name, params.getBackupName())); holder.vSubtitle2.setText(mContext.getString(R.string.in_app_flashing_restore_targets, Arrays.toString(params.getTargets()))); holder.vSubtitle3.setText(mContext.getString(R.string.in_app_flashing_location, params.getRomId())); holder.vSubtitle3.setVisibility(View.VISIBLE); break; } } } @Override public int getItemCount() { return mItems.size(); } } /** * Task to query the display name, size, and MIME type of a list of openable URIs. */ private class GetUriMetadataTask extends AsyncTask<Uri, Void, UriMetadata[]> { private ContentResolver mCR; @Override protected void onPreExecute() { mCR = getActivity().getContentResolver(); mQueryingMetadata = true; } @Override protected UriMetadata[] doInBackground(Uri... params) { return FileUtils.queryUriMetadata(mCR, params); } @Override protected void onPostExecute(UriMetadata[] metadatas) { mCR = null; if (isAdded()) { mQueryingMetadata = false; onQueriedMetadata(metadatas[0]); } } } private class SwitcherEventCallback implements VerifyZipTaskListener { @Override public void onVerifiedZip(int taskId, Uri uri, final VerificationResult result, final String romId) { if (taskId == mTaskIdVerifyZip) { mHandler.post(new Runnable() { @Override public void run() { InAppFlashingFragment.this.onVerifiedZip(romId, result); } }); } } } }