Java tutorial
/** * Newtifry 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. */ package com.newtifry.android; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import org.json.JSONException; import com.actionbarsherlock.app.SherlockPreferenceActivity; import com.actionbarsherlock.view.MenuItem; import com.google.android.gcm.GCMRegistrar; import com.newtifry.android.database.NewtifryMessage; import com.newtifry.android.database.NewtifrySource; import com.newtifry.android.remote.BackendRequest; import com.newtifry.android.remote.BackendResponse; import com.newtifry.android.R; import static com.newtifry.android.CommonUtilities.TAG; import android.annotation.SuppressLint; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Configuration; import android.graphics.Color; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.preference.CheckBoxPreference; import android.preference.ListPreference; import android.preference.Preference; import android.preference.Preference.OnPreferenceChangeListener; import android.preference.Preference.OnPreferenceClickListener; import android.text.Html; import android.util.Log; import android.widget.Toast; @SuppressWarnings("deprecation") public class SourceEditor2 extends SherlockPreferenceActivity { private final SourceEditor2 thisActivity = this; private NewtifrySource source = null; public static SourceEditor2ORMPreferencesMapper preferenceMapper = null; CheckBoxPreference serverEnabled; Preference titlePreference; Preference sourceKey; Preference preferenceTest; Preference localEnablePreference; CheckBoxPreference localEnablePreferenceCheckBox; Preference sourceNotificationPreference; ListPreference sourceColorPreference; public class SourceEditor2ORMPreferencesMapper implements SharedPreferences { protected List<OnSharedPreferenceChangeListener> listeners = new LinkedList<OnSharedPreferenceChangeListener>(); public SourceEditor2ORMPreferencesMapper() { } public NewtifrySource getSource() { return thisActivity.source; } public boolean contains(String key) { Map<String, ?> values = this.getAll(); return values.containsKey(key); } public Editor edit() { return new Editor(); } public Map<String, ?> getAll() { Map<String, Object> values = new HashMap<String, Object>(); values.put(Preferences.SOURCE_SERVER, thisActivity.source.getServerEnabled()); values.put(Preferences.SOURCE_LOCAL, thisActivity.source.getLocalEnabled()); values.put(Preferences.SOURCE_NOTIFICATION, thisActivity.source.getNotification()); values.put(Preferences.SOURCE_USE_NOTIFIER_PRO, thisActivity.source.getNotifierPro()); values.put(Preferences.SOURCE_VIBRATE_NOTIFY, thisActivity.source.getVibrate()); values.put(Preferences.SOURCE_LED_FLASH, thisActivity.source.getLedFlash()); values.put(Preferences.SOURCE_SPEAK_MESSAGE, thisActivity.source.getSpeakMessage()); values.put(Preferences.SOURCE_GLOBAL, thisActivity.source.getUseGlobalNotification()); values.put(Preferences.SOURCE_RINGTONE, thisActivity.source.getRingtone()); values.put(Preferences.SOURCE_TITLE, thisActivity.source.getTitle()); values.put(Preferences.SOURCE_SOURCE_COLOR, thisActivity.source.getSourceColor()); return values; } public boolean getBoolean(String key, boolean defValue) { // source_server_enable // source_local_enable // source_notification // source_usenotifierpro // source_ringtone // source_vibrate // source_ledflash // source_speakmessage if (key.equals(Preferences.SOURCE_SERVER)) { return thisActivity.source.getServerEnabled(); } else if (key.equals(Preferences.SOURCE_LOCAL)) { return thisActivity.source.getLocalEnabled(); } else if (key.equals(Preferences.SOURCE_NOTIFICATION)) { return thisActivity.source.getNotification(); } else if (key.equals(Preferences.SOURCE_USE_NOTIFIER_PRO)) { return thisActivity.source.getNotifierPro(); } else if (key.equals(Preferences.SOURCE_VIBRATE_NOTIFY)) { return thisActivity.source.getVibrate(); } else if (key.equals(Preferences.SOURCE_LED_FLASH)) { return thisActivity.source.getLedFlash(); } else if (key.equals(Preferences.SOURCE_SPEAK_MESSAGE)) { return thisActivity.source.getSpeakMessage(); } else if (key.equals(Preferences.SOURCE_GLOBAL)) { return thisActivity.source.getUseGlobalNotification(); } return false; } public float getFloat(String key, float defValue) { // No floats available. return 0; } public int getInt(String key, int defValue) { // No int's available. return 0; } public long getLong(String key, long defValue) { // No longs available. return 0; } public String getString(String key, String defValue) { // source_customringtone // source_title if (key.equals(Preferences.SOURCE_TITLE)) { return thisActivity.source.getTitle(); } else if (key.equals(Preferences.SOURCE_RINGTONE)) { return thisActivity.source.getRingtone(); } else if (key.equals(Preferences.SOURCE_SOURCE_COLOR)) { return thisActivity.source.getSourceColor(); } return null; } public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) { this.listeners.add(listener); } public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) { this.listeners.remove(listener); } public class Editor implements SharedPreferences.Editor { public android.content.SharedPreferences.Editor clear() { // Not applicable for this object. return null; } public boolean commit() { thisActivity.source.save(thisActivity); return true; } public android.content.SharedPreferences.Editor putBoolean(String key, boolean value) { // NOTE: This is not transactionally safe as it modifies the parent's object. // source_server_enable // source_local_enable // source_notification // source_usenotifierpro // source_ringtone // source_vibrate // source_ledflash // source_speakmessage // source_global if (key.equals(Preferences.SOURCE_SERVER)) { thisActivity.source.setServerEnabled(value); } else if (key.equals(Preferences.SOURCE_LOCAL)) { thisActivity.source.setLocalEnabled(value); } else if (key.equals(Preferences.SOURCE_NOTIFICATION)) { thisActivity.source.setNotification(value); } else if (key.equals(Preferences.SOURCE_USE_NOTIFIER_PRO)) { thisActivity.source.setNotifierPro(value); } else if (key.equals(Preferences.SOURCE_VIBRATE_NOTIFY)) { thisActivity.source.setVibrate(value); } else if (key.equals(Preferences.SOURCE_LED_FLASH)) { thisActivity.source.setLedFlash(value); } else if (key.equals(Preferences.SOURCE_SPEAK_MESSAGE)) { thisActivity.source.setSpeakMessage(value); } else if (key.equals(Preferences.SOURCE_GLOBAL)) { thisActivity.source.setUseGlobalNotification(value); } return this; } public android.content.SharedPreferences.Editor putFloat(String key, float value) { // No floats to set. return this; } public android.content.SharedPreferences.Editor putInt(String key, int value) { // No ints to set. return this; } public android.content.SharedPreferences.Editor putLong(String key, long value) { // No longs to set. return this; } public android.content.SharedPreferences.Editor putString(String key, String value) { // NOTE: This is not transactionally safe as it modifies the parent's object. // source_customringtone // source_title if (key.equals(Preferences.SOURCE_TITLE)) { thisActivity.source.setTitle(value); } else if (key.equals(Preferences.SOURCE_RINGTONE)) { thisActivity.source.setRingtone(value); } else if (key.equals(Preferences.SOURCE_SOURCE_COLOR)) { thisActivity.source.setSourceColor(value); } return this; } public android.content.SharedPreferences.Editor remove(String key) { // Nothing to do here - this doesn't make sense. return this; } public void apply() { thisActivity.source.save(thisActivity); } public android.content.SharedPreferences.Editor putStringSet(String arg0, Set<String> arg1) { // TODO Auto-generated method stub return null; } } public Set<String> getStringSet(String arg0, Set<String> arg1) { // TODO Auto-generated method stub return null; } } public SharedPreferences getSharedPreferences(String name, int mode) { return SourceEditor2.preferenceMapper; } /** Called when the activity is first created. */ public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getSource(); SourceEditor2.preferenceMapper = new SourceEditor2ORMPreferencesMapper(); this.addPreferencesFromResource(R.xml.source_preference_editor2); getSupportActionBar().setDisplayHomeAsUpEnabled(true); // Find and attach the onclick handlers. titlePreference = this.findPreference(Preferences.SOURCE_TITLE); titlePreference.setOnPreferenceChangeListener(immediateServerChangeListener); Preference serverEnabledPreference = this.findPreference(Preferences.SOURCE_SERVER); serverEnabledPreference.setOnPreferenceChangeListener(immediateServerChangeListener); serverEnabled = (CheckBoxPreference) serverEnabledPreference; localEnablePreference = this.findPreference(Preferences.SOURCE_LOCAL); localEnablePreference.setOnPreferenceChangeListener(localEnablePreferenceCheckListener); localEnablePreferenceCheckBox = (CheckBoxPreference) localEnablePreference; sourceKey = this.findPreference(Preferences.SOURCE_KEY); preferenceTest = this.findPreference(Preferences.SOURCE_TEST); preferenceTest.setOnPreferenceClickListener(testClickHandler); sourceColorPreference = (ListPreference) this.findPreference(Preferences.SOURCE_SOURCE_COLOR); sourceColorPreference.setOnPreferenceChangeListener(sourceColorChangeListener); sourceNotificationPreference = thisActivity.findPreference(Preferences.SOURCE_NOTIFICATION_SETTINGS); Preference sourceEmailKeyPreference = this.findPreference(Preferences.SOURCE_EMAIL); sourceEmailKeyPreference.setOnPreferenceClickListener(emailClickHandler); Preference messagesPreference = this.findPreference(Preferences.SOURCE_MESSAGES); messagesPreference.setOnPreferenceClickListener(messagesClickHandler); Preference deletePreference = this.findPreference(Preferences.SOURCE_DELETE); deletePreference.setOnPreferenceClickListener(deleteClickHandler); } OnPreferenceChangeListener sourceColorChangeListener = new OnPreferenceChangeListener() { public boolean onPreferenceChange(Preference preference, Object newValue) { updateSourcecolor((String) newValue); return true; } }; @SuppressLint("NewApi") public void updateSourcecolor(String value) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { if (value.equals("default")) { sourceColorPreference.setSummary(getString(R.string.froyo_color_summary)); } else { String hexColor = Integer.toHexString(Color.parseColor(value)); String html = "<font color=\"#" + hexColor.substring(hexColor.length() - 6, 8) + "\">" + getString(R.string.froyo_color_summary) + "</font>"; sourceColorPreference.setSummary(Html.fromHtml(html)); } return; } String uri = "drawable/icon_color_" + value; int imageResource = getResources().getIdentifier(uri, null, getPackageName()); Drawable icon = getResources().getDrawable(imageResource); sourceColorPreference.setIcon(icon); } public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); } public void onResume() { super.onResume(); // Reload our source data. this.source = null; // And finally, update the screen. this.loadFromSource(this.getSource()); } public void onPause() { super.onPause(); } /** * Load this activity from the given source. * @param source */ public void loadFromSource(NewtifrySource source) { // Create a new preferences mapper with updated source data. SourceEditor2.preferenceMapper = new SourceEditor2ORMPreferencesMapper(); titlePreference.setSummary(source.getTitle()); sourceKey.setSummary(source.getSourceKey()); // Force update the server enabled preference. serverEnabled.setChecked(source.getServerEnabled()); // sourceColorPreference.setValue(source.getSourceColor()); updateSourcecolor(source.getSourceColor()); this.toggleLocalEnablePreferences(source.getLocalEnabled()); this.toggleServerEnable(source.getServerEnabled()); } /** * Save this source. */ /* public void save( View view ) { // User clicked save button. // Prepare the new local object. SourceEditor2.source = null; NewtifrySource source = this.getSource(); EditText title = (EditText) findViewById(R.id.detail_title); source.setTitle(title.getText().toString()); CheckBox serverEnable = (CheckBox) findViewById(R.id.detail_serverenable); source.setServerEnabled(serverEnable.isChecked()); CheckBox localEnable = (CheckBox) findViewById(R.id.detail_localenable); source.setLocalEnabled(localEnable.isChecked()); // Now, send the updates to the server. On success, save the changes locally. BackendRequest request = new BackendRequest("/sources/edit"); request.add("id", source.getServerId().toString()); request.add("title", source.getTitle()); if( source.getServerEnabled() ) { request.add("enabled", "on"); } request.addMeta("source", source); request.addMeta("operation", "save"); request.setHandler(handler); request.startInThread(this, getString(R.string.source_saving_to_server), source.getAccountName()); } */ // Crude: determine what thing we changed based on the object's t /** * A listener for changes that must occur immediately on the server. */ OnPreferenceChangeListener immediateServerChangeListener = new OnPreferenceChangeListener() { public boolean onPreferenceChange(Preference preference, Object newValue) { // Crude: determine what thing we changed based on the object's type. // CAUTION: This won't work if we need to send more than a string or boolean to the server. BackendRequest request = new BackendRequest("/sources/edit"); request.add("id", thisActivity.getSource().getServerId().toString()); if (newValue instanceof String) { // Set the title. request.add("title", (String) newValue); if (thisActivity.getSource().getServerEnabled()) { request.add("enabled", "on"); } } else if (newValue instanceof Boolean) { request.add("title", thisActivity.getSource().getTitle()); // Set the server enabled value. Boolean boolValue = (Boolean) newValue; if (boolValue) { request.add("enabled", "on"); } } request.addMeta("source", thisActivity.getSource()); request.addMeta("operation", "save"); request.setHandler(handler); request.startInThread(thisActivity, getString(R.string.source_saving_to_server), thisActivity.getSource().getAccountName()); // Prevent the change from occuring here. return false; } }; /** * Toggle the server source enabled. */ public void toggleServerEnable(boolean serverEnabled) { // if enable enable only source and test // if disable : disable all localEnablePreference.setEnabled(serverEnabled); if (serverEnabled && !localEnablePreferenceCheckBox.isChecked()) { serverEnabled = false; } sourceNotificationPreference.setEnabled(serverEnabled); preferenceTest.setEnabled(serverEnabled); } /** * Toggle the local source enabled. */ OnPreferenceChangeListener localEnablePreferenceCheckListener = new OnPreferenceChangeListener() { public boolean onPreferenceChange(Preference preference, Object newValue) { thisActivity.toggleLocalEnablePreferences((Boolean) newValue); return true; } }; public void toggleLocalEnablePreferences(boolean localEnabled) { sourceNotificationPreference.setEnabled(localEnabled); preferenceTest.setEnabled(localEnabled); } /** * Delete this source. */ OnPreferenceClickListener deleteClickHandler = new OnPreferenceClickListener() { public boolean onPreferenceClick(Preference preference) { // User clicked delete button. // Confirm that's what they want. new AlertDialog.Builder(thisActivity).setTitle(getString(R.string.delete_source)) .setMessage(getString(R.string.delete_source_message)) .setPositiveButton(getString(R.string.delete), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { // Fire it off to the delete source function. deleteSource(thisActivity.getSource()); } }).setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { // No need to take any action. } }).show(); return true; } }; /** * Send a request to the backend to delete the source. * @param source */ public void deleteSource(NewtifrySource source) { // Now, send the updates to the server. On success, save the changes locally. BackendRequest request = new BackendRequest("/sources/delete"); request.add("id", source.getServerId().toString()); request.addMeta("operation", "delete"); request.addMeta("source", getSource()); request.setHandler(handler); request.startInThread(this, getString(R.string.source_deleting_from_server), source.getAccountName()); } /** * Send a request to the backend to test this source. * @param view */ OnPreferenceClickListener testClickHandler = new OnPreferenceClickListener() { public boolean onPreferenceClick(Preference preference) { if (BuildConfig.DEBUG) { // generate localy a message NewtifrySource source = getSource(); NewtifryMessage message = NewtifryMessage.fromDebug(thisActivity, 6443258397851648L, source.getServerId(), 1); // long serverId, long sourceId, int priority message.save(thisActivity); return true; } BackendRequest request = new BackendRequest("/sources/test"); request.add("id", getSource().getServerId().toString()); request.addMeta("operation", "test"); // v2.3.2 request.add("deviceid", GCMRegistrar.getRegistrationId(thisActivity.getApplicationContext())); request.add("language", Locale.getDefault().getDisplayLanguage(Locale.US)); request.addMeta("source", getSource()); request.setHandler(handler); request.startInThread(thisActivity, getString(R.string.source_testing_with_server), source.getAccountName()); return true; } }; /** * Email the source key to someone. */ OnPreferenceClickListener emailClickHandler = new OnPreferenceClickListener() { public boolean onPreferenceClick(Preference preference) { // User wants to email the key to someone. final Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND); emailIntent.setType("plain/text"); emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, getString(R.string.source_key_email_subject)); emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, String.format(getString(R.string.source_key_email_body), getSource().getSourceKey())); thisActivity.startActivity(Intent.createChooser(emailIntent, "Send key via email")); return true; } }; /** * View the messages of this source. */ OnPreferenceClickListener messagesClickHandler = new OnPreferenceClickListener() { public boolean onPreferenceClick(Preference preference) { Intent intent = new Intent(thisActivity, MessageList.class); intent.putExtra("sourceId", thisActivity.getSource().getId()); startActivity(intent); return true; } }; private 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() + getString(R.string.try_again), Toast.LENGTH_LONG) .show(); } else { try { // Fetch out metadata. BackendRequest request = response.getRequest(); NewtifrySource source = (NewtifrySource) request.getMeta("source"); String operation = (String) request.getMeta("operation"); if (operation.equals("save")) { // Load the source from the server information. We assume the server is correct. source.fromJSONObject(response.getJSON().getJSONObject("source")); // Save it to the database. source.save(thisActivity); Toast.makeText(thisActivity, getString(R.string.source_save_success), Toast.LENGTH_SHORT) .show(); // Reload the parent activity. thisActivity.source = null; thisActivity.loadFromSource(thisActivity.getSource()); } else if (operation.equals("delete")) { // Delete from local. source.delete(thisActivity); // Let the user know we're done. Toast.makeText(thisActivity, getString(R.string.source_delete_success), Toast.LENGTH_SHORT) .show(); // And exit this activity. thisActivity.finish(); } else if (operation.equals("test")) { // The server has been asked to test us. Toast.makeText(thisActivity, getString(R.string.source_test_success), 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, getString(R.string.server_invalid_response), Toast.LENGTH_LONG) .show(); } } } }; /** * Fetch the account that this source list is for. * * @return */ public NewtifrySource getSource() { if (this.source == null) { // Get the source from the intent. // We store it in a private variable to save us having to query the // DB each time. Intent sourceIntent = getIntent(); this.source = NewtifrySource.FACTORY.get(this, sourceIntent.getLongExtra("sourceId", 0)); } return this.source; } /** * When the home button is hit... */ public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: finish(); /* Intent intent = new Intent(getBaseContext(), SourceList.class); intent.putExtra("account",this.getSource().getAccountName()); startActivity(intent); */ return true; } return super.onOptionsItemSelected((android.view.MenuItem) item); } }