Java tutorial
/** * Copyright (C) 2010-2012 Regis Montoya (aka r3gis - www.r3gis.fr) * This file is part of CSipSimple. * * CSipSimple 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. * If you own a pjsip commercial license you can also redistribute it * and/or modify it under the terms of the GNU Lesser General Public License * as an android library. * * CSipSimple 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 CSipSimple. If not, see <http://www.gnu.org/licenses/>. */ package com.newcell.calltext.ui.account; import java.io.File; import java.util.ArrayList; import java.util.List; import android.app.Activity; import android.app.AlertDialog; import android.content.ContentResolver; import android.content.ContentUris; import android.content.ContentValues; import android.content.DialogInterface; import android.content.Intent; import android.database.ContentObserver; import android.database.Cursor; import android.graphics.Color; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.provider.BaseColumns; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; import android.support.v4.widget.CursorAdapter; import android.util.Log; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.Button; import android.widget.ListView; import android.widget.Toast; import com.actionbarsherlock.view.Menu; import com.actionbarsherlock.view.MenuInflater; import com.actionbarsherlock.view.MenuItem; import com.newcell.calltext.api.SipProfile; import com.newcell.calltext.backup.SipProfileJson; import com.newcell.calltext.R; import com.newcell.calltext.ui.account.AccountsEditListAdapter.AccountRowTag; import com.newcell.calltext.ui.account.AccountsEditListAdapter.OnCheckedRowListener; import com.newcell.calltext.utils.PreferencesWrapper; import com.newcell.calltext.widgets.CSSListFragment; import com.newcell.calltext.widgets.DragnDropListView; import com.newcell.calltext.widgets.DragnDropListView.DropListener; import com.newcell.calltext.wizards.BasePrefsWizard; import com.newcell.calltext.wizards.WizardChooser; import com.newcell.calltext.wizards.WizardUtils; import com.newcell.calltext.wizards.WizardUtils.WizardInfo; public class AccountsEditListFragment extends CSSListFragment implements /*OnQuitListener,*/ OnCheckedRowListener { private boolean dualPane; private Long curCheckPosition = SipProfile.INVALID_ID; //private String curCheckWizard = WizardUtils.EXPERT_WIZARD_TAG; private final Handler mHandler = new Handler(); private AccountStatusContentObserver statusObserver = null; private View mHeaderView; private AccountsEditListAdapter mAdapter; class AccountStatusContentObserver extends ContentObserver { public AccountStatusContentObserver(Handler h) { super(h); } public void onChange(boolean selfChange) { Log.d(THIS_FILE, "Accounts status.onChange( " + selfChange + ")"); ((BaseAdapter) getListAdapter()).notifyDataSetChanged(); } } private final static String CURRENT_CHOICE = "curChoice"; //private final static String CURRENT_WIZARD = "curWizard"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setHasOptionsMenu(true); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); ListView lv = getListView(); //getListView().setSelector(R.drawable.transparent); lv.setCacheColorHint(Color.TRANSPARENT); // View management View detailsFrame = getActivity().findViewById(R.id.details); dualPane = detailsFrame != null && detailsFrame.getVisibility() == View.VISIBLE; if (savedInstanceState != null) { // Restore last state for checked position. curCheckPosition = savedInstanceState.getLong(CURRENT_CHOICE, SipProfile.INVALID_ID); //curCheckWizard = savedInstanceState.getString(CURRENT_WIZARD); } setListShown(false); if (mAdapter == null) { if (mHeaderView != null) { lv.addHeaderView(mHeaderView, null, true); } mAdapter = new AccountsEditListAdapter(getActivity(), null); mAdapter.setOnCheckedRowListener(this); //getListView().setEmptyView(getActivity().findViewById(R.id.progress_container)); //getActivity().findViewById(android.R.id.empty).setVisibility(View.GONE); setListAdapter(mAdapter); registerForContextMenu(lv); // Prepare the loader. Either re-connect with an existing one, // or start a new one. getLoaderManager().initLoader(0, null, this); lv.setVerticalFadingEdgeEnabled(true); } if (dualPane) { // In dual-pane mode, the list view highlights the selected item. Log.d("lp", "dual pane mode"); lv.setChoiceMode(ListView.CHOICE_MODE_SINGLE); //lv.setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_LEFT); lv.setVerticalScrollBarEnabled(false); lv.setFadingEdgeLength(50); updateCheckedItem(); // Make sure our UI is in the correct state. //showDetails(curCheckPosition, curCheckWizard); } else { //getListView().setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_RIGHT); lv.setVerticalScrollBarEnabled(true); lv.setFadingEdgeLength(100); } } private static final int CHOOSE_WIZARD = 0; private static final int CHANGE_WIZARD = 1; // Menu stuff @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { menu.add(R.string.add_account).setIcon(android.R.drawable.ic_menu_add) .setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { onClickAddAccount(); return true; } }).setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); menu.add(R.string.reorder).setIcon(android.R.drawable.ic_menu_sort_by_size) .setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { AccountsEditListAdapter ad = (AccountsEditListAdapter) getListAdapter(); ad.toggleDraggable(); return true; } }).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); menu.add(R.string.backup_restore).setIcon(android.R.drawable.ic_menu_save) .setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { // Populate choice list List<String> items = new ArrayList<String>(); items.add(getResources().getString(R.string.backup)); final File backupDir = PreferencesWrapper.getConfigFolder(getActivity()); if (backupDir != null) { String[] filesNames = backupDir.list(); for (String fileName : filesNames) { items.add(fileName); } } final String[] fItems = (String[]) items.toArray(new String[0]); AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setTitle(R.string.backup_restore); builder.setItems(fItems, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int item) { if (item == 0) { SipProfileJson.saveSipConfiguration(getActivity()); } else { File fileToRestore = new File(backupDir + File.separator + fItems[item]); SipProfileJson.restoreSipConfiguration(getActivity(), fileToRestore); } } }); builder.setCancelable(true); AlertDialog backupDialog = builder.create(); backupDialog.show(); return true; } }); super.onCreateOptionsMenu(menu, inflater); } private static final String THIS_FILE = null; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Use custom drag and drop view View v = inflater.inflate(R.layout.accounts_edit_list, container, false); final DragnDropListView lv = (DragnDropListView) v.findViewById(android.R.id.list); lv.setGrabberId(R.id.grabber); // Setup the drop listener lv.setOnDropListener(new DropListener() { @Override public void drop(int from, int to) { Log.d(THIS_FILE, "Drop from " + from + " to " + to); int hvC = lv.getHeaderViewsCount(); from = Math.max(0, from - hvC); to = Math.max(0, to - hvC); int i; // First of all, compute what we get before move ArrayList<Long> orderedList = new ArrayList<Long>(); CursorAdapter ad = (CursorAdapter) getListAdapter(); for (i = 0; i < ad.getCount(); i++) { orderedList.add(ad.getItemId(i)); } // Then, invert in the current list the two items ids Long moved = orderedList.remove(from); orderedList.add(to, moved); // Finally save that in db if (getActivity() != null) { ContentResolver cr = getActivity().getContentResolver(); for (i = 0; i < orderedList.size(); i++) { Uri uri = ContentUris.withAppendedId(SipProfile.ACCOUNT_ID_URI_BASE, orderedList.get(i)); ContentValues cv = new ContentValues(); cv.put(SipProfile.FIELD_PRIORITY, i); cr.update(uri, cv, null, null); } } } }); OnClickListener addClickButtonListener = new OnClickListener() { @Override public void onClick(View v) { onClickAddAccount(); } }; // Header view mHeaderView = inflater.inflate(R.layout.generic_add_header_list, container, false); mHeaderView.setOnClickListener(addClickButtonListener); // Empty view Button bt = (Button) v.findViewById(android.R.id.empty); bt.setOnClickListener(addClickButtonListener); return v; } private void updateCheckedItem() { if (curCheckPosition != SipProfile.INVALID_ID) { for (int i = 0; i < getListAdapter().getCount(); i++) { long profId = getListAdapter().getItemId(i); if (profId == curCheckPosition) { getListView().setItemChecked(i, true); } } } else { for (int i = 0; i < getListAdapter().getCount(); i++) { getListView().setItemChecked(i, false); } } } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putLong(CURRENT_CHOICE, curCheckPosition); } @Override public void onResume() { super.onResume(); if (statusObserver == null) { statusObserver = new AccountStatusContentObserver(mHandler); getActivity().getContentResolver().registerContentObserver(SipProfile.ACCOUNT_STATUS_URI, true, statusObserver); } mAdapter.notifyDataSetChanged(); } @Override public void onPause() { super.onPause(); if (statusObserver != null) { getActivity().getContentResolver().unregisterContentObserver(statusObserver); statusObserver = null; } } @Override public void onListItemClick(ListView l, View v, int position, long id) { Log.d(THIS_FILE, "Checked " + position + " et " + id); ListView lv = getListView(); lv.setItemChecked(position, true); curCheckPosition = id; Cursor c = (Cursor) getListAdapter().getItem(position - lv.getHeaderViewsCount()); showDetails(id, c.getString(c.getColumnIndex(SipProfile.FIELD_WIZARD))); } /** * Helper function to show the details of a selected item, either by * displaying a fragment in-place in the current UI, or starting a * whole new activity in which it is displayed. */ private void showDetails(long profileId, String wizard) { //curCheckPosition = index; /* if (dualPane) { // If we are not currently showing a fragment for the new // position, we need to create and install a new one. AccountEditFragment df = AccountEditFragment.newInstance(profileId); //df.setOnQuitListener(this); // Execute a transaction, replacing any existing fragment // with this one inside the frame. FragmentTransaction ft = getFragmentManager().beginTransaction(); ft.replace(R.id.details, df, null); // ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); ft.setTransition(FragmentTransaction.TRANSIT_NONE); //if(profileId != Profile.INVALID_ID) { // ft.addToBackStack(null); //} ft.commit(); } else { */ // Otherwise we need to launch a new activity to display // the dialog fragment with selected text. Intent intent = new Intent(); //intent.setClass(getActivity(), AccountEdit.class); intent.setClass(getActivity(), BasePrefsWizard.class); if (profileId != SipProfile.INVALID_ID) { intent.putExtra(SipProfile.FIELD_ID, profileId); } intent.putExtra(SipProfile.FIELD_WIZARD, wizard); startActivity(intent); /* } */ } /* @Override public void onQuit() { curCheckPosition = SipProfile.INVALID_ID; if(dualPane) { showDetails(curCheckPosition, null); } } @Override public void onShowProfile(long profileId) { curCheckPosition = profileId; updateCheckedItem(); } */ @Override public void onToggleRow(AccountRowTag tag) { ContentValues cv = new ContentValues(); cv.put(SipProfile.FIELD_ACTIVE, !tag.activated); getActivity().getContentResolver() .update(ContentUris.withAppendedId(SipProfile.ACCOUNT_ID_URI_BASE, tag.accountId), cv, null, null); } @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { return new CursorLoader(getActivity(), SipProfile.ACCOUNT_URI, new String[] { SipProfile.FIELD_ID + " AS " + BaseColumns._ID, SipProfile.FIELD_ID, SipProfile.FIELD_DISPLAY_NAME, SipProfile.FIELD_WIZARD, SipProfile.FIELD_ACTIVE }, null, null, null); } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) { super.onLoadFinished(loader, data); // Select correct item if any updateCheckedItem(); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == Activity.RESULT_OK && data != null && data.getExtras() != null) { if (requestCode == CHOOSE_WIZARD) { // Wizard has been chosen, now create an account String wizardId = data.getStringExtra(WizardUtils.ID); Toast.makeText(getActivity(), "wizardId::" + wizardId, Toast.LENGTH_LONG).show(); if (wizardId != null) { showDetails(SipProfile.INVALID_ID, wizardId); } } else if (requestCode == CHANGE_WIZARD) { // Change wizard done for this account. String wizardId = data.getStringExtra(WizardUtils.ID); long accountId = data.getLongExtra(Intent.EXTRA_UID, SipProfile.INVALID_ID); if (wizardId != null && accountId != SipProfile.INVALID_ID) { ContentValues cv = new ContentValues(); cv.put(SipProfile.FIELD_WIZARD, wizardId); getActivity().getContentResolver().update( ContentUris.withAppendedId(SipProfile.ACCOUNT_ID_URI_BASE, accountId), cv, null, null); } } } } // Context menu stuff // Activate / deactive menu public static final int MENU_ITEM_ACTIVATE = Menu.FIRST; // Modify the account public static final int MENU_ITEM_MODIFY = Menu.FIRST + 1; // Delete the account public static final int MENU_ITEM_DELETE = Menu.FIRST + 2; // Change the wizard of the account public static final int MENU_ITEM_WIZARD = Menu.FIRST + 3; /** * Retrieve sip account from a given context menu info pressed * @param cmi The context menu info to retrieve infos from * @return corresponding sip profile if everything goes well, null if not able to retrieve profile */ private SipProfile profileFromContextMenuInfo(ContextMenuInfo cmi) { AdapterView.AdapterContextMenuInfo info; try { info = (AdapterView.AdapterContextMenuInfo) cmi; } catch (ClassCastException e) { Log.e(THIS_FILE, "bad menuInfo", e); return null; } Cursor c = (Cursor) getListAdapter().getItem(info.position - getListView().getHeaderViewsCount()); if (c == null) { // For some reason the requested item isn't available, do nothing return null; } return new SipProfile(c); } @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo); final SipProfile account = profileFromContextMenuInfo(menuInfo); if (account == null) { return; } WizardInfo wizardInfos = WizardUtils.getWizardClass(account.wizard); // Setup the menu header menu.setHeaderTitle(account.display_name); if (wizardInfos != null) { menu.setHeaderIcon(wizardInfos.icon); } menu.add(0, MENU_ITEM_ACTIVATE, 0, account.active ? R.string.deactivate_account : R.string.activate_account); menu.add(0, MENU_ITEM_MODIFY, 0, R.string.modify_account); menu.add(0, MENU_ITEM_DELETE, 0, R.string.delete_account); menu.add(0, MENU_ITEM_WIZARD, 0, R.string.choose_wizard); } @Override public boolean onContextItemSelected(android.view.MenuItem item) { final SipProfile account = profileFromContextMenuInfo(item.getMenuInfo()); if (account == null) { // For some reason the requested item isn't available, do nothing return super.onContextItemSelected(item); } switch (item.getItemId()) { case MENU_ITEM_DELETE: { getActivity().getContentResolver() .delete(ContentUris.withAppendedId(SipProfile.ACCOUNT_ID_URI_BASE, account.id), null, null); return true; } case MENU_ITEM_MODIFY: { showDetails(account.id, account.wizard); return true; } case MENU_ITEM_ACTIVATE: { ContentValues cv = new ContentValues(); cv.put(SipProfile.FIELD_ACTIVE, !account.active); getActivity().getContentResolver() .update(ContentUris.withAppendedId(SipProfile.ACCOUNT_ID_URI_BASE, account.id), cv, null, null); return true; } case MENU_ITEM_WIZARD: { Intent it = new Intent(getActivity(), WizardChooser.class); it.putExtra(Intent.EXTRA_UID, account.id); startActivityForResult(it, CHANGE_WIZARD); return true; } } return super.onContextItemSelected(item); } private void onClickAddAccount() { /* * 6/10/2014 Removed to simply use the BASIC account wizard startActivityForResult(new Intent(getActivity(), WizardChooser.class), CHOOSE_WIZARD); * */ /* * TODO:: * Add in progress circle and pull users account info from server. on download finished * open BASIC wizard with info received auto-populated * Use AsyncTask to call: * (JSONArray) AccountDownloader1.downloadJSON( (String) url) * Do all work in the AsyncTask, like progress circle, etc. * * OR * * startActivityForResult??? with AccountDownloader * http://developer.android.com/training/basics/intents/result.html * http://stackoverflow.com/questions/7151285/make-a-rest-api-call-from-an-intentservice-or-an-asynctask * * OR * * CursorLoaders * https://developer.android.com/training/load-data-background/index.html * http://stackoverflow.com/questions/5603504/android-3-0-what-are-the-advantages-of-using-loadermanager-instances-exactly */ showDetails(SipProfile.INVALID_ID, "BASIC"); } @Override public void changeCursor(Cursor c) { if (mAdapter != null) { mAdapter.changeCursor(c); } } }