Java tutorial
/* * Copyright (C) 2012 Pixmob (http://github.com/pixmob) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.pixmob.fm2.ui; import static org.pixmob.fm2.Constants.TAG; import java.io.IOException; import java.net.SocketTimeoutException; import java.net.UnknownHostException; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.pixmob.fm2.R; import org.pixmob.fm2.model.Account; import org.pixmob.fm2.model.AccountRepository; import org.pixmob.fm2.net.AccountNetworkClient; import org.pixmob.fm2.services.SyncService; import org.pixmob.fm2.util.HttpUtils; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.graphics.Bitmap; import android.os.AsyncTask; import android.os.Bundle; import android.support.v4.app.DialogFragment; import android.support.v4.app.Fragment; import android.support.v4.app.LoaderManager.LoaderCallbacks; import android.support.v4.content.AsyncTaskLoader; import android.support.v4.content.Loader; import android.support.v4.view.Menu; import android.support.v4.view.MenuItem; import android.util.Log; import android.view.LayoutInflater; import android.view.MenuInflater; import android.view.View; import android.view.ViewGroup; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.Toast; import com.bugsense.trace.BugSenseHandler; /** * {@link Fragment} for displaying account details. * @author Pixmob */ public class AccountDetailsFragment extends Fragment implements LoaderCallbacks<Set<String>> { private WebView webView; private View webLoadingPanel; private View webLoadingError; private boolean dualPane; public static AccountDetailsFragment newInstance(Account account) { final Bundle args = new Bundle(1); args.putSerializable("account", account); final AccountDetailsFragment f = new AccountDetailsFragment(); f.setArguments(args); return f; } public Account getAccount() { return (Account) getArguments().getSerializable("account"); } public void refresh() { onActionRefresh(); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); if (!dualPane) { menu.add(Menu.NONE, R.string.menu_refresh, Menu.NONE, R.string.menu_refresh) .setIcon(R.drawable.ic_menu_refresh).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); } menu.add(Menu.NONE, R.string.menu_delete_account, Menu.NONE, R.string.menu_delete_account) .setIcon(R.drawable.ic_menu_delete).setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.string.menu_refresh: onActionRefresh(); break; case R.string.menu_delete_account: onActionDeleteAccount(); break; } return super.onOptionsItemSelected(item); } private void onActionRefresh() { webLoadingPanel.setVisibility(View.VISIBLE); webLoadingError.setVisibility(View.GONE); webView.setVisibility(View.GONE); getLoaderManager().restartLoader(0, getArguments(), this); } private void onActionDeleteAccount() { ConfirmAccountDeletionDialogFragment.newInstance(getArguments()).show(getSupportFragmentManager(), "dialog"); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); final View detailsFrame = getActivity().findViewById(R.id.account_details); dualPane = detailsFrame != null && detailsFrame.getVisibility() == View.VISIBLE; setHasOptionsMenu(true); webLoadingError = getView().findViewById(R.id.account_web_loading_error); webLoadingPanel = getView().findViewById(R.id.account_web_loading_panel); webView = (WebView) getView().findViewById(R.id.account_web); webView.getSettings().setBuiltInZoomControls(true); webView.getSettings().setJavaScriptEnabled(true); webView.getSettings().setUserAgentString(HttpUtils.getUserAgent(getActivity())); webView.setWebViewClient(new WebViewClient() { @Override public void onPageFinished(WebView view, String url) { webLoadingPanel.setVisibility(View.GONE); webLoadingError.setVisibility(View.GONE); webView.setVisibility(View.VISIBLE); } @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { webLoadingPanel.setVisibility(View.VISIBLE); webLoadingError.setVisibility(View.GONE); webView.setVisibility(View.GONE); } @Override public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { webLoadingPanel.setVisibility(View.GONE); webLoadingError.setVisibility(View.VISIBLE); webView.setVisibility(View.GONE); } }); // Authenticate the user in a background task. // When authentication cookies are available, the method onLoadFinished // will be called, where we can load the account URL. getLoaderManager().initLoader(0, getArguments(), this); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.account_details, container, false); } @Override public Loader<Set<String>> onCreateLoader(int id, Bundle args) { final Account account = args != null ? (Account) args.getSerializable("account") : null; return new GetAuthCookies(getActivity(), account); } @Override public void onLoaderReset(Loader<Set<String>> loader) { } @Override public void onLoadFinished(Loader<Set<String>> loader, Set<String> cookies) { if (cookies != null && !cookies.isEmpty()) { // Build the Http Cookie header. final StringBuilder cookieHeader = new StringBuilder(128); for (final String cookie : cookies) { if (cookieHeader.length() != 0) { cookieHeader.append("; "); } cookieHeader.append(cookie); } final Map<String, String> httpHeaders = new HashMap<String, String>(1); httpHeaders.put("Cookie", cookieHeader.toString()); // Load the account URL with authentication cookies. webView.loadUrl("https://mobile.free.fr/moncompte/index.php?page=commande", httpHeaders); } } /** * Task for getting authentication cookies. * @author Pixmob */ private static class GetAuthCookies extends AsyncTaskLoader<Set<String>> { private final Account account; private Set<String> cookies; public GetAuthCookies(final Context context, final Account account) { super(context); this.account = account; } @Override protected void onStartLoading() { super.onStartLoading(); if (cookies != null && !cookies.isEmpty()) { deliverResult(cookies); } else { forceLoad(); } } @Override public Set<String> loadInBackground() { if (account != null) { try { return authenticate(); } catch (IOException e) { Log.e(TAG, "Cannot load authentication cookies for user " + account.login, e); if (!(e instanceof UnknownHostException) && !(e instanceof SocketTimeoutException)) { // Only report useful errors. BugSenseHandler.log(TAG, e); } } } return Collections.emptySet(); } private Set<String> authenticate() throws IOException { cookies = new HashSet<String>(4); final AccountNetworkClient client = new AccountNetworkClient(getContext()); if (!client.authenticate(account, cookies)) { throw new IOException("Authentication failed for user " + account.login); } return cookies; } } /** * Dialog for confirming account deletion. * @author Pixmob */ private static class ConfirmAccountDeletionDialogFragment extends DialogFragment { public static ConfirmAccountDeletionDialogFragment newInstance(Bundle arguments) { final ConfirmAccountDeletionDialogFragment f = new ConfirmAccountDeletionDialogFragment(); f.setArguments(arguments); return f; } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { return new AlertDialog.Builder(getActivity()).setTitle(R.string.menu_delete_account) .setIcon(android.R.drawable.ic_dialog_alert).setMessage(R.string.confirm_account_deletion) .setPositiveButton(R.string.dialog_delete, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { final boolean dualPane = getSupportFragmentManager() .findFragmentById(R.id.accounts) != null; final Account account = (Account) getArguments().getSerializable("account"); new DeleteAccountTask(getActivity(), account, dualPane).execute(); } }).setNegativeButton(R.string.dialog_cancel, null).create(); } } /** * Background task for deleting an account. * @author Pixmob */ private static class DeleteAccountTask extends AsyncTask<Void, Void, Void> { private final Activity context; private final Account account; private final boolean dualPane; public DeleteAccountTask(final Activity context, final Account account, final boolean dualPane) { this.context = context; this.account = account; this.dualPane = dualPane; } @Override protected Void doInBackground(Void... params) { final AccountRepository accountRepository = new AccountRepository(context); accountRepository.delete(account); return null; } @Override protected void onPostExecute(Void result) { super.onPostExecute(result); Toast.makeText(context, context.getString(R.string.account_deleted), Toast.LENGTH_SHORT).show(); if (dualPane) { context.startService(new Intent(context, SyncService.class)); } else { context.finish(); } } } }