diff --git a/.classpath b/.classpath index 5176974..28e3f84 100644 --- a/.classpath +++ b/.classpath @@ -5,5 +5,7 @@ + + diff --git a/AndroidManifest.xml b/AndroidManifest.xml index e361d00..dd1b200 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -5,15 +5,16 @@ android:versionName="1.0" > - + + + + + - - - - - @@ -38,13 +39,25 @@ android:label="@string/title_activity_sign_up" android:parentActivityName="com.codepath.beacon.ui.LoginActivity" > - - - - - - - + + + + + + + + + + + + + + + - + \ No newline at end of file diff --git a/libs/Parse-1.5.1.jar.properties b/libs/Parse-1.5.1.jar.properties new file mode 100644 index 0000000..c55a8b8 --- /dev/null +++ b/libs/Parse-1.5.1.jar.properties @@ -0,0 +1 @@ +doc=Parse-1.5.1-javadoc diff --git a/res/drawable-hdpi/ic_add.png b/res/drawable-hdpi/ic_add.png new file mode 100644 index 0000000..71838eb Binary files /dev/null and b/res/drawable-hdpi/ic_add.png differ diff --git a/res/drawable-hdpi/ic_beacon_btn.png b/res/drawable-hdpi/ic_beacon_btn.png new file mode 100644 index 0000000..6e4cf60 Binary files /dev/null and b/res/drawable-hdpi/ic_beacon_btn.png differ diff --git a/res/drawable-hdpi/ic_empty.png b/res/drawable-hdpi/ic_empty.png new file mode 100644 index 0000000..bb87a4e Binary files /dev/null and b/res/drawable-hdpi/ic_empty.png differ diff --git a/res/drawable-hdpi/ic_notification.png b/res/drawable-hdpi/ic_notification.png new file mode 100644 index 0000000..277a918 Binary files /dev/null and b/res/drawable-hdpi/ic_notification.png differ diff --git a/res/drawable-mdpi/ic_add.png b/res/drawable-mdpi/ic_add.png new file mode 100644 index 0000000..b9f5b7b Binary files /dev/null and b/res/drawable-mdpi/ic_add.png differ diff --git a/res/drawable-mdpi/ic_beacon_btn.png b/res/drawable-mdpi/ic_beacon_btn.png new file mode 100644 index 0000000..f88ed98 Binary files /dev/null and b/res/drawable-mdpi/ic_beacon_btn.png differ diff --git a/res/drawable-mdpi/ic_empty.png b/res/drawable-mdpi/ic_empty.png new file mode 100644 index 0000000..877a609 Binary files /dev/null and b/res/drawable-mdpi/ic_empty.png differ diff --git a/res/drawable-mdpi/ic_notification.png b/res/drawable-mdpi/ic_notification.png new file mode 100644 index 0000000..404200d Binary files /dev/null and b/res/drawable-mdpi/ic_notification.png differ diff --git a/res/drawable-xhdpi/ic_add.png b/res/drawable-xhdpi/ic_add.png new file mode 100644 index 0000000..2f056a1 Binary files /dev/null and b/res/drawable-xhdpi/ic_add.png differ diff --git a/res/drawable-xhdpi/ic_beacon_btn.png b/res/drawable-xhdpi/ic_beacon_btn.png new file mode 100644 index 0000000..08bb0da Binary files /dev/null and b/res/drawable-xhdpi/ic_beacon_btn.png differ diff --git a/res/drawable-xhdpi/ic_empty.png b/res/drawable-xhdpi/ic_empty.png new file mode 100644 index 0000000..1dd395a Binary files /dev/null and b/res/drawable-xhdpi/ic_empty.png differ diff --git a/res/drawable-xhdpi/ic_notification.png b/res/drawable-xhdpi/ic_notification.png new file mode 100644 index 0000000..6deff83 Binary files /dev/null and b/res/drawable-xhdpi/ic_notification.png differ diff --git a/res/drawable-xxhdpi/ic_add.png b/res/drawable-xxhdpi/ic_add.png new file mode 100644 index 0000000..201b949 Binary files /dev/null and b/res/drawable-xxhdpi/ic_add.png differ diff --git a/res/drawable-xxhdpi/ic_beacon_btn.png b/res/drawable-xxhdpi/ic_beacon_btn.png new file mode 100644 index 0000000..fb8ca8c Binary files /dev/null and b/res/drawable-xxhdpi/ic_beacon_btn.png differ diff --git a/res/drawable-xxhdpi/ic_empty.png b/res/drawable-xxhdpi/ic_empty.png new file mode 100644 index 0000000..14218d7 Binary files /dev/null and b/res/drawable-xxhdpi/ic_empty.png differ diff --git a/res/drawable-xxhdpi/ic_notification.png b/res/drawable-xxhdpi/ic_notification.png new file mode 100644 index 0000000..b8cad50 Binary files /dev/null and b/res/drawable-xxhdpi/ic_notification.png differ diff --git a/res/layout/activity_my_recipe.xml b/res/layout/activity_my_recipe.xml new file mode 100644 index 0000000..6e619d0 --- /dev/null +++ b/res/layout/activity_my_recipe.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/res/layout/activity_recipe_detail.xml b/res/layout/activity_recipe_detail.xml new file mode 100644 index 0000000..7616328 --- /dev/null +++ b/res/layout/activity_recipe_detail.xml @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/layout/fragment_empty_list.xml b/res/layout/fragment_empty_list.xml new file mode 100644 index 0000000..9f951fb --- /dev/null +++ b/res/layout/fragment_empty_list.xml @@ -0,0 +1,14 @@ + + + + + + diff --git a/res/layout/fragment_recipe_list.xml b/res/layout/fragment_recipe_list.xml new file mode 100644 index 0000000..c9b9f83 --- /dev/null +++ b/res/layout/fragment_recipe_list.xml @@ -0,0 +1,12 @@ + + + + + \ No newline at end of file diff --git a/res/layout/recipe_item.xml b/res/layout/recipe_item.xml new file mode 100644 index 0000000..64928f5 --- /dev/null +++ b/res/layout/recipe_item.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/res/menu/my_recipe.xml b/res/menu/my_recipe.xml new file mode 100644 index 0000000..11ef350 --- /dev/null +++ b/res/menu/my_recipe.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/res/values/strings.xml b/res/values/strings.xml index 40dff42..73e4a72 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -15,10 +15,23 @@ New User? "Sign up here" SignUpActivity - Refresh Looking for devices. Please wait.. No devices found - + BeaconFriendlyName + Approaching + send notification + On + RecipeDetailActivity + IF + Then + "Activated on:" + Date + Triggered: + Count + Distance: + Beacon Distance + Receive Notification with this beacon + Beacon Name and UUID diff --git a/src/com/codepath/beacon/BeaconApplication.java b/src/com/codepath/beacon/BeaconApplication.java index d932b43..be951e6 100644 --- a/src/com/codepath/beacon/BeaconApplication.java +++ b/src/com/codepath/beacon/BeaconApplication.java @@ -1,14 +1,12 @@ package com.codepath.beacon; -import com.parse.Parse; -import com.parse.ParseException; -import com.parse.ParseObject; -import com.parse.ParseUser; -import com.parse.SaveCallback; - import android.app.Application; import android.util.Log; +import com.codepath.beacon.models.Recipe; +import com.parse.Parse; +import com.parse.ParseObject; + public class BeaconApplication extends Application { private static final String LOG_TAG = BeaconApplication.class.getSimpleName(); private static final String APP_ID = "KopNnh31P28DZMDp9njtWRDpgUkn2qwrMBNZ53VJ"; @@ -19,6 +17,7 @@ public void onCreate() { // TODO Auto-generated method stub super.onCreate(); Log.d(LOG_TAG, "onCreate(). Initializing Parse"); + ParseObject.registerSubclass(Recipe.class); Parse.initialize(this, APP_ID, CLIENT_KEY); } } diff --git a/src/com/codepath/beacon/activity/MyRecipeActivity.java b/src/com/codepath/beacon/activity/MyRecipeActivity.java new file mode 100644 index 0000000..89500b9 --- /dev/null +++ b/src/com/codepath/beacon/activity/MyRecipeActivity.java @@ -0,0 +1,42 @@ +package com.codepath.beacon.activity; + +import android.content.Intent; +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentTransaction; +import android.view.Menu; +import android.view.MenuItem; +import android.view.Window; + +import com.codepath.beacon.R; +import com.codepath.beacon.fragments.RecipeListFragment; +import com.codepath.beacon.scan.BleActivity; + +public class MyRecipeActivity extends FragmentActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); + setContentView(R.layout.activity_my_recipe); + + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + // TODO: hardcode the userID for now, will pass in additional parameters + RecipeListFragment newFragment = RecipeListFragment.newInstance("rcao"); + transaction.replace(R.id.flrecipelist, newFragment); + transaction.commit(); + + } + + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.my_recipe, menu); + return true; + } + + public void onAddAction(MenuItem mi) { + + Intent scanIntent = new Intent(this, BleActivity.class); + startActivity(scanIntent); + } +} diff --git a/src/com/codepath/beacon/activity/RecipeDetailActivity.java b/src/com/codepath/beacon/activity/RecipeDetailActivity.java new file mode 100644 index 0000000..d102bfb --- /dev/null +++ b/src/com/codepath/beacon/activity/RecipeDetailActivity.java @@ -0,0 +1,41 @@ +package com.codepath.beacon.activity; + +import android.app.Activity; +import android.os.Bundle; +import android.widget.TextView; +import android.widget.ToggleButton; + +import com.codepath.beacon.R; +import com.codepath.beacon.models.Recipe; + +public class RecipeDetailActivity extends Activity { + private Recipe recipe; + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_recipe_detail); + populateRecipeDetail(recipe); + } + + private void populateRecipeDetail(Recipe recipe) { + // populate recipe detail page with recipe data + String fn = getIntent().getStringExtra("fn"); + String UUID = getIntent().getStringExtra("UUID"); + String activationDate = getIntent().getStringExtra("activationDate");; + String triggerCount = getIntent().getStringExtra("triggerCount"); + boolean status = getIntent().getBooleanExtra("status", false); + + TextView tvActivationDate = (TextView) findViewById(R.id.tvActivationDate); + tvActivationDate.setText(activationDate); + TextView tvTriggeredCount = (TextView) findViewById(R.id.tvTriggeredCount); + tvTriggeredCount.setText(triggerCount); + ToggleButton tbStatus = (ToggleButton) findViewById(R.id.tbStatus); + tbStatus.setChecked(status); + TextView tvBeaconnameandUUID = (TextView) findViewById(R.id.tvBeaconnameandUUID); + tvBeaconnameandUUID.setText(fn+"---"+UUID); + + //TODO: Need to call 3rd party lib to get distance or other beacon related information + TextView tvBeaconDistance = (TextView) findViewById(R.id.tvBeaconDistance); + } + +} \ No newline at end of file diff --git a/src/com/codepath/beacon/adapter/RecipeArrayAdapter.java b/src/com/codepath/beacon/adapter/RecipeArrayAdapter.java new file mode 100644 index 0000000..876c0cc --- /dev/null +++ b/src/com/codepath/beacon/adapter/RecipeArrayAdapter.java @@ -0,0 +1,85 @@ +package com.codepath.beacon.adapter; + +import java.text.SimpleDateFormat; +import java.util.List; + +import android.content.Context; +import android.content.Intent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.TextView; +import android.widget.ToggleButton; + +import com.codepath.beacon.R; +import com.codepath.beacon.activity.RecipeDetailActivity; +import com.codepath.beacon.models.Recipe; + +public class RecipeArrayAdapter extends ArrayAdapter { + private final int REQUEST_CODE = 20; + public RecipeArrayAdapter(Context context, List recipes) { + super(context, R.layout.recipe_item, recipes); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + Recipe recipe = (Recipe) getItem(position); + View v; + + if (convertView == null) { + LayoutInflater inflator = LayoutInflater.from(getContext()); + v = inflator.inflate(R.layout.recipe_item, parent, false); + } else { + v = convertView; + } + + // find the views within template + TextView tvBeaconName = (TextView) v.findViewById(R.id.tvbeaconname); + TextView tvTrigger = (TextView) v.findViewById(R.id.tvtrigger); + TextView tvNotification = (TextView) v.findViewById(R.id.tvnotification); + ToggleButton tbTrigger = (ToggleButton) v.findViewById(R.id.tbtrigger); + + //TODO add image to the list later + /* ImageView ivBeaconImage = (ImageView) v.findViewById(R.id.ivbeaconimage); + ivBeaconImage.setImageResource(android.R.color.transparent); + ImageLoader imageLoader = ImageLoader.getInstance(); + + // populate views with recipe data + imageLoader.displayImage("http://www.pendragon-it.com/wp-content/uploads/2014/06/ibeacon-660x375.png", ivBeaconImage); + */ + + tvBeaconName.setText(recipe.getFriendlyName()); + tvTrigger.setText(recipe.getTrigger()); + tvNotification.setText(recipe.getNotification()); + tbTrigger.setChecked(recipe.isStatus()); + + // pass recipe to activity view + v.setTag(recipe); + + v.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + Recipe recipe = (Recipe) v.getTag(); + String fn = recipe.getFriendlyName(); + String UUID = recipe.getUUID(); + SimpleDateFormat df = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss"); + String activationDate = df.format(recipe.getActivationDate()); + String triggerCount = Integer.toString(recipe.getTriggeredCount()); + boolean status = recipe.isStatus(); + + Intent i = new Intent(getContext(), RecipeDetailActivity.class); + i.putExtra("fn", fn); + i.putExtra("UUID", UUID); + i.putExtra("activationDate", activationDate); + i.putExtra("triggerCount", triggerCount); + i.putExtra("status", status); + getContext().startActivity(i); + } + }); + + return v; + + } + +} diff --git a/src/com/codepath/beacon/fragments/RecipeListFragment.java b/src/com/codepath/beacon/fragments/RecipeListFragment.java new file mode 100644 index 0000000..06a2d7e --- /dev/null +++ b/src/com/codepath/beacon/fragments/RecipeListFragment.java @@ -0,0 +1,127 @@ +package com.codepath.beacon.fragments; + +import java.util.ArrayList; +import java.util.List; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ListView; + +import com.codepath.beacon.R; +import com.codepath.beacon.adapter.RecipeArrayAdapter; +import com.codepath.beacon.models.Recipe; +import com.codepath.beacon.util.EndlessScrollListener; +import com.parse.FindCallback; +import com.parse.ParseException; +import com.parse.ParseObject; +import com.parse.ParseQuery; +import com.parse.ParseUser; + +public class RecipeListFragment extends Fragment { + protected ArrayList recipes; + protected ArrayAdapter aRecipes; + protected ListView lvRecipes; + protected int repCount =20; + + public static RecipeListFragment newInstance(String userID) { + RecipeListFragment recipeListFragment = new RecipeListFragment(); + Bundle args = new Bundle(); + args.putString("user_id", userID); + recipeListFragment.setArguments(args); + return recipeListFragment; + } + + protected void findMyRecipes(final String max_id, final boolean refresh) { + + ParseQuery query = ParseQuery.getQuery(Recipe.class); + ParseUser currentUser = ParseUser.getCurrentUser(); + query.addDescendingOrder("FN"); + // query.whereEqualTo("owner", ParseUser.getCurrentUser()); + if (!max_id.equals("0")) + query.whereGreaterThan("FN", max_id); + + // Execute the find asynchronously + query.findInBackground(new FindCallback() { + public void done(List itemList, ParseException e) { + if (e == null) { + // Access the array of results here + recipes = new ArrayList(itemList); + aRecipes.addAll(recipes); + } else { + Log.d("item", "Error: " + e.getMessage()); + } + } + }); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + recipes = new ArrayList(); + aRecipes = new RecipeArrayAdapter(getActivity(), recipes); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + // Defines the xml file for the fragment + View view = inflater.inflate(R.layout.fragment_recipe_list, container, false); + lvRecipes = (ListView) view.findViewById(R.id.lvRecipes); + lvRecipes.setAdapter(aRecipes); + findMyRecipes("0", false); + return view; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + // Attach the listener to the AdapterView onCreate + lvRecipes.setOnScrollListener(new EndlessScrollListener() { + public void onLoadMore(int page, int totalItemsCount) { + // Triggered only when new data needs to be appended to the list + customLoadMoreDataFromApi(totalItemsCount); + } + }); + +/* lvRecipes.setOnRefreshListener(new OnRefreshListener() { + @Override + public void onRefresh() { + findMyRecipes("0", true); + } + }); */ + } + + // Append more data into the adapter + public void customLoadMoreDataFromApi(int offset) { + String max_id; + + int recipeLen = recipes.size(); + if ((recipeLen > 0) && (recipeLen < offset-1)){ + max_id = ((Recipe) recipes.get(recipeLen-1)).getFriendlyName(); + findMyRecipes(max_id, false); + } + } + + public void insertRecipetoTop(Recipe r) { + aRecipes.insert(r, 0); + lvRecipes.setSelection(0); + } + + // should be called when an async task started + public void showProgressBar() { + getActivity().setProgressBarIndeterminateVisibility(true); + } + + // Should be called when an async task has finished + public void hideProgressBar() { + getActivity().setProgressBarIndeterminateVisibility(false); + } + +} diff --git a/src/com/codepath/beacon/models/Recipe.java b/src/com/codepath/beacon/models/Recipe.java new file mode 100644 index 0000000..2b1348b --- /dev/null +++ b/src/com/codepath/beacon/models/Recipe.java @@ -0,0 +1,112 @@ +package com.codepath.beacon.models; + +import java.util.Date; + +import com.parse.ParseClassName; +import com.parse.ParseObject; +import com.parse.ParseUser; + +@ParseClassName("Recipe") +public class Recipe extends ParseObject { + + private String notification; + + public Recipe() { + super(); + } + + public Recipe(String fn, String UUID) { + super(); + setFriendlyName(fn); + setUUID(UUID); + } + + public String getFriendlyName() { + return getString("FN"); + } + public void setFriendlyName(String friendlyName) { + put("FN", friendlyName); + } + + public Date getActivationDate() { + return getDate("activationdate"); + } + + public void setActivationDate(Date activationDate) { + put("activationdate", activationDate); + } + + public String getUUID() { + return getString("UUID"); + } + public void setUUID(String UUID) { + put("UUID", UUID); + } + + public boolean isPushNotification() { + return getBoolean("notification"); + } + + public void setPushNotification(boolean pushNotification) { + put("notification", pushNotification); + } + + public boolean isSms() { + return getBoolean("sms"); + } + + public void setSms(boolean sms) { + put("sms", sms); + } + + public String getContactNum() { + return getString("contactnumber"); + } + + public void setContactNum(String contactNum) { + put("contactnumber", contactNum); + } + + public String getNotification() { + if (isPushNotification()) + notification = "Notification"; + if (isSms()) + notification = "SMS"; + return notification; + } + + public boolean isStatus() { + return getBoolean("status"); + } + + public void setStatus(boolean status) { + put("status", status); + } + + public String getTrigger() { + return getString("trigger"); + } + + public void setTrigger(String trigger) { + put("trigger", trigger); + } + + public int getTriggeredCount() { + return getInt("triggercount"); + } + + public void setTriggeredCount(int triggeredCount) { + put("triggercount", triggeredCount); + } + + public void setOwner(ParseUser user) { + put("owner", user); + } + + // Get the user for this comment + public ParseUser getOwner() { + return getParseUser("owner"); + } + +} + diff --git a/src/com/codepath/beacon/util/EndlessScrollListener.java b/src/com/codepath/beacon/util/EndlessScrollListener.java new file mode 100644 index 0000000..52747b3 --- /dev/null +++ b/src/com/codepath/beacon/util/EndlessScrollListener.java @@ -0,0 +1,72 @@ +package com.codepath.beacon.util; + +import android.widget.AbsListView; +import android.widget.AbsListView.OnScrollListener; + + +public abstract class EndlessScrollListener implements OnScrollListener { + // The minimum amount of items to have below your current scroll position + // before loading more. + private int visibleThreshold = 5; + // The current offset index of data you have loaded + private int currentPage = 0; + // The total number of items in the dataset after the last load + private int previousTotalItemCount = 0; + // True if we are still waiting for the last set of data to load. + private boolean loading = true; + // Sets the starting page index + private int startingPageIndex = 0; + + public EndlessScrollListener() { + } + + public EndlessScrollListener(int visibleThreshold) { + this.visibleThreshold = visibleThreshold; + } + + public EndlessScrollListener(int visibleThreshold, int startPage) { + this.visibleThreshold = visibleThreshold; + this.startingPageIndex = startPage; + this.currentPage = startPage; + } + + // This happens many times a second during a scroll, so be wary of the code you place here. + // We are given a few useful parameters to help us work out if we need to load some more data, + // but first we check if we are waiting for the previous load to finish. + @Override + public void onScroll(AbsListView view,int firstVisibleItem,int visibleItemCount,int totalItemCount) + { + // If the total item count is zero and the previous isn't, assume the + // list is invalidated and should be reset back to initial state + if (totalItemCount < previousTotalItemCount) { + this.currentPage = this.startingPageIndex; + this.previousTotalItemCount = totalItemCount; + if (totalItemCount == 0) { this.loading = true; } + } + + // If it’s still loading, we check to see if the dataset count has + // changed, if so we conclude it has finished loading and update the current page + // number and total item count. + if (loading && (totalItemCount > previousTotalItemCount)) { + loading = false; + previousTotalItemCount = totalItemCount; + currentPage++; + } + + // If it isn’t currently loading, we check to see if we have breached + // the visibleThreshold and need to reload more data. + // If we do need to reload some more data, we execute onLoadMore to fetch the data. + if (!loading && (totalItemCount - visibleItemCount)<=(firstVisibleItem + visibleThreshold)) { + onLoadMore(currentPage + 1, totalItemCount); + loading = true; + } + } + + // Defines the process for actually loading more data based on page + public abstract void onLoadMore(int page, int totalItemsCount); + + @Override + public void onScrollStateChanged(AbsListView view, int scrollState) { + // Don't take any action on changed + } +} diff --git a/src/com/codepath/beacon/util/ParseRelativeDate.java b/src/com/codepath/beacon/util/ParseRelativeDate.java new file mode 100644 index 0000000..e72c7cf --- /dev/null +++ b/src/com/codepath/beacon/util/ParseRelativeDate.java @@ -0,0 +1,28 @@ +package com.codepath.beacon.util; + +import java.text.SimpleDateFormat; +import java.util.Locale; + +import android.net.ParseException; +import android.text.format.DateUtils; + + +public class ParseRelativeDate { + //getRelativeTimeAgo("Mon Apr 01 21:16:23 +0000 2014"); + public String getRelativeTimeAgo(String rawJsonDate) throws java.text.ParseException { + String twitterFormat = "EEE MMM dd HH:mm:ss ZZZZZ yyyy"; + SimpleDateFormat sf = new SimpleDateFormat(twitterFormat, Locale.ENGLISH); + sf.setLenient(true); + + String relativeDate = ""; + try { + long dateMillis = sf.parse(rawJsonDate).getTime(); + relativeDate = DateUtils.getRelativeTimeSpanString(dateMillis, + System.currentTimeMillis(), DateUtils.SECOND_IN_MILLIS).toString(); + } catch (ParseException e) { + e.printStackTrace(); + } + + return relativeDate; + } +}