edu.berkeley.boinc.attach.SelectionListActivity.java Source code

Java tutorial

Introduction

Here is the source code for edu.berkeley.boinc.attach.SelectionListActivity.java

Source

/*******************************************************************************
 * This file is part of BOINC.
 * http://boinc.berkeley.edu
 * Copyright (C) 2016 University of California
 * 
 * BOINC is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation,
 * either version 3 of the License, or (at your option) any later version.
 * 
 * BOINC 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 Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with BOINC.  If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/

package edu.berkeley.boinc.attach;

import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import android.view.View;
import android.widget.ListView;
import android.widget.Toast;
import edu.berkeley.boinc.R;
import edu.berkeley.boinc.client.IMonitor;
import edu.berkeley.boinc.client.Monitor;
import edu.berkeley.boinc.rpc.ProjectInfo;
import edu.berkeley.boinc.utils.Logging;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;

public class SelectionListActivity extends FragmentActivity {

    private ListView lv;
    ArrayList<ProjectListEntry> entries = new ArrayList<ProjectListEntry>();
    ArrayList<ProjectInfo> selected = new ArrayList<ProjectInfo>();

    // services
    private IMonitor monitor = null;
    private boolean mIsBound = false;
    private ProjectAttachService attachService = null;
    private boolean asIsBound = false;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (Logging.DEBUG)
            Log.d(Logging.TAG, "AttachProjectListActivity onCreate");

        doBindService();

        // setup layout
        setContentView(R.layout.attach_project_list_layout);
        lv = (ListView) findViewById(R.id.listview);
    }

    @Override
    protected void onDestroy() {
        if (Logging.VERBOSE)
            Log.v(Logging.TAG, "AttachProjectListActivity onDestroy");
        doUnbindService();
        super.onDestroy();
    }

    // check whether user has checked at least a single project
    // shows toast otherwise
    private Boolean checkProjectChecked() {
        for (ProjectListEntry tmp : entries) {
            if (tmp.checked)
                return true;
        }
        Toast toast = Toast.makeText(getApplicationContext(), R.string.attachproject_list_header,
                Toast.LENGTH_SHORT);
        toast.show();
        if (Logging.DEBUG)
            Log.d(Logging.TAG, "AttachProjectListActivity no project selected, stop!");
        return false;
    }

    // check whether device is online before starting connection attempt
    // as needed for AttachProjectLoginActivity (retrieval of ProjectConfig)
    // note: available internet does not guarantee connection to project server
    // is possible!
    private Boolean checkDeviceOnline() {
        ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(
                Context.CONNECTIVITY_SERVICE);
        NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
        Boolean online = activeNetworkInfo != null && activeNetworkInfo.isConnectedOrConnecting();
        if (!online) {
            Toast toast = Toast.makeText(getApplicationContext(), R.string.attachproject_list_no_internet,
                    Toast.LENGTH_SHORT);
            toast.show();
            if (Logging.DEBUG)
                Log.d(Logging.TAG, "AttachProjectListActivity not online, stop!");
        }
        return online;
    }

    // triggered by continue button
    public void continueClicked(View v) {
        if (!checkProjectChecked())
            return;
        if (!checkDeviceOnline())
            return;

        String selectedProjectsDebug = "";
        // get selected projects
        selected.clear();
        for (ProjectListEntry tmp : entries) {
            if (tmp.checked) {
                selected.add(tmp.info);
                selectedProjectsDebug += tmp.info.name + ",";
            }
        }
        if (Logging.DEBUG)
            Log.d(Logging.TAG, "SelectionListActivity: selected projects: " + selectedProjectsDebug);

        attachService.setSelectedProjects(selected); // returns immediately

        // start credential input activity
        startActivity(new Intent(this, CredentialInputActivity.class));
    }

    private ServiceConnection mMonitorConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            // This is called when the connection with the service has been established, getService returns 
            // the Monitor object that is needed to call functions.
            monitor = IMonitor.Stub.asInterface(service);
            mIsBound = true;

            UpdateProjectListAsyncTask task = new UpdateProjectListAsyncTask();
            task.execute();
        }

        public void onServiceDisconnected(ComponentName className) {
            // This should not happen
            monitor = null;
            mIsBound = false;
        }
    };

    private ServiceConnection mASConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            // This is called when the connection with the service has been established, getService returns 
            // the Monitor object that is needed to call functions.
            attachService = ((ProjectAttachService.LocalBinder) service).getService();
            asIsBound = true;
        }

        public void onServiceDisconnected(ComponentName className) {
            // This should not happen
            attachService = null;
            asIsBound = false;
        }
    };

    private void doBindService() {
        // start service to allow setForeground later on...
        startService(new Intent(this, Monitor.class));
        // Establish a connection with the service, onServiceConnected gets called when
        bindService(new Intent(this, Monitor.class), mMonitorConnection, Service.BIND_AUTO_CREATE);
        // bind to attach service
        bindService(new Intent(this, ProjectAttachService.class), mASConnection, Service.BIND_AUTO_CREATE);
    }

    private void doUnbindService() {
        if (mIsBound) {
            // Detach existing connection.
            unbindService(mMonitorConnection);
            mIsBound = false;
        }
        if (asIsBound) {
            // Detach existing connection.
            unbindService(mASConnection);
            asIsBound = false;
        }
    }

    private class UpdateProjectListAsyncTask extends AsyncTask<Void, Void, ArrayList<ProjectInfo>> {
        @Override
        protected ArrayList<ProjectInfo> doInBackground(Void... arg0) {
            ArrayList<ProjectInfo> data = null;
            boolean retry = true;
            // Try to get the project list for as long as the AsyncTask has not been canceled
            while (retry) {
                try {
                    data = (ArrayList<ProjectInfo>) monitor.getAttachableProjects();
                } catch (RemoteException e) {
                    if (Log.isLoggable(Logging.TAG, Log.WARN))
                        Log.w(Logging.TAG, e);
                }
                if (super.isCancelled())
                    return data; // Does not matter if data == null or not
                if (data == null) {
                    if (Logging.WARNING)
                        Log.w(Logging.TAG, "UpdateProjectListAsyncTask: failed to retrieve data, retry....");
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        if (Log.isLoggable(Logging.TAG, Log.DEBUG))
                            Log.d(Logging.TAG, e.getLocalizedMessage(), e);
                    }
                } else
                    retry = false;
            }
            if (Logging.DEBUG)
                Log.d(Logging.TAG, "monitor.getAttachableProjects returned with " + data.size() + " elements");
            // Clear current ProjectListEntries since we successfully have got new ProjectInfos
            SelectionListActivity.this.entries.clear();
            // Transform ProjectInfos into ProjectListEntries
            for (int i = data.size() - 1; i >= 0; i--) {
                if (super.isCancelled())
                    return data;
                SelectionListActivity.this.entries.add(new ProjectListEntry(data.get(i)));
            }
            // Set preferred Collator for ProjectListEntries before sorting
            if (SelectionListActivity.ProjectListEntry.collator == null) {
                SelectionListActivity.ProjectListEntry.collator = SelectionListActivity.ProjectListEntry
                        .getCollator();
            }
            // Sort ProjectListEntries off the UI thread
            // Unfortunately, there is no way to stop this sort operation if this AsyncTask gets canceled
            Collections.sort(SelectionListActivity.this.entries);
            // Dispose Collator instance after sorting
            SelectionListActivity.ProjectListEntry.collator = null;
            return data;
        }

        protected final void onPostExecute(final ArrayList<ProjectInfo> result) {
            if (result == null)
                return;

            SelectionListActivity.this.entries.add(new ProjectListEntry()); // add account manager option to bottom of list
            SelectionListAdapter listAdapter = new SelectionListAdapter(SelectionListActivity.this, R.id.listview,
                    entries);
            lv.setAdapter(listAdapter);
        }
    }

    static final class ProjectListEntry implements Comparable<SelectionListActivity.ProjectListEntry> {
        public ProjectInfo info;
        public boolean checked;
        public boolean am; //indicates that element is account manager entry

        /**
         * The {@link Collator} used when comparing {@code ProjectListEntry}s.
         * This member is usually only set when performing comparison operations
         * in bulk. Otherwise, it should be set to {@code null} to avoid having
         * a {@link Collator} instance lingering around when not attaching
         * projects (most of the time). Furthermore, when comparing
         * {@code ProjectListEntry}s in bulk the {@link Collator} instance
         * stored by this member does not need to get reallocated and setup on
         * every comparison.
         *
         * @see SelectionListActivity.ProjectListEntry#getCollator()
         * @see Collator
         */
        static Collator collator;

        public ProjectListEntry(ProjectInfo info) {
            this.info = info;
            this.checked = false;
        }

        /**
         * Creates Account manager list object
         */
        public ProjectListEntry() {
            this.am = true;
        }

        /**
         * Compares this {@code ProjectListEntry} instance to {@code p} based
         * on {@link ProjectInfo#name}. The comparison is <i>case-insensitive</i>.
         *
         * @param p the {@code ProjectListEntry} to compare to
         * @return {@code 0} if both {@link ProjectInfo#name}s are equal,<br>
         * {@code -1} if {@code this} {@link ProjectInfo#name} comes before
         * {@code p}'s {@link ProjectInfo#name} in the current locale's
         * {@link Collator#getInstance() collation},<br>else {@code 1}
         * @see SelectionListActivity.ProjectListEntry#info
         * @see ProjectInfo#name
         * @see Comparable#compareTo(T)
         */
        public final int compareTo(final SelectionListActivity.ProjectListEntry p) {
            return (SelectionListActivity.ProjectListEntry.collator == null
                    ? SelectionListActivity.ProjectListEntry.getCollator()
                    : SelectionListActivity.ProjectListEntry.collator).compare(this.info.name, p.info.name);
        }

        /**
         * Gets the preferred locale specific {@link Collator} for comparing {@code ProjectListEntry}s.
         *
         * @return the preferred {@link Collator}
         */
        static final Collator getCollator() {
            final Collator collator;
            (collator = Collator.getInstance()).setStrength(Collator.SECONDARY);
            collator.setDecomposition(Collator.NO_DECOMPOSITION);
            return collator;
        }
    }
}