2013年12月12日 星期四

iOS App 串接 Dropbox API第一次就上手

前言
最近發現我好飢渴阿(大誤!)
很多網路服務API都很有趣, 也很完整, 高手滿坑滿谷.
就像Steve Jobs說的 'stay hungry stay foolish'
今天就來玩玩Dropbox的API吧~


申請APP帳號
Dropbox開發者首頁(Link)
左側有App Console的標籤, 點擊然後選擇Create app.

> 要先同意使用者條款跟隱私權政策


> 然後要選擇app的類型.
1. 先選右邊的Dropbox API app
2. 然後選擇Files and datastores (注意!選Datastores only沒法對data做access動作!)
    就像這位仁兄遇到的問題一樣.
3. 我只允許我的app access自己app所建立的資料夾
4. 設定一下app名稱 (之後還可以更改)


> 建立完成, 複製App key以及App secret等等要用.


> 一切就緒, 讓我們切換到Core API的標籤頁, 點選Install SDK


先從Example Project試試
剛剛下載的iOS SDK裡面有個examples > DBRoulette 開啟DBRoulette.xcodeproj
把剛剛的App key跟App secret 貼到 DBRouletteAppDelegate.m
注意!root = kDBRootAppFolder/kDBRootDropbox 不要用預設的nil
不然跑起來會有錯誤訊息:
[WARNING] DropboxSDK: error making request to /1/metadata/(null) - (400) Expected 'root' ...

然後DBRoulette-Info.plist右鍵點選Open as > Source code
注意!db-(your app key) 記得保留'db-' .

設定完成來執行看看吧!

一開始可能會有一堆錯誤視窗跑出來.
因為資料夾是空的呀~不過登入自己的Dropbox可以發現.

Dropbox有建立了一個新的資料夾了, 丟一些照片進去就大功告成了!
下台一鞠躬, 謝謝大家XD
Sent from Evernote

2013年12月11日 星期三

iOS App share with Google Plus

前言
Google Plus的分享功能要加到iOS專案裡面不會太困難.
官方網站的原文教學

環境設定
下載Google+ iOS SDK (official link)
檢查一下
AssetsLibrary.framework
Foundation.framework
CoreLocation.framework
CoreMotion.framework
CoreGraphics.framework
CoreText.framework
MediaPlayer.framework
Security.framework
SystemConfiguration.framework
UIKit.framework

並從下載回來的Google+ iOS SDK資料夾內
拖曳&import
GooglePlus.framework
GoogleOpenSource.framework



建立一個API project
舊版的設定頁面


註冊一個app


完成後應該長得像這樣


新版的設定頁面
啟用Google+API


註冊一個app


填寫必要資訊


完成後應該長得像這樣



程式
在AppDelegate.m

#import 
#import  
static NSString * const kClientID = @"blahblahblah.apps.googleusercontent.com";

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Set app's client ID for |GPPSignIn| and |GPPShare|.
    [GPPSignIn sharedInstance].clientID = kClientID;
    ...
}

在ViewController.m
@interface YourViewController ()  {
     ...
- (void)viewDidLoad {
     [super viewDidLoad];
     [GPPShare sharedInstance].delegate = self;
}
     ...
- (void)gplusBtnPressed:(id)sender {   
    id shareBuilder = [[GPPShare sharedInstance] shareDialog];
   
    // This line will fill out the title, description, and thumbnail of the item
    // you're sharing based on the URL you included.
    //[shareBuilder setURLToShare:[NSURL URLWithString:@"The url you want to share"]];
    [shareBuilder setContentDeepLinkID:@"DeepLinkID"];
    [shareBuilder setTitle:@" 標題 "
               description:@" 描述"]
              thumbnailURL:[NSURL URLWithString:[@"縮圖網址"]]];

    [shareBuilder setPrefillText:msg];
    [shareBuilder open];
}

#pragma mark - GPPShareDelegate
- (void)finishedSharing:(BOOL)shared {
   
}

- (void)reportAuthStatus {
    if ([GPPSignIn sharedInstance].authentication) {
        NSLog(@"Status: Authenticated");
    } else {
        // To authenticate, use Google+ sign-in button.
        NSLog(@"Status: Not authenticated");
    }
}

如果分享時發生Error 404 Not Found, 那最有可能就是client ID沒有符合

希望大家都分享順利囉~
Sent from Evernote

[Android] share with Facebook SDK 3.5

前言
原本在開發者沙盒(SandBox)模式下一切都還滿順利的,
不料上到Google Play發現原本的功能不work, 
花了一些時間才發現小細節.
自己記錄一下, 同時也給需要的人參考一下.

以下我就自己的開發歷程, 
先從沙盒模式說起, 
最後再談關閉沙盒模式要注意的地方.


下載&Import FaceBook SDK
可以參考官方文件的原文教學
我是用SDK 3.5.2 (Official Download Link / Github SDK3.6)

I'll skip the installation of Facebook in emulator here.

> Right click / 'File' on top

> Existing Android Code Into Workspace

> make sure the 'facebook' project is checked

如果Project有Error可以先檢查android-support-v4.jar的版本是否一致
最快的方法就是把最新的jar在project list裡面copy起來,
然後把其他project有用到的都先delete掉, 再paste上去.


環境設定
接下來看看我們的project要做哪些設定
1. library

2. Manifest設定
     
     
        
    

3. res/values/strings.xml加上
(your Facebook App ID)


Facebook上建立應用程式後, copy App ID

4. 在Facebook App設定頁面加上Hash Key
Mac User
keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore | openssl sha1 -binary | openssl base64



程式 - Facebook分享
// facebook
private static final String PERMISSION = "publish_actions";
private UiLifecycleHelper uiHelper;
private boolean canPresentShareDialog;
private PendingAction pendingAction = PendingAction.NONE;
private enum PendingAction {
     NONE,
    POST_PHOTO,
    POST_STATUS_UPDATE
}

// Facebook Login
Session.openActiveSession(this, true, new Session.StatusCallback()
{
     // callback when session changes state
     @Override
     public void call(final Session session, SessionState state, Exception exception)
     {
          if (session.isOpened())
          {   
               // make request to the /me API
               Request request = Request.newMeRequest(session, new Request.GraphUserCallback()
               {
                    @Override
                    public void onCompleted(GraphUser user, Response response)
                    {
                         // If the response is successful
                        Log.d("facebook", "GraphUserCallback" + user.getId()+" " + response.toString());
                                        
                        if (session == Session.getActiveSession())
                        {
                              if (user != null)
                             {
                                   performPublish(PendingAction.POST_STATUS_UPDATE, canPresentShareDialog);
                             }
                        }
                                        
                        if (response.getError() != null)
                        {
                              // Handle errors, will do so later.
                        }
                     }
               });
               request.executeAsync();
          }
     }
});

private void performPublish(PendingAction action, boolean allowNoSession) {
     Session session = Session.getActiveSession();
    if (session != null) {
        pendingAction = action;
        if (hasPublishPermission()) {
               // We can do the action right away.
            handlePendingAction();
            return;
        } else if (session.isOpened()) {
            // We need to get new permissions, then complete the action when we get called back.
            session.requestNewPublishPermissions(new Session.NewPermissionsRequest(this, PERMISSION));
            return;
          }
    }

    if (allowNoSession) {
          pendingAction = action;
        handlePendingAction();
     }
}
    
@SuppressWarnings("incomplete-switch")
private void handlePendingAction() {
    PendingAction previouslyPendingAction = pendingAction;
    // These actions may re-set pendingAction if they are still pending, but we assume they
    // will succeed.
    pendingAction = PendingAction.NONE;

    switch (previouslyPendingAction) {
          case POST_PHOTO:
               //postPhoto();
            break;
        case POST_STATUS_UPDATE:
            postStatusUpdate();
            break;
    }
}
    
private FacebookDialog.ShareDialogBuilder createShareDialogBuilder() {
    return new FacebookDialog.ShareDialogBuilder(this)
          .setName("fb app name")
        .setDescription("app description")
        .setLink("site link");
}
    
private void postStatusUpdate() {
     if (canPresentShareDialog) {
        FacebookDialog shareDialog = createShareDialogBuilder().build();
        uiHelper.trackPendingDialogCall(shareDialog.present());
    } else if (hasPublishPermission()) {
        Bundle params = new Bundle();
        params.putString("name", " link主標題 ");
        params.putString("caption", " link副標題 ");
        params.putString("message", " 描述 ");
        params.putString("link", " 分享連結 ");
        params.putString("picture", " 圖片url ");

        Request request = new Request(Session.getActiveSession(), "me/feed", params, HttpMethod.POST);
        request.setCallback(new Request.Callback() {
               @Override
             public void onCompleted(Response response) {
                    showPublishResult(null, response.getGraphObject(), response.getError());
             }
        });
        request.executeAsync();
     } else {
          pendingAction = PendingAction.POST_STATUS_UPDATE;
    }
}
    
private void showPublishResult(String message, GraphObject result, FacebookRequestError error) {
     String title = null;
    String alertMessage = null;
    if (error == null) {
          title = getString(R.string.success);
        alertMessage = getString(R.string.successfully_posted_post);
    } else {
        title = getString(R.string.error);
        alertMessage = error.getErrorMessage();
    }

    new AlertDialog.Builder(this)
          .setTitle(title)
        .setMessage(alertMessage)
        .setPositiveButton(R.string.ok, null)
        .show();
}

@Override
protected void onActivityResult(int requestCode, int responseCode, Intent intent) {  
    // fb 登入結果
    Session.getActiveSession().onActivityResult(this, requestCode, responseCode, intent);
}


正式版本
1. Facebook APP設定頁面一定要把SandBox mode關閉

2. 確認Android app package name
> Export Signed Application Package

> KeyStore path別忘記副檔名.keystore !

> 輸入key的資訊 (一個keystore可以有很多把keys), Alias就是key的名稱

3. 複製正式版本的hashed key (建議在keystore的目錄下)
keytool -exportcert -alias (key的名稱) -keystore (keystore名稱).keystore | openssl sha1 -binary | openssl base64

這樣submit的正式版app就能夠正常分享資訊到Facebook啦!

Sent from Evernote

2013年12月4日 星期三

在iOS app用webview播放YouTube影片

前言
如果要直接launch YouTube app來播放, 那需要URL scheme:
  • youtube://
  • http://www.youtube.com/v/VIDEO_IDENTIFIER
  • http://www.youtube.com/watch?v=VIDEO_IDENTIFIER
今天我需要用嵌入webview 方式播放YouTube影片來提供比較好的使用者體驗:D



實作
原理就是用webview 的loadHTMLString 方法來讀取我們assign好的HTML內容.
廢話不說, 直接上code!
webplayer = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)];
    webplayer.scalesPageToFit = YES;
    webplayer.delegate = self;
    [self.view addSubview:webplayer];
    NSString *videoURL = [NSString stringWithFormat:@"http://www.youtube.com/embed/%@", youtubeID];
    NSString *videoHTML = [NSString stringWithFormat:@"\
                 \
                 \
                 \
                 \
                 \
                 \
                 \
                 ", videoURL];
    [webplayer loadHTMLString:videoHTML baseURL:nil];
    webplayer.backgroundColor = [UIColor blackColor];
    webplayer.opaque = NO;



重點一
如果是使用xib來製作webplayer 那可以直接把delegate給定

不然就是在.m
webplayer.delegate = self;
以及在.h 加上
@interface webPlayerViewController : UIViewController <UIWebViewDelegate> 


重點二
videoURL 是 http://www.youtube.com/embed/(youtubeID)
不是一般網址
https://www.youtube.com/watch?v=b1aHBlaC0de
也不是分享用的縮址
http://youtu.be/b1aHBlaC0de
舊版的嵌入網址也不適用
www.youtube.com/v/b1aHBlaC0de?version=3&amp;hl=zh_TW&amp;rel=0


最後重點
HTML 樣式的調整, 可以觀察一下NSString中一些跳脫字元的使用方式.
另外, 我們可以加上一個按鈕來關閉這頁.
UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithTitle:@"Done" 
         style:UIBarButtonItemStylePlain 
         target:self 
         action:@selector(onClickedDone:)];
self.navigationItem.rightBarButtonItem = doneButton;
[doneButton release];

- (void)onClickedDone:(id)sender {
    [webplayer release];
    webplayer = nil;
    //popViewController or dismissViewController
}


參考連結: MightyMeta
Sent from Evernote     

內容回應