Android Open Source - artifactly Artifactly






From Project

Back to project page artifactly.

License

The source code is released under:

Apache License

If you think the Android project artifactly listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * Copyright 2011 Thomas Amsler/*  www  . ja v  a  2 s . c om*/
 *
 * 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.artifactly.client;

import org.artifactly.client.service.ArtifactlyService;
import org.artifactly.client.service.LocalService;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
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.SharedPreferences;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.location.Location;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.webkit.ConsoleMessage;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.widget.Toast;

public class Artifactly extends Activity implements ApplicationConstants {

  
  private static final String ARTIFACTLY_URL = "file:///android_asset/artifactly.html";

  private static final String PROD_LOG_TAG = " ** A.A. **";
  //private static final String DEBUG_LOG_TAG = " ** DEBUG A.A. **";

  // Preferences
  private static final String PREFS_NAME = "ArtifactlyPrefsFile";

  // Constants
  private static final String EMPTY_STRING = "";

  // JavaScript function constants
  private static final String JAVASCRIPT_PREFIX = "javascript:";
  private static final String JAVASCRIPT_FUNCTION_OPEN_PARENTHESIS = "(";
  private static final String JAVASCRIPT_FUNCTION_CLOSE_PARENTHESIS = ")";
  private static final String JAVASCRIPT_BRIDGE_PREFIX = "android";

  // JavaScript functions
  private static final String GET_ARTIFACTS_CALLBACK = "getArtifactsCallback";
  private static final String GET_ARTIFACT_CALLBACK = "getArtifactCallback";
  private static final String GET_ARTIFACTS_FOR_CURRENT_LOCATION_CALLBACK = "getArtifactsForCurrentLocationCallback";
  private static final String GET_LOCATIONS_OPTIONS_CALLBACK = "getLocationsOptionsCallback";
  private static final String GET_LOCATIONS_LIST_CALLBACK = "getLocationsListCallback";
  private static final String CREATE_ARTIFACT_CALLBACK = "createArtifactCallback";
  private static final String HAS_ARTIFACTS_AT_LOCATION_CALLBACK = "hasArtifactsAtLocationCallback";
  private static final String RESET_WEBVIEW = "resetWebView";
  private static final String SHOW_OPTIONS_PAGE = "showOptionsPage";
  private static final String SHOW_MAP_PAGE = "showMapPage";
  private static final String SHOW_APP_INFO_PAGE = "showAppInfoPage";
  private static final String SHOW_WELCOME_PAGE = "showWelcomePage";
  private static final String SHOW_ORIENTATION_PORTRAIT_MODE = "showOrientationPortraitMode";
  private static final String SHOW_ORIENTATION_LANDSCAPE_MODE = "showOrientationLandscapeMode";
  private static final String SHOW_ARTIFACTS_PAGE = "showArtifactsPage";
  private static final String BROADCAST_CURRENT_LOCATION = "broadcastCurrentLocation";
  
  // Thread sleep time before we try the callJavaScriptFunction(...) call again
  private static final int THREAD_SLEEP_BEFORE_RETRY_ACCESS_LOCALSERVICE = 1000;
  private WebView webView = null;

  private Handler mHandler = new Handler();

  // Access to service API
  private ServiceConnection serviceConnection = getServiceConnection();
  private LocalService localService = null;
  private boolean isBound = false;

  private IntentFilter locationUpdateIntentFilter = new IntentFilter(LOCATION_UPDATE_INTENT);
  private IntentFilter connectivityIntentFilter = new IntentFilter(android.net.ConnectivityManager.CONNECTIVITY_ACTION);
  private IntentFilter hasArtifactsAtCurrentLocationIntentFilter = new IntentFilter(HAS_ARTIFACTS_AT_CURRENT_LOCATION_INTENT);
  private BroadcastReceiver locationUpdateBroadcastReceiver = null;
  private BroadcastReceiver connectivityBroadcastReceiver = null;
  private BroadcastReceiver hasArtifactsAtCurrentLocationReceiver = null;
  
  private boolean canAccessInternet = true;
  
  private String version = "";
  
  /*
   * (non-Javadoc)
   * @see android.app.Activity#onCreate(android.os.Bundle)
   */
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    
    // Setting up the WebView
    webView = (WebView) findViewById(R.id.webview);
    webView.getSettings().setJavaScriptEnabled(true);
    
    webView.addJavascriptInterface(new JavaScriptInterface(), JAVASCRIPT_BRIDGE_PREFIX);
    
    // Disable the vertical scroll bar
    webView.setVerticalScrollBarEnabled(false);

    webView.setWebChromeClient(new WebChromeClient() {
      
      public boolean onConsoleMessage(ConsoleMessage cm) {
      
        Log.d(PROD_LOG_TAG, cm.message() + " -- From line " + cm.lineNumber() + " of " + cm.sourceId() );
        return true;
      }
    });
    
    webView.loadUrl(ARTIFACTLY_URL);
    
    /*
     * Calling startService so that the service keeps running. e.g. After application installation
     * The start of the service at boot is handled via a BroadcastReceiver and the BOOT_COMPLETED action
     */
    startService(new Intent(this, ArtifactlyService.class));

    // Bind to the service
    bindService(new Intent(this, ArtifactlyService.class), serviceConnection, BIND_AUTO_CREATE);
    isBound = true;

    // Instantiate the broadcast receiver
    locationUpdateBroadcastReceiver = new BroadcastReceiver() {

      @Override
      public void onReceive(Context context, Intent intent) {

        new GetArtifactsForCurrentLocationTask().execute();
      }
    };
    
    connectivityBroadcastReceiver = new BroadcastReceiver() {
      
      @Override
      public void onReceive(Context arg0, Intent arg1) {
        
        canAccessInternet = hasConnectivity();
      }
    };
    
    hasArtifactsAtCurrentLocationReceiver = new BroadcastReceiver() {
      
      @Override
      public void onReceive(Context context, Intent intent) {
        
        if(null != localService) {

          Location location = localService.getLocation();
          callJavaScriptFunction(BROADCAST_CURRENT_LOCATION, locationToJSON(location));
        }
      }
    };
    
    // Initialize connectivity flag
    canAccessInternet = hasConnectivity();
    
    // Get version information from AndroidManifest.xml
    try {
      
      version = this.getPackageManager().getPackageInfo(this.getPackageName(), 0).versionName;
    }
    catch (NameNotFoundException e) {
      
      Log.w(PROD_LOG_TAG, "Exception accessing version information", e);
    }
  }

  /*
   * (non-Javadoc)
   * @see android.app.Activity#onCreateOptionsMenu(android.view.Menu)
   */
  @Override
  public boolean onCreateOptionsMenu(Menu menu) {

    MenuInflater inflater = getMenuInflater();
      inflater.inflate(R.menu.opitons, menu);
      return true;
  }
  
  /*
   * (non-Javadoc)
   * @see android.app.Activity#onOptionsItemSelected(android.view.MenuItem)
   */
  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
      
    // Handle item selection
      switch (item.getItemId()) {
      case R.id.options:
          callJavaScriptFunction(SHOW_OPTIONS_PAGE);
          return true;
      case R.id.map:
        callJavaScriptFunction(SHOW_MAP_PAGE);
          return true;
      case R.id.info:
        callJavaScriptFunction(SHOW_APP_INFO_PAGE);
        return true;
      case R.id.welcome:
        callJavaScriptFunction(SHOW_WELCOME_PAGE);
        return true;
      case R.id.feedback:
        emailFeedback();
        return true;
      default:
          return super.onOptionsItemSelected(item);
      }
  }
  
  /*
   * Handle back button clicks in webview
   * @see android.app.Activity#onKeyDown(int, android.view.KeyEvent)
   */
  @Override
  public boolean onKeyDown(int keyCode, KeyEvent event) {
    
      if ((keyCode == KeyEvent.KEYCODE_BACK) && webView.canGoBack()) {
      
        webView.goBack();
          return true;
      }
      return super.onKeyDown(keyCode, event);
  }
  
  /*
   * (non-Javadoc)
   * @see android.app.Activity#onStart()
   */
  @Override
  public void onStart() {
    super.onStart();

    if(!isBound) {
      
      // Connect to the local service API
      bindService(new Intent(this, ArtifactlyService.class), serviceConnection, BIND_AUTO_CREATE);
      isBound = true;
    }
  }

  /*
   * (non-Javadoc)
   * @see android.app.Activity#onResume()
   */
  @Override
  public void onResume() {
    super.onResume();

    if(!isBound) {

      // Connect to the local service API
      bindService(new Intent(this, ArtifactlyService.class), serviceConnection, BIND_AUTO_CREATE);
      isBound = true;
    }
    
    // Register broadcast receivers
    registerReceiver(locationUpdateBroadcastReceiver, locationUpdateIntentFilter);
    registerReceiver(connectivityBroadcastReceiver, connectivityIntentFilter);
    registerReceiver(hasArtifactsAtCurrentLocationReceiver, hasArtifactsAtCurrentLocationIntentFilter);
  }

  /*
   * (non-Javadoc)
   * @see android.app.Activity#onPause()
   */
  @Override
  public void onPause() {
    super.onPause();

    if(isBound) {

      isBound = false;

      try {
        
        unbindService(serviceConnection);
      }
      catch(IllegalArgumentException e) {
        
        Log.w(PROD_LOG_TAG, "onPause() -> unbindService() caused an IllegalArgumentException", e);
      }
    }
    
    // Unregister broadcast receivers
    unregisterReceiver(locationUpdateBroadcastReceiver);
    unregisterReceiver(connectivityBroadcastReceiver);
    unregisterReceiver(hasArtifactsAtCurrentLocationReceiver);
  }

  /*
   * (non-Javadoc)
   * @see android.app.Activity#onStop()
   */
  @Override
  public void onStop() {
    super.onStop();

    // Reset the WebView to show the welcome page
    callJavaScriptFunction(RESET_WEBVIEW);
    
    if(isBound) {

      isBound = false;

      try {
        
        unbindService(serviceConnection);
      }
      catch(IllegalArgumentException e) {
        
        Log.w(PROD_LOG_TAG, "onStop() -> unbindService() caused an IllegalArgumentException", e);
      }
    }
  }

  /*
   * (non-Javadoc)
   * @see android.app.Activity#onDestroy()
   */
  @Override
  public void onDestroy() {
    super.onDestroy();

    if(isBound) {

      isBound = false;

      try {
      
        unbindService(serviceConnection);
      }
      catch(IllegalArgumentException e) {
        
        Log.w(PROD_LOG_TAG, "onDestroy() -> unbindService() caused an IllegalArgumentException", e);
      }
    }
  }

  @Override
  public void onConfigurationChanged (Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    
    switch(newConfig.orientation) {
    
      case Configuration.ORIENTATION_PORTRAIT:
        
        callJavaScriptFunction(SHOW_ORIENTATION_PORTRAIT_MODE);
        break;
      case Configuration.ORIENTATION_LANDSCAPE:
        
        callJavaScriptFunction(SHOW_ORIENTATION_LANDSCAPE_MODE);
        break;
    }
  }
  
  /*
   * (non-Javadoc)
   * @see android.app.Activity#onNewIntent(android.content.Intent)
   */
  @Override
  public void onNewIntent(Intent intent) {

    callJavaScriptFunction(SHOW_ARTIFACTS_PAGE);
    new GetArtifactsForCurrentLocationTask().execute();
  }
  
  /*
   * Helper method to call JavaScript methods
   */
  private void callJavaScriptFunction(final String functionName, final String json) {

    mHandler.post(new Runnable() {

      public void run() {

        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(JAVASCRIPT_PREFIX);
        stringBuilder.append(functionName);
        stringBuilder.append(JAVASCRIPT_FUNCTION_OPEN_PARENTHESIS);
        stringBuilder.append(json);
        stringBuilder.append(JAVASCRIPT_FUNCTION_CLOSE_PARENTHESIS);
        
        try {
        
          webView.loadUrl(stringBuilder.toString());
        }
        catch(Exception e1) {
          
          Log.w(PROD_LOG_TAG, "callJavaScriptFunction(...) name = " + functionName);
        }
      }
    });
  }
  
  /*
   * Helper method to call JavaScript methods without JSON data
   */
  private void callJavaScriptFunction(final String functionName) {

    callJavaScriptFunction(functionName, "");
  }

  // Define methods that are called from JavaScript
  public class JavaScriptInterface {
    
    public String getVersion() {
      
      return version;
    }
    
    public void setRadius(int radius) {

      if(PREFERENCE_RADIUS_MIN <= radius && radius <= PREFERENCE_RADIUS_MAX) {

        SharedPreferences settings = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
        SharedPreferences.Editor editor = settings.edit();
        editor.putInt(PREFERENCE_RADIUS, radius);
        editor.commit();
        
        // Getting radius unit
        String unit = settings.getString(PREFERENCE_RADIUS_UNIT, PREFERENCE_RADIUS_UNIT_DEFAULT);
        
        String message = String.format(getResources().getString(R.string.set_location_radius), radius, unit);
        Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
      }
      else {
        
        String message = String.format(getResources().getString(R.string.set_location_radius_error), PREFERENCE_RADIUS_MIN, PREFERENCE_RADIUS_MAX);
        Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
      }
    }
    
    public void setRadiusUnit(String type) {
      
      SharedPreferences settings = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
      SharedPreferences.Editor editor = settings.edit();
      editor.putString(PREFERENCE_RADIUS_UNIT, type);
      editor.commit();
      
      String unit = "";
      if(UNIT_M.equals(type)) {
        
        unit = "meters";
      }
      else if(UNIT_KM.equals(type)) {
        
        unit = "kilometers";
      }
      else if(UNIT_FT.equals(type)) {
        
        unit = "feet";
      }
      else if(UNIT_MI.equals(type)) {
        
        unit = "miles";
      }

      String message = String.format(getResources().getString(R.string.preference_radius_unit), unit);
      Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
    }

    public void setSoundNotificationPreference(boolean preference) {

      SharedPreferences settings = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
      SharedPreferences.Editor editor = settings.edit();
      editor.putBoolean(PREFERENCE_SOUND_NOTIFICATION, preference);
      editor.commit();
      
      if(preference) {
        
        Toast.makeText(getApplicationContext(), R.string.preference_sound_alert_on, Toast.LENGTH_SHORT).show();
      }
      else {
        
        Toast.makeText(getApplicationContext(), R.string.preference_sound_alert_off, Toast.LENGTH_SHORT).show();
      }
    }

    public void setLoadStaticMapPreference(boolean preference) {
      
      SharedPreferences settings = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
      SharedPreferences.Editor editor = settings.edit();
      editor.putBoolean(PREFERENCE_LOAD_STATIC_MAP, preference);
      editor.commit();
      
      if(preference) {
        
        Toast.makeText(getApplicationContext(), R.string.preference_load_maps_on, Toast.LENGTH_SHORT).show();
      }
      else {
        
        Toast.makeText(getApplicationContext(), R.string.preference_load_maps_off, Toast.LENGTH_SHORT).show();
      }
    }
    
    public String getPreferences() {

      SharedPreferences settings = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
      
      JSONObject preferences = new JSONObject();
      
      try {
        
        // Sound notification preference
        preferences.put("soundNotification", settings.getBoolean(PREFERENCE_SOUND_NOTIFICATION, PREFERENCE_SOUND_NOTIFICATION_DEFAULT));

        // Radius preference
        preferences.put("radius", settings.getInt(PREFERENCE_RADIUS, PREFERENCE_RADIUS_DEFAULT));
        
        // Radius unit preference
        preferences.put("radiusUnit", settings.getString(PREFERENCE_RADIUS_UNIT, PREFERENCE_RADIUS_UNIT_DEFAULT));

        // Static map loading preference
        preferences.put("loadStaticMap", settings.getBoolean(PREFERENCE_LOAD_STATIC_MAP, PREFERENCE_LOAD_STATIC_MAP_DEFAULT));

      }
      catch(JSONException e) {

        Toast.makeText(getApplicationContext(), R.string.loading_preferences_error, Toast.LENGTH_SHORT).show();
      }

      return preferences.toString();
    }
    
    public void deleteArtifact(String artifactId, String locationId) {

      new DeleteArtifactTask().execute(artifactId, locationId);
    }
    
    public void deleteLocation(String locationId) {
      
      new DeleteLocationTask().execute(locationId);
    }

    public void createArtifact(String artifactName, String artifactData, String locationName, String locationLat, String locationLng) {
      
      new CreateArtifactTask().execute(artifactName, artifactData, locationName, locationLat, locationLng);
    }

    public void showRadius() {
      
      SharedPreferences settings = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
      int radius = settings.getInt(PREFERENCE_RADIUS, PREFERENCE_RADIUS_DEFAULT);
      String message = String.format(getResources().getString(R.string.set_location_radius), radius);
      Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
    }

    public String getLocation() {

      Location location = localService.getLocation();
      return locationToJSON(location);
    }

    public void getArtifact(String artId, String locId) {

      new GetArtifactTask().execute(artId, locId);
    }

    public void getArtifacts() {

      new GetArtifactsTask().execute();
    }

    public void getArtifactsForCurrentLocation() {

      new GetArtifactsForCurrentLocationTask().execute();
    }

    public boolean canAccessInternet() {

      return canAccessInternet;
    }
    
    public boolean canLoadStaticMap() {
      
      SharedPreferences settings = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
      boolean canLoadStaticMap = settings.getBoolean(PREFERENCE_LOAD_STATIC_MAP, PREFERENCE_LOAD_STATIC_MAP_DEFAULT);
      
      return canLoadStaticMap && canAccessInternet;
    }
    
    public String getApiKeys() {
      
      JSONObject apiKeys = new JSONObject();
      
      try {
        
        apiKeys.put("localSearch", getResources().getString(R.string.google_search_api_key));
        apiKeys.put("placesSearch", getResources().getString(R.string.google_places_api_key));
      }
      catch(JSONException e) {

        Toast.makeText(getApplicationContext(), R.string.loading_api_keys_error, Toast.LENGTH_SHORT).show();
      }

      return apiKeys.toString();
    }
    
    public void getLocations(String callback) {
      
      new GetLocationsTask().execute(callback);
    }
    
    public void updateArtifact(String artId, String artName, String artData, String locId, String locName) {
      
      new UpdateArtifactTask().execute(artId, artName, artData, locId, locName);
    }
    
    public void updateArtifactData(String artId, String artData) {
      
      new UpdateArtifactDataTask().execute(artId, artData);
    }
    
    public void updateLocation(String locId, String locName, String locLat, String locLng) {
      
      new UpdateLocationTask().execute(locId, locName, locLat, locLng);
    }
    
    public void updateLocationCoodinates(String locId, String locName, String locLat, String locLng) {
      
      new UpdateLocationCoodinatesTask().execute(locId, locName, locLat, locLng);
    }
    
    public void showMessage(String messageKey) {
      
      if("update_location_help".equals(messageKey)) {
      
        Toast.makeText(getApplicationContext(), R.string.update_location_help, Toast.LENGTH_LONG).show();
      }
    }
    
    public void hasArtifactsAtLocation(String locId) {
      
      new HasArtifactsAtLocationTask().execute(locId);
    }
  }
  
  // Method that handles the mail feedback
  private void emailFeedback() {
    
    String toList[] = { "feedback@artifactly.org" };
    
    Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
    emailIntent.setType("plain/text");
    emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, toList);
    emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, getResources().getString(R.string.email_feedback_subject));
    
    startActivity(Intent.createChooser(emailIntent, getResources().getString(R.string.email_feedback_chooser)));
  }

  // Method that returns a service connection
  private ServiceConnection getServiceConnection() {

    return new ServiceConnection() {

      public void onServiceConnected(ComponentName componentName, IBinder iBinder) {

        localService = (LocalService)iBinder;
        isBound = true;
      }

      public void onServiceDisconnected(ComponentName componentName) {

        localService = null;
        isBound = false;
      }
    };
  }

  /*
   * Helper method that turns a Location object into JSON
   */
  private String locationToJSON(Location location) {
    
    JSONObject data = new JSONObject();
    
    if(null == location) {

      Toast.makeText(getApplicationContext(), R.string.get_location_error, Toast.LENGTH_LONG).show();
      
      try {
        
        data.put("locLat", 0.0d);
        data.put("locLng", 0.0d);
        data.put("locAccuracy", 0.0d);
      }
      catch (JSONException e) {

        Log.e(PROD_LOG_TAG, "Error while populating JSONArray", e);
      }
    }
    else {

      try {

        data.put("locLat", location.getLatitude());
        data.put("locLng", location.getLongitude());
        data.put("locAccuracy", location.getAccuracy());
      }
      catch (JSONException e) {

        Log.e(PROD_LOG_TAG, "Error while population JSONArray", e);
      }
    }
    
    return data.toString();
  }
  
  /*
   * Helper method that checks if a string is a valid Double
   */
  private boolean isDouble(String number) {
    
    try {
      
      Double.parseDouble(number);
    }
    catch (NumberFormatException nfe) {
      
      return false;
    }
    
    return true;
  }
  
  /*
   * Helper method to check if there is network connectivity
   */
  private boolean hasConnectivity() {
    
    ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
      
    if(null == connectivityManager) {
      
      return false;
    }
    
    NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
    
    if(null != networkInfo && networkInfo.isAvailable() && networkInfo.isConnected()) {
      
      return true;
    }
    else {
      
      return false;
    }
  }
  
  
  private class CreateArtifactTask extends AsyncTask<String, Void, Byte> {

    /*
     * args[0] = artifactName
     * args[1] = artifactData
     * args[2] = locationName
     * args[3] = locationLat
     * args[4] = locationLng
     * 
     */
    @Override
    protected Byte doInBackground(String... args) {
      
      if(null == args[0] || EMPTY_STRING.equals(args[0])) {

        return Byte.valueOf(ARTIFACT_NAME_ERROR);
      }
      
      if(null == args[2] || EMPTY_STRING.equals(args[2])) {

        return Byte.valueOf(LOCATION_NAME_ERROR);
      }
      
      byte state = 0;
      
      // If latitude and longitude are provided we use them, otherwise we use the current location
      // TODO: Add check if provided latitude and longitude are valid GEO points
      if(null != args[3] &&
          !EMPTY_STRING.equals(args[3]) &&
          null != args[4] &&
          !EMPTY_STRING.equals(args[4]) &&
          isDouble(args[3]) &&
          isDouble(args[4])) {
        
        state = localService.createArtifact(args[0], args[1], args[2], args[3], args[4]);
      }
      else {
        
        /*
         * This cases shouldn't happen. Log error message
         */
        Log.e(PROD_LOG_TAG, "Create artifact didn't have a valid location associated");
      }
      
      return Byte.valueOf(state);
    }
    
    @Override
    protected void onPostExecute(Byte result) {
    
      byte state = result.byteValue();
      boolean returnValue = false;
      
      if((state ^ CREATE_ARTIFACT_LOCATION_ERROR) == IS_MATCH) {
        
        Toast.makeText(getApplicationContext(), R.string.create_artifact_failure, Toast.LENGTH_LONG).show();
        returnValue = false;
      }
      else if((state ^ CHOOSE_DIFFERENT_LOC_NAME) == IS_MATCH) {
        
        Toast.makeText(getApplicationContext(), R.string.create_artifact_provide_different_location_name, Toast.LENGTH_LONG).show();
        returnValue = false;
      }
      else if((state ^ ARTIFACT_AND_LOCATION_EXIST) == IS_MATCH) {
        
        Toast.makeText(getApplicationContext(), R.string.create_artifact_already_exists, Toast.LENGTH_LONG).show();
        returnValue = false;
      }
      else if((state ^ ARTIFACT_NAME_ERROR) == IS_MATCH) {
        
        Toast.makeText(getApplicationContext(), R.string.create_artifact_name_error, Toast.LENGTH_SHORT).show();
        returnValue = false;
      }
      else if((state ^ LOCATION_NAME_ERROR) == IS_MATCH) {
        
        Toast.makeText(getApplicationContext(), R.string.create_artifact_location_name_error, Toast.LENGTH_SHORT).show();
        returnValue = false;
      }
      else {
        
        //new GetArtifactsForCurrentLocationTask().execute();
        Toast.makeText(getApplicationContext(), R.string.create_artifact_success, Toast.LENGTH_SHORT).show();
        returnValue = true;
      }
      
      JSONObject data = new JSONObject();
      try {
        
        data.put("isSuccess", returnValue);
      }
      catch (JSONException e) {
        
        Log.e(PROD_LOG_TAG, "ERROR: json.put()", e);
      }
      
      callJavaScriptFunction(CREATE_ARTIFACT_CALLBACK, data.toString());
    }
  }
  private class GetArtifactsForCurrentLocationTask extends AsyncTask<Void, Void, Void> {

    @Override
    protected Void doInBackground(Void... params) {

      /*
       * Check if the localService is null, in which case we wait for some time and try again
       */
      if(null == localService) {
        
        try {

          // Give enough time so that the localService can bind
          Thread.sleep(THREAD_SLEEP_BEFORE_RETRY_ACCESS_LOCALSERVICE);
        }
        catch(Exception e) {

          Log.w(PROD_LOG_TAG, "EXCEPTION: Thread.sleep(N)", e);
        }
      }
      
      if(null != localService) {

        String result = localService.getArtifactsForCurrentLocation();
        callJavaScriptFunction(GET_ARTIFACTS_FOR_CURRENT_LOCATION_CALLBACK, result);
      }
      else {
        
        callJavaScriptFunction(GET_ARTIFACTS_FOR_CURRENT_LOCATION_CALLBACK, "[]");
      }
      
      return null;
    }
  }

  private class GetArtifactsTask extends AsyncTask<Void, Void, Void> {

    @Override
    protected Void doInBackground(Void... arg0) {

      if(null == localService) {

        callJavaScriptFunction(GET_ARTIFACTS_CALLBACK, "[]");
      }
      else {

        String result = localService.getArtifacts();
        callJavaScriptFunction(GET_ARTIFACTS_CALLBACK, result);
      }
      
      return null;
    }  
  }
  
  private class GetLocationsTask extends AsyncTask<String, Void, Void> {
      
    @Override
    protected Void doInBackground(String... args) {

      if(null == localService) {

        if("options".equals(args[0])) {
          
          callJavaScriptFunction(GET_LOCATIONS_OPTIONS_CALLBACK, "[]");
          
        }
        else if("list".equals(args[0])) {
          
          callJavaScriptFunction(GET_LOCATIONS_LIST_CALLBACK, "[]");
        }
      }
      else {

        String result = localService.getLocations();
        
        if("options".equals(args[0])) {
          
          callJavaScriptFunction(GET_LOCATIONS_OPTIONS_CALLBACK, result);
        }
        else if("list".equals(args[0])) {
          
          callJavaScriptFunction(GET_LOCATIONS_LIST_CALLBACK, result);
        }
      }
      
      return null;
    }  
  }

  private class GetArtifactTask extends AsyncTask<String, Void, Boolean> {

    @Override
    protected Boolean doInBackground(String... args) {

      if(null == localService) {
        
        callJavaScriptFunction(GET_ARTIFACT_CALLBACK, "{}");
        return Boolean.FALSE;
      }
      else {
        
        String result = localService.getAtrifact(args[0], args[1]);
        callJavaScriptFunction(GET_ARTIFACT_CALLBACK, result);
      }

      return Boolean.TRUE;
    }
    
    @Override
    protected void onPostExecute(Boolean result) {
    
      /*
       * Show Toast in here because onPostExecute executes in UI thread
       */
      if(!result.booleanValue()) {
        
        Toast.makeText(getApplicationContext(), R.string.get_artifact_failure, Toast.LENGTH_LONG).show();
      }
    }
  }
  
  private class UpdateArtifactDataTask extends AsyncTask<String, Void, Integer> {

    @Override
    protected Integer doInBackground(String... args) {
      
      if(null == localService) {
        
        return Integer.valueOf(-2);
      }
      else if(null == args[0] || "".equals(args[0])) {
      
        return Integer.valueOf(-3);
      }      
      else {
        
        return Integer.valueOf(localService.updateArtifactData(args[0], args[1]));
      }
    }
    
    @Override
    protected void onPostExecute(Integer result) {
    
      /*
       * Show Toast in here because onPostExecute executes in UI thread
       */
      switch(result.intValue()) {
      
        case -1:
          // general error
          Toast.makeText(getApplicationContext(), R.string.update_artifact_failure, Toast.LENGTH_LONG).show();
          break;
        case 1:
          new GetArtifactsForCurrentLocationTask().execute();
          Toast.makeText(getApplicationContext(), R.string.update_artifact_success, Toast.LENGTH_SHORT).show();
          break;
        default:
          Log.e(PROD_LOG_TAG, "ERROR: unexpected update artifact result");
      }
    }
  }
  
  private class UpdateArtifactTask extends AsyncTask<String, Void, Integer> {
    
    @Override
    protected Integer doInBackground(String... args) {

      if(null == localService) {
        
        return Integer.valueOf(-2);
      }
      else if(null == args[0] ||
          null == args[1] ||
          null == args[3] ||
          null == args[4] ||
          "".equals(args[0]) ||
          "".equals(args[1]) ||
          "".equals(args[3]) ||
          "".equals(args[4])) {
      
        // Above, we check all the fields except args[2], which is the location data. It can be null/empty
        return Integer.valueOf(-3);
      }      
      else {
        
        return Integer.valueOf(localService.updateArtifact(args[0], args[1], args[2], args[3], args[4]));
      }
    }
    
    @Override
    protected void onPostExecute(Integer result) {
    
      /*
       * Show Toast in here because onPostExecute executes in UI thread
       */
      switch(result.intValue()) {
      
        case -4:
          // location name collision
          Toast.makeText(getApplicationContext(), R.string.update_artifact_duplicate_location_name, Toast.LENGTH_LONG).show();
          break;
        case -3:
          // invalid input
          Toast.makeText(getApplicationContext(), R.string.update_artifact_invalid_input, Toast.LENGTH_LONG).show();
          break;
        case -2:
          // general error
          Toast.makeText(getApplicationContext(), R.string.update_artifact_failure, Toast.LENGTH_LONG).show();
          break;
        case -1:
          // artifact name collision
          Toast.makeText(getApplicationContext(), R.string.update_artifact_duplicate_artifact_name, Toast.LENGTH_LONG).show();
          break;
        case 1:
          new GetArtifactsForCurrentLocationTask().execute();
          Toast.makeText(getApplicationContext(), R.string.update_artifact_success, Toast.LENGTH_SHORT).show();
          break;
        default:
          Log.e(PROD_LOG_TAG, "ERROR: unexpected update artifact result");
      }
    }
  }
  
  private class UpdateLocationTask extends AsyncTask<String, Void, Integer> {
    
    @Override
    protected Integer doInBackground(String ... args) {
      
      if(null == localService) {
        
        return Integer.valueOf(-2);
      }
      else if(null == args[0] || null == args[1] || null == args[2] || null == args[3] ||
          "".equals(args[0]) || "".equals(args[1]) || "".equals(args[2]) || "".equals(args[3])) {
        
        // Above, we check all the fields. They cannot be null or empty
        return Integer.valueOf(-3);
      }
      else {
        
        return Integer.valueOf(localService.updateLocation(args[0], args[1], args[2], args[3]));
      }
    }
    
    @Override
    protected void onPostExecute(Integer result) {
    
      switch(result.intValue()) {
      
        case -3:
          // invalid input
          Toast.makeText(getApplicationContext(), R.string.update_location_invalid_input, Toast.LENGTH_LONG).show();
          break;
        case -2:
          // general error
          Toast.makeText(getApplicationContext(), R.string.update_location_failure, Toast.LENGTH_LONG).show();
          break;
        case -1: 
          // location name collision
          Toast.makeText(getApplicationContext(), R.string.update_location_duplicate_location_name, Toast.LENGTH_LONG).show();
          break;
        case 1:
          new GetArtifactsForCurrentLocationTask().execute();
          new GetLocationsTask().execute("list");
          Toast.makeText(getApplicationContext(), R.string.update_location_success, Toast.LENGTH_SHORT).show();
          break;
        default:
          Log.e(PROD_LOG_TAG, "ERROR: unexpected update location result");
      }
    }
  }
  
  private class UpdateLocationCoodinatesTask extends AsyncTask<String, Void, Integer> {
    
    @Override
    protected Integer doInBackground(String ... args) {
      
      if(null == localService) {
        
        return Integer.valueOf(-2);
      }
      else if(null == args[0] || null == args[1] || null == args[2] || null == args[3] ||
          "".equals(args[0]) || "".equals(args[1]) || "".equals(args[2]) || "".equals(args[3])) {
        
        // Above, we check all the fields. They cannot be null or empty
        return Integer.valueOf(-3);
      }
      else {
        
        return Integer.valueOf(localService.updateLocationCoodinates(args[0], args[1], args[2], args[3]));
      }
    }
    
    @Override
    protected void onPostExecute(Integer result) {
    
      switch(result.intValue()) {
      
        case -3:
          // invalid input
          Toast.makeText(getApplicationContext(), R.string.update_location_invalid_input, Toast.LENGTH_LONG).show();
          break;
        case -2:
          // general error
          Toast.makeText(getApplicationContext(), R.string.update_location_failure, Toast.LENGTH_LONG).show();
          break;
        case -1: 
          // coordinate collision
          Toast.makeText(getApplicationContext(), R.string.update_location_coordinates_exist, Toast.LENGTH_LONG).show();
          break;
        case 1:
          new GetArtifactsForCurrentLocationTask().execute();
          new GetLocationsTask().execute("list");
          Toast.makeText(getApplicationContext(), R.string.update_location_success, Toast.LENGTH_SHORT).show();
          break;
        default:
          Log.e(PROD_LOG_TAG, "ERROR: unexpected update location result");
      }
    }
  }
  
  
  private class DeleteLocationTask extends AsyncTask<String, Void, Integer> {

    @Override
    protected Integer doInBackground(String... args) {

      if(null == localService || null == args[0] || "".equals(args[0])) {

        // Above, we check the parameter. It cannot be null or empty
        return Integer.valueOf(-2);
      }
      else {

        return Integer.valueOf(localService.deleteLocation(args[0]));
      }
    }

    @Override
    protected void onPostExecute(Integer result) {

      switch(result.intValue()) {
      case -2: // Intentional no break statement for this case
      case -1:
        Toast.makeText(getApplicationContext(), R.string.delete_location_failure, Toast.LENGTH_LONG).show();
        break;
      case 0:
        Toast.makeText(getApplicationContext(), R.string.delete_location_partial, Toast.LENGTH_LONG).show();
        break;
      case 1:
        new GetLocationsTask().execute("list");
        new GetArtifactsForCurrentLocationTask().execute();
        Toast.makeText(getApplicationContext(), R.string.delete_location_success, Toast.LENGTH_SHORT).show();
        break;
      default:
        Log.e(PROD_LOG_TAG, "ERROR: unexpected deleteLocation() status");
      }
    }
  }
  
  private class DeleteArtifactTask extends AsyncTask<String, Void, Integer> {

    @Override
    protected Integer doInBackground(String... args) {
      
      if(null == localService || null == args[0] || null == args[1] || "".equals(args[0]) || "".equals(args[1])) {
        
        // Above, we check the parameters. They cannot be null or empty
        return Integer.valueOf(-2);
      }
      else {
        
        return Integer.valueOf(localService.deleteArtifact(args[0], args[1]));
      }
    }
    
    @Override
    protected void onPostExecute(Integer result) {
      
      switch(result.intValue()) {
      case -2:
      case -1:
        Toast.makeText(getApplicationContext(), R.string.delete_artifact_failure, Toast.LENGTH_LONG).show();
        break;
      case 1:
        new GetArtifactsForCurrentLocationTask().execute();
        Toast.makeText(getApplicationContext(), R.string.delete_artifact_success, Toast.LENGTH_SHORT).show();
        break;
      default:
        Log.e(PROD_LOG_TAG, "ERROR: unexpected deleteArtifact() status");
      }
    }
  }
  
  private class HasArtifactsAtLocationTask extends AsyncTask<String, Void, Void> {

    @Override
    protected Void doInBackground(String... args) {
      
      
      if(null == localService) {
        
        // We don't do anything
        return null;
      }
      
      boolean hasArtifacts = localService.hasArtifactsAtLocation(args[0]);
      
      /*
       * We only call the JS callback if there are artifacts so that we can hide the delete button
       */
      if(hasArtifacts) {
        
        callJavaScriptFunction(HAS_ARTIFACTS_AT_LOCATION_CALLBACK);
      }
      
      return null;
    }
  }
}




Java Source Code List

org.artifactly.client.ApplicationConstants.java
org.artifactly.client.Artifactly.java
org.artifactly.client.content.DbAdapter.java
org.artifactly.client.service.ArtifactlyService.java
org.artifactly.client.service.LocalServiceImpl.java
org.artifactly.client.service.LocalService.java
org.artifactly.client.service.StartServiceAtBoot.java