motional 0.1.0
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 +20 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/Guardfile +13 -0
- data/LICENSE.txt +22 -0
- data/README.md +128 -0
- data/Rakefile +15 -0
- data/app/app_delegate.rb +14 -0
- data/lib/motional.rb +11 -0
- data/lib/motional/asset.rb +426 -0
- data/lib/motional/assets.rb +95 -0
- data/lib/motional/assets_filter.rb +33 -0
- data/lib/motional/core.rb +84 -0
- data/lib/motional/group.rb +245 -0
- data/lib/motional/library.rb +32 -0
- data/lib/motional/representation.rb +101 -0
- data/lib/motional/representations.rb +55 -0
- data/lib/motional/version.rb +3 -0
- data/motional.gemspec +24 -0
- data/resources/sample.jpg +0 -0
- data/resources/sample.mp4 +0 -0
- data/spec/motional/asset_spec.rb +274 -0
- data/spec/motional/assets_spec.rb +53 -0
- data/spec/motional/group_spec.rb +137 -0
- data/spec/motional/library_spec.rb +18 -0
- data/spec/motional/representation_spec.rb +57 -0
- data/spec/motional/representations_spec.rb +43 -0
- data/spec/spec_helper.rb +45 -0
- metadata +130 -0
@@ -0,0 +1,32 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
module MotionAL
|
4
|
+
#
|
5
|
+
# A wrapper of ALAssetLibrary class.
|
6
|
+
#
|
7
|
+
# An instance of ALAssetsLibrary provides access to the videos and photos that are under the control of the Photos application.
|
8
|
+
# The library includes those that are in the Saved Photos album, those coming from iTunes, and those that were directly imported into the device. You use it to retrieve the list of all asset groups and to save images and videos into the Saved Photos album.
|
9
|
+
#
|
10
|
+
# And added some convinience methods.
|
11
|
+
#
|
12
|
+
class Library
|
13
|
+
# @return [Class] An alias of MotionAL::Group class
|
14
|
+
attr_reader :groups
|
15
|
+
|
16
|
+
# @return [MotionAL::Library] Singleton instance.
|
17
|
+
def self.instance
|
18
|
+
Dispatch.once { @@instance ||= new }
|
19
|
+
@@instance
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
@groups = MotionAL::Group
|
24
|
+
end
|
25
|
+
|
26
|
+
# An instance of ALAssetLibrary.
|
27
|
+
# @return [ALAssetsLibrary]
|
28
|
+
def al_asset_library
|
29
|
+
@al_asset_library ||= ALAssetsLibrary.new
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
module MotionAL
|
4
|
+
#
|
5
|
+
# A wrapper of ALRepresentation class.
|
6
|
+
#
|
7
|
+
# An ALAssetRepresentation object encapsulates one of the representations of a given ALAsset object.
|
8
|
+
# A given asset in the library may have more than one representation. For example, if a camera provides RAW and JPEG versions of an image, the resulting asset will have two representations—one for the RAW file and one for the JPEG file.
|
9
|
+
#
|
10
|
+
# And added some convinience methods.
|
11
|
+
#
|
12
|
+
class Representation
|
13
|
+
attr_reader :asset, :al_asset_representation
|
14
|
+
|
15
|
+
# @param asset [MotionAL::Asset] A parent asset.
|
16
|
+
# @param al_asset_representation [ALAssetRepresentation] An instance of ALAssetRepresentation.
|
17
|
+
def initialize(asset, al_asset_representation)
|
18
|
+
@asset = asset
|
19
|
+
@al_asset_representation = al_asset_representation
|
20
|
+
end
|
21
|
+
|
22
|
+
# return a NSConcreteData(kind of NSData) object for the representation file.
|
23
|
+
#
|
24
|
+
# support only jpeg and png.
|
25
|
+
#
|
26
|
+
# @return [NSConcreteData]
|
27
|
+
def data
|
28
|
+
ui_image = UIImage.imageWithCGImage(self.cg_image)
|
29
|
+
if self.filename =~ /.jpe?g$/i
|
30
|
+
NSData.alloc.initWithData(UIImageJPEGRepresentation(ui_image, 0.0))
|
31
|
+
elsif self.filename =~ /.png$/i
|
32
|
+
NSData.alloc.initWithData(UIImagePNGRepresentation(ui_image))
|
33
|
+
else
|
34
|
+
nil
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# @param options [Hash] described for CGImageSourceCreateWithData or CGImageSourceCreateWithURL.
|
39
|
+
# @return [CGImageRef] A full resolution CGImage of the representation.
|
40
|
+
def cg_image_with_options(options)
|
41
|
+
@al_asset_representation.CGImageWithOptions(options)
|
42
|
+
end
|
43
|
+
|
44
|
+
# The orientation of the representation.
|
45
|
+
#
|
46
|
+
# @return [Symbol] :up, :down :left, :right, :up_mirrored, :down_mirrored, :left_mirrored or :right_mirrored
|
47
|
+
#
|
48
|
+
# @see MotionAL.asset_orientations
|
49
|
+
def orientation
|
50
|
+
MotionAL.asset_orientations.key(@al_asset.valueForProperty(ALAssetPropertyOrientation))
|
51
|
+
end
|
52
|
+
|
53
|
+
# A CGImage of the representation.
|
54
|
+
#
|
55
|
+
# @return [CGImageRef]
|
56
|
+
# @return [nil] When the representation has no CGImage.
|
57
|
+
def full_resolution_image
|
58
|
+
@al_asset_representation.fullResolutionImage
|
59
|
+
end
|
60
|
+
alias_method :cg_image, :full_resolution_image
|
61
|
+
|
62
|
+
# A CGImage of the representation that is appropriate for displaying full screen.
|
63
|
+
#
|
64
|
+
# @return [CGImageRef]
|
65
|
+
# @return [nil] When the representation has no CGImage.
|
66
|
+
def full_screen_image
|
67
|
+
@al_asset_representation.fullScreenImage
|
68
|
+
end
|
69
|
+
|
70
|
+
# Metadata of the representation.
|
71
|
+
#
|
72
|
+
# @return [Hash] A multidimensional hash.
|
73
|
+
# @return [nil] When the representation has no metadata or incompatible metadata.
|
74
|
+
def metadata
|
75
|
+
@al_asset_representation.metadata
|
76
|
+
end
|
77
|
+
|
78
|
+
class << self
|
79
|
+
private
|
80
|
+
# wrapper for method
|
81
|
+
# @!macro [attach] make_wrapper
|
82
|
+
# The representation's $1
|
83
|
+
#
|
84
|
+
# @method $1
|
85
|
+
# @return [$2]
|
86
|
+
def make_wrapper_for_method(method_name, type_of_return)
|
87
|
+
define_method(method_name) do
|
88
|
+
@al_asset_representation.send(method_name)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
make_wrapper_for_method(:scale, "Float")
|
94
|
+
make_wrapper_for_method(:dimensions, "CGSize")
|
95
|
+
make_wrapper_for_method(:filename, "String")
|
96
|
+
make_wrapper_for_method(:size, "Fixnum")
|
97
|
+
make_wrapper_for_method(:url, "NSURL")
|
98
|
+
make_wrapper_for_method(:UTI, "String")
|
99
|
+
alias_method :name, :filename
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
module MotionAL
|
4
|
+
#
|
5
|
+
# A collection of representations.
|
6
|
+
# Representations belongs to the asset.
|
7
|
+
#
|
8
|
+
class Representations
|
9
|
+
# @param asset [MotionAL::Asset]
|
10
|
+
def initialize(asset)
|
11
|
+
@asset = asset
|
12
|
+
end
|
13
|
+
|
14
|
+
# Find a representation by a specified representation UTI.
|
15
|
+
#
|
16
|
+
# @param representation_uti [String] A representation's UTI
|
17
|
+
# @return [nil]
|
18
|
+
#
|
19
|
+
# @yield [representation]
|
20
|
+
# @yieldparam representation [MotionAL::Representation] A found representation.
|
21
|
+
#
|
22
|
+
# @example
|
23
|
+
# asset.representations.find_by_uti(representation_uti) do |rep|
|
24
|
+
# p rep.filename
|
25
|
+
# end
|
26
|
+
def find_by_uti(representation_uti, &block)
|
27
|
+
al_rep = @asset.al_asset.representationForUTI(representation_uti)
|
28
|
+
if al_rep
|
29
|
+
block.call(Representation.new(@asset, al_rep))
|
30
|
+
else
|
31
|
+
nil # not found
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Find and enumerate representations of the asset.
|
36
|
+
#
|
37
|
+
# @return [nil]
|
38
|
+
#
|
39
|
+
# @yield [representation]
|
40
|
+
# @yieldparam representation [MotionAL::Representation] A found representation.
|
41
|
+
#
|
42
|
+
# @example
|
43
|
+
# asset.representations.find_all do |rep|
|
44
|
+
# p rep.filename
|
45
|
+
# end
|
46
|
+
def find_all(&block)
|
47
|
+
@asset.representation_utis.each do |uti|
|
48
|
+
find_by_uti(uti) do |rep|
|
49
|
+
block.call(rep)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
alias_method :each, :find_all
|
54
|
+
end
|
55
|
+
end
|
data/motional.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'motional/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "motional"
|
8
|
+
spec.version = MotionAL::VERSION
|
9
|
+
spec.authors = ["akahigeg"]
|
10
|
+
spec.email = ["akahigeg@gmail.com"]
|
11
|
+
spec.description = %q{AssetLibrary framework wrapper for RubyMotion}
|
12
|
+
spec.summary = %q{AssetLibrary framework wrapper for RubyMotion}
|
13
|
+
spec.homepage = "https://github.com/akahigeg/motional"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "motion-redgreen"
|
24
|
+
end
|
Binary file
|
Binary file
|
@@ -0,0 +1,274 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
describe MotionAL::Asset do
|
4
|
+
before do
|
5
|
+
@library = MotionAL.library
|
6
|
+
|
7
|
+
MotionAL::Asset.find_all do |asset, error|
|
8
|
+
@existent_asset = asset if asset.asset_type == :photo && @existent_asset.nil?
|
9
|
+
@existent_video_asset = asset if asset.asset_type == :video && @existent_video_asset.nil?
|
10
|
+
end
|
11
|
+
wait_async(0.5)
|
12
|
+
|
13
|
+
@video_url = NSBundle.mainBundle.URLForResource('sample', withExtension:"mp4")
|
14
|
+
|
15
|
+
MotionAL::Group.find_all do |group, error|
|
16
|
+
@test_group = group if group.name == TEST_GROUP_NAME
|
17
|
+
end
|
18
|
+
wait_async
|
19
|
+
|
20
|
+
MotionAL::Group.find_camera_roll do |group, error|
|
21
|
+
@saved_photos = group
|
22
|
+
end
|
23
|
+
wait_async
|
24
|
+
end
|
25
|
+
|
26
|
+
shared "asset creation" do
|
27
|
+
it "should create new asset" do
|
28
|
+
@calling_create_method.should.change do
|
29
|
+
wait_async(0.5)
|
30
|
+
@saved_photos.assets.count(:all)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe ".create" do
|
36
|
+
describe "when pass a CGImage with metadata" do
|
37
|
+
before do
|
38
|
+
@calling_create_method = Proc.new do
|
39
|
+
MotionAL::Asset.create(@existent_asset.full_resolution_image, @existent_asset.metadata)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
behaves_like "asset creation"
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "when pass a CGImage with orientation" do
|
47
|
+
before do
|
48
|
+
@calling_create_method = Proc.new do
|
49
|
+
MotionAL::Asset.create(@existent_asset.full_resolution_image, orientation: :up)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
behaves_like "asset creation"
|
54
|
+
end
|
55
|
+
|
56
|
+
describe "when pass a NSData" do
|
57
|
+
before do
|
58
|
+
@calling_create_method = Proc.new do
|
59
|
+
MotionAL::Asset.create(@existent_asset.data, @existent_asset.metadata)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
behaves_like "asset creation"
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "when pass a video path" do
|
67
|
+
before do
|
68
|
+
@calling_create_method = Proc.new do
|
69
|
+
MotionAL::Asset.create(@video_url)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
behaves_like "asset creation"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe "#save_new" do
|
78
|
+
describe "when pass a NSData" do
|
79
|
+
before do
|
80
|
+
@calling_create_method = Proc.new do
|
81
|
+
@new_asset = nil
|
82
|
+
@existent_asset.save_new(@existent_asset.data, @existent_asset.metadata) {|a| @new_asset = a }
|
83
|
+
wait_async(0.5)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
behaves_like "asset creation"
|
88
|
+
|
89
|
+
it "new asset have the 'original_asset'" do
|
90
|
+
@new_asset.original_asset.filename.should == @existent_asset.filename
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe "when pass a video path" do
|
95
|
+
before do
|
96
|
+
@calling_create_method = Proc.new do
|
97
|
+
@new_asset = @existent_video_asset.save_new(@video_url)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
behaves_like "asset creation"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe "#update" do
|
106
|
+
describe "when pass a NSData" do
|
107
|
+
before do
|
108
|
+
@calling_update_method = Proc.new do
|
109
|
+
@existent_asset.update(@existent_asset.data, @existent_asset.metadata)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should not create new asset" do
|
114
|
+
@calling_update_method.should.not.change do
|
115
|
+
@saved_photos.assets.count(:all)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
describe "when pass a video path" do
|
121
|
+
before do
|
122
|
+
@calling_update_method = Proc.new do
|
123
|
+
@existent_video_asset.update(@video_url)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
it "should not create new asset" do
|
128
|
+
@calling_update_method.should.not.change do
|
129
|
+
@saved_photos.assets.count(:all)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe "#find_by_url" do
|
136
|
+
it "should find the Asset" do
|
137
|
+
asset = nil
|
138
|
+
MotionAL::Asset.find_by_url(@existent_asset.url) do |asset|
|
139
|
+
asset = asset
|
140
|
+
end
|
141
|
+
wait_async
|
142
|
+
|
143
|
+
asset.should.instance_of MotionAL::Asset
|
144
|
+
end
|
145
|
+
|
146
|
+
it "should return nil when unknown url given" do
|
147
|
+
url = NSURL.URLWithString("http://hogehoge")
|
148
|
+
asset = nil
|
149
|
+
MotionAL::Asset.find_by_url(url) {|a| asset = a }
|
150
|
+
wait_async
|
151
|
+
|
152
|
+
asset.should.be.nil
|
153
|
+
end
|
154
|
+
|
155
|
+
it "should accept url string" do
|
156
|
+
asset = nil
|
157
|
+
MotionAL::Asset.find_by_url(@existent_asset.url.absoluteString) do |asset|
|
158
|
+
asset = asset
|
159
|
+
end
|
160
|
+
wait_async
|
161
|
+
|
162
|
+
asset.url.should.equal @existent_asset.url
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
describe "#all" do
|
167
|
+
it "should avail order asc" do
|
168
|
+
assets = []
|
169
|
+
|
170
|
+
MotionAL::Asset.find_all(order: :asc) {|a| assets << a }
|
171
|
+
wait_async
|
172
|
+
|
173
|
+
assets.size.should > 1
|
174
|
+
assets.first.url.should.equal @existent_asset.url
|
175
|
+
end
|
176
|
+
|
177
|
+
it "should avail order desc" do
|
178
|
+
assets = []
|
179
|
+
|
180
|
+
MotionAL::Asset.find_all(order: :desc) {|a| assets << a }
|
181
|
+
wait_async
|
182
|
+
|
183
|
+
assets.size.should > 1
|
184
|
+
assets.last.url.should.equal @existent_asset.url
|
185
|
+
end
|
186
|
+
|
187
|
+
it "should avail group option" do
|
188
|
+
assets_a = []
|
189
|
+
assets_b = []
|
190
|
+
|
191
|
+
MotionAL::Asset.find_all {|a| assets_a << a }
|
192
|
+
MotionAL::Asset.find_all(group: @test_group) {|a| assets_b << a }
|
193
|
+
wait_async
|
194
|
+
|
195
|
+
assets_a.size.should.equal @saved_photos.assets.count(:all)
|
196
|
+
assets_a.size.should.not.equal assets_b.size
|
197
|
+
end
|
198
|
+
|
199
|
+
it "should avail indexset option" do
|
200
|
+
indexset = NSMutableIndexSet.indexSetWithIndexesInRange(1..2)
|
201
|
+
assets = []
|
202
|
+
|
203
|
+
MotionAL::Asset.find_all(indexset: indexset) {|a| assets << a }
|
204
|
+
wait_async
|
205
|
+
|
206
|
+
assets.size.should.equal 2
|
207
|
+
|
208
|
+
# @saved_photos.assets[1].url.should.equal assets.first.url
|
209
|
+
end
|
210
|
+
|
211
|
+
it "should avail indexset option with order option" do
|
212
|
+
indexset = NSMutableIndexSet.indexSetWithIndexesInRange(1..3)
|
213
|
+
assets = []
|
214
|
+
|
215
|
+
MotionAL::Asset.find_all(indexset: indexset, order: :desc) {|a| assets << a }
|
216
|
+
wait_async
|
217
|
+
|
218
|
+
assets.size.should.equal 3
|
219
|
+
|
220
|
+
# @saved_photos.assets[1..3].reverse.first.url.should.equal assets.first.url
|
221
|
+
end
|
222
|
+
|
223
|
+
it "should avail filter option" do
|
224
|
+
assets = []
|
225
|
+
photos = []
|
226
|
+
|
227
|
+
MotionAL::Asset.find_all(filter: :all) {|a| assets << a }
|
228
|
+
MotionAL::Asset.find_all(filter: :photo) {|a| photos << a }
|
229
|
+
wait_async
|
230
|
+
|
231
|
+
assets.size.should.not.equal photos.size
|
232
|
+
end
|
233
|
+
# TODO: limit and offset option
|
234
|
+
end
|
235
|
+
|
236
|
+
describe "#editable?" do
|
237
|
+
it "asset is created by this App should be editable" do
|
238
|
+
@existent_asset.should.be.editable
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
describe "#representation" do
|
243
|
+
it "should be default representation" do
|
244
|
+
@existent_asset.representation.should == @existent_asset.default_representation
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
describe "#representations" do
|
249
|
+
it "should instance of Representations" do
|
250
|
+
@existent_asset.representations.should.instance_of MotionAL::Representations
|
251
|
+
end
|
252
|
+
|
253
|
+
it "should have Representation instance" do
|
254
|
+
@existent_asset.representations.find_all do |rep|
|
255
|
+
@rep = rep
|
256
|
+
end
|
257
|
+
@rep.should.instance_of MotionAL::Representation
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
describe "#asset_type" do
|
262
|
+
it "should be human readable" do
|
263
|
+
@existent_asset.asset_type.should.equal :photo
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
describe "#orientation" do
|
268
|
+
it "should be human readable" do
|
269
|
+
@existent_asset.orientation.should.equal :up
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
# TODO: what's happen when treat raw file
|
274
|
+
end
|