// -*- mode: java; c-basic-offset: 2; -*- // Copyright 2009-2011 Google, All Rights reserved // Copyright 2011-2012 MIT, All rights reserved // Released under the Apache License, Version 2.0 // http://www.apache.org/licenses/LICENSE-2.0 package com.google.appinventor.components.runtime; import com.google.appinventor.components.annotations.DesignerComponent; import com.google.appinventor.components.annotations.DesignerProperty; import com.google.appinventor.components.annotations.PropertyCategory; import com.google.appinventor.components.annotations.SimpleFunction; import com.google.appinventor.components.annotations.SimpleObject; import com.google.appinventor.components.annotations.SimpleProperty; import com.google.appinventor.components.common.ComponentCategory; import com.google.appinventor.components.common.PropertyTypeConstants; import com.google.appinventor.components.common.YaVersion; import com.google.appinventor.components.runtime.errors.YailRuntimeError; import com.google.appinventor.components.runtime.util.JsonUtil; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import android.content.Context; import android.content.SharedPreferences; import org.json.JSONException; /** * Persistently store YAIL values on the phone using tags to store and retrieve. * * @author markf@google.com (Mark Friedman) */ @DesignerComponent(version = YaVersion.TINYDB_COMPONENT_VERSION, description = "TinyDB is a non-visible component that stores data for an app. " + "<p> Apps created with App Inventor are initialized each time they run: " + "If an app sets the value of a variable and the user then quits the app, " + "the value of that variable will not be remembered the next time the app is run. " + "In contrast, TinyDB is a <em> persistent </em> data store for the app, " + "that is, the data stored there will be available each time the app is " + "run. An example might be a game that saves the high score and " + "retrieves it each time the game is played. </<p> " + "<p> Data items are strings stored under <em>tags</em> . To store a data " + "item, you specify the tag it should be stored under. Subsequently, you " + "can retrieve the data that was stored under a given tag. </p>" + "<p> There is only one data store per app. Even if you have multiple TinyDB " + "components, they will use the same data store. To get the effect of " + "separate stores, use different keys. Also each app has its own data " + "store. You cannot use TinyDB to pass data between two different apps on " + "the phone, although you <em>can</em> use TinyDb to shares data between the " + "different screens of a multi-screen app. </p> " + "<p>When you are developing apps using the AI Companion, all the apps " + "using that companion will share the same TinyDb. That sharing will disappear " + "once the apps are packaged. But, during development, you should be careful to clear " + "the TinyDb each time you start working on a new app.</p>", category = ComponentCategory.STORAGE, nonVisible = true, iconName = "images/tinyDB.png") @SimpleObject public class TinyDB extends AndroidNonvisibleComponent implements Component, Deleteable { private SharedPreferences sharedPreferences; private Context context; // this was a local in constructor and final not private /** * Creates a new TinyDB component. * * @param container the Form that this component is contained in. */ public TinyDB(ComponentContainer container) { super(container.$form()); context = (Context) container.$context(); sharedPreferences = context.getSharedPreferences("TinyDB1", Context.MODE_PRIVATE); } /** * Store the given value under the given tag. The storage persists on the * phone when the app is restarted. * * @param tag The tag to use * @param valueToStore The value to store. Can be any type of value (e.g. * number, text, boolean or list). */ @SimpleFunction public void StoreValue(final String tag, final Object valueToStore) { final SharedPreferences.Editor sharedPrefsEditor = sharedPreferences.edit(); try { sharedPrefsEditor.putString(tag, JsonUtil.getJsonRepresentation(valueToStore)); sharedPrefsEditor.commit(); } catch (JSONException e) { throw new YailRuntimeError("Value failed to convert to JSON.", "JSON Creation Error."); } } /** * Retrieve the value stored under the given tag. If there's no such tag, then return valueIfTagNotThere. * * @param tag The tag to use * @param valueIfTagNotThere The value returned if tag in not in TinyDB * @return The value stored under the tag. Can be any type of value (e.g. * number, text, boolean or list). */ @SimpleFunction public Object GetValue(final String tag, final Object valueIfTagNotThere) { try { String value = sharedPreferences.getString(tag, ""); // If there's no entry with tag as a key then return the empty string. // was return (value.length() == 0) ? "" : JsonUtil.getObjectFromJson(value); return (value.length() == 0) ? valueIfTagNotThere : JsonUtil.getObjectFromJson(value); } catch (JSONException e) { throw new YailRuntimeError("Value failed to convert from JSON.", "JSON Creation Error."); } } /** * Return a list of all the tags in the data store * * @param * @return a list of all keys. */ @SimpleFunction public Object GetTags() { List<String> keyList = new ArrayList<String>(); Map<String,?> keyValues = sharedPreferences.getAll(); // here is the simple way to get keys keyList.addAll(keyValues.keySet()); java.util.Collections.sort(keyList); return keyList; } /** * Clear the entire data store * */ @SimpleFunction public void ClearAll() { final SharedPreferences.Editor sharedPrefsEditor = sharedPreferences.edit(); sharedPrefsEditor.clear(); sharedPrefsEditor.commit(); } /** * Clear the entry with the given tag * * @param tag The tag to remove. */ @SimpleFunction public void ClearTag(final String tag) { final SharedPreferences.Editor sharedPrefsEditor = sharedPreferences.edit(); sharedPrefsEditor.remove(tag); sharedPrefsEditor.commit(); } @Override public void onDelete() { final SharedPreferences.Editor sharedPrefsEditor = sharedPreferences.edit(); sharedPrefsEditor.clear(); sharedPrefsEditor.commit(); } }