Sbktech
I was looking into how the Google Play library 現実に 得るs OAuth 記念品s on に代わって of apps, as the actual nuts and bolts remain undocumented.
Its 実施 申し込む/申し出s some 利益/興味ing insights into how Google 扱うs 問題/発行するs that 刈る up up when using OAuth in an Android app. The 残り/休憩(する) of this 地位,任命する 推定するs you know a bit about Android, OAuth 同様に as how to use the Google Play Services library to 得る 接近 記念品s. Sorry if all this is 負かす/撃墜する in the 少しのd, but that's where the fun bits are!
To get a disclaimer out of the way. These are just my 観察s after poking at さまざまな dex とじ込み/提出するs and 網状組織 traffic and I've undoubtedly 行方不明になるd many things; but I do hope to show you the 幅の広い 輪郭(を描く) of how it 作品.
There are three points of 利益/興味.
- 批判的な code runs only within a Google 調印するd app (eg: the com.google.android.gms/.auth.GetToken service.)
- This service 認可するs apps 地元で, and 得るs 接近 記念品s using a 地元で 蓄える/店d master 記念品. It 効果的に 取って代わるs the 伝統的な web-based OAuth 是認 flow.
- Third-party apps are identified by their 署名 and 一括 指名する, rather than an 使用/適用-重要な/使用/適用-secret.
The green areas run "信用d" code. Your app uses the Google Play Services (弁護士の)依頼人 library; but the library itself doesn't run 批判的な code. It instead 今後s calls to services and activities running within a 分かれて 任命する/導入するd app ("Google Play Services".)
Here are some 利益/興味ing bits of the manifest とじ込み/提出する for com.google.android.gms (the "Google Play services" app) so you get a feel for how it is 始める,決める up.
<manifest android:sharedUserId="com.google.uid.株d" 一括="com.google.android.gms" ...> <service android:指名する=".auth.GetToken" android:輸出(する)d="true" android:過程="com.google.過程.gapps"/> <activity android:指名する=".auth.login.LoginActivity" android:輸出(する)d="true" android:過程="com.google.android.gms.ui" .../>
As an app writer, you typically call GoogleAuthUtil.getToken() from your 使用/適用. The Play Services library first 立証するs that the 要求するd Google apps (com.android.vending and com.google.android.gms) are 任命する/導入するd, and also that they have an 許容できる 見解/翻訳/版 and 署名. The 許容できる 見解/翻訳/版 numbers and 署名 are embedded 直接/まっすぐに within the library. Your requested OAuth 範囲 is passed to the service, and now we're running inside the 信用d Play Services app.
Things start to get 利益/興味ing within the get-記念品 service.
This service first retrieves the app 一括 指名する and 署名 of the 報知係. This pair (caller_package_name,caller_signature) is used to identify the 報知係. This 相互の 身元確認,身分証明/立証 by the service and the calling library takes place 権利 at the 手始め; and 推定では makes it more difficult for either the 報知係 or a rogue "Play Service" to spoof their 身元 to the other.
The service 直接/まっすぐに manages app 是認, shows dialogs to the 使用者 as needed, and creates 接近 記念品s for requested 範囲s. In other words, it 成し遂げるs the app-是認 that would さもなければ typically be done by the web-場所/位置.
This approach does have some advantages. By using a 地元で running service in a 信用d Google app, Google can take advantage of some of the 安全 features within Android.
For example, by using the 一括 署名 to identify your 使用/適用, it 除去するs the need to embed 使用/適用 ids and secrets in your apk (which can often be 抽出するd out of a downloaded 使用/適用; 許すing the bad guys to 調印する requests as if they (機の)カム from your app.) 一括 署名s are much harder to spoof - you'll need the 私的な 調印 重要な which is (hopefully!) never 明らかにする/漏らすd; so this is a better way to identify an app.
その上の, all the 接近 是認 UI and 記念品 取得/買収 logic is sandboxed inside the play app rather than 存在 left to the app-writer. 推定では, this 減ずるs the "attack-surface", and also 許すs bugs to be 演説(する)/住所d quickly by updating this 選び出す/独身 app.
You might now be imagining the flip 味方する of such a powerful Android service, and you'd be 権利. This service has to be 安全な・保証する and 正確に 警報 the 使用者 during 是認; for once 準備/条項d, it is 有能な of creating 接近 記念品s on に代わって of any app, and with any 範囲.
The get-記念品 service does all this using what I call a master 記念品 that it 得るs from an undocumented authentication endpoint at https://android.(弁護士の)依頼人s.google.com/auth. Here's how it 作品.
When you first 追加する an account to the 装置 (say during 装置 体制/機構) the service 地位,任命するs your password, which is additionally encrypted with, I believe, a public 重要な whose 私的な 相当するもの is 利用できる to the web endpoint.
POST https://android.(弁護士の)依頼人s.google.com/auth Parameters ---------- accountType: HOSTED_OR_GOOGLE Email: xxx@gmail.com has_permission: 1 add_account: 1 EncryptedPasswd: <some base64 encoding> service: ac2dm source: android androidId: <deviceid> device_country: us operatorCountry: us lang: en sdk_version: 17
A successful login returns 支援する a bunch of 使用者 (警察などへの)密告,告訴(状) and 記念品s, one per line.
SID=... LSID=... Auth=... services=hist,mail,lh2,talk,oz Email=... 記念品=1/zMASTERTOKEN GooglePlusUpgrade=1 PicasaUser=... RopText= RopRevision=1 firstName=... lastName=...
公式文書,認める the 記念品 field - this is the one master 記念品 to 支配する them all.
The master 記念品 is 蓄える/店d on the 装置 using the AccountManager. You should be aware that in most 装置 configurations, AccountManager 蓄える/店s this 記念品 in an unencrypted sqlite database (accounts.db - usually somewhere under /data/system.) 保護 is まず第一に/本来 through the basic linux とじ込み/提出する-system 接近 支配(する)/統制するs - the directories are accessible only to system 過程s.
My understanding of the Android 安全 Team's position is that anything else is fundamentally 安全 theatre. Encrypting the data or the filesystem is a tricky 支配する and 解答s are often contentious. At any 率; it means rooted 装置s (or 装置s that can be rooted through an OS/driver 証拠不十分) are at 危険 of exposing the master 記念品 - so be aware.
Next, a 始める,決める of 核心 google services request OAuth 記念品s for their 範囲s. This also 明らかにする/漏らすs how the get-記念品 service 生成するs 接近 記念品s using the master 記念品. Here for example, is how it creates a 記念品 for one of the 範囲s requested by the market app.
POST https://android.(弁護士の)依頼人s.google.com/auth Parameters ---------- accountType: HOSTED_OR_GOOGLE Email: ... has_permission: 1 記念品: 1/zMASTERTOKEN service: sierra source: android androidId: <deviceid> app: com.android.vending client_sig: 38918a453d07199354f8b19af05ec6562ced5788 device_country: us operatorCountry: us lang: en sdk_version: 17
and sure enough - it gets 支援する:
SID=... LSID=... Auth=<auth_token> issueAdvice=自動車 services=hist,mail,lh2,talk,oz
Indeed, all it takes is to 追加する the has_permission=1 旗 to a request 含む/封じ込めるing the master 記念品, and 負かす/撃墜する comes an 接近 記念品 for the 願望(する)d 範囲. I also believe this 許可 is automatically 追加するd if the service notices that the requestor 署名 is the same as the google app 署名; which is in fact the SHA value you see above.
What happens when you request a 記念品 from your own app 経由で GoogleUtils.getToken() for the userinfo.profile 範囲?
POST https://android.(弁護士の)依頼人s.google.com/auth Headers ------- 装置: <deviceid> app: <app-一括-指名する> Parameters ---------- device_country: us operatorCountry: us lang: en_US sdk_version: 17 google_play_services_version: 4132532 accountType: HOSTED_OR_GOOGLE Email: <email> source: android androidId: <device_id> app: <app-一括-指名する> client_sig: <app-sha-署名> service: oauth2:https://www.googleapis.com/auth/userinfo.profile 記念品: 1/zMASTERTOKEN
公式文書,認める the absence of the has_permission=1 旗, and that the client_sig is now the 署名 of the calling app.
The 返答 is:
issueAdvice=同意 許可=見解(をとる)+basic+(警察などへの)密告,告訴(状)+about+your+account ScopeConsentDetails=%0AView+your+指名する%2C+public+profile+URL%2C+and+photo%0AView+your+gender%0AView+your+country%2C+la nguage%2C+and+timezone%0A ConsentDataBase64=...
The 使用者-interface is controlled by the issueAdvice 旗 in the 返答. Automatically 認可するd apps get the issueAdvice=自動車 旗 and an 接近 記念品. issueAdvice=同意 原因(となる)s the service to return an 意図 that if 開始する,打ち上げるd, shows a suitable 同意 dialog. (The Play Services (弁護士の)依頼人 library bundles this 意図 into a UserRecoverableAuthException.)
What happens when you 認可する a 同意 dialog? Nothing much - the service 単に 追加するs the has_permission=1 旗 to a 類似の request and gets 支援する an 接近 記念品. It really can create 接近 記念品s for any and all 範囲s.
By the way - this also 示すs how the 立証するd app call 機械装置 likely 作品. If you 明示する a audience:server:client_id 範囲, the 記念品 service passes it as usual with the (caller_package,caller_signature) pair to the server. The server checks if this matches the (警察などへの)密告,告訴(状) you 分かれて 登録(する)d for that app, and returns a JSON Web 記念品 主張するing this fact.
自然に, all this assumes the basic Android system, 同様に as the "信用d" Play Services app can securely identify the calling 一括; and that nobody other than the 信用d app has 接近 to the master 記念品.
Given those 仮定/引き受けることs, it's a nice technique. The Play Services App 含む/封じ込めるs an omnipotent "地元の-oauth-service"; playing the 役割 of the web-based 是認 flow but with an Android flavor. Third-party apps are identified 直接/まっすぐに by their app 署名, 除去するing the need to embed app secrets within the apk とじ込み/提出する.
Most 使用者s need (and should) enter their google password only when setting up their 装置. Apps no longer use the inherently insecure Webview approach to 誘発する/引き起こす the 是認 flow; nor do they need to use the ぎこちない and tedious flow 経由で a browser 意図. The app never sees anything other than the 接近 記念品 itself. 批判的な code runs only in the Play Services app, so bugs can be 直す/買収する,八百長をするd by just updating one app.
Downsides?
Be aware there's a master 記念品 蓄える/店d on your Android 装置 which has the latent ability to 認める 接近 to services you might not even be 接近ing from it. If that 記念品 is ever exposed, you should assume that all data associated with the account is up for 得る,とらえるs. Use the Android 装置 経営者/支配人 to 減ずる the window of 適切な時期 if your 装置 is stolen, or manage this master 記念品 from your 安全 settings. Or, use a low-value account just for your android 装置s; and keep 批判的な 文書s in a separate account.
I recently needed to dig into Picasa's 内部の databases to get some (警察などへの)密告,告訴(状) that it appeared to 蓄える/店 only there, and not finding the answer on the interwebs, here are my 公式文書,認めるs about their 判型. Please do let me know if you have more (警察などへの)密告,告訴(状) about this とじ込み/提出する 判型.
The 公式文書,認めるs are for the Mac OS, Picasa 見解/翻訳/版 3.9.0.522.
The database とじ込み/提出するs are 設立する under
$HOME/Library/使用/適用 Support/Google/Picasa3/db3
on the Macs, and there are 同等(の) 場所s on other 壇・綱領・公約s. Under here are a 始める,決める of とじ込み/提出するs with a .pmp suffix, which are the database とじ込み/提出するs.
[BTW: The とじ込み/提出するs with the .db suffix just 持つ/拘留する thumbnails of さまざまな groups of images. They are in the 基準 windows thumbs.db 判型, and here's a link that has more useful (警察などへの)密告,告訴(状) about this 判型.]
Each .pmp とじ込み/提出する 代表するs a field in a (米)棚上げする/(英)提議する, and the (米)棚上げする/(英)提議する is identified by a ありふれた prefix as follows:
$ ls -1 catdata_* catdata_0 catdata_catpri.pmp catdata_name.pmp catdata_state.pmp
The とじ込み/提出する with the _0 suffix is a marker とじ込み/提出する to identify the (米)棚上げする/(英)提議する, and each .pmp とじ込み/提出する 株ing that prefix is a field for that (米)棚上げする/(英)提議する. For instance, catdata_state.pmp 含む/封じ込めるs 記録,記録的な/記録するs for the field 明言する/公表する in the (米)棚上げする/(英)提議する catdata, and so 前へ/外へ.
All とじ込み/提出するs start with the four 魔法 bytes: 0xcd 0xcc 0xcc 0x3f
The marker とじ込み/提出するs (ie, とじ込み/提出するs that end in _0) only 含む/封じ込める the 魔法 bytes.
The pmp とじ込み/提出する is in little-endian 判型 rather than the usual 網状組織 byte/big-endian 判型.
There are several areas where I just see constants -- I don't know the 目的 of these and I'll 名簿(に載せる)/表(にあげる) them out. Please 公式文書,認める: all values are 現在のd in little-endian 判型, so if you hex-捨てる a とじ込み/提出する, you should see the bytes 逆転するd.
Header
4bytes: 魔法: 0x3fcccccd
2bytes: field-type: unsigned short.
2bytes: 0x1332 -- constant.
4bytes: 0x00000002 -- constant.
2bytes: field-type: unsigned short -- 同一の with field-type above.
2bytes: 0x1332 -- constant.
4bytes: number-of-入ること/参加(者)s: unsigned int.
に引き続いて the header are "number-of-入ること/参加(者)s" 記録,記録的な/記録するs, whose 判型 depends on the field-type. The field-type values are:
0x0: null-終結させるd strings. I 港/避難所't 実験(する)d how (if at all) it can 蓄える/店 unicode.
0x1: unsigned integers, 4 bytes.
0x2: dates, 8 bytes as a 二塁打. The date is 代表するd in Microsoft's Variant Time 判型. The 8 bytes are a 二塁打, and the value is the number of days from midnight Dec 30, 1899. わずかの values are fractions of a day, so for instance, 3.25 代表するs 6:00 A.M. on January 2, 1900. While 消極的な values are 合法的 in the Microsoft 判型 and 示すs days 事前の to Dec 30, 1899, the Picasa 使用者 interface 現在/一般に 妨げるs dates older than Dec 31, 1903 from 存在 used.
0x3: byte field, 1 unsigned byte.
0x4: unsigned long, 8bytes.
0x5: unsigned short, 2bytes.
0x6: null-終結させるd string. (かもしれない csv strings?)
0x7: unsigned int, 4 bytes.
The (独立の)存在s are 索引d by their 記録,記録的な/記録する number in each とじ込み/提出する. Ie, fetching the 7273'rd 記録,記録的な/記録する in all とじ込み/提出するs 指名するd imagedata_*pmp gives (警察などへの)密告,告訴(状) about the fields for (独立の)存在 #7273 in the imagedata (米)棚上げする/(英)提議する.
You might 推定する/予想する every "field とじ込み/提出する" for a given (米)棚上げする/(英)提議する to 含む/封じ込める the same number of 記録,記録的な/記録するs, but this is not always the 事例/患者. I 推定する/予想する the underlying library returns the 同等(の) of undefined when fetching fields for a 記録,記録的な/記録する beyond the "end" of any given field とじ込み/提出する.
Finally, a small java program to 捨てる out whatever (警察などへの)密告,告訴(状) I've gathered thus far. 収集する, and run against a 始める,決める of .pmp とじ込み/提出するs.
Here is a 見本 run.
$ javac -g -d . Read.java $ java Read "$HOME/Library/使用/適用 Support/Google/Picasa3/db3/catdata_name.pmp" /使用者s/kbs/Library/使用/適用 Support/Google/Picasa3/db3/catdata_name.pmp:type=0 nentries: 10 [0] Labels [1] 事業/計画(する)s (内部の) [2] Folders on Disk [3] iPhoto Library [4] Web Albums [5] Web 運動 [6] 輸出(する)d Pictures [7] Other Stuff [8] Hidden Folders [9] People
And here's the code.
輸入する java.io.*; 輸入する java.util.*; public class Read { public static 無効の main(String args[]) throws Exception { for (int i=0;i <args.length; i++) { doit(args[i]); } } 私的な final static 無効の doit(String p) throws Exception { DataInputStream din = new DataInputStream (new BufferedInputStream (new FileInputStream(p))); 捨てる(din, p); din.の近くに(); } 私的な final static 無効の 捨てる(DataInputStream din, String path) throws Exception { // header long 魔法 = readUnsignedInt(din); if (魔法 != 0x3fcccccd) { throw new IOException("Failed magic1 "+Long.toString(魔法,16)); } int type = readUnsignedShort(din); System.out.println(path+":type="+Integer.toString(type, 16)); if ((魔法=readUnsignedShort(din)) != 0x1332) { throw new IOException("Failed magic2 "+Long.toString(魔法,16)); } if ((魔法=readUnsignedInt(din)) != 0x2) { throw new IOException("Failed magic3 "+Long.toString(魔法,16)); } if ((魔法=readUnsignedShort(din)) != type) { throw new IOException("Failed repeat type "+ Long.toString(魔法,16)); } if ((魔法=readUnsignedShort(din)) != 0x1332) { throw new IOException("Failed magic4 "+Long.toString(魔法,16)); } long v = readUnsignedInt(din); System.out.println("nentries: "+v); // 記録,記録的な/記録するs. if (type == 0) { dumpStringField(din,v); } else if (type == 0x1) { dump4byteField(din,v); } else if (type == 0x2) { dumpDateField(din,v); } else if (type == 0x3) { dumpByteField(din, v); } else if (type == 0x4) { dump8byteField(din, v); } else if (type == 0x5) { dump2byteField(din,v); } else if (type == 0x6) { dumpStringField(din,v); } else if (type == 0x7) { dump4byteField(din,v); } else { throw new IOException("Unknown type: "+Integer.toString(type,16)); } } 私的な final static 無効の dumpStringField(DataInputStream din, long ne) throws IOException { for (long i=0; i<ne; i++) { String v = getString(din); System.out.println("["+i+"] "+v); } } 私的な final static 無効の dumpByteField(DataInputStream din, long ne) throws IOException { for (long i=0; i<ne; i++) { int v = din.readUnsignedByte(); System.out.println("["+i+"] "+v); } } 私的な final static 無効の dump2byteField(DataInputStream din, long ne) throws IOException { for (long idx=0; idx<ne; idx++) { int v = readUnsignedShort(din); System.out.println("["+idx+"] "+v); } } 私的な final static 無効の dump4byteField(DataInputStream din, long ne) throws IOException { for (long idx=0; idx<ne; idx++) { long v = readUnsignedInt(din); System.out.println("["+idx+"] "+v); } } 私的な final static 無効の dump8byteField(DataInputStream din, long ne) throws IOException { int[] bytes = new int[8]; for (long idx=0;idx<ne; idx++) { for (int i=0; i<8; i++) { bytes[i] = din.readUnsignedByte(); } System.out.print("["+idx+"] "); for (int i=7; i>=0; i--) { String x = Integer.toString(bytes[i],16); if (x.length() == 1) { System.out.print("0"); } System.out.print(x); } System.out.println(); } } 私的な final static 無効の dumpDateField(DataInputStream din, long ne) throws IOException { int[] bytes = new int[8]; for (long idx=0;idx<ne; idx++) { long ld = 0; for (int i=0; i<8; i++) { bytes[i] = din.readUnsignedByte(); long tmp = bytes[i]; tmp <<= (8*i); ld += tmp; } System.out.print("["+idx+"] "); for (int i=7; i>=0; i--) { String x = Integer.toString(bytes[i],16); if (x.length() == 1) { //System.out.print("0"); } //System.out.print(x); } //System.out.print(" "); 二塁打 d = 二塁打.longBitsToDouble(ld); //System.out.print(d); //System.out.print(" "); // days past unix 時代. d -= 25569d; long ut = Math.一連の会議、交渉/完成する(d*86400l*1000l); System.out.println(new Date(ut)); } } 私的な final static String getString(DataInputStream din) throws IOException { StringBuffer sb = new StringBuffer(); int c; while((c = din.read()) != 0) { sb.append((char)c); } return sb.toString(); } 私的な final static int readUnsignedShort(DataInputStream din) throws IOException { int ch1 = din.read(); int ch2 = din.read(); if ((ch1 | ch2) < 0) throw new EOFException(); return ((ch2<<8) + ch1<<0); } 私的な final static long readUnsignedInt(DataInputStream din) throws IOException { int ch1 = din.read(); int ch2 = din.read(); int ch3 = din.read(); int ch4 = din.read(); if ((ch1 | ch2 | ch3 | ch4) < 0) throw new EOFException(); long ret = (((long)ch4)<<24) + (((long)ch3)<<16) + (((long)ch2)<<8) + (((long)ch1)<<0); return ret; } }
Recently, I read a few stories from Andy Hertzfeld's 場所/位置, which are terrific stories about the 開発 of the Macintosh. As I read it, I started to 見解(をとる) a few ビデオs, some podcasts etc that were connected to the stories. Wouldn't it be nice to bundle all of this into a 選び出す/独身 epub, so it's all in one place?
井戸/弁護士席, you can always do that by editing an epub in Sigil or other 道具s. However, I've 設立する it convenient to take out some of the tedium through a couple of small scripts that bundle everything together from txt とじ込み/提出するs. The scripts do very little -- it's best ふさわしい when you pretty much have straight text, and perhaps a 始める,決める of マスコミ that you want to 挿入する into the text at appropriate places.
It also assumes that you're reasonably comfortable with the 命令(する)-line, and can を取り引きする a couple of thrown-together scripts. [Fortunately, they are small so I hope they will at least be a starting point.]
Here's how I 組み立てる/集結する my epub. First, I create a directory that will 持つ/拘留する all the content I want in the epub.
$ mkdir macintosh_stories
Next, I create text とじ込み/提出するs -- one for each 一時期/支部, to 含む/封じ込める the text. So (say) I create one とじ込み/提出する called macintosh_stories/01.txt that 含む/封じ込めるs this:
#Alice Even though Bruce Daniels was the 経営者/支配人 of the Lisa ソフトウェア team, he was very supportive of the Mac 事業/計画(する). He had written the mouse-based text editor that we were using on the Lisa to 令状 all of our code, and he even transferred to the Mac team as a mere programmer ...
You can 示す (2 levels) of headlines with the # character. One # is the largest headline, and ## gives a subheading. There isn't anything much else it does, though you can notate italics by _italics_.
This is 十分な to create the first epub -- and you run it as
$ gen.sh macintosh_stories "Macintosh Stories" "Andy Hertzfeld"
Let it do its thing, and your epub will be left under macintosh_stories/out.epub
To create more 一時期/支部s -- keep 追加するing more txt とじ込み/提出するs. The sequence of 一時期/支部s are 厳密に 決定するd by the filename of the text とじ込み/提出する -- so I usually create text とじ込み/提出するs like 01_xxx.txt, 02_xxx.txt and so on.
To 追加する a cover and other マスコミ, first create a directory called マスコミ
$ mkdir macintosh_stories/マスコミ
If you then create a とじ込み/提出する called macintosh_stories/マスコミ/cover.jpg, the scripts will 追加する the cover to the epub.
To 追加する images into the とじ込み/提出する, first place them into the マスコミ directory. For example, in the above story -- there's an image of the 一括ing, which I've saved as macintosh_stories/マスコミ/alice_packaging.jpg
From within 01.txt, I 言及する to it as:
... disk was enclosed in a small cardboard box designed to look like a finely printed, old fashioned 調書をとる/予約する, 完全にする with an (a)手の込んだ/(v)詳述する woodcut on the cover, that 含む/封じ込めるd a hidden Dead Kennedy's logo, in 尊敬の印 to one of Capp's favorite 禁止(する)d. [> マスコミ/alice_packaging.jpg <] Since Alice didn't (問題を)取り上げる the whole disk, Capps 含むing a few other goodies with it, 含むing a font and "Amazing", a fascinating maze ...
Now I was curious how the game itself looked. So I downloaded a youtube ビデオ. Please 公式文書,認める that it must be an mp4 とじ込み/提出する. This too, I put into the マスコミ directory, and embed it the same way in my txt とじ込み/提出する.
... Since Alice didn't (問題を)取り上げる the whole disk, Capps 含むing a few other goodies with it, 含むing a font and "Amazing", a fascinating maze 生成するing program that he wrote. [> マスコミ/alice.mp4 <] When I saw the 完全にするd 一括ing, I was surprised to discover that ...
You can also embed 音声部の in the same way 同様に, interviews, podcasts, or 音声部の 見解/翻訳/版s all work 井戸/弁護士席. But please 公式文書,認める that it must be in the .m4a 判型. This is the only 判型 that 作品 on the Color Nook. I use ffmpeg to 変える an mp3s into m4a, and that seems to do the trick.
Here is a zip とじ込み/提出する of the scripts and just for kicks, the 見本 epub.
Labels: epub
I did some poking around how Flipboard lays out content, and here are my 観察s.
- The portrait and landscape layouts are 同一の -- the 内部の content of an article reflows when the orientation flips, but the 全体にわたる article layout remains 正確に/まさに the same. So in the 残り/休憩(する) of this, I'll only 言及する to the portrait orientation.
-
The layout 過程 is almost certainly "recursive rectangle cutting" rather than packing a 始める,決める of rectangles. In other words, start with a big rectangle, then make a 完全にする 削減(する) horizontally or vertically, and recursively 削減(する) each smaller rectangle. The tell-tale 調印する of such a 過程 is you never see a layout like on the 権利. Ie, there is always at least one 削減(する) that goes from one 味方する of the rectangle 存在 削減(する) to its opposite 味方する, and so on, recursively.
- You can therefore associate a "削減(する) tree" with any layout, where each node 代表するs a 削減(する) -- 水平の or vertical -- and is labelled with the 場所 of the 削減(する) position(s). The choice of cutting positions are heuristics, and Flipboard's approach seems to be to 選ぶ small integer 割合s of the parent.
If you ignore "二重の" 割合s (eg: 1/3 and 2/3 would be 二重のs of each other) Flipboard 選ぶs 削減(する) positions that are 位置を示すd 1/2, 1/3, 1/4, 2/5, and 3/8 of the parent. Which one to 選ぶ at any step is かもしれない a combination of how large the content is, and some randomization.


That said, there's more than one way to approach the problem, and it can be useful to understand the underlying design goals, rather than 見解(をとる) it as 純粋に a problem of "packing" content. (Example 経由で Gridness)
Many modern designers and magazines layout content within a grid, and you can see an example about the underlying ideas here.
In essense, the page is divided into a grid that 似ているs a checkerboard, and each element of 利益/興味 is reflowed into a contiguous subset of 封鎖するs. The 意図 is to 設立する a visual structure to the content, which the grid helps to 持続する. Whitespace is often just as important as content, and 結局 each grid 封鎖する ends up 存在 either used as content or whitespace. (This is often overlooked in many "packing" approaches to layout, though you may be able to 会社にする/組み込む it as "blank content" to be packed along with everything else.)
存在 aware of an underlying grid can 潜在的に 簡単にする algorithms 同様に as 許すing 内部の content to settle along grid lines. A 公正に/かなり basic approach can still use rectangle cutting, but just select one of the grid lines at each 削減(する) (rather than 割合s of the parent, as Flipboard does.) It can also 許す you on occasion, to create 非,不,無-rectangular areas, 特に with reflowable content. For example, you may be able to subtract a 始める,決める of 封鎖するs out of an enclosing rectangular 始める,決める, to 追加する 関係のある content, and so on. You can see it in the pullquotes in this example from the Behance 網状組織.
I spent a couple of 週末s playing with a simple 計画/陰謀-ish interpreter in Flex, you can see the results below.
Click the 計画/陰謀! button to open up a console, click on the Eval button in the console to do some simple calls into flex.
It's not really 計画/陰謀, and it's pretty 天然のまま -- but it has enough (lambdas and static 範囲d ariables) that you can do a reasonable 量 of flex 原型ing, or "within-app-拡張s" 公正に/かなり comfortably. Always assuming that you find 計画/陰謀 comfortable, but that's another story...
I've used a subset of Javadot notation to call 反対するs and methods within flex. So for instance, if you want to reposition the button, you can 簡単に eval
(.y$ my-button 200)
which is the notation to 始める,決める a 所有物/資産/財産 on an instance. To call a method, you 供給する the method followed by the 反対する. For example, the underlying canvas in the app is 言及/関連d by the variable workspace, so to 追加する a new child you eval
(.addChild workspace (mx.支配(する)/統制するs.ColorPicker.))
which also shows how you can make a new instance of a class (by appending a "." to the class 指名する.)
You can also create 機能(する)/行事s that can be 登録(する)d within Flex. For example, you can 登録(する) a listener for click events on the button with
(.addEventListener my-button "click" (lambda (event) (mx.支配(する)/統制するs.警報.show event)))
and now clicking the button should put up an 警報.
You can also 負担 存在するing 計画/陰謀 code by using the .loadFile method in the interpreter. And As an extra 特別手当, I also wrote a little 視察官 (in Flex, not 計画/陰謀) that 診察するs variables, 所有物/資産/財産s and methods in any 反対する. To see both these things, eval(.loadFile evaluator "http://schemeflex.googlecode.com/svn/trunk/src/計画/陰謀/util.scm")
followed by(検査/視察する my-button)
and you should see (rather slowly) an 視察官 and you can play with the さまざまな settings in the button, click on 言及/関連d 反対するs to open new 視察官s, etc. The 視察官 is やめる inefficiently 器具/実施するd, but 適する to 調査する some of the hidden nooks in Flex and see what they do.
A couple of 魔法 variables: workspace is the canvas where the evaluator runs, and evaluator is a 言及/関連 to the 現在/一般に running interpreter 反対する.
Get the source code here Enjoy!