Java tutorial
/** * Notifry for Android. * * Copyright 2011 Daniel Foote * * 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. * * The contents of this are heavily based on this blog post: * http://blog.notdot.net/2010/05/Authenticating-against-App-Engine-from-an-Android-app * Thanks dude for your awesome writeup! */ package com.notifry.android; import java.util.ArrayList; import java.util.HashMap; import org.json.JSONException; import android.accounts.AccountManager; import android.app.AlertDialog; import android.app.ListActivity; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.content.res.Configuration; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.preference.PreferenceManager; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import com.google.android.c2dm.C2DMessaging; import com.notifry.android.database.NotifryAccount; import com.notifry.android.remote.BackendRequest; import com.notifry.android.remote.BackendResponse; public class ChooseAccount extends ListActivity implements CompoundButton.OnCheckedChangeListener, View.OnClickListener { /** * Handler for when you check or uncheck an account, which fires off a * request to the server. * * @param account * @param state */ public void checkedAccount(NotifryAccount account, boolean state) { // Refresh the account object. In case it's changed. NotifryAccount refreshedAccount = NotifryAccount.FACTORY.get(this, account.getId()); // Add some metadata to the request so we know how to deal with it // afterwards. HashMap<String, Object> metadata = new HashMap<String, Object>(); metadata.put("account", refreshedAccount); String statusMessage = "Busy..."; if (state) { // Enable the account. metadata.put("operation", "register"); metadata.put("registration", C2DMessaging.getRegistrationId(this)); statusMessage = getString(R.string.registering_with_server); } else { // Disable the account. metadata.put("operation", "deregister"); statusMessage = getString(R.string.deregistering_with_server); } // And send off the request. refreshedAccount.registerWithBackend(this, C2DMessaging.getRegistrationId(this), state, statusMessage, handler, metadata); } /** * Handler for when you click an account name. * * @param account */ public void clickAccountName(NotifryAccount account) { // If enabled, launch the sources list for this account. if (account.getEnabled()) { Intent intent = new Intent(getBaseContext(), SourceList.class); intent.putExtra("account", account.getAccountName()); startActivity(intent); } } @Override public void onCheckedChanged(CompoundButton checkbox, boolean checked) { Long id = (Long) checkbox.getTag(); NotifryAccount account = NotifryAccount.FACTORY.get(this, id); this.checkedAccount(account, checked); } @Override public void onClick(View textview) { Long id = (Long) textview.getTag(); NotifryAccount account = NotifryAccount.FACTORY.get(this, id); this.clickAccountName(account); } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); setContentView(R.layout.screen_accounts); } /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); AccountManager accountManager = AccountManager.get(getApplicationContext()); // Sync our database. Only on create. NotifryAccount.FACTORY.syncAccountList(this, accountManager); // Set the layout, and allow text filtering. setContentView(R.layout.screen_accounts); getListView().setTextFilterEnabled(true); } @Override public boolean onCreateOptionsMenu(Menu menu) { boolean result = super.onCreateOptionsMenu(menu); menu.add(0, REFRESH_IDS, 0, R.string.refresh_ids).setIcon(android.R.drawable.ic_menu_rotate) .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT); return result; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: Intent intent = new Intent(this, Notifry.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(intent); return true; case REFRESH_IDS: // Dispatch this to the updater service. Intent intentData = new Intent(getBaseContext(), UpdaterService.class); intentData.putExtra("type", "registration"); intentData.putExtra("registration", C2DMessaging.getRegistrationId(this)); startService(intentData); Toast.makeText(thisActivity, getString(R.string.background_refreshing), Toast.LENGTH_SHORT).show(); return true; } return super.onOptionsItemSelected(item); } @Override public void onResume() { super.onResume(); // When coming back, refresh our list of accounts. refreshView(); // Show the help. SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this); if (settings.getBoolean("chooseAccountFirstTime", true)) { // Show first time help. new AlertDialog.Builder(this).setTitle(getString(R.string.accounts_and_sources)) .setMessage(getString(R.string.accounts_help)).setPositiveButton(getString(R.string.ok), null) .show(); // And don't show again. Editor editor = settings.edit(); editor.putBoolean("chooseAccountFirstTime", false); editor.commit(); } } /** * Refresh the list of accounts viewed by this activity. */ public void refreshView() { // Refresh our list of accounts. ArrayList<NotifryAccount> accounts = NotifryAccount.FACTORY.listAll(this); this.setListAdapter(new AccountArrayAdapter(this, this, R.layout.account_list_row, accounts)); } /** * An array adapter to put accounts into the list view. * * @author daniel */ private class AccountArrayAdapter extends ArrayAdapter<NotifryAccount> { public AccountArrayAdapter(ChooseAccount parentActivity, Context context, int textViewResourceId, ArrayList<NotifryAccount> objects) { super(context, textViewResourceId, objects); this.parentActivity = parentActivity; this.accounts = objects; } @Override public View getView(int position, View convertView, ViewGroup parent) { // Inflate a view if required. if (convertView == null) { LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = inflater.inflate(R.layout.account_list_row, null); } convertView.setClickable(true); convertView.setLongClickable(true); // Find the account. final NotifryAccount account = this.accounts.get(position); // And set the values on our row. if (account != null) { TextView title = (TextView) convertView.findViewById(R.id.account_row_account_name); CheckBox enabled = (CheckBox) convertView.findViewById(R.id.account_row_account_enabled); ImageView nextView = (ImageView) convertView.findViewById(R.id.account_row_account_next); if (title != null) { title.setText(account.getAccountName()); title.setTag(account.getId()); title.setClickable(true); title.setOnClickListener(parentActivity); } if (enabled != null) { enabled.setChecked(account.getEnabled()); enabled.setTag(account.getId()); enabled.setOnCheckedChangeListener(parentActivity); } if (nextView != null) { if (account.getEnabled()) { nextView.setImageResource(R.drawable.arrow_next); nextView.setClickable(true); nextView.setTag(account.getId()); nextView.setOnClickListener(parentActivity); nextView.setVisibility(View.VISIBLE); } else { nextView.setVisibility(View.GONE); } } } return convertView; } final private ChooseAccount parentActivity; private final ArrayList<NotifryAccount> accounts; } private static final int REFRESH_IDS = 1; private static final String TAG = "Notifry"; private final ChooseAccount thisActivity = this; /** * Private handler class that is the callback for when the external requests * are complete. */ private final Handler handler = new Handler() { @Override public void handleMessage(Message msg) { // Fetch out the response. BackendResponse response = (BackendResponse) msg.obj; // Was it successful? if (response.isError()) { // No, not successful. Toast.makeText(thisActivity, response.getError() + " - Please try again.", Toast.LENGTH_LONG) .show(); } else { try { // Fetch out metadata. BackendRequest request = response.getRequest(); NotifryAccount account = (NotifryAccount) request.getMeta("account"); String operation = (String) request.getMeta("operation"); // Determine our operation. if (operation.equals("register")) { // We were registering the account. // The server would have given us a registration ID. account.setServerRegistrationId( Long.parseLong(response.getJSON().getJSONObject("device").getString("id"))); // Enable the account. account.setEnabled(true); // We need a refresh. account.setRequiresSync(true); // Store the registration ID. account.setLastC2DMId((String) request.getMeta("registration")); // Persist it. account.save(thisActivity); refreshView(); Toast.makeText(thisActivity, String.format( getString(R.string.registering_with_server_complete, account.getAccountName())), Toast.LENGTH_SHORT).show(); } else if (operation.equals("deregister")) { // We've deregistered the account. account.setServerRegistrationId(null); account.setEnabled(false); account.setLastC2DMId(null); // Persist it to the database. account.save(thisActivity); refreshView(); Toast.makeText(thisActivity, String.format( getString(R.string.deregistering_with_server_complete, account.getAccountName())), Toast.LENGTH_SHORT).show(); } } catch (JSONException e) { // The response doesn't look like we expected. Log.d(TAG, "Invalid response from server: " + e.getMessage()); Toast.makeText(thisActivity, "Invalid response from the server.", Toast.LENGTH_LONG).show(); refreshView(); } } } }; }