2023年3月20日 星期一

Kotlin Multiplatform

 When I was in the Neo bank. What the mobile architecture I designed and participated also used shared lib layer to apply clean architecture. What we chose to is using kmm - kotlin multiplatform. 

 https://docs.google.com/presentation/d/e/2PACX-1vTsuwwY8Q2dFTE39jD_jCKACMNNpwyqIn61FwiwjAyEBFMlB8NTYIFQ2V1hL4AfXrbfbRBj27Xa_2-K/pub?start=false&loop=false&delayms=3000


As the result was great back then. There are advantages as following

1. the model serialization is same to all platforms. There were rarely defect only happened to one platform

2. The business logic also can be written in the same layer and applied the use cases.

3. Reduced the effort of debugging process. 


Cons are
1. Every time you modified the shared lib. Your main app also need to update to the lib version code.

2. Big change to the shared lib will also impact all platforms if there's no dependencies inversion or interface segregation on this shared lib. 

I would recommend enterprise project using this architecture since pros > cons as my prospective. Especially find the defect through the large scale systems wasn't that easy.

2021年10月26日 星期二

Frida, hook your android application.

 initialization

1. https://github.com/sensepost/objection install objection as the instruction.

2. patch your apk https://github.com/sensepost/objection/wiki/Patching-Android-Applications

3. adb install -t your apk

4. once you launch your app, the app will be white black and waiting for the `objection explore`

5. memory search xxxx


hook

https://medium.com/csg-govtech/how-you-should-secure-your-androids-app-biometric-authentication-10d9231215e4


useful command lines

  577  frida -U -f com.dbs.mbanking.tw -l android-keystore-audit/frida-scripts/fingerprint-bypass.js 

  578  adb shell pm path `package name`

  579  adb pull /data/app/~~6ohSJjUoGZa22X_AhKiKxA==/com.dbs.mbanking.tw-W2_ViQB4emAQjMRXvDkcSg==/base.apk

  580  objection patchapk --source base.apk 

  581  adb install -t base.objection.apk 


There's easier way to detect password

1. dump hprof file from your runtime app.

2. grep "keyword" xxx.hprof

you can dump hprof file via Profiler or using some commend line

https://github.com/hydrated/Android-Utilities/blob/master/DumpMemory/log_memory.sh

https://github.com/OWASP/owasp-mstg/blob/master/Document/0x05d-Testing-Data-Storage.md#overview-2

Starting 2021, mstg will scan sensitive data in memory. Either in Jvm or ios environment. The immutable String will be in the heap space and hard to be collected. So what you can do is don't use string to transit it until e2ee. In that case, you need to salting your password in the String instance or passing it always using char array. Here is an example of salting.

https://github.com/hydrated/Android-Utilities/blob/master/ReverseSpannableStringBuilder.java

Dbs MFE

 https://medium.com/dbs-tech-blog/micro-front-ends-the-conflict-terminator-5a447f0428a6

and yes, I am the co-author

2021年9月18日 星期六

Android memory accessibility

Android這十年來跟ios比一直有痛點,就是感覺比較容易crash,除了天生上java遇到null就是crash之外,另一個就是早期Android在設計的時候,針對記憶體存取,設計了一個看起來能讓許多app能跑在android上一個不錯的機制,可惜的是眾多developers並沒有遵守這個原則去開發。就是Activity state。

https://developer.android.com/topic/libraries/architecture/saving-states
https://developer.android.com/guide/components/activities/activity-lifecycle#asem



這個設計的用意是,當app在前景時,記憶體隨你用,當然你用到炸你app一樣crash。只是當你app進背景後,android會幫你保持activity/fragment stack而保持gc的權力,這樣下一個被launch的app依舊有full system的memory可以使用。當user又回到你的app時,整個stack/state馬上被resume。快速的回到你當初app的狀態。


用兩個平台對於進背景的app的不同來解釋這個機制。

ios,進背景後,application被系統回收,下次resume app後就從頭重新launch,一切都是fresh new。

android,進背景後,允許你存資料進state,依照系統對記憶體的需求回收你的activity and fragment,甚至singleton,回來以後只要application還沒被回收,你會被系統重新re-create last state,也就是說你當下在哪一頁系統就幫你把activity, fragment stack全都延續並且顯示最後一頁,如果你用來render UI的data不存在並且你沒有有效的fetch方式,app自然直接crash。所以後期android在某一版後終於也受不了,在系統層直接做了一個保護,當你的app crash後幫你重新launch app,保護了開發者們沒注意到的東西,但還是會有系統上的風險。




說的容易做的難,我本身已經有10+ app開發經驗了,我只能說目前只做了兩個uncrashable android app,剛好也只有這兩個android從0->1開始設計,第一個是幫axiata home pow做的android TV launcher,看起來應該是有個幾百萬使用者以及還在線,但是那個只是硬需。不會crash的方式就是資料全落地,所有的epg, channel, program全都在local資料庫,所有的UI rendering都depend on those data。每一頁都只要簡單的保持uid就能從資料庫中重新fetch當時最後的data,一切都很簡單,一切都很完美,這也是android後期一直在推崇你資料就落地,幾乎大部分的大app也都照做的。

第二個app,很遺憾,那個是neo bank的app,資料落地幾乎是死罪🤣。我也沒想過工作十年後又要回到十年前做的法,資料都得保持在記憶體,哈哈哈。不能用就不能用,那就是回到十年前android一開始的設計,把資料的保持用Saved instance state來作。不得不說真的很痛苦,每一頁activity,每一頁fragment都要寫functions,protocol來連接那個state,非常的unpractical。

好險當時我有七~八個志同道合的隊員,對於這個記憶體的保護很有堅持跟理想,在不斷的sprint遞疊跟演進後,我們發現了viewmodel有個stateviewmodel
https://developer.android.com/topic/libraries/architecture/viewmodel-savedstate

在此應該要先前情提要一下,當時那app是2019年尾開始開發,整個architecture都是用最新的jetpack mvvm,以至於後來這個stateviewmodel出來以後,我們當時每一頁activity,fragment用來保持state的那些functions,protocol全都不用寫了!stateviewmodel他就有能力保持他的記憶體配合Saved instance state,如此一來,拿爛手機的users們,不會轉帳到一半切換到其他app查轉出帳號,回來這個neo bank app後,整個記憶體被回收剛剛輸入過的資料全部規零,甚至crash得重新登入🤣

這就是整個android記憶體的故事了,十年來有著這些的故事,以及我知道目前市面上應該還是大部分的android dev都還不知道這些事情,或者知道了也不知道怎樣的architecture才能有效的解決android記憶體使用的問題。只能怪當初設計android的人太聰明(?)了,導致這十年來沒有太多android dev follow當時他們的想法。

很遺憾這篇不會有implementation detail。

甚至更精采的,可以參考sharedviewmodel,如果你有傳值的煩惱。


Ref.

https://developer.android.com/topic/libraries/architecture/viewmodel-savedstate

https://developer.android.com/topic/libraries/architecture/saving-states

https://developer.android.com/guide/components/activities/activity-lifecycle#asem

2021年1月20日 星期三

Android multiple modular developing

      My previous app was an android banking app. It is a neo bank. And it got tons of features relative to different financial products. I establish the basic architecture with mvvm + navigation component . One activity with fragments. Everything works perfectly thanks for jetpack. Easy to refactor and developing based on this architecture. Also a lot of interesting details with di, motionlayout, paging, navigation, coroutine, viewmodel. Again still work perfectly. 

     What makes this app more special is the multiple modular developing, we manage 8 engineers at the whole time, 10+ with out-source agencies developers. How do we avoid code conflicts and make codes more clear. I introduce the concept of the multiple modular developing. There are some modern software companies are already using them and share their experience on the internet. (#1)

     Basically design your app separated by features. and I believe those features are in 
distinguish UI layer in your app (If you are saying it's not, I serious doubt the UX and everything on that app). For example your app is a tab-layout, each tab represented a feature. Then you can separate your app into different features. The whole launch app was in the App module and each tabs' codes are lying each `Feature #` modules. And move the whole common codes(string res, network, database, utilities..etc) into the bottom layers of modules. So the basic diagram would looks like this.

     



 
     Furthermore as your app growth, the diagram can be designed more and more complicated like this






    There are pros and one con of this developing methods. 

  1. Good for you team members to know where they should focus on and totally ignore other teams simultaneously developing codes.
  2. All the features are reusable, disposable based on requirement. As long as you manager your project dependencies clean and compact. 
  3. Google play now support `Dynamic Feature` based on this architecture, maybe someday your pm has crazy thoughts on this feature.
  4. Test would be easier and single launch is possible if you design and mock it right.
     The con is. As the modules growth, the required build time of android studio takes longer. And there are some methods to avoid this.
  1. Use di to inject blank UI so other modules won't be involved during builds.
  2. Use local maven to store the local build of each modules. They won't be rebuild anymore. Only focus and rebuild on the module you work on.(#2)
     Here I shared a simple app demonstrate such a concept.
https://github.com/hydrated/CurrencySample


Appendix

#2



2020年6月10日 星期三

ViewParentCompat of NestedScrollingParent2 NestedScrollingChild2

日前在做MotionLayout發現example可以輕易的將scrolling event往外傳,然後MotionLayout接收後就自動Parse完畢trigger transition。覺得神奇。

https://github.com/android/views-widgets-samples/blob/master/ConstraintLayoutExamples/motionlayout/src/main/java/com/google/androidstudio/motionlayoutexample/utils/TouchFrameLayout.java


基本上Android開始把可以Scrolling的View用一個叫ViewParentCompat的東西作協調器,然後可以透過viewparentcompat中定義的parent child來互相傳達scrolling event

public class RecyclerView extends ViewGroup implements ScrollingView
        ,NestedScrollingChild2, NestedScrollingChild3 {

從RecyclerView中,是個child的角色,然後自己scrolling時,往外丟出scrolling event

RecyclerView.class
@Overridepublic boolean dispatchNestedPreScroll(int dx, int dy, 
        int[] consumed, int[] offsetInWindow, int type) {

    return getScrollingChildHelper().dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow,            type);}

ViewParentCompat如果是個Parent的角色,自然就會接收到scrolling event

ViewParentCompat.class
if (parent instanceof NestedScrollingParent2) {
    // First try the NestedScrollingParent2 API    
    ((NestedScrollingParent2) parent).onNestedPreScroll(target, dx,
         dy, consumed, type);}


那麼Parent就會收到callback onNestedPreScroll(xxxx
@Overridepublic void onNestedPreScroll(@NonNull View target, int dx,
        int dy, @NonNull int[] consumed, int type) {
    getMotionLayout().onNestedPreScroll(target, dx, dy, 
        consumed, type);}

接著把所有callback event導入MotionLayout,看起來就可以吳鳳接軌。


Beautiful, isn't it.








2019年1月22日 星期二

Slow firebase querying

Regarding your follow-up question, I would like to add more context about load spike.
The percentage shown roughly represents the max I/O operation per second that your database can handle, with the caveat that it also depends on the actual size of the operation events to what sort of throughput you get. During this peak, some of your operations will not be executed on the time you expect them to trigger. In other words, when it reaches 100%, your database will start to queue up additional requests in a backlog until they can be executed. This surfaces as latency and in severe cases (if you stay at 100% for an extended period of time) the DB can even appear unresponsive (although ultimately the requests get served, just very slowly). The greater the load percentage your project is incurring, the greater the latency you will be experiencing.

These load peaks usually caused by the following (which also applies to your database operations inside Cloud Functions):
  • Large individual reads, typically a value listen high up in the tree
  • Accessing specific node on a huge collection
  • Queries against large collections
  • Too frequent writes
  • Deleting or exporting large data
If I may, you could check the following suggestions that may help:
  • Archive or delete nodes in any lists that contain more than 100k items (or items that are queried hundreds of times per second with more than 10k nodes). This is true when querying over huge collections even if the limit is set to minimum, e.g. using limitToFirst(10). Firebase Database needs to first traverse all the data and build index in memory and only then fetch the results.
  • Ensure queries are efficient and only retrieving what people can consume (i.e. fetch 20 records initially and get more as needed, don't try to preload 200)
  • If you have a backup process or server script downloading large chunks of your DB daily/hourly/etc, switch to our automated backups and use that for analysis/backups/et al
  • Throttle the number of bytes being deleted at once by navigating deeper to your nodes, and delete it in chunks to avoid unresponsiveness.
  • For write operations:
  • If these are coming from many clients, we would suggest to randomize the time of operation.
  • If these are coming from your backend, we would advise to smear these requests over longer period of time.

2018年12月23日 星期日

Fragment消失時

1. 使用的viewpager or something 使用錯誤的getFragmentManager() or getChildFragmentManager()

2. 該頁Fragment layout可能有異常,嘗試使用ViewGroup包妥善

2018年12月4日 星期二

Android Focus一些概念

android提供了一些焦点相关的属性,在现有的框架层下通过设置View的属性来获得焦点:
  • android:focusable:设置一个控件能否获得焦点
  • android:nextFocusDown:(当按下键时)下一个获得焦点的控件
  • android:nextFocusDown:(当按下键时)下一个获得焦点的控件
  • android:nextFocusLeft:(当按下键时)下一个获得焦点的控件
  • android:nextFocusRight:(当按下键时)下一个获得焦点的控 **注意:**如果按下某个方向键时,想让焦点停留在自身,可以使用android:nextFocusRight:"@null"或者android:nextFocusRight:"@id/自身id"
這只有在對view是有效的,對viewgroup等類是無效的 原因在於

private View findNextUserSpecifiedFocus(ViewGroup root, View focused, int direction) {
// check for user specified next focus
View userSetNextFocus = focused.findUserSetNextFocus(root, direction);
View cycleCheck = userSetNextFocus;
boolean cycleStep = true; // we want the first toggle to yield false
while (userSetNextFocus != null) {
if (userSetNextFocus.isFocusable()
&& userSetNextFocus.getVisibility() == View.VISIBLE
&& (!userSetNextFocus.isInTouchMode()
|| userSetNextFocus.isFocusableInTouchMode())) {
return userSetNextFocus;
}
userSetNextFocus = userSetNextFocus.findUserSetNextFocus(root, direction);
if (cycleStep = !cycleStep) {
cycleCheck = cycleCheck.findUserSetNextFocus(root, direction);
if (cycleCheck == userSetNextFocus) {
// found a cycle, user-specified focus forms a loop and none of the views
// are currently focusable.
break;
}
}
}
return null;
} 他在搜尋usersetnextfocus是用該focus view去找的 viewgroup自然對於該function就無作用。


一般來說focusSearch是focus主要的來源,如果都不override他就是一路找到decorView?或許吧,使用FocusSearchInstance來找,那個Util會把子Views focusable的全找出來作排序用rect的方式去找最靠近的focus, 除非你有指定id. 原生的四大主件LinearLayout RelativeLayout ...全都沒有複寫這method. 只有Leanback library的元件才有,所以客製化的頁面UI幾乎全都要自己override這method來控制focus。至少我認為是這樣。

@Overridepublic View focusSearch(View focused, int direction) {
    View returnView = null;
    ViewParent trace = focused.getParent();
    while (trace != null) {
        if (trace instanceof SlidingTabLayout) {
            returnView = FocusFinder.getInstance().findNextFocus((ViewGroup) trace, focused, direction);            break;        } else if (trace instanceof ViewPager) {
            returnView = FocusFinder.getInstance().findNextFocus((ViewGroup) trace, focused, direction);            break;        }
        trace = trace.getParent();    }

    if (trace instanceof SlidingTabLayout && direction == View.FOCUS_DOWN) {
        return pager;    }

    if (trace instanceof ViewPager && direction == View.FOCUS_UP) {
        return tabs.getSelectedView();    }

    return returnView == null ? super.focusSearch(focused, direction) : returnView;}

以上是一些客製化的範例。

2018年6月22日 星期五

The concept of using Google api using javascript.

There are 2 stand ways to call the google-apis,


  1. Use oauth client
  2. Use oauth server-to server applications


Concept.
For 1. The oauth flows are pretty standard for any oauth2 applications.
First you ask permission to the user that granted you to access the calendar.
Then there is a access_token and a refresh_token.
So that you can access user’s calendar via that access_token.

For 2. This is under the G-suite admin authority to manipulating G-suite users data
using a delegated account.
Create a service account and let that account has access to the data you want to manipulate.



The flow charts are
For 1.
Get token using oauth2  => Initialize an google-apis client with token and scopes =>
Use client to send the api

For 2.
Create Service Account Client in the google console =>
Create Security in G-suite app using G-suite admin account =>
JWT can make request to access G-suite users


There are 3  approach ways.


  1. Using googleapis in could-functions in node.js.


Implementation details.


For 2.

  1. To create a service account as a delegate.
  2. Use the credential to authorize the JWTclient.
  3. Add that service account to the calendar you want to manipulate.
  4. Manipulate calendar using AWT like google apis.


Reference of creating google calendar, setup gcp project.

2017年11月14日 星期二

Android Interacting with Other Apps using Messenger or AIDL

在Linux階層上的IPC,Android用了iBinder來包裝之間溝通的管道。iBinder有兩種形式可以傳遞資料。預備知識是使用android的service並且exported,可使其他application能夠喚起該service。


第一種是用Messenger封裝起來,使用Message來傳遞,開啟Service後,建立一個Messenger instance,使用Messenger的getBinder提出傳給service,而client也是透過取得的binder來創建Messenger來溝通。並且可以使用Messenger.replyTo來建立一個收訊息的接口來跟遠端service溝通。溝通的單位是bundle。

第二種是使用Android Interface Definition Language,簡單的說就是iBinder中定好溝通的規則,也就是functions。在server(service端)建立好iInterface的aidl實作的binder,在service中回傳該binder,然後在client端中也是用aidl檔承接轉型該ibinder至iInterface,雙方就透過iInterface中定義好的functions溝通。





一些概念解釋:


iBinder就是android中建立在ipc上的java層,用來使用ipc來溝通的。

Messenger其實就是封裝iBinder,使用Messenger類來透過ibinder來溝通。

AIDL則是在ibinder層先定義好傳輸資料,接著雙方用該接口來互動。該檔案需在sdk中編譯,並且對應相同的pacakge name,比如說你aidl package name是com.handsome.hydra你就得放在src/main/aidl/com/handsome/hydra/的資料夾下放IInterface.aidl,此後sdk編譯好後你才能使用IInterface.stub方法。

若你有使用ipc shared memory, message queue的經驗,基本上java那層就是封裝過的實作層,讓你用他們定義好的資料傳輸方式來使用ipc就是了。

2017年7月31日 星期一

Build nexus player AOSP

我用Virtualbox裝了Ubuntu14.04來安裝,比較保險,I like to do it in an old fashion way.

基本上全部的步驟都在document裡面了
https://source.android.com/source/requirements

我列出過程中需要注意的地方

  • adb usb driver跟fastboot usb driver是不同的
基本上用sdk的universal usb driver都可以support. 只是兩個是不同的,都得assign進vm
  • fastboot某些權限需要root user. 只能跑在root user裡
build好以後用fastboot燒時指定product directory.
export ANDROID_PRODUCT_OUT="/home/hydrated/Android/out/target/product/fugu"


之後flashall進去以後如果順利 LeanbackLauncher長這樣 (wtf..)

Remote也辦法正常配對,我甚至認為這篇文章可以subtitle: how to pair your nexus remote without native supported.

好險有adb,用盡我對android的了解,終於找到辦法

     1. 首先先用am start AddAccessoryActivity
am start -n com.android.tv.settings/.accessories.AddAccessoryActivity


     2. 正常的remote配對方法,按下back and home,就能配對了。

     3. 找到device後別開心,因為你按不下enter!(wtf again),好險一樣有adb
shell input keyevent 66

..終於配對成功,只花兩天而已。然後software keyboard也沒支援,打wifi password時長這樣

fugu:/ $ input keyevent 46                                                     
fugu:/ $ input keyevent 43                                                     
fugu:/ $ input keyevent 29                                                     
fugu:/ $ input keyevent 41                                                     
fugu:/ $ input keyevent 51                                                     
fugu:/ $ input keyevent 29                                                     
fugu:/ $ input keyevent 42                                                     
fugu:/ $ input keyevent 32                                                     
fugu:/ $ input keyevent 33                                                     
fugu:/ $ input keyevent 46                                                     

fugu:/ $ input keyevent 66

adb shell input text 'password'

另外一些make指令
mmm -j32 packages/apps/TvSettings/QuickSettings/

其他build framework的啦,build kernel的啦都有,只是我的人生不需要去回想那些就是了...






2016年8月2日 星期二

Android Memory Inspection


中文blog翻譯

  • 一些名詞解釋。

    Shallow heap和Retained heap。Shallow heap表示對象本身所占內存大小,一個內存大小100bytes的對象Shallow heap就是100bytes。Retained heap表示通過回收這一個對象總共能回收的內存,比方說一個100bytes的對象還直接或者間接地持有了另外3個100bytes的對象引用,回收這個對象的時候如果另外3個對象沒有其他引用也能被回收掉的時候,Retained heap就是400bytes。


    VSS
     - Virtual Set Size 虛擬耗用內存(包含共享庫占用的內存)
  • RSS - Resident Set Size 實際使用物理內存(包含共享庫占用的內存)
  • PSS - Proportional Set Size 實際使用的物理內存(比例分配共享庫占用的內存)
  • USS - Unique Set Size 進程獨自占用的物理內存(不包含共享庫占用的內存)
一般來說內存占用大小有如下規律:VSS >= RSS >= PSS >= USS
Overview
The aim of this post is to provide information that will assist in interpreting memory reports from various tools so the true memory usage for Linux processes and the system can be determined.
Android has a tool called procrank (/system/xbin/procrank), which lists out the memory usage of Linux processes in order from highest to lowest usage. The sizes reported per process are VSS, RSS, PSS, and USS.
For the sake of simplicity in this description, memory will be expressed in terms of pages, rather than bytes. Linux systems like ours manage memory in 4096 byte pages at the lowest level.
VSS (reported as VSZ from ps) is . This size also includes memory that may not be resident in RAM like mallocs that have been allocated but not written to. VSS is of very little use for determing real memory usage of a process.
RSS is the. RSS can be misleading, because it reports the total all of the shared libraries that the process uses, even though a shared library is only loaded into memory once regardless of how many processes use it. RSS is not an accurate representation of the memory usage for a single process.
PSS , i.e. if three processes all use a shared library that has 30 pages, that library will only contribute 10 pages to the PSS that is reported for each of the three processes. PSS is a very useful number because when the PSS for all processes in the system are summed together, that is a good representation for the total memory usage in the system. When a process is killed, the shared libraries that contributed to its PSS will be proportionally distributed to the PSS totals for the remaining processes still using that library. In this way PSS can be slightly misleading, because when a process is killed, PSS does not accurately represent the memory returned to the overall system.
USS is . USS is an extremely useful number because it indicates the true incremental cost of running a particular process. When a process is killed, the USS is the total memory that is actually returned to the system. USS is the best number to watch when initially suspicious of memory leaks in a process.
For systems that have Python available, there is also a nice tool called smem that will report memory statistics including all of these categories.

2016年7月28日 星期四

swift 語言一些心得

這其實是1.0的心得,現在已經出到3.0了 其實這份也不用看細節了,直接看一些特別的語法特性就好了。

https://drive.google.com/file/d/0B6BL3rjr8LORb3ptY2g0a3ZpQVE/view?usp=sharing

2016年6月12日 星期日

CAMediaTimingFunction

Basically I want to make a dots animation view like this.


So I look into the CAMediaTimingFunction and it's a bezier function.

https://en.wikipedia.org/wiki/B%C3%A9zier_curve

There is a website that you can view your bezier curve in CAMediaTimingFunction

http://netcetera.org/camtf-playground.html

So I just create 3 dots with different CAMediaTimingFunction that changes alpha like this.




But eventually it don't really have to use CAMediaTimingFunction to approach this. Just create animation groups will be more precise.


2016年6月2日 星期四

iCloud Key-value storage development


The goal is store your app preference to iCloud for backup. That only involved to the key-value storage of iCould.

To use iCloud in an app. You need to configure the in the apple develop like this





Where you can store different type of data like .


Which only key-value storage don't need a container, others are needed a container to store the data in. Look into the apple document would be more helpful.

iCloudDesignGuide

Once you setup the iCould, you could be able to access NSUbiquitous store


    [NSUbiquitousKeyValueStore defaultStore]

and store your key-value data like UserDefault to the ubiquitouskeyvaluestore
  
    [[NSUbiquitousKeyValueStore defaultStore] setObject:obj forKey:key];
    [[NSUbiquitousKeyValueStore defaultStore] synchronize];


and you register a notification of the key
NSUbiquitousKeyValueStoreDidChangeExternallyNotification


And analysis the notification you will get the key-value backup from your iCould account.

    NSDictionary *userInfo = [notificationObject userInfo];
    NSInteger reasonForChangeValue = [[userInfo objectForKey:NSUbiquitousKeyValueStoreChangeReasonKey] integerValue];
        
     if (reasonForChangeValue == NSUbiquitousKeyValueStoreInitialSyncChange)
    {
          // your merge code here
    }


That's it.
You can take look into the
https://github.com/hydrated/iOS-Utilities/tree/master/iCouldSync 


Furthermore you want to make more functionality,
NSUbiquitousKeyValueStoreChangeReasonKey has 4 values can be listening notification. like account change , violate limitation storage.


2015年6月25日 星期四

UITableView and ListView in Polymorphism

UITableView and ListView都有dequeue cell and item的作用,一些心得,

可使用autolayout
_mainTableView.estimatedRowHeight =  44;

_mainTableView.rowHeight = UITableViewAutomaticDimension;

一個tableview的cell會保持自己的instance,你無法取代新的instance該裡面的cell,尤其UIWebView塞進cell更是難以計算高度,用之前千萬三思。


Android ListView更為複雜,一個getView會call三次左右才決定一個item,我設計了一個polymorphism 的架構來控制items


interface {

    public abstract int getType();
    public abstract void setViewHolder(View convertView);
    public abstract void inflateViewHolder(View convertView);
    public abstract View getTemplateView(ViewGroup parent);

}



@Override
public int getItemViewType(int position) {
    return ((interface) getItem(position)).getType();
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {

    final int type = getItemViewType(position);

    if (convertView != null) {
        mList.get(position).inflateViewHolder(convertView);
    } else {
        convertView = mList.get(position).getTemplateView(parent);
        mList.get(position).setViewHolder(convertView);
    }

return convertView;

}

mList必須保持所有子item的data instance,注意只有data不存view,那麼getItemViewType內的gettype決定你item type,之後需傳入convertView給該子item作render。以及null的情況下new一個新的instance view給convertView。

目前使用起來沒出問題,listview item開兩萬個也無礙,我想應該是可行的。

2015年5月27日 星期三

git xcode merge branch xcodeproj/project.pbxproj conflict的解法

這檔案控管project檔案的,多人開發會有衝突,小型project還能用人力自行merge,太大就難了,我是使用這github的script當mergetool

https://github.com/simonwagner/mergepbx

此外多人開發storyboard也是個issue,我目前並沒有merge的方法,只能從切畫面當xib來使用。

2015年3月11日 星期三

使用Reveal破解任意iOS app view hierarchy

Reveal這套軟體可以用來破解任意iOS app view hierarchy,內容應該正確 這我沒辦法確認

參考網路上的文章 http://c.blog.sina.com.cn/profile.php?blogid=cb8a22ea89000gtw 雖然只有六步,但這六步其實都不簡單。 


1. Jailbreak過後的裝置,這步只能你自己想辦法了,通常照ios等級會有不同的方法。

2. 安裝reveal,找出裡面的libReveal.dylib,並且安裝至mobile內的/Library/MobileSubstrate/DynamicLibraries 


reveal選menu的help/show reveal library in finder就有libs了


JB過後的device會有Cydia,Cydia內可以安裝openssh,需等Cydia自己更新後才搜尋的到,網路上說可以加入Repo http://weamdev.org/repo,但我還沒找到加入法就可以安裝openssh了。 


而後需要安裝Cydia substrate後,/Library/MobileSubstrate/DynamicLibraries才會出現 我是透過scp將檔案裝入,並且加入一個file named 'libReveal.plist'
{ Filter = { Bundles = ( "com.yourdomain.yourapp" ); }; } 

其中bundle name如果是他人app,則你還需在itunes中自行下載他的app,而後在你的硬碟裡找出 ~/Music/iTunes/iTunes Media/Mobile Applications/ 這目錄下你下載的.ipa,更名至.zip。

unzip後,在iTunesMetadata.plist中找出key softwareVersionBundleId
<key>softwareVersionBundleId</key>

<string>"XXXXXXX"</string>

其中XXXXXXX則為你需要的bundle name 

3. 重開設備或者re-spring(我不會),而後你就可以在Reveal中享受你的成果了。



還需注意的是,JB過的安裝openSSH device最好換掉root密碼,預設為alpine