glass 0.0.1.1 → 0.0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +0 -0
- data/Gemfile +0 -0
- data/LICENSE.txt +0 -0
- data/README.md +105 -1
- data/Rakefile +0 -0
- data/glass.gemspec +3 -0
- data/lib/glass.rb +287 -9
- data/lib/glass/config.rb +65 -0
- data/lib/glass/contacts/contact.rb +66 -0
- data/lib/glass/locations/location.rb +64 -0
- data/lib/glass/subscriptions/subscription.rb +222 -0
- data/lib/glass/timeline/timeline_item.rb +400 -0
- data/lib/glass/version.rb +1 -1
- data/rebuild.sh +4 -0
- metadata +57 -3
data/.gitignore
CHANGED
File without changes
|
data/Gemfile
CHANGED
File without changes
|
data/LICENSE.txt
CHANGED
File without changes
|
data/README.md
CHANGED
@@ -4,6 +4,12 @@ This Gem is meant to help you quickly building application for Google Glass than
|
|
4
4
|
|
5
5
|
This is totally a beginning. Nothing is done.
|
6
6
|
|
7
|
+
This gem is using google/api-client.
|
8
|
+
|
9
|
+
If you think google/api-client is b***s*** this gem is what you need.
|
10
|
+
|
11
|
+
This gem is using Redis to store clients credentials.
|
12
|
+
|
7
13
|
## Installation
|
8
14
|
|
9
15
|
Add this line to your application's Gemfile:
|
@@ -20,7 +26,105 @@ Or install it yourself as:
|
|
20
26
|
|
21
27
|
## Usage
|
22
28
|
|
23
|
-
|
29
|
+
Setup Credential
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
require 'glass'
|
33
|
+
Glass::Mirror.client_id = ENV['GLASS_CLIENT_ID']
|
34
|
+
Glass::Mirror.client_secret = ENV['GLASS_CLIENT_SECRET']
|
35
|
+
Glass::Mirror.redirect_uri = ENV['GLASS_REDIRECT_URI']
|
36
|
+
Glass::Mirror.scopes = [ Add requested scopes]
|
37
|
+
# Default is 'https://www.googleapis.com/auth/drive.file',
|
38
|
+
# 'https://www.googleapis.com/auth/userinfo.profile',
|
39
|
+
```
|
40
|
+
|
41
|
+
You are a Glassware Explorer Now !
|
42
|
+
Get a way to get the authorization code from user app install. I don't know how for now. If you find let me know !
|
43
|
+
And do the OAuth trick
|
44
|
+
```ruby
|
45
|
+
ok_glass = Glass::Mirror.build_with_code(authorization_code)
|
46
|
+
```
|
47
|
+
### Token Persistence
|
48
|
+
|
49
|
+
As in OAuth2.0, Google API issue a Refresh_Token the first time you Authorize the client, so you can get new token later.
|
50
|
+
You need to store this token. Out of the box Glass's Gem use Redis to do it.
|
51
|
+
|
52
|
+
Glass use a namespace on your redis. So don't bother about this (even if you can specify one too).
|
53
|
+
|
54
|
+
By default, Glass use local redis store (localhost:6379).
|
55
|
+
|
56
|
+
You can specify the Redis store this way :
|
57
|
+
|
58
|
+
For Heroku/RedisToGo users
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
Glass::Mirror.redis = ENV["REDISTOGO_URL"]
|
62
|
+
```
|
63
|
+
|
64
|
+
|
65
|
+
## Overview
|
66
|
+
|
67
|
+
### Timeline
|
68
|
+
|
69
|
+
Let's say you want to tell "Hi" to the user.
|
70
|
+
Build it and send it.
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
item = Glass::TimelineItem.new()
|
74
|
+
item.text="Hi"
|
75
|
+
ok_glass.insert(item)
|
76
|
+
```
|
77
|
+
|
78
|
+
### Subscriptions
|
79
|
+
|
80
|
+
Let's say you want to subscribe to location update.
|
81
|
+
You need to provide a call_back_url, on wich Google will post the datas.
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
subscription = Glass::Subscription.new()
|
85
|
+
subscription.collection="location"
|
86
|
+
subscription.call_back_url="https://glassware.herokuapp.com/location_update"
|
87
|
+
ok_glass.insert(subscription)
|
88
|
+
```
|
89
|
+
|
90
|
+
### Location
|
91
|
+
|
92
|
+
There is a quicker way to get Location update
|
93
|
+
```ruby
|
94
|
+
Glass::Location.subscribe(ok_glass,call_back_url)
|
95
|
+
```
|
96
|
+
|
97
|
+
|
98
|
+
|
99
|
+
## Going Further
|
100
|
+
|
101
|
+
As you are a Glassware Explorer you can explore yourself. Access the google/api-client Client this way :
|
102
|
+
```ruby
|
103
|
+
ok_glass.client # Get the discovered Google::API on wich you can make request
|
104
|
+
```
|
105
|
+
|
106
|
+
Have fun ! And please, show me any use. Just send me a tweet : @TheDamFr
|
107
|
+
|
108
|
+
|
109
|
+
### Bypassing Redis
|
110
|
+
|
111
|
+
You can bypass Redis. I don't want to know why you would, but you can do it easily.
|
112
|
+
|
113
|
+
Just do :
|
114
|
+
```ruby
|
115
|
+
Glass::Mirror.no_redis = true # Glass are so sad !
|
116
|
+
```
|
117
|
+
|
118
|
+
Now you need to handle the storage yourself !
|
119
|
+
|
120
|
+
```ruby
|
121
|
+
credentials = Glass::Mirror.get_credentials(authorization_code)
|
122
|
+
|
123
|
+
# Store Credentials !
|
124
|
+
|
125
|
+
ok_glass = Glass::Mirror.build_client(credentials)
|
126
|
+
```
|
127
|
+
That's okay ! (for now)
|
24
128
|
|
25
129
|
## Contributing
|
26
130
|
|
data/Rakefile
CHANGED
File without changes
|
data/glass.gemspec
CHANGED
@@ -21,4 +21,7 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.add_development_dependency "bundler", "~> 1.3"
|
22
22
|
spec.add_development_dependency "rake"
|
23
23
|
spec.add_runtime_dependency "google-api-client"
|
24
|
+
spec.add_runtime_dependency "redis"
|
25
|
+
spec.add_runtime_dependency "redis-namespace"
|
26
|
+
spec.add_runtime_dependency "httparty"
|
24
27
|
end
|
data/lib/glass.rb
CHANGED
@@ -1,18 +1,296 @@
|
|
1
1
|
require "glass/version"
|
2
|
+
require 'redis'
|
3
|
+
require 'redis-namespace'
|
4
|
+
require 'glass/config'
|
5
|
+
require 'google/api_client'
|
6
|
+
require 'json'
|
2
7
|
|
3
8
|
module Glass
|
4
9
|
|
10
|
+
MIRROR= "mirror"
|
11
|
+
|
12
|
+
INSERT= "INSERT"
|
13
|
+
UPDATE= "UPDATE"
|
14
|
+
DELETE= "DELETE"
|
15
|
+
|
16
|
+
|
5
17
|
class Mirror
|
6
|
-
attr_accessor :scopes, :client_id, :redirect_uri, :cient_secret
|
7
|
-
@@client_id
|
8
|
-
@@cient_secret
|
9
|
-
@@redirect_uri
|
10
|
-
@@scopes = [
|
11
|
-
'https://www.googleapis.com/auth/drive.file',
|
12
|
-
'https://www.googleapis.com/auth/userinfo.profile',
|
13
|
-
# Add other requested scopes.
|
14
|
-
]
|
15
18
|
|
19
|
+
|
20
|
+
class << self
|
21
|
+
attr_accessor :scopes, :client_id, :redirect_uri, :client_secret
|
22
|
+
@client_id
|
23
|
+
@client_secret
|
24
|
+
@redirect_uri
|
25
|
+
@scopes = [
|
26
|
+
'https://www.googleapis.com/auth/drive.file',
|
27
|
+
'https://www.googleapis.com/auth/userinfo.profile',
|
28
|
+
# Add other requested scopes.
|
29
|
+
]
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
@@config = Config.new()
|
34
|
+
|
35
|
+
attr_accessor :client
|
36
|
+
@client
|
37
|
+
|
38
|
+
def self.hello_world
|
39
|
+
"Hello World!"
|
40
|
+
end
|
41
|
+
|
42
|
+
# Returns the current Redis connection. If none has been created, will
|
43
|
+
# create a new one.
|
44
|
+
def self.redis
|
45
|
+
@@config.redis
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.redis= val
|
49
|
+
@@config.redis = val
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.redis_id
|
53
|
+
@@config.redis_id
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.no_redis= val
|
57
|
+
@@config.no_redis=val
|
58
|
+
end
|
59
|
+
|
60
|
+
##
|
61
|
+
# Retrieved stored credentials for the provided user ID.
|
62
|
+
#
|
63
|
+
# @param [String] user_id
|
64
|
+
# User's ID.
|
65
|
+
# @return [Signet::OAuth2::Client]
|
66
|
+
# Stored OAuth 2.0 credentials if found, nil otherwise.
|
67
|
+
#
|
68
|
+
def self.get_stored_credentials(user_id)
|
69
|
+
unless @@config.no_redis
|
70
|
+
hash = Redis.get(user_id)
|
71
|
+
client = Google::APIClient.new
|
72
|
+
client.authorization.dup
|
73
|
+
client.update_token!(hash)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
# Store OAuth 2.0 credentials in the application's database.
|
79
|
+
#
|
80
|
+
# @param [String] user_id
|
81
|
+
# User's ID.
|
82
|
+
# @param [Signet::OAuth2::Client] credentials
|
83
|
+
# OAuth 2.0 credentials to store.
|
84
|
+
#
|
85
|
+
def self.store_credentials(user_id, credentials)
|
86
|
+
unless @@config.no_redis
|
87
|
+
hash = Hash.new()
|
88
|
+
hash[:access_token] = credentials.access_token
|
89
|
+
hash[:refresh_token] = credentials.refresh_token
|
90
|
+
hash[:expires_in] = credentials.expires_in
|
91
|
+
hash[:issued_at] = credentials.issued_at
|
92
|
+
|
93
|
+
Redis.set(user_id, hash)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
##
|
98
|
+
# Exchange an authorization code for OAuth 2.0 credentials.
|
99
|
+
#
|
100
|
+
# @param [String] auth_code
|
101
|
+
# Authorization code to exchange for OAuth 2.0 credentials.
|
102
|
+
# @return [Signet::OAuth2::Client]
|
103
|
+
# OAuth 2.0 credentials.
|
104
|
+
#
|
105
|
+
def self.exchange_code(authorization_code)
|
106
|
+
client = Google::APIClient.new
|
107
|
+
client.authorization.client_id = client_id
|
108
|
+
client.authorization.client_secret = client_secret
|
109
|
+
client.authorization.code = authorization_code
|
110
|
+
client.authorization.redirect_uri = redirect_uri
|
111
|
+
|
112
|
+
begin
|
113
|
+
client.authorization.fetch_access_token!
|
114
|
+
return client.authorization
|
115
|
+
rescue Signet::AuthorizationError
|
116
|
+
raise CodeExchangeError.new(nil)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
##
|
121
|
+
# Send a request to the UserInfo API to retrieve the user's information.
|
122
|
+
#
|
123
|
+
# @param [Signet::OAuth2::Client] credentials
|
124
|
+
# OAuth 2.0 credentials to authorize the request.
|
125
|
+
# @return [Google::APIClient::Schema::Oauth2::V2::Userinfo]
|
126
|
+
# User's information.
|
127
|
+
#
|
128
|
+
def self.get_user_info(credentials)
|
129
|
+
client = Google::APIClient.new
|
130
|
+
client.authorization = credentials
|
131
|
+
oauth2 = client.discovered_api('oauth2', 'v2')
|
132
|
+
result = client.execute!(:api_method => oauth2.userinfo.get)
|
133
|
+
user_info = nil
|
134
|
+
if result.status == 200
|
135
|
+
user_info = result.data
|
136
|
+
else
|
137
|
+
puts "An error occurred: #{result.data['error']['message']}"
|
138
|
+
end
|
139
|
+
if user_info != nil && user_info.id != nil
|
140
|
+
return user_info
|
141
|
+
end
|
142
|
+
raise NoUserIdError, "Unable to retrieve the user's Google ID."
|
143
|
+
end
|
144
|
+
|
145
|
+
##
|
146
|
+
# Retrieve authorization URL.
|
147
|
+
#
|
148
|
+
# @param [String] user_id
|
149
|
+
# User's Google ID.
|
150
|
+
# @param [String] state
|
151
|
+
# State for the authorization URL.
|
152
|
+
# @return [String]
|
153
|
+
# Authorization URL to redirect the user to.
|
154
|
+
#
|
155
|
+
def self.get_authorization_url(user_id, state)
|
156
|
+
client = Google::APIClient.new
|
157
|
+
client.authorization.client_id = client_id
|
158
|
+
client.authorization.redirect_uri = redirect_uri
|
159
|
+
client.authorization.scope = scopes
|
160
|
+
|
161
|
+
return client.authorization.authorization_uri(
|
162
|
+
:options => {
|
163
|
+
:approval_prompt => :force,
|
164
|
+
:access_type => :offline,
|
165
|
+
:user_id => user_id,
|
166
|
+
:state => state
|
167
|
+
}).to_s
|
168
|
+
end
|
169
|
+
|
170
|
+
##
|
171
|
+
# Retrieve credentials using the provided authorization code.
|
172
|
+
#
|
173
|
+
# This function exchanges the authorization code for an access token and queries
|
174
|
+
# the UserInfo API to retrieve the user's Google ID.
|
175
|
+
# If a refresh token has been retrieved along with an access token, it is stored
|
176
|
+
# in the application database using the user's Google ID as key.
|
177
|
+
# If no refresh token has been retrieved, the function checks in the application
|
178
|
+
# database for one and returns it if found or raises a NoRefreshTokenError
|
179
|
+
# with an authorization URL to redirect the user to.
|
180
|
+
#
|
181
|
+
# @param [String] auth_code
|
182
|
+
# Authorization code to use to retrieve an access token.
|
183
|
+
# @param [String] state
|
184
|
+
# State to set to the authorization URL in case of error.
|
185
|
+
# @return [Signet::OAuth2::Client]
|
186
|
+
# OAuth 2.0 credentials containing an access and refresh token.
|
187
|
+
#
|
188
|
+
def self.get_credentials(authorization_code, state="OAuth Failed")
|
189
|
+
user_id = ''
|
190
|
+
begin
|
191
|
+
credentials = exchange_code(authorization_code)
|
192
|
+
user_info = get_user_info(credentials)
|
193
|
+
user_id = user_info.id
|
194
|
+
if credentials.refresh_token != nil
|
195
|
+
store_credentials(user_id, credentials)
|
196
|
+
return credentials
|
197
|
+
else
|
198
|
+
credentials = get_stored_credentials(user_id)
|
199
|
+
if credentials != nil && credentials.refresh_token != nil
|
200
|
+
return credentials
|
201
|
+
end
|
202
|
+
end
|
203
|
+
rescue CodeExchangeError => error
|
204
|
+
print 'An error occurred during code exchange.'
|
205
|
+
# Drive apps should try to retrieve the user and credentials for the current
|
206
|
+
# session.
|
207
|
+
# If none is available, redirect the user to the authorization URL.
|
208
|
+
error.authorization_url = get_authorization_url(user_id, state)
|
209
|
+
raise error
|
210
|
+
rescue NoUserIdError
|
211
|
+
print 'No user ID could be retrieved.'
|
212
|
+
end
|
213
|
+
|
214
|
+
authorization_url = get_authorization_url(user_id, state)
|
215
|
+
raise NoRefreshTokenError.new(authorization_url)
|
216
|
+
end
|
217
|
+
|
218
|
+
|
219
|
+
##
|
220
|
+
# Build a Mirror client instance.
|
221
|
+
#
|
222
|
+
# @param [Signet::OAuth2::Client] credentials
|
223
|
+
# OAuth 2.0 credentials.
|
224
|
+
# @return [Google::APIClient]
|
225
|
+
# Client instance
|
226
|
+
def self.build_client(credentials)
|
227
|
+
m = Mirror.new()
|
228
|
+
m.client = Google::APIClient.new
|
229
|
+
m.client.authorization = credentials
|
230
|
+
m.client = m.client.discovered_api('mirror', 'v1')
|
231
|
+
m
|
232
|
+
end
|
233
|
+
|
234
|
+
def self.build_with_code(authorization_code)
|
235
|
+
return build_client(get_credentials(authorization_code))
|
236
|
+
end
|
237
|
+
|
238
|
+
def insert(item)
|
239
|
+
item.insert(client)
|
240
|
+
end
|
241
|
+
|
242
|
+
def delete(item)
|
243
|
+
item.delete(client)
|
244
|
+
end
|
245
|
+
|
246
|
+
end
|
247
|
+
|
248
|
+
##
|
249
|
+
# Error raised when an error occurred while retrieving credentials.
|
250
|
+
#
|
251
|
+
class GetCredentialsError < StandardError
|
252
|
+
##
|
253
|
+
# Initialize a NoRefreshTokenError instance.
|
254
|
+
#
|
255
|
+
# @param [String] authorize_url
|
256
|
+
# Authorization URL to redirect the user to in order to in order to request
|
257
|
+
# offline access.
|
258
|
+
#
|
259
|
+
def initialize(authorization_url)
|
260
|
+
@authorization_url = authorization_url
|
261
|
+
end
|
262
|
+
|
263
|
+
def authorization_url=(authorization_url)
|
264
|
+
@authorization_url = authorization_url
|
265
|
+
end
|
266
|
+
|
267
|
+
def authorization_url
|
268
|
+
return @authorization_url
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
##
|
273
|
+
# Error raised when a code exchange has failed.
|
274
|
+
#
|
275
|
+
class CodeExchangeError < GetCredentialsError
|
276
|
+
end
|
277
|
+
|
278
|
+
##
|
279
|
+
# Error raised when no refresh token has been found.
|
280
|
+
#
|
281
|
+
class NoRefreshTokenError < GetCredentialsError
|
282
|
+
end
|
283
|
+
|
284
|
+
##
|
285
|
+
# Error raised when no user ID could be retrieved.
|
286
|
+
#
|
287
|
+
class NoUserIdError < StandardError
|
16
288
|
end
|
17
289
|
|
290
|
+
|
18
291
|
end
|
292
|
+
|
293
|
+
require 'glass/contacts/contact'
|
294
|
+
require 'glass/locations/location'
|
295
|
+
require 'glass/subscriptions/subscription'
|
296
|
+
require 'glass/timeline/timeline_item'
|
data/lib/glass/config.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
module Glass
|
2
|
+
class Config
|
3
|
+
|
4
|
+
|
5
|
+
attr_accessor :no_redis
|
6
|
+
|
7
|
+
##
|
8
|
+
# Accepts:
|
9
|
+
# 1. A 'hostname:port' String
|
10
|
+
# 2. A 'hostname:port:db' String (to select the Redis db)
|
11
|
+
# 3. A 'hostname:port/namespace' String (to set the Redis namespace)
|
12
|
+
# 4. A Redis URL String 'redis://host:port'
|
13
|
+
# 5. An instance of `Redis`, `Redis::Client`, `Redis::DistRedis`,
|
14
|
+
# or `Redis::Namespace`.
|
15
|
+
def redis=(server)
|
16
|
+
if @no_redis then return nil end
|
17
|
+
return if server == "" or server.nil?
|
18
|
+
|
19
|
+
@redis = case server
|
20
|
+
when String
|
21
|
+
if server['redis://']
|
22
|
+
redis = Redis.connect(:url => server, :thread_safe => true)
|
23
|
+
else
|
24
|
+
server, namespace = server.split('/', 2)
|
25
|
+
host, port, db = server.split(':')
|
26
|
+
|
27
|
+
redis = Redis.new(
|
28
|
+
:host => host,
|
29
|
+
:port => port,
|
30
|
+
:db => db,
|
31
|
+
:thread_safe => true
|
32
|
+
)
|
33
|
+
end
|
34
|
+
Redis::Namespace.new(namespace || :glass, :redis => redis)
|
35
|
+
when Redis::Namespace, Redis::Distributed
|
36
|
+
server
|
37
|
+
when Redis
|
38
|
+
Redis::Namespace.new(:glass, :redis => server)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def redis
|
43
|
+
if @no_redis then return nil end
|
44
|
+
return @redis if @redis
|
45
|
+
self.redis = Redis.respond_to?(:connect) ? Redis.connect : "localhost:6379"
|
46
|
+
self.redis
|
47
|
+
end
|
48
|
+
|
49
|
+
def redis_id
|
50
|
+
if @no_redis then return nil end
|
51
|
+
# support 1.x versions of redis-rb
|
52
|
+
if redis.respond_to?(:server)
|
53
|
+
redis.server
|
54
|
+
elsif redis.respond_to?(:nodes) # distributed
|
55
|
+
redis.nodes.map(&:id).join(', ')
|
56
|
+
else
|
57
|
+
redis.client.id
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
@no_redis
|
62
|
+
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
|
2
|
+
module Glass
|
3
|
+
CONTACT="contact"
|
4
|
+
|
5
|
+
# A person or group that can be used as a creator or a contact.
|
6
|
+
#
|
7
|
+
class Contact
|
8
|
+
|
9
|
+
class << self
|
10
|
+
# The type of resource. This is always mirror#contact.
|
11
|
+
#
|
12
|
+
attr_accessor :kind
|
13
|
+
end
|
14
|
+
|
15
|
+
@@kind = MIRROR+"#"+CONTACT
|
16
|
+
|
17
|
+
# The ID of the application that created this contact.
|
18
|
+
# This is populated by the API
|
19
|
+
attr_reader :source
|
20
|
+
@source
|
21
|
+
|
22
|
+
# An ID for this contact.
|
23
|
+
# This is generated by the application and is treated as an opaque token.
|
24
|
+
#
|
25
|
+
attr_reader :id
|
26
|
+
@id
|
27
|
+
|
28
|
+
# The name to display for this contact.
|
29
|
+
#
|
30
|
+
attr_accessor :displayName
|
31
|
+
@displayName
|
32
|
+
|
33
|
+
# Set of image URLs to display for a contact.
|
34
|
+
# Most contacts will have a single image, but a "group" contact may include up to 8 image URLs and they will be resized and cropped into a mosaic on the client.
|
35
|
+
#
|
36
|
+
attr_accessor :imageUrls
|
37
|
+
@imageUrls=[]
|
38
|
+
|
39
|
+
# The type for this contact. This is used for sorting in UIs. Allowed values are:
|
40
|
+
# INDIVIDUAL - Represents a single person. This is the default.
|
41
|
+
# GROUP - Represents more than a single person.
|
42
|
+
#
|
43
|
+
attr_accessor :type
|
44
|
+
@type
|
45
|
+
|
46
|
+
# A list of MIME types that a contact supports.
|
47
|
+
# The contact will be shown to the user if any of its acceptTypes matches any of the types of the attachments on the item.
|
48
|
+
# If no acceptTypes are given, the contact will be shown for all items.
|
49
|
+
#
|
50
|
+
attr_accessor :acceptTypes
|
51
|
+
@acceptTypes=[]
|
52
|
+
|
53
|
+
# Primary phone number for the contact.
|
54
|
+
# This can be a fully-qualified number, with country calling code and area code, or a local number.
|
55
|
+
#
|
56
|
+
attr_accessor :phoneNumber
|
57
|
+
@phoneNumber
|
58
|
+
|
59
|
+
# Priority for the contact to determine ordering in a list of contacts.
|
60
|
+
# Contacts with higher priorities will be shown before ones with lower priorities.
|
61
|
+
#
|
62
|
+
attr_accessor :priority
|
63
|
+
@priority
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'glass/subscriptions/subscription.rb'
|
2
|
+
module Glass
|
3
|
+
LOCATION="location"
|
4
|
+
|
5
|
+
# A geographic location that can be associated with a timeline item.
|
6
|
+
#
|
7
|
+
class Location
|
8
|
+
|
9
|
+
class << self
|
10
|
+
# The type of resource. This is always mirror#location.
|
11
|
+
#
|
12
|
+
attr_reader :kind
|
13
|
+
|
14
|
+
def subscribe(mirror, call_back_url)
|
15
|
+
s = Subscription.new()
|
16
|
+
s.mirror=mirror
|
17
|
+
s.callbackUrl=call_back_url
|
18
|
+
s.collection=Subscription::LOCATION
|
19
|
+
s.operation << UPDATE
|
20
|
+
s.insert
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
@@kind
|
25
|
+
|
26
|
+
|
27
|
+
# The ID of the location.
|
28
|
+
#
|
29
|
+
attr_reader :id
|
30
|
+
@id
|
31
|
+
|
32
|
+
# The time at which this location was captured, formatted according to RFC 3339.
|
33
|
+
#
|
34
|
+
attr_reader :timestamp
|
35
|
+
@timestamp
|
36
|
+
|
37
|
+
# The latitude, in degrees.
|
38
|
+
#
|
39
|
+
attr_accessor :latitude
|
40
|
+
@latitude
|
41
|
+
|
42
|
+
# The longitude, in degrees.
|
43
|
+
attr_accessor :longitude
|
44
|
+
@longitude
|
45
|
+
|
46
|
+
# The accuracy of the location fix in meters.
|
47
|
+
#
|
48
|
+
attr_accessor :accuracy
|
49
|
+
@accuracy
|
50
|
+
|
51
|
+
# The name to be displayed. This may be a business name or a user-defined place, such as "Home".
|
52
|
+
#
|
53
|
+
attr_accessor :displayName
|
54
|
+
@displayName
|
55
|
+
|
56
|
+
# The full address of the location.
|
57
|
+
#
|
58
|
+
attr_accessor :address
|
59
|
+
@address
|
60
|
+
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
@@ -0,0 +1,222 @@
|
|
1
|
+
require 'json'
|
2
|
+
module Glass
|
3
|
+
SUBSCRIPTION="subscription"
|
4
|
+
|
5
|
+
# A subscription to events on a collection.
|
6
|
+
#
|
7
|
+
class Subscription
|
8
|
+
class << self
|
9
|
+
# The type of resource. This is always mirror#subscription.
|
10
|
+
#
|
11
|
+
attr_accessor :kind
|
12
|
+
|
13
|
+
##
|
14
|
+
# Subscribe to notifications for the current user.
|
15
|
+
#
|
16
|
+
# @param [Google::APIClient] client
|
17
|
+
# Authorized client instance.
|
18
|
+
# @param [String] collection
|
19
|
+
# Collection to subscribe to (supported values are "timeline" and "locations").
|
20
|
+
# @param [String] user_token
|
21
|
+
# Opaque token used by the Glassware to identify the user the notification
|
22
|
+
# pings are sent for (recommended).
|
23
|
+
# @param [String] verify_token
|
24
|
+
# Opaque token used by the Glassware to verify that the notification pings are
|
25
|
+
# sent by the API (optional).
|
26
|
+
# @param [String] callback_url
|
27
|
+
# URL receiving notification pings (must be HTTPS).
|
28
|
+
# @param [Array] operation
|
29
|
+
# List of operations to subscribe to. Valid values are "UPDATE", "INSERT" and
|
30
|
+
# "DELETE" or nil to subscribe to all.
|
31
|
+
# @return nil
|
32
|
+
def subscribe(mirror, collection, user_token, verify_token, callback_url, operation)
|
33
|
+
subscription = mirror.subscriptions.insert.request_schema.new({
|
34
|
+
'collection' => collection,
|
35
|
+
'userToken' => user_token,
|
36
|
+
'verifyToken' => verify_token,
|
37
|
+
'callbackUrl' => callback_url,
|
38
|
+
'operation' => operation})
|
39
|
+
result = client.execute(
|
40
|
+
:api_method => mirror.subscriptions.insert,
|
41
|
+
:body_object => subscription)
|
42
|
+
if result.error?
|
43
|
+
puts "An error occurred: #{result.data['error']['message']}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# Delete a subscription to a collection.
|
49
|
+
#
|
50
|
+
# @param [Google::APIClient] client
|
51
|
+
# Authorized client instance.
|
52
|
+
# @param [String] collection
|
53
|
+
# Collection to unsubscribe from (supported values are "timeline" and
|
54
|
+
# "locations").
|
55
|
+
# @return nil
|
56
|
+
def unsubscribe_from_notifications(client, collection)
|
57
|
+
mirror = client.discovered_api('mirror', 'v1')
|
58
|
+
result = client.execute(
|
59
|
+
:api_method => mirror.subscriptions.delete,
|
60
|
+
:parameters => { 'id' => collection })
|
61
|
+
if result.error?
|
62
|
+
puts "An error occurred: #{result.data['error']['message']}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
@@kind = MIRROR+"#"+SUBSCRIPTION
|
69
|
+
|
70
|
+
##
|
71
|
+
# The Mirror Api
|
72
|
+
#
|
73
|
+
attr_accessor :mirror
|
74
|
+
@mirror
|
75
|
+
|
76
|
+
# The ID of the subscription.
|
77
|
+
#
|
78
|
+
attr_accessor :id
|
79
|
+
@id
|
80
|
+
|
81
|
+
# The time at which this subscription was last modified, formatted according to RFC 3339.
|
82
|
+
#
|
83
|
+
attr_accessor :updated
|
84
|
+
@updated
|
85
|
+
|
86
|
+
# The collection to subscribe to. Allowed values are:
|
87
|
+
# timeline - Changes in the timeline including insertion, deletion, and updates.
|
88
|
+
# locations - Location updates.
|
89
|
+
attr_accessor :collection
|
90
|
+
@collection
|
91
|
+
TIMELINE="timeline"
|
92
|
+
LOCATIONS="locations"
|
93
|
+
|
94
|
+
# A list of operations that should be subscribed to.
|
95
|
+
# An empty list indicates that all operations on the collection should be subscribed to. Allowed values are:
|
96
|
+
# UPDATE - The item has been updated.
|
97
|
+
# INSERT - A new item has been inserted.
|
98
|
+
# DELETE - The item has been deleted.
|
99
|
+
attr_accessor :operation
|
100
|
+
@operation= []
|
101
|
+
|
102
|
+
# The URL where notifications should be delivered (must start with https://).
|
103
|
+
#
|
104
|
+
attr_accessor :callbackUrl
|
105
|
+
@callbackUrl
|
106
|
+
|
107
|
+
# A secret token sent to the subscriber in notifications
|
108
|
+
# so that it can verify that the notification was generated by Google.
|
109
|
+
#
|
110
|
+
attr_accessor :verifyToken
|
111
|
+
@verifyToken
|
112
|
+
|
113
|
+
# An opaque token sent to the subscriber in notifications so that it can determine the ID of the user.
|
114
|
+
#
|
115
|
+
attr_accessor :userToken
|
116
|
+
@userToken
|
117
|
+
|
118
|
+
# Container object for notifications.
|
119
|
+
# This is not populated in the Subscription resource.
|
120
|
+
#
|
121
|
+
attr_accessor :notification
|
122
|
+
@notification
|
123
|
+
|
124
|
+
class Notification
|
125
|
+
# The collection that generated the notification.
|
126
|
+
#
|
127
|
+
attr_accessor :collection
|
128
|
+
@collection
|
129
|
+
|
130
|
+
# The ID of the item that generated the notification.
|
131
|
+
#
|
132
|
+
attr_accessor :itemId
|
133
|
+
@itemId
|
134
|
+
|
135
|
+
# The type of operation that generated the notification.
|
136
|
+
#
|
137
|
+
attr_accessor :operation
|
138
|
+
@operation
|
139
|
+
|
140
|
+
# The secret verify token provided by the service when it subscribed for notifications.
|
141
|
+
#
|
142
|
+
attr_accessor :verifyToken
|
143
|
+
@verifyToken
|
144
|
+
|
145
|
+
# The user token provided by the service when it subscribed for notifications.
|
146
|
+
#
|
147
|
+
attr_accessor :userToken
|
148
|
+
@userToken
|
149
|
+
|
150
|
+
# A list of actions taken by the user that triggered the notification.
|
151
|
+
#
|
152
|
+
attr_accessor :userActions
|
153
|
+
@userActions=[]
|
154
|
+
|
155
|
+
class Action
|
156
|
+
# The type of action. The value of this can be:
|
157
|
+
# SHARE - the user shared an item.
|
158
|
+
# REPLY - the user replied to an item.
|
159
|
+
# REPLY_ALL - the user replied to all recipients of an item.
|
160
|
+
# CUSTOM - the user selected a custom menu item on the timeline item.
|
161
|
+
# DELETE - the user deleted the item.
|
162
|
+
# PIN - the user pinned the item.
|
163
|
+
# UNPIN - the user unpinned the item.
|
164
|
+
# In the future, additional types may be added. UserActions with unrecognized types should be ignored.
|
165
|
+
#
|
166
|
+
attr_accessor :type
|
167
|
+
@type
|
168
|
+
|
169
|
+
SHARE="SHARE"
|
170
|
+
REPLY="REPLY"
|
171
|
+
REPLY_ALL="REPLY_ALL"
|
172
|
+
CUSTOM="CUSTOM"
|
173
|
+
DELETE="DELETE"
|
174
|
+
PIN="PIN"
|
175
|
+
UNPIN="UNPIN"
|
176
|
+
|
177
|
+
# An optional payload for the action.
|
178
|
+
#
|
179
|
+
# For actions of type CUSTOM, this is the ID of the custom menu item that was selected.
|
180
|
+
#
|
181
|
+
attr_accessor :payload
|
182
|
+
@payload
|
183
|
+
|
184
|
+
end
|
185
|
+
|
186
|
+
##
|
187
|
+
# Subscribe to notifications for the current user.
|
188
|
+
#
|
189
|
+
# @param [Google::APIClient] Mirror
|
190
|
+
# Authorized client instance.
|
191
|
+
#
|
192
|
+
# @return nil
|
193
|
+
def insert(mirror=@mirror)
|
194
|
+
subscription = mirror.subscriptions.insert.request_schema.new(to_json)
|
195
|
+
result = client.execute(
|
196
|
+
:api_method => mirror.subscriptions.insert,
|
197
|
+
:body_object => subscription)
|
198
|
+
if result.error?
|
199
|
+
puts "An error occurred: #{result.data['error']['message']}"
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
##
|
204
|
+
# Delete a subscription to a collection.
|
205
|
+
#
|
206
|
+
# @param [Google::APIClient] client
|
207
|
+
# Authorized client instance.
|
208
|
+
# @return nil
|
209
|
+
def delete(mirror=@mirror)
|
210
|
+
result = client.execute(
|
211
|
+
:api_method => mirror.subscriptions.delete,
|
212
|
+
:parameters => { 'id' => collection })
|
213
|
+
if result.error?
|
214
|
+
puts "An error occurred: #{result.data['error']['message']}"
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
end
|
219
|
+
|
220
|
+
end
|
221
|
+
|
222
|
+
end
|
@@ -0,0 +1,400 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Glass
|
4
|
+
TIMELINE_ITEM="timelineItem"
|
5
|
+
|
6
|
+
# Each item in the user's timeline is represented as a TimelineItem JSON structure, described below.
|
7
|
+
#
|
8
|
+
class TimelineItem
|
9
|
+
|
10
|
+
def initialize(client=nil)
|
11
|
+
@client = client
|
12
|
+
end
|
13
|
+
|
14
|
+
class << self
|
15
|
+
|
16
|
+
# The type of resource. This is always mirror#timelineItem.
|
17
|
+
#
|
18
|
+
attr_reader :kind
|
19
|
+
|
20
|
+
|
21
|
+
def get(client, id)
|
22
|
+
client.exexute(
|
23
|
+
:api_method => client.timeline.get,
|
24
|
+
:parameters => {:id => id}
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
DISPLAY_TIME="displayTime"
|
29
|
+
WRITE_TIME="writeTime"
|
30
|
+
|
31
|
+
# Retrieves a list of timeline items for the authenticated user.
|
32
|
+
# @param [string] bundleId
|
33
|
+
# If true, tombstone records for deleted items will be returned.
|
34
|
+
# @param [boolean] includeDeleted
|
35
|
+
# If true, tombstone records for deleted items will be returned.
|
36
|
+
# @param [integer] maxResults
|
37
|
+
# The maximum number of items to include in the response, used for paging.
|
38
|
+
# @param [string] orderBy
|
39
|
+
# Controls the order in which timeline items are returned.
|
40
|
+
# Acceptable values are:
|
41
|
+
# "displayTime": Results will be ordered by displayTime (default). This is the same ordering as is used in the timeline on the device.
|
42
|
+
# "writeTime": Results will be ordered by the time at which they were last written to the data store.
|
43
|
+
# @param [string] pageToken
|
44
|
+
# Token for the page of results to return.
|
45
|
+
# @param [boolean] pinnedOnly
|
46
|
+
# If true, only pinned items will be returned.
|
47
|
+
# @param [string] sourceItemId
|
48
|
+
# If provided, only items with the given sourceItemId will be returned.
|
49
|
+
#
|
50
|
+
def list(client, params={})
|
51
|
+
result=[]
|
52
|
+
parameters = params
|
53
|
+
api_result = client.execute(
|
54
|
+
:api_method => mirror.timeline.list,
|
55
|
+
:parameters => parameters)
|
56
|
+
if api_result.success?
|
57
|
+
data = api_result.data
|
58
|
+
unless data.items.empty?
|
59
|
+
result << data.items
|
60
|
+
parameters[:pageToken]= data.next_page_token
|
61
|
+
result << list(client, parameters)
|
62
|
+
end
|
63
|
+
else
|
64
|
+
puts "An error occurred: #{result.data['error']['message']}"
|
65
|
+
end
|
66
|
+
result
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
##
|
72
|
+
# The Client
|
73
|
+
#
|
74
|
+
attr_reader :client
|
75
|
+
@client
|
76
|
+
|
77
|
+
# The ID of the timeline item. This is unique within a user's timeline.
|
78
|
+
#
|
79
|
+
attr_accessor :id
|
80
|
+
|
81
|
+
# A URL that can be used to retrieve this item.
|
82
|
+
#
|
83
|
+
attr_accessor :selfLink
|
84
|
+
|
85
|
+
# The time at which this item was created, formatted according to RFC 3339.
|
86
|
+
#
|
87
|
+
attr_reader :created
|
88
|
+
|
89
|
+
# The time at which this item was last modified, formatted according to RFC 3339.
|
90
|
+
#
|
91
|
+
attr_accessor :updated
|
92
|
+
|
93
|
+
# The time that should be displayed when this item is viewed in the timeline,
|
94
|
+
# formatted according to RFC 3339. This user's timeline is sorted chronologically on display time,
|
95
|
+
# so this will also determine where the item is displayed in the timeline.
|
96
|
+
# If not set by the service, the display time defaults to the updated time.
|
97
|
+
#
|
98
|
+
attr_reader :displayTime
|
99
|
+
|
100
|
+
# When true, indicates this item is deleted, and only the ID property is set.
|
101
|
+
#
|
102
|
+
attr_accessor :isDeleted
|
103
|
+
|
104
|
+
# ETag for this item.
|
105
|
+
#
|
106
|
+
attr_reader :etag
|
107
|
+
|
108
|
+
# The user or group that created this item.
|
109
|
+
#
|
110
|
+
attr_accessor :creator
|
111
|
+
|
112
|
+
# If this item was generated as a reply to another item,
|
113
|
+
# this field will be set to the ID of the item being replied to.
|
114
|
+
# This can be used to attach a reply to the appropriate conversation or post.
|
115
|
+
#
|
116
|
+
attr_accessor :inReplyTo
|
117
|
+
|
118
|
+
# Text content of this item.
|
119
|
+
#
|
120
|
+
attr_accessor :text
|
121
|
+
|
122
|
+
# The speakable version of the content of this item.
|
123
|
+
# Along with the READ_ALOUD menu item,
|
124
|
+
# use this field to provide text that would be clearer when read aloud,
|
125
|
+
# or to provide extended information to what is displayed visually on Glass.
|
126
|
+
#
|
127
|
+
attr_accessor :speakableText
|
128
|
+
|
129
|
+
# A list of media attachments associated with this item.
|
130
|
+
#
|
131
|
+
attr_accessor :attachments
|
132
|
+
|
133
|
+
# The geographic location associated with this item.
|
134
|
+
#
|
135
|
+
attr_accessor :location
|
136
|
+
|
137
|
+
# A list of menu items that will be presented to the user when this item is selected in the timeline.
|
138
|
+
#
|
139
|
+
attr_accessor :menuItems
|
140
|
+
|
141
|
+
# Controls how notifications for this item are presented on the device.
|
142
|
+
# If this is missing, no notification will be generated.
|
143
|
+
#
|
144
|
+
attr_accessor :notification
|
145
|
+
|
146
|
+
|
147
|
+
# When true, indicates this item is pinned,
|
148
|
+
# which means it's grouped alongside "active" items like navigation and hangouts,
|
149
|
+
# on the opposite side of the home screen from historical (non-pinned) timeline items.
|
150
|
+
#
|
151
|
+
attr_accessor :isPinned
|
152
|
+
|
153
|
+
# The title of this item.
|
154
|
+
#
|
155
|
+
attr_accessor :title
|
156
|
+
|
157
|
+
# HTML content for this item. If both text and html are provided for an item,
|
158
|
+
# the html will be rendered in the timeline.
|
159
|
+
#
|
160
|
+
attr_accessor :html
|
161
|
+
|
162
|
+
# The bundle ID for this item. Services can specify a bundleId to group many items together.
|
163
|
+
# They appear under a single top-level item on the device.
|
164
|
+
#
|
165
|
+
attr_accessor :bundleId
|
166
|
+
|
167
|
+
# Additional pages of HTML content associated with this item.
|
168
|
+
# If this field is specified, the item will be displayed as a bundle,
|
169
|
+
# with the html field as the cover. It is an error to specify this field without specifying the html field.
|
170
|
+
#
|
171
|
+
attr_accessor :htmlPages
|
172
|
+
|
173
|
+
# Opaque string you can use to map a timeline item to data in your own service.
|
174
|
+
#
|
175
|
+
attr_accessor :sourceItemId
|
176
|
+
|
177
|
+
# A canonical URL pointing to the canonical/high quality version of the data represented by the timeline item.
|
178
|
+
#
|
179
|
+
attr_accessor :canonicalUrl
|
180
|
+
|
181
|
+
# Whether this item is a bundle cover.
|
182
|
+
#
|
183
|
+
# If an item is marked as a bundle cover,
|
184
|
+
# it will be the entry point to the bundle of items that have the same bundleId as that item.
|
185
|
+
# It will be shown only on the main timeline — not within the opened bundle.
|
186
|
+
#
|
187
|
+
# On the main timeline, items that are shown are:
|
188
|
+
# Items that have isBundleCover set to true
|
189
|
+
# Items that do not have a bundleId
|
190
|
+
#
|
191
|
+
# Items that do not have a bundleId
|
192
|
+
# In a bundle sub-timeline, items that are shown are:
|
193
|
+
# Items that have the bundleId in question AND isBundleCover set to false
|
194
|
+
|
195
|
+
attr_accessor :isBundleCover
|
196
|
+
|
197
|
+
# For pinned items, this determines the order in which the item is displayed in the timeline,
|
198
|
+
# with a higher score appearing closer to the clock. Note: setting this field is currently not supported.
|
199
|
+
#
|
200
|
+
attr_accessor :pinScore
|
201
|
+
|
202
|
+
# A list of contacts or groups that this item has been shared with.
|
203
|
+
#
|
204
|
+
attr_accessor :recipients
|
205
|
+
|
206
|
+
@@kind= MIRROR+"#"+TIMELINE_ITEM
|
207
|
+
@id
|
208
|
+
@selfLink
|
209
|
+
@created
|
210
|
+
@updated
|
211
|
+
@displayTime
|
212
|
+
@isDeleted
|
213
|
+
@etag
|
214
|
+
@creator #Todo User and Group
|
215
|
+
@inReplyTo
|
216
|
+
@text
|
217
|
+
@speakableText
|
218
|
+
@attachments=[]
|
219
|
+
@location
|
220
|
+
@menuItems=[]
|
221
|
+
@notification
|
222
|
+
@isPinned
|
223
|
+
@title
|
224
|
+
@html
|
225
|
+
@bundleId
|
226
|
+
@htmlPages=[]
|
227
|
+
@sourceItemId
|
228
|
+
@canonicalUrl
|
229
|
+
@isBundleCover
|
230
|
+
@pinScore
|
231
|
+
@recipients=[]
|
232
|
+
|
233
|
+
# Represents media content, such as a photo, that can be attached to a timeline item.
|
234
|
+
#
|
235
|
+
class Attachment
|
236
|
+
|
237
|
+
# The ID of the attachment.
|
238
|
+
#
|
239
|
+
attr_reader :id
|
240
|
+
@id
|
241
|
+
|
242
|
+
# The MIME type of the attachment.
|
243
|
+
# (supported content types are 'image/*', 'video/*' and 'audio/*').
|
244
|
+
#
|
245
|
+
attr_accessor :contentType
|
246
|
+
@contentType
|
247
|
+
|
248
|
+
TYPE_IMAGE='image/*'
|
249
|
+
TYPE_VIDEO='video/*'
|
250
|
+
TYPE_AUDIO='audio/*'
|
251
|
+
|
252
|
+
# The URL for the content.
|
253
|
+
#
|
254
|
+
attr_accessor :contentUrl
|
255
|
+
@contentUrl
|
256
|
+
|
257
|
+
# Indicates that the contentUrl is not available because the attachment content is still being processed.
|
258
|
+
# If the caller wishes to retrieve the content, it should try again later.
|
259
|
+
#
|
260
|
+
attr_reader :isProcessingContent
|
261
|
+
@isProcessingContent
|
262
|
+
end
|
263
|
+
|
264
|
+
class Notification
|
265
|
+
|
266
|
+
# Describes how important the notification is. Allowed values are:
|
267
|
+
# DEFAULT - Notifications of default importance. A chime will be played to alert contacts.
|
268
|
+
attr_accessor :level
|
269
|
+
|
270
|
+
# The time at which the notification should be delivered.
|
271
|
+
#
|
272
|
+
attr_accessor :deliveryTime
|
273
|
+
|
274
|
+
@level
|
275
|
+
@deliveryTime
|
276
|
+
end
|
277
|
+
|
278
|
+
class MenuItems
|
279
|
+
|
280
|
+
# The ID for this menu item. This is generated by the application and is treated as an opaque token.
|
281
|
+
#
|
282
|
+
attr_accessor :id
|
283
|
+
|
284
|
+
# Controls the behavior when the user picks the menu option. Allowed values are:
|
285
|
+
# CUSTOM - Custom action set by the service. When the user selects this menuItem,
|
286
|
+
# the API triggers a notification to your callbackUrl with the userActions.type set to CUSTOM and the userActions.payload set to the ID of this menu item.
|
287
|
+
# This is the default value.
|
288
|
+
# Built-in actions:
|
289
|
+
# REPLY - Initiate a reply to the timeline item using the voice recording UI. The creator attribute must be set in the timeline item for this menu to be available.
|
290
|
+
# REPLY_ALL - Same behavior as REPLY. The original timeline item's recipients will be added to the reply item.
|
291
|
+
# DELETE - Delete the timeline item.
|
292
|
+
# SHARE - Share the timeline item with the available contacts.
|
293
|
+
# READ_ALOUD - Read the timeline item's speakableText aloud; if this field is not set, read the text field; if none of those fields are set, this menu item is ignored.
|
294
|
+
# VOICE_CALL - Initiate a phone call using the timeline item's creator.phone_number attribute as recipient.
|
295
|
+
# NAVIGATE - Navigate to the timeline item's location.
|
296
|
+
# TOGGLE_PINNED - Toggle the isPinned state of the timeline item.
|
297
|
+
#
|
298
|
+
attr_accessor :actions
|
299
|
+
|
300
|
+
# For CUSTOM items, a list of values controlling the appearance of the menu item in each of its states.
|
301
|
+
# A value for the DEFAULT state must be provided.
|
302
|
+
# If the PENDING or CONFIRMED states are missing, they will not be shown.
|
303
|
+
#
|
304
|
+
attr_accessor :values
|
305
|
+
|
306
|
+
# If set to true on a CUSTOM menu item, that item will be removed from the menu after it is selected.
|
307
|
+
#
|
308
|
+
attr_accessor :removeWhenSelected
|
309
|
+
|
310
|
+
@id
|
311
|
+
@actions
|
312
|
+
@values=[]
|
313
|
+
@removeWhenSelected
|
314
|
+
|
315
|
+
REPLY="REPLY"
|
316
|
+
REPLY_ALL="REPLY_ALL"
|
317
|
+
DELETE="DELETE"
|
318
|
+
SHARE="SHARE"
|
319
|
+
READ_ALOUD="READ_ALOUD"
|
320
|
+
VOICE_CALL="VOICE_CALL"
|
321
|
+
NAVIGATE="NAVIGATE"
|
322
|
+
TOGGLE_PINNED="TOOGLE_PINNED"
|
323
|
+
CUSTOM="CUSTOM"
|
324
|
+
|
325
|
+
class Values
|
326
|
+
|
327
|
+
# The name to display for the menu item.
|
328
|
+
#
|
329
|
+
attr_accessor :displayName
|
330
|
+
|
331
|
+
# URL of an icon to display with the menu item.
|
332
|
+
#
|
333
|
+
attr_accessor :iconURL
|
334
|
+
|
335
|
+
# The state that this value applies to. Allowed values are:
|
336
|
+
# DEFAULT - Default value shown when displayed in the menuItems list.
|
337
|
+
# PENDING - Value shown when the menuItem has been selected by the user but can still be cancelled.
|
338
|
+
# CONFIRMED - Value shown when the menuItem has been selected by the user and can no longer be cancelled.
|
339
|
+
attr_accessor :state
|
340
|
+
|
341
|
+
@displayName
|
342
|
+
@iconURL
|
343
|
+
@state
|
344
|
+
|
345
|
+
DEFAULT="DEFAULT"
|
346
|
+
PENDING="PENDING"
|
347
|
+
CONFIRMED="CONFIRMED"
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
|
352
|
+
##
|
353
|
+
# Insert a new Timeline Item in the user's glass.
|
354
|
+
#
|
355
|
+
# @param [Google::APIClient::API] client
|
356
|
+
# Authorized client instance.
|
357
|
+
# @return Array[Google::APIClient::Schema::Mirror::V1::TimelineItem]
|
358
|
+
# Timeline item instance if successful, nil otherwise.
|
359
|
+
def insert!(mirror=@client)
|
360
|
+
timeline_item = self
|
361
|
+
result = []
|
362
|
+
if file_upload?
|
363
|
+
for file in file_to_upload
|
364
|
+
media = Google::APIClient::UploadIO.new(file.contentUrl, file.content_type)
|
365
|
+
result << client.execute!(
|
366
|
+
:api_method => mirror.timeline.insert,
|
367
|
+
:body_object => timeline_item,
|
368
|
+
:media => media,
|
369
|
+
:parameters => {
|
370
|
+
:uploadType => 'multipart',
|
371
|
+
:alt => 'json'})
|
372
|
+
end
|
373
|
+
else
|
374
|
+
result << client.execute(
|
375
|
+
:api_method => mirror.timeline.insert,
|
376
|
+
:body_object => timeline_item)
|
377
|
+
end
|
378
|
+
return result.data
|
379
|
+
end
|
380
|
+
|
381
|
+
def file_upload?
|
382
|
+
flag=false
|
383
|
+
@attachments.each { |attachment|
|
384
|
+
flag = true unless attachment.id
|
385
|
+
}
|
386
|
+
return flag
|
387
|
+
end
|
388
|
+
|
389
|
+
def file_to_upload
|
390
|
+
file_to_upload=[]
|
391
|
+
@attachments.each { |attachment|
|
392
|
+
file_to_upload << attachment unless attachment.id
|
393
|
+
}
|
394
|
+
file_to_upload
|
395
|
+
end
|
396
|
+
|
397
|
+
|
398
|
+
end
|
399
|
+
|
400
|
+
end
|
data/lib/glass/version.rb
CHANGED
data/rebuild.sh
ADDED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: glass
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.1.
|
4
|
+
version: 0.0.1.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-05-29 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -59,6 +59,54 @@ dependencies:
|
|
59
59
|
- - ! '>='
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: redis
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: redis-namespace
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :runtime
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: httparty
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :runtime
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
62
110
|
description: Glass Gem for Google Glass
|
63
111
|
email:
|
64
112
|
- dam.cavailles@laposte.net
|
@@ -73,7 +121,13 @@ files:
|
|
73
121
|
- Rakefile
|
74
122
|
- glass.gemspec
|
75
123
|
- lib/glass.rb
|
124
|
+
- lib/glass/config.rb
|
125
|
+
- lib/glass/contacts/contact.rb
|
126
|
+
- lib/glass/locations/location.rb
|
127
|
+
- lib/glass/subscriptions/subscription.rb
|
128
|
+
- lib/glass/timeline/timeline_item.rb
|
76
129
|
- lib/glass/version.rb
|
130
|
+
- rebuild.sh
|
77
131
|
homepage: https://github.com/thedamfr/glass
|
78
132
|
licenses:
|
79
133
|
- MIT
|
@@ -95,7 +149,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
95
149
|
version: '0'
|
96
150
|
requirements: []
|
97
151
|
rubyforge_project:
|
98
|
-
rubygems_version: 1.8.
|
152
|
+
rubygems_version: 1.8.25
|
99
153
|
signing_key:
|
100
154
|
specification_version: 3
|
101
155
|
summary: This Gem is meant to allow you to quickly build a Google Glass Application.
|