Repository: entaq/GoogleAppsScript
Branch: master
Commit: 672e8209d88d
Files: 98
Total size: 185.6 KB
Directory structure:
gitextract_o4r4wtx7/
├── .gitignore
├── Android/
│ ├── ClientCode/
│ │ ├── AndroidManifest.xml
│ │ ├── README
│ │ ├── gen/
│ │ │ └── com/
│ │ │ └── google/
│ │ │ └── android/
│ │ │ └── gcm/
│ │ │ └── demo/
│ │ │ └── app/
│ │ │ ├── BuildConfig.java
│ │ │ ├── Manifest.java
│ │ │ └── R.java
│ │ ├── libs/
│ │ │ ├── android-support-v4.jar
│ │ │ └── gcm.jar
│ │ ├── project.properties
│ │ ├── res/
│ │ │ ├── layout/
│ │ │ │ ├── main.xml
│ │ │ │ └── rowlayout.xml
│ │ │ ├── menu/
│ │ │ │ ├── inventory.xml
│ │ │ │ └── options_menu.xml
│ │ │ ├── values/
│ │ │ │ ├── dimens.xml
│ │ │ │ └── strings.xml
│ │ │ ├── values-sw600dp/
│ │ │ │ └── dimens.xml
│ │ │ └── values-sw720dp-land/
│ │ │ └── dimens.xml
│ │ └── src/
│ │ └── com/
│ │ └── google/
│ │ └── android/
│ │ └── gcm/
│ │ └── demo/
│ │ └── app/
│ │ ├── CommonUtilities.java
│ │ ├── DemoActivity.java
│ │ ├── GCMIntentService.java
│ │ ├── Item.java
│ │ ├── ServerUtilities.java
│ │ └── SimpleArrayAdapter.java
│ ├── GCM_Registrar_Script/
│ │ ├── code.gs
│ │ ├── scriptdb.gs
│ │ └── ui.html
│ ├── InventoryContentService.gs
│ ├── SharedDb.gs
│ ├── readme.md
│ └── track-and-notify.gs
├── ContentService/
│ ├── Addressbook-VCF/
│ │ ├── Code.gs
│ │ └── vcard_template.html
│ ├── Gmail-RSS/
│ │ ├── code.gs
│ │ └── rss.html
│ ├── Inventory-JSON-API-iOS/
│ │ ├── Inventory-iOS/
│ │ │ ├── Inventory/
│ │ │ │ ├── AppDelegate.h
│ │ │ │ ├── AppDelegate.m
│ │ │ │ ├── Inventory-Info.plist
│ │ │ │ ├── Inventory-Prefix.pch
│ │ │ │ ├── MasterViewController.h
│ │ │ │ ├── MasterViewController.m
│ │ │ │ ├── en.lproj/
│ │ │ │ │ ├── InfoPlist.strings
│ │ │ │ │ └── MainStoryboard.storyboard
│ │ │ │ └── main.m
│ │ │ └── Inventory.xcodeproj/
│ │ │ ├── project.pbxproj
│ │ │ ├── project.xcworkspace/
│ │ │ │ ├── contents.xcworkspacedata
│ │ │ │ └── xcuserdata/
│ │ │ │ └── anagarajan.xcuserdatad/
│ │ │ │ ├── UserInterfaceState.xcuserstate
│ │ │ │ └── WorkspaceSettings.xcsettings
│ │ │ └── xcuserdata/
│ │ │ └── anagarajan.xcuserdatad/
│ │ │ ├── xcdebugger/
│ │ │ │ └── Breakpoints.xcbkptlist
│ │ │ └── xcschemes/
│ │ │ ├── InventorySample.xcscheme
│ │ │ └── xcschememanagement.plist
│ │ └── code.gs
│ ├── Simple-JSONP/
│ │ ├── SimpleHtml-JSFiddle.html
│ │ └── code.gs
│ └── readme.md
├── DriveSDK/
│ ├── AppsScript.gs
│ ├── ChromeWebStoreVersion/
│ │ └── SimpleZipAndSend.gs
│ ├── FlowManager.gs
│ └── readme.md
├── FusionTables/
│ ├── RowUtilities.gs
│ ├── SampleDataExchange.gs
│ └── readme.md
├── GoogleAppsAdminAudit.gs
├── IO2013/
│ ├── Drive/
│ │ ├── Code.gs
│ │ ├── README.md
│ │ ├── SpreadsheetUtils.gs
│ │ └── ui.html
│ └── YouTubeAnalytics/
│ ├── Code.gs
│ ├── README.md
│ ├── oauth2.gs
│ ├── oauthsuccess.html
│ ├── twitter.gs
│ └── ui.html
├── NYTimesCampaignContribution.gs
├── README.md
├── SAP/
│ ├── Forms/
│ │ ├── Code.gs
│ │ └── product_template.html
│ ├── Gmail Schemas/
│ │ ├── Code.gs
│ │ └── mail_template.html
│ ├── README.md
│ └── Sheets/
│ ├── Code.gs
│ └── ui.html
├── Salesforce.com/
│ ├── OAuthAndUploadContactsToSalesforce.gs
│ ├── RowUtilities.gs
│ ├── ScanEmailToSalesforce.gs
│ └── readme.md
├── ScriptDbVisualizer/
│ ├── Code.gs
│ ├── ScriptDbConsole.html
│ └── readme.md
├── Twilio/
│ ├── MakePhoneCall/
│ │ ├── MakePhoneCall.gs
│ │ └── twiml.html
│ ├── RecieveSMS/
│ │ └── RecieveSMS.gs
│ └── readme.md
├── WhiteHouseHackday/
│ ├── Code.gs
│ ├── README.md
│ └── ui.html
└── YouTube/
├── CreateGDLCalendar.gs
├── YouTubeAnalytics.gs
└── readme.md
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
#ignore these files
.DS_Store
================================================
FILE: Android/ClientCode/AndroidManifest.xml
================================================
================================================
FILE: Android/ClientCode/README
================================================
GCM demo app
------------
1.To create the project settings, type:
android update project --name GCMDemo -p . --target android-16
2.To build the app, type:
ant clean debug
(but set SENDER_ID and SERVER_URL on /src/com/google/android/gcm/demo/app/CommonUtilities.java first)
3.To install the app, type
ant installd
================================================
FILE: Android/ClientCode/gen/com/google/android/gcm/demo/app/BuildConfig.java
================================================
/** Automatically generated file. DO NOT MODIFY */
package com.google.android.gcm.demo.app;
public final class BuildConfig {
public final static boolean DEBUG = true;
}
================================================
FILE: Android/ClientCode/gen/com/google/android/gcm/demo/app/Manifest.java
================================================
/* AUTO-GENERATED FILE. DO NOT MODIFY.
*
* This class was automatically generated by the
* aapt tool from the resource data it found. It
* should not be modified by hand.
*/
package com.google.android.gcm.demo.app;
public final class Manifest {
public static final class permission {
/**
Creates a custom permission so only this app can receive its messages.
NOTE: the permission *must* be called PACKAGE.permission.C2D_MESSAGE,
where PACKAGE is the application's package name.
*/
public static final String C2D_MESSAGE="com.google.android.gcm.demo.app.permission.C2D_MESSAGE";
}
}
================================================
FILE: Android/ClientCode/gen/com/google/android/gcm/demo/app/R.java
================================================
/* AUTO-GENERATED FILE. DO NOT MODIFY.
*
* This class was automatically generated by the
* aapt tool from the resource data it found. It
* should not be modified by hand.
*/
package com.google.android.gcm.demo.app;
public final class R {
public static final class attr {
}
public static final class dimen {
/** Default screen margins, per the Android Design guidelines.
Customize dimensions originally defined in res/values/dimens.xml (such as
screen margins) for sw720dp devices (e.g. 10" tablets) in landscape here.
*/
public static final int activity_horizontal_margin=0x7f040000;
public static final int activity_vertical_margin=0x7f040001;
}
public static final class drawable {
public static final int ic_launcher=0x7f020000;
public static final int ic_stat_gcm=0x7f020001;
}
public static final class id {
public static final int action_settings=0x7f070004;
public static final int display=0x7f070000;
public static final int inventoryCount=0x7f070003;
public static final int inventoryName=0x7f070001;
public static final int label=0x7f070002;
public static final int options_clear=0x7f070007;
public static final int options_exit=0x7f070008;
public static final int options_refresh_data=0x7f070009;
public static final int options_register=0x7f070005;
public static final int options_unregister=0x7f070006;
}
public static final class layout {
public static final int main=0x7f030000;
public static final int rowlayout=0x7f030001;
}
public static final class menu {
public static final int inventory=0x7f060000;
public static final int options_menu=0x7f060001;
}
public static final class string {
public static final int action_settings=0x7f050013;
public static final int already_registered=0x7f050002;
public static final int app_name=0x7f050000;
public static final int error_config=0x7f050001;
public static final int gcm_deleted=0x7f050008;
public static final int gcm_error=0x7f050006;
public static final int gcm_message=0x7f050005;
public static final int gcm_recoverable_error=0x7f050007;
public static final int gcm_registered=0x7f050003;
public static final int gcm_unregistered=0x7f050004;
public static final int hello_world=0x7f050014;
public static final int options_clear=0x7f050010;
public static final int options_exit=0x7f050011;
public static final int options_refresh_data=0x7f050015;
public static final int options_register=0x7f05000e;
public static final int options_unregister=0x7f05000f;
public static final int server_register_error=0x7f05000c;
public static final int server_registered=0x7f05000a;
public static final int server_registering=0x7f050009;
public static final int server_unregister_error=0x7f05000d;
public static final int server_unregistered=0x7f05000b;
public static final int title_activity_inventory=0x7f050012;
}
}
================================================
FILE: Android/ClientCode/project.properties
================================================
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system edit
# "ant.properties", and override values to adapt the script to your
# project structure.
#
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
# Project target.
target=android-17
================================================
FILE: Android/ClientCode/res/layout/main.xml
================================================
================================================
FILE: Android/ClientCode/res/layout/rowlayout.xml
================================================
================================================
FILE: Android/ClientCode/res/menu/inventory.xml
================================================
================================================
FILE: Android/ClientCode/res/menu/options_menu.xml
================================================
================================================
FILE: Android/ClientCode/res/values/dimens.xml
================================================
16dp16dp
================================================
FILE: Android/ClientCode/res/values/strings.xml
================================================
Apps Script GDLPlease set the %1$s constant and recompile the app.Device is already registered on server.From GCM: device successfully registered!From GCM: device successfully unregistered!From GCM: you got message!From GCM: error (%1$s).From GCM: recoverable error (%1$s).From GCM: server deleted %1$d pending messages!Trying (attempt %1$d/%2$d) to register device on Demo Server.From Demo Server: successfully added device!From Demo Server: successfully removed device!Could not register device on Demo Server after %1$d attempts.Could not unregister device on Demo Server (%1$s).RegisterUnregisterClearExitInventorySettingsHello world!Refresh Data
================================================
FILE: Android/ClientCode/res/values-sw600dp/dimens.xml
================================================
================================================
FILE: Android/ClientCode/res/values-sw720dp-land/dimens.xml
================================================
128dp
================================================
FILE: Android/ClientCode/src/com/google/android/gcm/demo/app/CommonUtilities.java
================================================
/*
* Copyright 2012 Google Inc.
*
* 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.google.android.gcm.demo.app;
import android.content.Context;
import android.content.Intent;
/**
* Helper class providing methods and constants common to other classes in the
* app.
*/
public final class CommonUtilities {
/**
* Base URL of the Demo Server (such as http://my_host:8080/gcm-demo)
*/
static final String SERVER_URL = "https://script.google.com/macros/s/YOUR_SERVER_URL/exec";
/**
* Google API project id registered to use GCM.
*/
static final String SENDER_ID = "YOUR_SENDER_ID";
/**
* Tag used on log messages.
*/
static final String TAG = "GCMDemo";
/**
* Intent used to display a message in the screen.
*/
static final String DISPLAY_MESSAGE_ACTION =
"com.google.android.gcm.demo.app.DISPLAY_MESSAGE";
/**
* Intent's extra that contains the message to be displayed.
*/
static final String EXTRA_MESSAGE = "message";
/**
* Notifies UI to display a message.
*
* This method is defined in the common helper because it's used both by
* the UI and the background service.
*
* @param context application's context.
* @param message message to be displayed.
*/
static void displayMessage(Context context, String message) {
Intent intent = new Intent(DISPLAY_MESSAGE_ACTION);
intent.putExtra(EXTRA_MESSAGE, message);
context.sendBroadcast(intent);
}
}
================================================
FILE: Android/ClientCode/src/com/google/android/gcm/demo/app/DemoActivity.java
================================================
/*
* Copyright 2012 Google Inc.
*
* 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.google.android.gcm.demo.app;
import static com.google.android.gcm.demo.app.CommonUtilities.DISPLAY_MESSAGE_ACTION;
import static com.google.android.gcm.demo.app.CommonUtilities.EXTRA_MESSAGE;
import static com.google.android.gcm.demo.app.CommonUtilities.SENDER_ID;
import static com.google.android.gcm.demo.app.CommonUtilities.SERVER_URL;
import java.util.ArrayList;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.json.JSONArray;
import org.json.JSONObject;
import com.google.android.gcm.GCMRegistrar;
import android.app.ListActivity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.TextView;
/**
* Main UI for the demo app.
*/
public class DemoActivity extends ListActivity {
TextView mDisplay;
AsyncTask mRegisterTask;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
checkNotNull(SERVER_URL, "SERVER_URL");
checkNotNull(SENDER_ID, "SENDER_ID");
// Make sure the device has the proper dependencies.
GCMRegistrar.checkDevice(this);
// Make sure the manifest was properly set - comment out this line
// while developing the app, then uncomment it when it's ready.
GCMRegistrar.checkManifest(this);
setContentView(R.layout.main);
mDisplay = (TextView) findViewById(R.id.display);
registerReceiver(mHandleMessageReceiver,
new IntentFilter(DISPLAY_MESSAGE_ACTION));
final String regId = GCMRegistrar.getRegistrationId(this);
final Context context = this;
if (regId.equals("")) {
// Automatically registers application on startup.
GCMRegistrar.register(this, SENDER_ID);
} else {
// Device is already registered on GCM, check server.
if (GCMRegistrar.isRegisteredOnServer(this)) {
// Skips registration.
mDisplay.append(getString(R.string.already_registered) + "\n");
} else {
// Try to register again, but not in the UI thread.
// It's also necessary to cancel the thread onDestroy(),
// hence the use of AsyncTask instead of a raw thread.
mRegisterTask = new AsyncTask() {
@Override
protected Void doInBackground(Void... params) {
boolean registered =
ServerUtilities.register(context, regId);
// At this point all attempts to register with the app
// server failed, so we need to unregister the device
// from GCM - the app will try to register again when
// it is restarted. Note that GCM will send an
// unregistered callback upon completion, but
// GCMIntentService.onUnregistered() will ignore it.
if (!registered) {
GCMRegistrar.unregister(context);
}
return null;
}
@Override
protected void onPostExecute(Void result) {
mRegisterTask = null;
}
};
mRegisterTask.execute(null, null, null);
}
}
MyTask task = new MyTask();
task.execute();
}
private class MyTask extends AsyncTask {
ArrayList items = new ArrayList();
@Override
protected Void doInBackground(Void... params) {
try {
HttpClient hc = new DefaultHttpClient();
String URL = "https://script.google.com/macros/s/YOU_DATA_URL/exec";
HttpGet get = new HttpGet(URL);
HttpResponse rp = hc.execute(get);
if(rp.getStatusLine().getStatusCode() == HttpStatus.SC_OK)
{
String result = EntityUtils.toString(rp.getEntity());
JSONArray objects = new JSONArray(result);
for (int i = 0; i < objects.length(); i++) {
JSONObject session = objects.getJSONObject(i);
Item item = new Item();
item.name = session.getString("item");
item.count = session.getString("quantity");
items.add(item);
}
}
} catch (Exception e) {
Log.e("ItemFeed", "Error loading JSON", e);
}
return null;
}
@Override
protected void onPostExecute(Void result) {
SimpleArrayAdapter adapter = new SimpleArrayAdapter(DemoActivity.this,items);
setListAdapter(adapter);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.options_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) {
/*
* Typically, an application registers automatically, so options
* below are disabled. Uncomment them if you want to manually
* register or unregister the device (you will also need to
* uncomment the equivalent options on options_menu.xml).
*/
case R.id.options_register:
GCMRegistrar.register(this, SENDER_ID);
return true;
case R.id.options_unregister:
GCMRegistrar.unregister(this);
return true;
case R.id.options_clear:
mDisplay.setText(null);
return true;
case R.id.options_exit:
finish();
return true;
case R.id.options_refresh_data:
MyTask task = new MyTask();
task.execute();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
protected void onDestroy() {
if (mRegisterTask != null) {
mRegisterTask.cancel(true);
}
unregisterReceiver(mHandleMessageReceiver);
GCMRegistrar.onDestroy(this);
super.onDestroy();
}
private void checkNotNull(Object reference, String name) {
if (reference == null) {
throw new NullPointerException(
getString(R.string.error_config, name));
}
}
private final BroadcastReceiver mHandleMessageReceiver =
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String newMessage = intent.getExtras().getString(EXTRA_MESSAGE);
mDisplay.append(newMessage + "\n");
MyTask task = new MyTask();
task.execute();
}
};
}
================================================
FILE: Android/ClientCode/src/com/google/android/gcm/demo/app/GCMIntentService.java
================================================
/*
* Copyright 2012 Google Inc.
*
* 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.google.android.gcm.demo.app;
import static com.google.android.gcm.demo.app.CommonUtilities.SENDER_ID;
import static com.google.android.gcm.demo.app.CommonUtilities.displayMessage;
import android.annotation.TargetApi;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.util.Log;
import com.google.android.gcm.GCMBaseIntentService;
import com.google.android.gcm.GCMRegistrar;
/**
* IntentService responsible for handling GCM messages.
*/
public class GCMIntentService extends GCMBaseIntentService {
@SuppressWarnings("hiding")
private static final String TAG = "GCMIntentService";
public GCMIntentService() {
super(SENDER_ID);
}
@Override
protected void onRegistered(Context context, String registrationId) {
Log.i(TAG, "Device registered: regId = " + registrationId);
displayMessage(context, getString(R.string.gcm_registered));
ServerUtilities.register(context, registrationId);
}
@Override
protected void onUnregistered(Context context, String registrationId) {
Log.i(TAG, "Device unregistered");
displayMessage(context, getString(R.string.gcm_unregistered));
if (GCMRegistrar.isRegisteredOnServer(context)) {
ServerUtilities.unregister(context, registrationId);
} else {
// This callback results from the call to unregister made on
// ServerUtilities when the registration to the server failed.
Log.i(TAG, "Ignoring unregister callback");
}
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
@Override
protected void onMessage(Context context, Intent intent) {
Log.i(TAG, "Received message");
String message = intent.getExtras().getString("message", getString(R.string.gcm_message));
displayMessage(context, message);
// notifies user
generateNotification(context, message);
}
@Override
protected void onDeletedMessages(Context context, int total) {
Log.i(TAG, "Received deleted messages notification");
String message = getString(R.string.gcm_deleted, total);
displayMessage(context, message);
// notifies user
generateNotification(context, message);
}
@Override
public void onError(Context context, String errorId) {
Log.i(TAG, "Received error: " + errorId);
displayMessage(context, getString(R.string.gcm_error, errorId));
}
@Override
protected boolean onRecoverableError(Context context, String errorId) {
// log message
Log.i(TAG, "Received recoverable error: " + errorId);
displayMessage(context, getString(R.string.gcm_recoverable_error,
errorId));
return super.onRecoverableError(context, errorId);
}
/**
* Issues a notification to inform the user that server has sent a message.
*/
private static void generateNotification(Context context, String message) {
int icon = R.drawable.ic_stat_gcm;
long when = System.currentTimeMillis();
NotificationManager notificationManager = (NotificationManager)
context.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new Notification(icon, message, when);
String title = context.getString(R.string.app_name);
Intent notificationIntent = new Intent(context, DemoActivity.class);
// set intent so it does not start a new activity
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP |
Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent intent =
PendingIntent.getActivity(context, 0, notificationIntent, 0);
notification.setLatestEventInfo(context, title, message, intent);
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notification.defaults = Notification.DEFAULT_ALL;
notificationManager.notify(0, notification);
}
}
================================================
FILE: Android/ClientCode/src/com/google/android/gcm/demo/app/Item.java
================================================
package com.google.android.gcm.demo.app;
public class Item {
String name;
String count;
}
================================================
FILE: Android/ClientCode/src/com/google/android/gcm/demo/app/ServerUtilities.java
================================================
/*
* Copyright 2012 Google Inc.
*
* 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.google.android.gcm.demo.app;
import static com.google.android.gcm.demo.app.CommonUtilities.SERVER_URL;
import static com.google.android.gcm.demo.app.CommonUtilities.TAG;
import static com.google.android.gcm.demo.app.CommonUtilities.displayMessage;
import com.google.android.gcm.GCMRegistrar;
import android.content.Context;
import android.util.Log;
import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
/**
* Helper class used to communicate with the demo server.
*/
public final class ServerUtilities {
private static final int MAX_ATTEMPTS = 5;
private static final int BACKOFF_MILLI_SECONDS = 2000;
private static final Random random = new Random();
/**
* Register this account/device pair within the server.
*
* @return whether the registration succeeded or not.
*/
static boolean register(final Context context, final String regId) {
Log.i(TAG, "registering device (regId = " + regId + ")");
String serverUrl = SERVER_URL;// + "/register";
Map params = new HashMap();
params.put("regId", regId);
params.put("type", "register");
long backoff = BACKOFF_MILLI_SECONDS + random.nextInt(1000);
// Once GCM returns a registration id, we need to register it in the
// demo server. As the server might be down, we will retry it a couple
// times.
for (int i = 1; i <= MAX_ATTEMPTS; i++) {
Log.d(TAG, "Attempt #" + i + " to register");
try {
displayMessage(context, context.getString(
R.string.server_registering, i, MAX_ATTEMPTS));
post(serverUrl, params);
GCMRegistrar.setRegisteredOnServer(context, true);
String message = context.getString(R.string.server_registered);
CommonUtilities.displayMessage(context, message);
return true;
} catch (IOException e) {
// Here we are simplifying and retrying on any error; in a real
// application, it should retry only on unrecoverable errors
// (like HTTP error code 503).
Log.e(TAG, "Failed to register on attempt " + i, e);
if (i == MAX_ATTEMPTS) {
break;
}
try {
Log.d(TAG, "Sleeping for " + backoff + " ms before retry");
Thread.sleep(backoff);
} catch (InterruptedException e1) {
// Activity finished before we complete - exit.
Log.d(TAG, "Thread interrupted: abort remaining retries!");
Thread.currentThread().interrupt();
return false;
}
// increase backoff exponentially
backoff *= 2;
}
}
String message = context.getString(R.string.server_register_error,
MAX_ATTEMPTS);
CommonUtilities.displayMessage(context, message);
return false;
}
/**
* Unregister this account/device pair within the server.
*/
static void unregister(final Context context, final String regId) {
Log.i(TAG, "unregistering device (regId = " + regId + ")");
String serverUrl = SERVER_URL;// + "/unregister";
Map params = new HashMap();
params.put("regId", regId);
params.put("type","unregister");
try {
post(serverUrl, params);
GCMRegistrar.setRegisteredOnServer(context, false);
String message = context.getString(R.string.server_unregistered);
CommonUtilities.displayMessage(context, message);
} catch (IOException e) {
// At this point the device is unregistered from GCM, but still
// registered in the server.
// We could try to unregister again, but it is not necessary:
// if the server tries to send a message to the device, it will get
// a "NotRegistered" error message and should unregister the device.
String message = context.getString(R.string.server_unregister_error,
e.getMessage());
CommonUtilities.displayMessage(context, message);
}
}
/**
* Issue a POST request to the server.
*
* @param endpoint POST address.
* @param params request parameters.
*
* @throws IOException propagated from POST.
*/
private static void post(String endpoint, Map params)
throws IOException {
URL url;
try {
url = new URL(endpoint);
} catch (MalformedURLException e) {
throw new IllegalArgumentException("invalid url: " + endpoint);
}
StringBuilder bodyBuilder = new StringBuilder();
Iterator> iterator = params.entrySet().iterator();
// constructs the POST body using the parameters
while (iterator.hasNext()) {
Entry param = iterator.next();
bodyBuilder.append(param.getKey()).append('=')
.append(param.getValue());
if (iterator.hasNext()) {
bodyBuilder.append('&');
}
}
String body = bodyBuilder.toString();
Log.v(TAG, "Posting '" + body + "' to " + url);
byte[] bytes = body.getBytes();
HttpURLConnection conn = null;
try {
conn = (HttpURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setUseCaches(false);
conn.setFixedLengthStreamingMode(bytes.length);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded;charset=UTF-8");
// post the request
OutputStream out = conn.getOutputStream();
out.write(bytes);
out.close();
// handle the response
int status = conn.getResponseCode();
if (status != 200) {
throw new IOException("Post failed with error code " + status);
}
} finally {
if (conn != null) {
conn.disconnect();
}
}
}
}
================================================
FILE: Android/ClientCode/src/com/google/android/gcm/demo/app/SimpleArrayAdapter.java
================================================
package com.google.android.gcm.demo.app;
import java.util.ArrayList;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
public class SimpleArrayAdapter extends ArrayAdapter {
private final Context context;
private final ArrayList values;
public SimpleArrayAdapter(Context context, ArrayList values) {
super(context, R.layout.rowlayout, values);
this.context = context;
this.values = values;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rowView = inflater.inflate(R.layout.rowlayout, parent, false);
Item i = values.get(position);
TextView nameTextView = (TextView) rowView.findViewById(R.id.inventoryName);
TextView countTextView = (TextView) rowView.findViewById(R.id.inventoryCount);
nameTextView.setText(i.name);
countTextView.setText(i.count);
return rowView;
}
}
================================================
FILE: Android/GCM_Registrar_Script/code.gs
================================================
function doGet() {
return HtmlService.createHtmlOutputFromFile('ui');
}
//import shared db library
//used the shared version intead of -
//ScriptDb.getMyDb()
var db = SharedDb.getDb();
function doPost(e) {
if(e.parameter.regId){
var reg = db.query({regId : e.parameter.regId}).next();
if(reg && e.parameter.type === 'unregister'){
db.remove(reg);
}
else if(!reg && e.parameter.type === 'register'){
db.save(e.parameter);
}
}
}
================================================
FILE: Android/GCM_Registrar_Script/scriptdb.gs
================================================
var db = SharedDb.getDb();
function saveObjToDb(obj){
try{
var savedObj = db.save(obj);
return "Saved ID - " + savedObj.getId();
}catch(e){
log(e);
throw(e);
}
}
function getCount(query){
try{
return "Current count for query is - " + db.count(query);
}catch(e){
log(e);
throw(e);
}
}
function loadIDsFromDb(idlist){
try{
return db.load(idlist);
}catch(e){
log(e);
throw(e);
}
}
function deleteByIds(idlist){
var obs_to_remove = loadIDsFromDb(idlist);
var results = db.removeBatch(obs_to_remove, false);
if (db.allOk(results)) {
return "Delete by IDs successfull!";
}
var failedObs = [];
for (var i = 0; i < results.length; i++) {
if (!results[i].successful()) {
failedObs.push(obs_to_remove[i]);
}
}
return "Failed to delete " + failedObs.length + " item(s) out of " + results.length;
}
function queryFromDb(query){
try{
var result = db.query(query);
var response = {};
while (result.hasNext()) {
var current = result.next();
response[current.getId()] = current;
}
return response;
}catch(e){
log(e);
throw(e);
}
}
function deleteAll(query) {
try{
while (true) {
var result = db.query(query);
if (result.getSize() == 0) {
break;
}
while (result.hasNext()) {
db.remove(result.next());
}
}
return "Delete for specified query successful!";
}catch(e){
log(e);
throw(e);
}
}
function log(msg){
//write to a logger here
//DocsList.getFileById('0B0JNj_IM2wiPMS1lZTFhZjVjNC0yZTBjLTRiOWItYWVhMy0yYTU1ZjdkMGVkMGE').append(msg+'\n');
}
================================================
FILE: Android/GCM_Registrar_Script/ui.html
================================================
Simple ScriptDb tester
Enter a JavaScript Object to store, a ScriptDb query to run or IDs to lookup and press the appropriate button below
By Arun Nagarajan
================================================
FILE: Android/InventoryContentService.gs
================================================
function doGet() {
var ss = SpreadsheetApp.openById('YOUR_SS_ID');
var sheet = ss.getSheets()[0];
// Get the range of cells that store employee data.
var employeeDataRange = ss.getRangeByName("inventoryData");
// For every row of employee data, generate an employee object.
var employeeObjects = getRowsData(sheet, employeeDataRange);
return ContentService.createTextOutput(JSON.stringify(employeeObjects)).setMimeType(ContentService.MimeType.JSON);
}
// getRowsData iterates row by row in the input range and returns an array of objects.
// Each object contains all the data for a given row, indexed by its normalized column name.
// Arguments:
// - sheet: the sheet object that contains the data to be processed
// - range: the exact range of cells where the data is stored
// - columnHeadersRowIndex: specifies the row number where the column names are stored.
// This argument is optional and it defaults to the row immediately above range;
// Returns an Array of objects.
function getRowsData(sheet, range, columnHeadersRowIndex) {
columnHeadersRowIndex = columnHeadersRowIndex || range.getRowIndex() - 1;
var numColumns = range.getLastColumn() - range.getColumn() + 1;
var headersRange = sheet.getRange(columnHeadersRowIndex, range.getColumn(), 1, numColumns);
var headers = headersRange.getValues()[0];
return getObjects(range.getValues(), normalizeHeaders(headers));
}
// For every row of data in data, generates an object that contains the data. Names of
// object fields are defined in keys.
// Arguments:
// - data: JavaScript 2d array
// - keys: Array of Strings that define the property names for the objects to create
function getObjects(data, keys) {
var objects = [];
for (var i = 0; i < data.length; ++i) {
var object = {};
var hasData = false;
for (var j = 0; j < data[i].length; ++j) {
var cellData = data[i][j];
if (isCellEmpty(cellData)) {
continue;
}
object[keys[j]] = cellData;
hasData = true;
}
if (hasData) {
objects.push(object);
}
}
return objects;
}
// Returns an Array of normalized Strings.
// Arguments:
// - headers: Array of Strings to normalize
function normalizeHeaders(headers) {
var keys = [];
for (var i = 0; i < headers.length; ++i) {
var key = normalizeHeader(headers[i]);
if (key.length > 0) {
keys.push(key);
}
}
return keys;
}
// Normalizes a string, by removing all alphanumeric characters and using mixed case
// to separate words. The output will always start with a lower case letter.
// This function is designed to produce JavaScript object property names.
// Arguments:
// - header: string to normalize
// Examples:
// "First Name" -> "firstName"
// "Market Cap (millions) -> "marketCapMillions
// "1 number at the beginning is ignored" -> "numberAtTheBeginningIsIgnored"
function normalizeHeader(header) {
var key = "";
var upperCase = false;
for (var i = 0; i < header.length; ++i) {
var letter = header[i];
if (letter == " " && key.length > 0) {
upperCase = true;
continue;
}
if (!isAlnum(letter)) {
continue;
}
if (key.length == 0 && isDigit(letter)) {
continue; // first character must be a letter
}
if (upperCase) {
upperCase = false;
key += letter.toUpperCase();
} else {
key += letter.toLowerCase();
}
}
return key;
}
// Returns true if the cell where cellData was read from is empty.
// Arguments:
// - cellData: string
function isCellEmpty(cellData) {
return typeof(cellData) == "string" && cellData == "";
}
// Returns true if the character char is alphabetical, false otherwise.
function isAlnum(char) {
return char >= 'A' && char <= 'Z' ||
char >= 'a' && char <= 'z' ||
isDigit(char);
}
// Returns true if the character char is a digit, false otherwise.
function isDigit(char) {
return char >= '0' && char <= '9';
}
// Given a JavaScript 2d Array, this function returns the transposed table.
// Arguments:
// - data: JavaScript 2d Array
// Returns a JavaScript 2d Array
// Example: arrayTranspose([[1,2,3],[4,5,6]]) returns [[1,4],[2,5],[3,6]].
function arrayTranspose(data) {
if (data.length == 0 || data[0].length == 0) {
return null;
}
var ret = [];
for (var i = 0; i < data[0].length; ++i) {
ret.push([]);
}
for (var i = 0; i < data.length; ++i) {
for (var j = 0; j < data[i].length; ++j) {
ret[j][i] = data[i][j];
}
}
return ret;
}
================================================
FILE: Android/SharedDb.gs
================================================
var db = ScriptDb.getMyDb();
function getDb(){
return db;
}
function sendGCM(msg){
msg = msg || 'hello world!'; //give default message for debugging
var regIds = [];
var result = db.query({});
while (result.hasNext()) {
var current = result.next();
regIds.push(current.regId);
}
var apiKey = 'YOUR_API_KEY';
var payload = {'registration_ids' : regIds,
'data' : {
'message' : msg
}};
var urlFetchOptions = {'contentType' : 'application/json',
'headers' : {'Authorization' : 'key=' + apiKey},
'method' : 'post',
'payload' : JSON.stringify(payload)};
var gcmUrl = 'https://android.googleapis.com/gcm/send';
var response = UrlFetchApp.fetch(gcmUrl,urlFetchOptions).getContentText()
Logger.log(response);//for testing purposes. improve error handling here
}
================================================
FILE: Android/readme.md
================================================
Click the image below for video with more details -
[](http://www.youtube.com/watch?v=RSgMEtRl0sw&list=PL68F511F6E3C122EB)
================================================
FILE: Android/track-and-notify.gs
================================================
//create an onEdit trigger for this! in a spreadsheet bound script
//import shared db library
function sendNotification(){
var msg = "Edited at: " + new Date().toTimeString();
SharedDb.sendGCM(msg);
}
================================================
FILE: ContentService/Addressbook-VCF/Code.gs
================================================
function doGet() {
var contact = ContactsApp.getContact('youremail@gmail.com');
var t = HtmlService.createTemplateFromFile('vcard_template');
t.contact = contact;
var output = t.evaluate().getContent();
return ContentService.createTextOutput(output).downloadAsFile('ContactCard.vcf');
}
================================================
FILE: ContentService/Addressbook-VCF/vcard_template.html
================================================
BEGIN:VCARD
FN:= contact.getFullName(); ?>
TITLE:General Manager
ORG:= contact.getFullName(); ?>;North American Division;Manufacturing
ADR;POSTAL;WORK:;;P.O. Box 10010;AnyCity;AnyState;00000;U.S.A.
LABEL;POSTAL;WORK;ENCODING=QUOTED-PRINTABLE:P.O. Box 10010=0D=0A=
Anywhere, TN 37849=0D=0A=U.S.A.
ADR;PARCEL;WORK:;133 Anywhere St.;Suite 360;AnyCity;AnyState;00000;U.S.A.
LABEL;POSTAL;WORK;ENCODING=QUOTED-PRINTABLE:133 Anywhere St.=0D=0A=
Anywhere, TN 37849=0D=0A=U.S.A.
TEL;Work;VOICE;MESG;PREF:+1-234-456-7891 x56473
TEL;Home:+1-234-456-7891
TEL;Pager:+1-234-456-7891
TEL;Cell:+1-234-456-7891
TEL;Modem;FAX:+1-234-456-7891,,*3
EMAIL;Internet:anywhere@anywhere.com
URL:http://www.anywhere.com/mrh.vcf
UID:http://www.anywhere.com/mrh.vcf
TZ:-0500
BDAY:1997-11-29
REV:20090401T065518
VERSION:2.1
END:VCARD
================================================
FILE: ContentService/Gmail-RSS/code.gs
================================================
function doGet() {
var content = HtmlService.createTemplateFromFile('rss').evaluate().getContent();
return ContentService.createTextOutput(content).setMimeType(ContentService.MimeType.RSS);
}
================================================
FILE: ContentService/Gmail-RSS/rss.html
================================================
Gmail Feed
var threads = GmailApp.getInboxThreads();
var messages = GmailApp.getMessagesForThreads(threads);
for (var t in threads){ ?>
= threads[t].getFirstMessageSubject() ?>= messages[t][0].getBody() ?>= threads[t].getId() ?>= threads[t].getLastMessageDate().toUTCString() ?>
} ?>
================================================
FILE: ContentService/Inventory-JSON-API-iOS/Inventory-iOS/Inventory/AppDelegate.h
================================================
#import
@interface AppDelegate : UIResponder
@property (strong, nonatomic) UIWindow *window;
@end
================================================
FILE: ContentService/Inventory-JSON-API-iOS/Inventory-iOS/Inventory/AppDelegate.m
================================================
#import "AppDelegate.h"
@implementation AppDelegate
@synthesize window = _window;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application
{
/*
Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
*/
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
/*
Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
*/
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
/*
Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
*/
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
/*
Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
*/
}
- (void)applicationWillTerminate:(UIApplication *)application
{
/*
Called when the application is about to terminate.
Save data if appropriate.
See also applicationDidEnterBackground:.
*/
}
@end
================================================
FILE: ContentService/Inventory-JSON-API-iOS/Inventory-iOS/Inventory/Inventory-Info.plist
================================================
CFBundleDevelopmentRegionenCFBundleDisplayName${PRODUCT_NAME}CFBundleExecutable${EXECUTABLE_NAME}CFBundleIconFilesCFBundleIdentifierentaq.${PRODUCT_NAME:rfc1034identifier}CFBundleInfoDictionaryVersion6.0CFBundleName${PRODUCT_NAME}CFBundlePackageTypeAPPLCFBundleShortVersionString1.0CFBundleSignature????CFBundleVersion1.0LSRequiresIPhoneOSUIMainStoryboardFileMainStoryboardUIRequiredDeviceCapabilitiesarmv7UISupportedInterfaceOrientationsUIInterfaceOrientationPortraitUIInterfaceOrientationLandscapeLeftUIInterfaceOrientationLandscapeRight
================================================
FILE: ContentService/Inventory-JSON-API-iOS/Inventory-iOS/Inventory/Inventory-Prefix.pch
================================================
#import
#ifndef __IPHONE_5_0
#warning "This project uses features only available in iOS SDK 5.0 and later."
#endif
#ifdef __OBJC__
#import
#import
#endif
================================================
FILE: ContentService/Inventory-JSON-API-iOS/Inventory-iOS/Inventory/MasterViewController.h
================================================
#import
@interface MasterViewController : UITableViewController {
NSArray *Items;
}
- (void)fetchItems;
- (IBAction)refreshItems:(id)sender;
@end
================================================
FILE: ContentService/Inventory-JSON-API-iOS/Inventory-iOS/Inventory/MasterViewController.m
================================================
#import "MasterViewController.h"
@implementation MasterViewController
- (void)awakeFromNib
{
[super awakeFromNib];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
[self fetchItems];
}
- (void)fetchItems
{
NSString *ServiceURL = @"https://script.google.com/macros/s/AKfycbyonCfUtv-tbIAUO4okOYI8sUCQR-hnJGcOAq8QCQ_86XYc8qg/exec";
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData* data = [NSData dataWithContentsOfURL:
[NSURL URLWithString: ServiceURL]];
NSError* error;
Items = [NSJSONSerialization JSONObjectWithData:data
options:kNilOptions
error:&error];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
});
}
- (IBAction)refreshItems:(id)sender {
[self fetchItems];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return Items.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"ItemCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSDictionary *Item = [Items objectAtIndex:indexPath.row];
NSString *text = [Item objectForKey:@"item"];
NSString *name = [Item objectForKey:@"quantity"];
cell.textLabel.text = text;
cell.detailTextLabel.text = [NSString stringWithFormat:@"Current quantity %@", name];
return cell;
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
@end
================================================
FILE: ContentService/Inventory-JSON-API-iOS/Inventory-iOS/Inventory/en.lproj/InfoPlist.strings
================================================
/* Localized versions of Info.plist keys */
================================================
FILE: ContentService/Inventory-JSON-API-iOS/Inventory-iOS/Inventory/en.lproj/MainStoryboard.storyboard
================================================
================================================
FILE: ContentService/Inventory-JSON-API-iOS/Inventory-iOS/Inventory/main.m
================================================
#import
#import "AppDelegate.h"
int main(int argc, char *argv[])
{
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
================================================
FILE: ContentService/Inventory-JSON-API-iOS/Inventory-iOS/Inventory.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
69BB74F716B2D0C500EC6AF4 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 69BB74F616B2D0C500EC6AF4 /* Default-568h@2x.png */; };
6F4DFB9E14BA2B2200B110D2 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F4DFB9D14BA2B2200B110D2 /* UIKit.framework */; };
6F4DFBA014BA2B2200B110D2 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F4DFB9F14BA2B2200B110D2 /* Foundation.framework */; };
6F4DFBA214BA2B2200B110D2 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F4DFBA114BA2B2200B110D2 /* CoreGraphics.framework */; };
6F4DFBA814BA2B2200B110D2 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6F4DFBA614BA2B2200B110D2 /* InfoPlist.strings */; };
6F4DFBAA14BA2B2200B110D2 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F4DFBA914BA2B2200B110D2 /* main.m */; };
6F4DFBAE14BA2B2200B110D2 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F4DFBAD14BA2B2200B110D2 /* AppDelegate.m */; };
6F4DFBB114BA2B2200B110D2 /* MainStoryboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6F4DFBAF14BA2B2200B110D2 /* MainStoryboard.storyboard */; };
6F4DFBB414BA2B2200B110D2 /* MasterViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F4DFBB314BA2B2200B110D2 /* MasterViewController.m */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
69BB74F616B2D0C500EC6AF4 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; };
6F4DFB9914BA2B2200B110D2 /* Inventory.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Inventory.app; sourceTree = BUILT_PRODUCTS_DIR; };
6F4DFB9D14BA2B2200B110D2 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
6F4DFB9F14BA2B2200B110D2 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
6F4DFBA114BA2B2200B110D2 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
6F4DFBA514BA2B2200B110D2 /* Inventory-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Inventory-Info.plist"; sourceTree = ""; };
6F4DFBA714BA2B2200B110D2 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; };
6F4DFBA914BA2B2200B110D2 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
6F4DFBAB14BA2B2200B110D2 /* Inventory-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Inventory-Prefix.pch"; sourceTree = ""; };
6F4DFBAC14BA2B2200B110D2 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
6F4DFBAD14BA2B2200B110D2 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
6F4DFBB014BA2B2200B110D2 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = en; path = en.lproj/MainStoryboard.storyboard; sourceTree = ""; };
6F4DFBB214BA2B2200B110D2 /* MasterViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MasterViewController.h; sourceTree = ""; };
6F4DFBB314BA2B2200B110D2 /* MasterViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MasterViewController.m; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
6F4DFB9614BA2B2200B110D2 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
6F4DFB9E14BA2B2200B110D2 /* UIKit.framework in Frameworks */,
6F4DFBA014BA2B2200B110D2 /* Foundation.framework in Frameworks */,
6F4DFBA214BA2B2200B110D2 /* CoreGraphics.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
6F4DFB8E14BA2B2200B110D2 = {
isa = PBXGroup;
children = (
69BB74F616B2D0C500EC6AF4 /* Default-568h@2x.png */,
6F4DFBA314BA2B2200B110D2 /* InventorySample */,
6F4DFB9C14BA2B2200B110D2 /* Frameworks */,
6F4DFB9A14BA2B2200B110D2 /* Products */,
);
sourceTree = "";
};
6F4DFB9A14BA2B2200B110D2 /* Products */ = {
isa = PBXGroup;
children = (
6F4DFB9914BA2B2200B110D2 /* Inventory.app */,
);
name = Products;
sourceTree = "";
};
6F4DFB9C14BA2B2200B110D2 /* Frameworks */ = {
isa = PBXGroup;
children = (
6F4DFB9D14BA2B2200B110D2 /* UIKit.framework */,
6F4DFB9F14BA2B2200B110D2 /* Foundation.framework */,
6F4DFBA114BA2B2200B110D2 /* CoreGraphics.framework */,
);
name = Frameworks;
sourceTree = "";
};
6F4DFBA314BA2B2200B110D2 /* InventorySample */ = {
isa = PBXGroup;
children = (
6F4DFBAC14BA2B2200B110D2 /* AppDelegate.h */,
6F4DFBAD14BA2B2200B110D2 /* AppDelegate.m */,
6F4DFBAF14BA2B2200B110D2 /* MainStoryboard.storyboard */,
6F4DFBB214BA2B2200B110D2 /* MasterViewController.h */,
6F4DFBB314BA2B2200B110D2 /* MasterViewController.m */,
6F4DFBA414BA2B2200B110D2 /* Supporting Files */,
);
name = InventorySample;
path = Inventory;
sourceTree = "";
};
6F4DFBA414BA2B2200B110D2 /* Supporting Files */ = {
isa = PBXGroup;
children = (
6F4DFBA514BA2B2200B110D2 /* Inventory-Info.plist */,
6F4DFBA614BA2B2200B110D2 /* InfoPlist.strings */,
6F4DFBA914BA2B2200B110D2 /* main.m */,
6F4DFBAB14BA2B2200B110D2 /* Inventory-Prefix.pch */,
);
name = "Supporting Files";
sourceTree = "";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
6F4DFB9814BA2B2200B110D2 /* Inventory */ = {
isa = PBXNativeTarget;
buildConfigurationList = 6F4DFBBA14BA2B2200B110D2 /* Build configuration list for PBXNativeTarget "Inventory" */;
buildPhases = (
6F4DFB9514BA2B2200B110D2 /* Sources */,
6F4DFB9614BA2B2200B110D2 /* Frameworks */,
6F4DFB9714BA2B2200B110D2 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = Inventory;
productName = "Twitter Test";
productReference = 6F4DFB9914BA2B2200B110D2 /* Inventory.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
6F4DFB9014BA2B2200B110D2 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0420;
};
buildConfigurationList = 6F4DFB9314BA2B2200B110D2 /* Build configuration list for PBXProject "Inventory" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = 6F4DFB8E14BA2B2200B110D2;
productRefGroup = 6F4DFB9A14BA2B2200B110D2 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
6F4DFB9814BA2B2200B110D2 /* Inventory */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
6F4DFB9714BA2B2200B110D2 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
6F4DFBA814BA2B2200B110D2 /* InfoPlist.strings in Resources */,
6F4DFBB114BA2B2200B110D2 /* MainStoryboard.storyboard in Resources */,
69BB74F716B2D0C500EC6AF4 /* Default-568h@2x.png in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
6F4DFB9514BA2B2200B110D2 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
6F4DFBAA14BA2B2200B110D2 /* main.m in Sources */,
6F4DFBAE14BA2B2200B110D2 /* AppDelegate.m in Sources */,
6F4DFBB414BA2B2200B110D2 /* MasterViewController.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
6F4DFBA614BA2B2200B110D2 /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
6F4DFBA714BA2B2200B110D2 /* en */,
);
name = InfoPlist.strings;
sourceTree = "";
};
6F4DFBAF14BA2B2200B110D2 /* MainStoryboard.storyboard */ = {
isa = PBXVariantGroup;
children = (
6F4DFBB014BA2B2200B110D2 /* en */,
);
name = MainStoryboard.storyboard;
sourceTree = "";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
6F4DFBB814BA2B2200B110D2 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
CLANG_ENABLE_OBJC_ARC = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 5.0;
SDKROOT = iphoneos;
};
name = Debug;
};
6F4DFBB914BA2B2200B110D2 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
CLANG_ENABLE_OBJC_ARC = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 5.0;
OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1";
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
6F4DFBBB14BA2B2200B110D2 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "Inventory/Inventory-Prefix.pch";
INFOPLIST_FILE = "$(SRCROOT)/Inventory/Inventory-Info.plist";
PRODUCT_NAME = Inventory;
WRAPPER_EXTENSION = app;
};
name = Debug;
};
6F4DFBBC14BA2B2200B110D2 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "Inventory/Inventory-Prefix.pch";
INFOPLIST_FILE = "$(SRCROOT)/Inventory/Inventory-Info.plist";
PRODUCT_NAME = Inventory;
WRAPPER_EXTENSION = app;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
6F4DFB9314BA2B2200B110D2 /* Build configuration list for PBXProject "Inventory" */ = {
isa = XCConfigurationList;
buildConfigurations = (
6F4DFBB814BA2B2200B110D2 /* Debug */,
6F4DFBB914BA2B2200B110D2 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
6F4DFBBA14BA2B2200B110D2 /* Build configuration list for PBXNativeTarget "Inventory" */ = {
isa = XCConfigurationList;
buildConfigurations = (
6F4DFBBB14BA2B2200B110D2 /* Debug */,
6F4DFBBC14BA2B2200B110D2 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 6F4DFB9014BA2B2200B110D2 /* Project object */;
}
================================================
FILE: ContentService/Inventory-JSON-API-iOS/Inventory-iOS/Inventory.xcodeproj/project.xcworkspace/contents.xcworkspacedata
================================================
================================================
FILE: ContentService/Inventory-JSON-API-iOS/Inventory-iOS/Inventory.xcodeproj/project.xcworkspace/xcuserdata/anagarajan.xcuserdatad/WorkspaceSettings.xcsettings
================================================
HasAskedToTakeAutomaticSnapshotBeforeSignificantChangesSnapshotAutomaticallyBeforeSignificantChanges
================================================
FILE: ContentService/Inventory-JSON-API-iOS/Inventory-iOS/Inventory.xcodeproj/xcuserdata/anagarajan.xcuserdatad/xcdebugger/Breakpoints.xcbkptlist
================================================
================================================
FILE: ContentService/Inventory-JSON-API-iOS/Inventory-iOS/Inventory.xcodeproj/xcuserdata/anagarajan.xcuserdatad/xcschemes/InventorySample.xcscheme
================================================
================================================
FILE: ContentService/Inventory-JSON-API-iOS/Inventory-iOS/Inventory.xcodeproj/xcuserdata/anagarajan.xcuserdatad/xcschemes/xcschememanagement.plist
================================================
SchemeUserStateInventorySample.xcschemeorderHint0SuppressBuildableAutocreation6F4DFB9814BA2B2200B110D2primary
================================================
FILE: ContentService/Inventory-JSON-API-iOS/code.gs
================================================
function doGet() {
var ss = SpreadsheetApp.openById('0AiFUpEyZ-UKzdElxM3NpbFI4QUJwdmxqSXVlSjJmNmc');
var sheet = ss.getSheets()[0];
// Get the range of cells that store employee data.
var employeeDataRange = ss.getRangeByName("inventoryData");
// For every row of employee data, generate an employee object.
var employeeObjects = getRowsData(sheet, employeeDataRange);
return ContentService.createTextOutput(JSON.stringify(employeeObjects)).setMimeType(ContentService.MimeType.JSON);
}
// getRowsData iterates row by row in the input range and returns an array of objects.
// Each object contains all the data for a given row, indexed by its normalized column name.
// Arguments:
// - sheet: the sheet object that contains the data to be processed
// - range: the exact range of cells where the data is stored
// - columnHeadersRowIndex: specifies the row number where the column names are stored.
// This argument is optional and it defaults to the row immediately above range;
// Returns an Array of objects.
function getRowsData(sheet, range, columnHeadersRowIndex) {
columnHeadersRowIndex = columnHeadersRowIndex || range.getRowIndex() - 1;
var numColumns = range.getLastColumn() - range.getColumn() + 1;
var headersRange = sheet.getRange(columnHeadersRowIndex, range.getColumn(), 1, numColumns);
var headers = headersRange.getValues()[0];
return getObjects(range.getValues(), normalizeHeaders(headers));
}
// For every row of data in data, generates an object that contains the data. Names of
// object fields are defined in keys.
// Arguments:
// - data: JavaScript 2d array
// - keys: Array of Strings that define the property names for the objects to create
function getObjects(data, keys) {
var objects = [];
for (var i = 0; i < data.length; ++i) {
var object = {};
var hasData = false;
for (var j = 0; j < data[i].length; ++j) {
var cellData = data[i][j];
if (isCellEmpty(cellData)) {
continue;
}
object[keys[j]] = cellData;
hasData = true;
}
if (hasData) {
objects.push(object);
}
}
return objects;
}
// Returns an Array of normalized Strings.
// Arguments:
// - headers: Array of Strings to normalize
function normalizeHeaders(headers) {
var keys = [];
for (var i = 0; i < headers.length; ++i) {
var key = normalizeHeader(headers[i]);
if (key.length > 0) {
keys.push(key);
}
}
return keys;
}
// Normalizes a string, by removing all alphanumeric characters and using mixed case
// to separate words. The output will always start with a lower case letter.
// This function is designed to produce JavaScript object property names.
// Arguments:
// - header: string to normalize
// Examples:
// "First Name" -> "firstName"
// "Market Cap (millions) -> "marketCapMillions
// "1 number at the beginning is ignored" -> "numberAtTheBeginningIsIgnored"
function normalizeHeader(header) {
var key = "";
var upperCase = false;
for (var i = 0; i < header.length; ++i) {
var letter = header[i];
if (letter == " " && key.length > 0) {
upperCase = true;
continue;
}
if (!isAlnum(letter)) {
continue;
}
if (key.length == 0 && isDigit(letter)) {
continue; // first character must be a letter
}
if (upperCase) {
upperCase = false;
key += letter.toUpperCase();
} else {
key += letter.toLowerCase();
}
}
return key;
}
// Returns true if the cell where cellData was read from is empty.
// Arguments:
// - cellData: string
function isCellEmpty(cellData) {
return typeof(cellData) == "string" && cellData == "";
}
// Returns true if the character char is alphabetical, false otherwise.
function isAlnum(char) {
return char >= 'A' && char <= 'Z' ||
char >= 'a' && char <= 'z' ||
isDigit(char);
}
// Returns true if the character char is a digit, false otherwise.
function isDigit(char) {
return char >= '0' && char <= '9';
}
// Given a JavaScript 2d Array, this function returns the transposed table.
// Arguments:
// - data: JavaScript 2d Array
// Returns a JavaScript 2d Array
// Example: arrayTranspose([[1,2,3],[4,5,6]]) returns [[1,4],[2,5],[3,6]].
function arrayTranspose(data) {
if (data.length == 0 || data[0].length == 0) {
return null;
}
var ret = [];
for (var i = 0; i < data[0].length; ++i) {
ret.push([]);
}
for (var i = 0; i < data.length; ++i) {
for (var j = 0; j < data[i].length; ++j) {
ret[j][i] = data[i][j];
}
}
return ret;
}
// Exercise:
function runExercise() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[1];
// Get the range of cells that store employee data.
var employeeDataRange = sheet.getRange("B1:F5");
// For every row of employee data, generate an employee object.
var employeeObjects = getColumnsData(sheet, employeeDataRange);
var thirdEmployee = employeeObjects[2];
var stringToDisplay = "The third column is: " + thirdEmployee.firstName + " " + thirdEmployee.lastName;
stringToDisplay += " (id #" + thirdEmployee.employeeId + ") working in the ";
stringToDisplay += thirdEmployee.department + " department and with phone number ";
stringToDisplay += thirdEmployee.phoneNumber;
ss.msgBox(stringToDisplay);
}
// Given a JavaScript 2d Array, this function returns the transposed table.
// Arguments:
// - data: JavaScript 2d Array
// Returns a JavaScript 2d Array
// Example: arrayTranspose([[1,2,3],[4,5,6]]) returns [[1,4],[2,5],[3,6]].
function arrayTranspose(data) {
if (data.length == 0 || data[0].length == 0) {
return null;
}
var ret = [];
for (var i = 0; i < data[0].length; ++i) {
ret.push([]);
}
for (var i = 0; i < data.length; ++i) {
for (var j = 0; j < data[i].length; ++j) {
ret[j][i] = data[i][j];
}
}
return ret;
}
// getColumnsData iterates column by column in the input range and returns an array of objects.
// Each object contains all the data for a given column, indexed by its normalized row name.
// Arguments:
// - sheet: the sheet object that contains the data to be processed
// - range: the exact range of cells where the data is stored
// - rowHeadersColumnIndex: specifies the column number where the row names are stored.
// This argument is optional and it defaults to the column immediately left of the range;
// Returns an Array of objects.
function getColumnsData(sheet, range, rowHeadersColumnIndex) {
rowHeadersColumnIndex = rowHeadersColumnIndex || range.getColumnIndex() - 1;
var headersTmp = sheet.getRange(range.getRow(), rowHeadersColumnIndex, range.getNumRows(), 1).getValues();
var headers = normalizeHeaders(arrayTranspose(headersTmp)[0]);
return getObjects(arrayTranspose(range.getValues()), headers);
}
================================================
FILE: ContentService/Simple-JSONP/SimpleHtml-JSFiddle.html
================================================
welcome
================================================
FILE: ContentService/Simple-JSONP/code.gs
================================================
function doGet(request) {
var result = 'hello world';
var content = request.parameters.prefix + '(' +JSON.stringify(result) + ')';
return ContentService.createTextOutput(content)
.setMimeType(ContentService.MimeType.JSON);
}
================================================
FILE: ContentService/readme.md
================================================
Click the image below for video with more details -
[](http://www.youtube.com/watch?v=JRGzVdliQOQ&list=PL68F511F6E3C122EB)
================================================
FILE: DriveSDK/AppsScript.gs
================================================
//look for the first mtg right now in the user's calendar from now till next 2 hours
function createMeetingNotes() {
var lowerBound = new Date();
var upperBound = new Date();
upperBound.setHours(lowerBound.getHours()+2);
var appts = CalendarApp.getEvents(lowerBound, upperBound);
if(!appts || appts.length ==0){
Logger.log('No current appointments!');
return '';
}
//naive implementation for demo - ideally we'll provide a picker if double booked
var appt = appts[0];
var doc = DocumentApp.create('Meeting notes for ' + appt.getTitle());
doc.appendParagraph('Time: ' + appt.getStartTime());
doc.appendParagraph('Location: ' + appt.getLocation());
doc.appendParagraph('Attendees: ' + getEmails(appt));
doc.appendHorizontalRule();
doc.appendParagraph(appt.getDescription());
return doc.getUrl();
}
function getEmails(appt){
var guests = appt.getGuestList(true);
var emails = '';
for(var i=0;i