/* © 2010 Stephan Reichholf <stephan at reichholf dot net> * * Licensed under the Create-Commons Attribution-Noncommercial-Share Alike 3.0 Unported * http://creativecommons.org/licenses/by-nc-sa/3.0/ */ package net.reichholf.dreamdroid.fragment; import android.app.Activity; import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; import android.preference.PreferenceManager; import android.support.annotation.NonNull; import android.support.v7.view.ActionMode; import android.support.v7.widget.RecyclerView; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.Toast; import com.afollestad.materialdialogs.DialogAction; import com.afollestad.materialdialogs.MaterialDialog; import net.reichholf.dreamdroid.DatabaseHelper; import net.reichholf.dreamdroid.DreamDroid; import net.reichholf.dreamdroid.Profile; import net.reichholf.dreamdroid.R; import net.reichholf.dreamdroid.activities.SimpleToolbarFragmentActivity; import net.reichholf.dreamdroid.adapter.recyclerview.ProfileAdapter; import net.reichholf.dreamdroid.asynctask.DetectDevicesTask; import net.reichholf.dreamdroid.fragment.abs.BaseRecyclerFragment; import net.reichholf.dreamdroid.fragment.dialogs.PositiveNegativeDialog; import net.reichholf.dreamdroid.helpers.ExtendedHashMap; import net.reichholf.dreamdroid.helpers.Statics; import net.reichholf.dreamdroid.widget.helper.ItemSelectionSupport; import java.io.Serializable; import java.util.ArrayList; /** * Shows a list of all connection profiles * * @author sre */ public class ProfileListFragment extends BaseRecyclerFragment implements DetectDevicesTask.DetectDevicesTaskHandler { private boolean mIsActionMode; private boolean mIsActionModeRequired; private Profile mProfile; private ArrayList<Profile> mProfiles; private ArrayList<ExtendedHashMap> mProfileMapList; private ArrayList<Profile> mDetectedProfiles; private RecyclerView.Adapter mAdapter; private DetectDevicesTask mDetectDevicesTask; private MaterialDialog mProgress; private int mCurrentPos; public static final String KEY_ACTIVE_PROFILE = "active_profile"; @Override public void onDialogAction(int action, Object details, String dialogTag) { switch (action) { case Statics.ACTION_DELETE_CONFIRMED: DatabaseHelper dbh = DatabaseHelper.getInstance(getAppCompatActivity()); if (dbh.deleteProfile(mProfile)) { showToast(getString(R.string.profile_deleted) + " '" + mProfile.getName() + "'"); } else { showToast(getString(R.string.profile_not_deleted) + " '" + mProfile.getName() + "'"); } // TODO Add error handling reloadProfiles(); mProfile = Profile.getDefault(); mAdapter.notifyDataSetChanged(); break; } } private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() { // Called when the action mode is created; startActionMode() was called @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { // Inflate a menu resource providing context menu items MenuInflater inflater = mode.getMenuInflater(); inflater.inflate(R.menu.profilelist_context, menu); mIsActionMode = true; mIsActionModeRequired = false; mSelectionSupport.setChoiceMode(ItemSelectionSupport.ChoiceMode.SINGLE); return true; } // Called each time the action mode is shown. Always called after onCreateActionMode, but // may be called multiple times if the mode is invalidated. @Override public boolean onPrepareActionMode(ActionMode mode, Menu menu) { return true; } // Called when the user selects a contextual menu item @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { mode.finish(); // Action picked, so close the CAB return onItemClicked(item.getItemId()); } // Called when the user exits the action mode @Override public void onDestroyActionMode(ActionMode mode) { mIsActionMode = false; if (mIsActionModeRequired) return; final RecyclerView rv = getRecyclerView(); mSelectionSupport.setItemChecked(mSelectionSupport.getCheckedItemPosition(), false); rv.post(new Runnable() { @Override public void run() { mSelectionSupport.setChoiceMode(ItemSelectionSupport.ChoiceMode.NONE); } } ); } }; private void detectDevices() { if (mDetectedProfiles == null) { if (mDetectDevicesTask != null) { mDetectDevicesTask.cancel(true); mDetectDevicesTask = null; } if (mProgress != null) { mProgress.dismiss(); } MaterialDialog.Builder builder = new MaterialDialog.Builder(getAppCompatActivity()); builder.progress(true, 0) .progressIndeterminateStyle(true) .title(R.string.searching) .content(R.string.searching_known_devices) .cancelable(false); mProgress = builder.build(); mProgress.show(); mDetectDevicesTask = new DetectDevicesTask(this); mDetectDevicesTask.execute(); } else { if (mDetectedProfiles.size() == 0) { mDetectedProfiles = null; detectDevices(); } else { onDevicesDetected(mDetectedProfiles); } } } private void addAllDetectedDevices() { DatabaseHelper dbh = DatabaseHelper.getInstance(getAppCompatActivity()); for (Profile p : mDetectedProfiles) { if (dbh.addProfile(p)) { showToast(getText(R.string.profile_added) + " '" + p.getName() + "'"); } else { showToast(getText(R.string.profile_not_added) + " '" + p.getName() + "'"); } } reloadProfiles(); } /** * @param profiles A list of profiles for auto-discovered dreamboxes */ @Override public void onDevicesDetected(ArrayList<Profile> profiles) { mProgress.dismiss(); mDetectedProfiles = profiles; MaterialDialog.Builder builder = new MaterialDialog.Builder(getActivity()); builder.title(R.string.autodiscover_dreamboxes); if (mDetectedProfiles.size() > 0) { CharSequence[] items = new CharSequence[profiles.size()]; for (int i = 0; i < profiles.size(); i++) { items[i] = String.format("%s (%s)", profiles.get(i).getName(), profiles.get(i).getHost()); } builder.items(items); builder.positiveText(R.string.reload); builder.negativeText(R.string.add_all); builder.itemsCallback(new MaterialDialog.ListCallback() { @Override public void onSelection(MaterialDialog dialog, View itemView, int which, CharSequence text) { mProfile = mDetectedProfiles.get(which); editProfile(); } }); builder.onPositive(new MaterialDialog.SingleButtonCallback() { @Override public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { mDetectedProfiles = null; detectDevices(); } }); builder.onNegative(new MaterialDialog.SingleButtonCallback() { @Override public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { addAllDetectedDevices(); } }); } else { builder.content(R.string.autodiscovery_failed); builder.neutralText(android.R.string.ok); } builder.show(); } public void onCreate(Bundle savedInstanceState) { mCardListStyle = true; mHasFabMain = true; mEnableReload = false; super.onCreate(savedInstanceState); setHasOptionsMenu(true); initTitle(getString(R.string.profiles)); mCurrentPos = -1; mProfiles = new ArrayList<>(); mProfileMapList = new ArrayList<>(); mProfile = Profile.getDefault(); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.card_recycler_content, container, false); registerFab(R.id.fab_main, R.string.profile_add, R.drawable.ic_action_fab_add, new View.OnClickListener() { @Override public void onClick(View v) { createProfile(); } }); return view; } @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); mAdapter = new ProfileAdapter(getActivity(), mProfileMapList); getRecyclerView().setAdapter(mAdapter); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); reloadProfiles(); if (savedInstanceState != null) { int pos = savedInstanceState.getInt("cursorPosition"); if (pos < mProfiles.size()) { mProfile = mProfiles.get(pos); } } } @Override public void onItemClick(RecyclerView parent, View view, int position, long id) { mProfile = mProfiles.get(position); if (mIsActionMode) { mSelectionSupport.setItemChecked(position, true); return; } activateProfile(); } @Override public boolean onItemLongClick(RecyclerView parent, View view, int position, long id) { mProfile = mProfiles.get(position); getAppCompatActivity().startSupportActionMode(mActionModeCallback); mSelectionSupport.setItemChecked(position, true); return true; } @Override public void onRefresh() { reloadProfiles(); } private void reloadProfiles() { DatabaseHelper dbh = DatabaseHelper.getInstance(getAppCompatActivity()); mProfiles.clear(); mProfileMapList.clear(); mProfiles.addAll(dbh.getProfiles()); SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getAppCompatActivity()); for (Profile m : mProfiles) { boolean isActive = false; int activeProfileId = sp.getInt(DreamDroid.CURRENT_PROFILE, -1); if (activeProfileId > -1 && activeProfileId == m.getId()) { isActive = true; } ExtendedHashMap map = new ExtendedHashMap(); map.put(DatabaseHelper.KEY_PROFILE_PROFILE, m.getName()); map.put(DatabaseHelper.KEY_PROFILE_HOST, m.getHost()); map.put(KEY_ACTIVE_PROFILE, isActive); mProfileMapList.add(map); } mAdapter.notifyDataSetChanged(); mSwipeRefreshLayout.setRefreshing(false); } protected boolean onListItemLongClick(AdapterView<?> a, View v, int position, long id) { mCurrentPos = position; startActionMode(); return true; } protected void startActionMode() { mProfile = mProfiles.get(mCurrentPos); mActionMode = getAppCompatActivity().startSupportActionMode(mActionModeCallback); mSelectionSupport.setItemChecked(mCurrentPos, true); } @Override public void onSaveInstanceState(Bundle outState) { outState.putInt("profileId", mProfile.getId()); super.onSaveInstanceState(outState); } @Override public void createOptionsMenu(Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.profiles, menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { return onItemClicked(item.getItemId()); } /** * @param id The id of the selected menu item (<code>MENU_*</code> statics) * @return true if click was handled, false otherwise */ protected boolean onItemClicked(int id) { switch (id) { case (Statics.ITEM_ADD_PROFILE): createProfile(); break; case Statics.ITEM_DETECT_DEVICES: detectDevices(); break; case Statics.ITEM_EDIT: editProfile(); break; case Statics.ITEM_DELETE: getMultiPaneHandler().showDialogFragment( PositiveNegativeDialog.newInstance(mProfile.getName(), R.string.confirm_delete_profile, android.R.string.yes, Statics.ACTION_DELETE_CONFIRMED, android.R.string.no, Statics.ACTION_NONE), "dialog_delete_profile_confirm"); break; default: return false; } return true; } /* * (non-Javadoc) * * @see android.support.v4.app.Fragment#onActivityResult(int, int, * android.content.Intent) */ @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == Statics.REQUEST_EDIT_PROFILE) { if (resultCode == Activity.RESULT_OK) { reloadProfiles(); } } } /** * Activates the selected profile */ private void activateProfile() { if (DreamDroid.setCurrentProfile(getAppCompatActivity(), mProfile.getId(), true)) { showToast(getText(R.string.profile_activated) + " '" + mProfile.getName() + "'"); } else { showToast(getText(R.string.profile_not_activated) + " '" + mProfile.getName() + "'"); } reloadProfiles(); } /** * Opens a <code>ProfileEditActivity</code> for the selected profile */ private void editProfile() { openProfileEditActivity(getActivity(), mProfile); } /** * Opens a <code>ProfileEditActivity</code> for creating a new profile */ private void createProfile() { openProfileEditActivity(getActivity(), null); } /** * Opens a <code>ProfileEditActivity</code>. * @param activity The calling activity * @param profile The selected profile that should be edited, null if a new one should be created instead. */ public static void openProfileEditActivity(Activity activity, Profile profile) { ExtendedHashMap data = new ExtendedHashMap(); data.put("action", Intent.ACTION_EDIT); if (profile != null) { data.put("profile", profile); } Intent intent = new Intent(activity, SimpleToolbarFragmentActivity.class); intent.putExtra("fragmentClass", ProfileEditFragment.class); intent.putExtra("titleResource", profile == null ? R.string.profile_add : R.string.edit_profile); intent.putExtra("serializableData", (Serializable) data); activity.startActivityForResult(intent, Statics.REQUEST_EDIT_PROFILE); } /** * Shows a toast * * @param text The text to show as toast */ protected void showToast(String text) { Toast toast = Toast.makeText(getAppCompatActivity(), text, Toast.LENGTH_LONG); toast.show(); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { return false; } @Override public boolean onKeyUp(int keyCode, KeyEvent event) { return false; } }