Java tutorial
/******************************************************************************* * This file is part of BOINC. * http://boinc.berkeley.edu * Copyright (C) 2012 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; import android.app.Dialog; import android.app.Service; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Configuration; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.view.Window; import android.widget.AdapterView; import android.widget.Button; import android.widget.ListView; import android.widget.TextView; import android.support.v4.app.ActionBarDrawerToggle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentTransaction; import android.support.v4.widget.DrawerLayout; import android.support.v7.app.ActionBarActivity; import edu.berkeley.boinc.adapter.NavDrawerListAdapter; import edu.berkeley.boinc.adapter.NavDrawerListAdapter.NavDrawerItem; import edu.berkeley.boinc.attach.SelectionListActivity; import edu.berkeley.boinc.client.ClientStatus; import edu.berkeley.boinc.client.Monitor; import edu.berkeley.boinc.client.IMonitor; import edu.berkeley.boinc.rpc.Project; import edu.berkeley.boinc.utils.BOINCDefs; import edu.berkeley.boinc.utils.Logging; import java.util.ArrayList; public class BOINCActivity extends ActionBarActivity { public static IMonitor monitor; private Integer clientComputingStatus = -1; private Integer numberProjectsInNavList = 0; static Boolean mIsBound = false; // app title (changes with nav bar selection) private CharSequence mTitle; // nav drawer title private CharSequence mDrawerTitle; private DrawerLayout mDrawerLayout; private ListView mDrawerList; private ActionBarDrawerToggle mDrawerToggle; private NavDrawerListAdapter mDrawerListAdapter; private ServiceConnection mConnection = 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; determineStatus(); } public void onServiceDisconnected(ComponentName className) { // This should not happen monitor = null; mIsBound = false; Log.e(Logging.TAG, "BOINCActivity onServiceDisconnected"); } }; private BroadcastReceiver mClientStatusChangeRec = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (Logging.VERBOSE) Log.d(Logging.TAG, "BOINCActivity ClientStatusChange - onReceive()"); determineStatus(); } }; private IntentFilter ifcsc = new IntentFilter("edu.berkeley.boinc.clientstatuschange"); @Override public void onCreate(Bundle savedInstanceState) { if (Logging.DEBUG) Log.d(Logging.TAG, "BOINCActivity onCreate()"); super.onCreate(savedInstanceState); setContentView(R.layout.main); // setup navigation bar mTitle = mDrawerTitle = getTitle(); mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); mDrawerList = (ListView) findViewById(R.id.list_slidermenu); mDrawerList.setOnItemClickListener(new ListView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { // display view for selected nav drawer item dispatchNavBarOnClick(mDrawerListAdapter.getItem(position), false); } }); mDrawerListAdapter = new NavDrawerListAdapter(getApplicationContext()); mDrawerList.setAdapter(mDrawerListAdapter); // enabling action bar app icon and behaving it as toggle button getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setHomeButtonEnabled(true); mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, R.drawable.ic_drawer, //nav menu toggle icon R.string.app_name, // nav drawer open - description for accessibility R.string.app_name // nav drawer close - description for accessibility ) { public void onDrawerClosed(View view) { getSupportActionBar().setTitle(mTitle); // calling onPrepareOptionsMenu() to show action bar icons supportInvalidateOptionsMenu(); } public void onDrawerOpened(View drawerView) { getSupportActionBar().setTitle(mDrawerTitle); mDrawerListAdapter.notifyDataSetChanged(); // force redraw of all items (adapter.getView()) in order to adapt changing icons or number of tasks/notices // calling onPrepareOptionsMenu() to hide action bar icons supportInvalidateOptionsMenu(); } }; mDrawerLayout.setDrawerListener(mDrawerToggle); // pre-select fragment // 1. check if explicitly requested fragment present // e.g. after initial project attach. int targetFragId = getIntent().getIntExtra("targetFragment", -1); // 2. if no explicit request, try to restore previous selection if (targetFragId < 0 && savedInstanceState != null) targetFragId = savedInstanceState.getInt("navBarSelectionId"); NavDrawerItem item = null; if (targetFragId < 0) { // if non of the above, go to default item = mDrawerListAdapter.getItem(0); } else item = mDrawerListAdapter.getItemForId(targetFragId); if (item != null) dispatchNavBarOnClick(item, true); else if (Logging.WARNING) Log.w(Logging.TAG, "onCreate: fragment selection returned null"); //bind monitor service doBindService(); } @Override protected void onSaveInstanceState(Bundle outState) { outState.putInt("navBarSelectionId", mDrawerListAdapter.selectedMenuId); super.onSaveInstanceState(outState); } @Override protected void onDestroy() { if (Logging.DEBUG) Log.d(Logging.TAG, "BOINCActivity onDestroy()"); doUnbindService(); super.onDestroy(); } @Override protected void onNewIntent(Intent intent) { if (Logging.DEBUG) Log.d(Logging.TAG, "BOINCActivity onNewIntent()"); // onNewIntent gets called if activity is brought to front via intent, but was still alive, so onCreate is not called again // getIntent always returns the intent activity was created of, so this method is the only hook to receive an updated intent // e.g. after (not initial) project attach super.onNewIntent(intent); // navigate to explicitly requested fragment (e.g. after project attach) int id = intent.getIntExtra("targetFragment", -1); if (Logging.DEBUG) Log.d(Logging.TAG, "BOINCActivity onNewIntent() for target fragment: " + id); NavDrawerItem item = mDrawerListAdapter.getItemForId(id); if (item != null) dispatchNavBarOnClick(item, false); else if (Logging.WARNING) Log.w(Logging.TAG, "onNewIntent: requested target fragment is null, for id: " + id); } @Override protected void onResume() { // gets called by system every time activity comes to front. after onCreate upon first creation super.onResume(); registerReceiver(mClientStatusChangeRec, ifcsc); determineStatus(); } @Override protected void onPause() { // gets called by system every time activity loses focus. if (Logging.VERBOSE) Log.v(Logging.TAG, "BOINCActivity onPause()"); super.onPause(); unregisterReceiver(mClientStatusChangeRec); } 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), mConnection, Service.BIND_AUTO_CREATE); } private void doUnbindService() { if (mIsBound) { // Detach existing connection. unbindService(mConnection); mIsBound = false; } } /* public IMonitor getMonitorService() { if(!mIsBound) if(Logging.WARNING) Log.w(Logging.TAG, "Fragment trying to obtain serive reference, but Monitor not bound in BOINCActivity"); return monitor; }*/ public void startAttachProjectListActivity() { if (Logging.DEBUG) Log.d(Logging.TAG, "BOINCActivity attempt to start "); startActivity(new Intent(this, SelectionListActivity.class)); } /** * React to selection of nav bar item * @param item * @param position * @param init */ private void dispatchNavBarOnClick(NavDrawerItem item, boolean init) { // update the main content by replacing fragments if (item == null) { if (Logging.WARNING) Log.w(Logging.TAG, "dispatchNavBarOnClick returns, item null."); return; } if (Logging.DEBUG) Log.d(Logging.TAG, "dispatchNavBarOnClick for item with id: " + item.getId() + " title: " + item.getTitle() + " is project? " + item.isProjectItem()); FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); Boolean fragmentChanges = false; if (init) { // if init, setup status fragment ft.replace(R.id.status_container, new StatusFragment()); } if (!item.isProjectItem()) { switch (item.getId()) { case R.string.tab_tasks: ft.replace(R.id.frame_container, new TasksFragment()); fragmentChanges = true; break; case R.string.tab_notices: ft.replace(R.id.frame_container, new NoticesFragment()); fragmentChanges = true; break; case R.string.tab_projects: ft.replace(R.id.frame_container, new ProjectsFragment()); fragmentChanges = true; break; case R.string.menu_help: Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse("http://boinc.berkeley.edu/wiki/BOINC_Help")); startActivity(i); break; case R.string.menu_about: final Dialog dialog = new Dialog(this); dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); dialog.setContentView(R.layout.dialog_about); Button returnB = (Button) dialog.findViewById(R.id.returnB); TextView tvVersion = (TextView) dialog.findViewById(R.id.version); try { tvVersion.setText(getString(R.string.about_version) + " " + getPackageManager().getPackageInfo(getPackageName(), 0).versionName); } catch (NameNotFoundException e) { if (Logging.WARNING) Log.w(Logging.TAG, "version name not found."); } returnB.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { dialog.dismiss(); } }); dialog.show(); break; case R.string.menu_eventlog: startActivity(new Intent(this, EventLogActivity.class)); break; case R.string.projects_add: startActivity(new Intent(this, SelectionListActivity.class)); break; case R.string.tab_preferences: ft.replace(R.id.frame_container, new PrefsFragment()); fragmentChanges = true; break; default: if (Logging.ERROR) Log.d(Logging.TAG, "dispatchNavBarOnClick() could not find corresponding fragment for " + item.getTitle()); break; } } else { // ProjectDetailsFragment. Data shown based on given master URL Bundle args = new Bundle(); args.putString("url", item.getProjectMasterUrl()); Fragment frag = new ProjectDetailsFragment(); frag.setArguments(args); ft.replace(R.id.frame_container, frag); fragmentChanges = true; } mDrawerLayout.closeDrawer(mDrawerList); if (fragmentChanges) { ft.commit(); setTitle(item.getTitle()); mDrawerListAdapter.selectedMenuId = item.getId(); //highlight item persistently mDrawerListAdapter.notifyDataSetChanged(); // force redraw } if (Logging.DEBUG) Log.d(Logging.TAG, "displayFragmentForNavDrawer() " + item.getTitle()); } // tests whether status is available and whether it changed since the last event. private void determineStatus() { try { if (mIsBound) { Integer newComputingStatus = monitor.getComputingStatus(); if (newComputingStatus != clientComputingStatus) { // computing status has changed, update and invalidate to force adaption of action items clientComputingStatus = newComputingStatus; supportInvalidateOptionsMenu(); } if (numberProjectsInNavList != monitor.getProjects().size()) numberProjectsInNavList = mDrawerListAdapter .compareAndAddProjects((ArrayList<Project>) monitor.getProjects()); //setAppTitle(); } } catch (Exception e) { } } public final boolean onKeyDown(final int keyCode, final KeyEvent keyEvent) { if (keyCode == KeyEvent.KEYCODE_MENU) { if (this.mDrawerLayout.isDrawerOpen(this.mDrawerList)) { this.mDrawerLayout.closeDrawer(this.mDrawerList); } else { this.mDrawerLayout.openDrawer(this.mDrawerList); } return true; } return super.onKeyDown(keyCode, keyEvent); } @Override public boolean onCreateOptionsMenu(Menu menu) { if (Logging.DEBUG) Log.d(Logging.TAG, "BOINCActivity onCreateOptionsMenu()"); MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.main_menu, menu); return true; } @Override public boolean onPrepareOptionsMenu(Menu menu) { if (Logging.DEBUG) Log.d(Logging.TAG, "BOINCActivity onPrepareOptionsMenu()"); // run mode, set title and icon based on status MenuItem runMode = menu.findItem(R.id.run_mode); if (clientComputingStatus == ClientStatus.COMPUTING_STATUS_NEVER) { // display play button runMode.setTitle(R.string.menu_run_mode_enable); runMode.setIcon(R.drawable.playw); } else { // display stop button runMode.setTitle(R.string.menu_run_mode_disable); runMode.setIcon(R.drawable.pausew); } return super.onPrepareOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { if (Logging.DEBUG) Log.d(Logging.TAG, "BOINCActivity onOptionsItemSelected()"); // toggle drawer if (mDrawerToggle.onOptionsItemSelected(item)) { return true; } switch (item.getItemId()) { case R.id.run_mode: if (item.getTitle().equals(getApplication().getString(R.string.menu_run_mode_disable))) { if (Logging.DEBUG) Log.d(Logging.TAG, "run mode: disable"); new WriteClientModeAsync().execute(BOINCDefs.RUN_MODE_NEVER); } else if (item.getTitle().equals(getApplication().getString(R.string.menu_run_mode_enable))) { if (Logging.DEBUG) Log.d(Logging.TAG, "run mode: enable"); new WriteClientModeAsync().execute(BOINCDefs.RUN_MODE_AUTO); } else if (Logging.DEBUG) Log.d(Logging.TAG, "run mode: unrecognized command"); return true; case R.id.projects_add: startActivity(new Intent(this, SelectionListActivity.class)); return true; default: return super.onOptionsItemSelected(item); } } @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); // Sync the toggle state after onRestoreInstanceState has occurred. mDrawerToggle.syncState(); } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); // Pass any configuration change to the drawer toggls mDrawerToggle.onConfigurationChanged(newConfig); } @Override public void setTitle(CharSequence title) { mTitle = title; getSupportActionBar().setTitle(mTitle); } private final class WriteClientModeAsync extends AsyncTask<Integer, Void, Boolean> { @Override protected Boolean doInBackground(Integer... params) { // setting provided mode for both, CPU computation and network. Boolean runMode; try { runMode = monitor.setRunMode(params[0]); } catch (RemoteException e) { runMode = false; } Boolean networkMode; try { networkMode = monitor.setNetworkMode(params[0]); } catch (RemoteException e) { networkMode = false; } return runMode && networkMode; } @Override protected void onPostExecute(Boolean success) { if (success) try { monitor.forceRefresh(); } catch (RemoteException e) { } else if (Logging.WARNING) Log.w(Logging.TAG, "setting run and network mode failed"); } } }