You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Delphi Firemonkey中通过Intents对接Dropbox API的OAuth回调问题

我来帮你搞定Delphi Android上Dropbox OAuth从TWebBrowser切换到系统浏览器的问题!你遇到的「只能收到Cancelled消息」大概率是回调配置没做对,咱们一步步把两个核心问题(获取返回数据、处理浏览器回调)解决掉:

一、先搞定AndroidManifest的回调配置(关键!)

首先得让系统知道你的应用能处理Dropbox授权后的回调URL,这一步没做好,浏览器根本找不到你的应用,自然只会判定为授权取消。

  1. 打开Delphi项目里的AndroidManifest.template.xml文件
  2. 在主Activity标签(一般是com.embarcadero.firemonkey.FMXNativeActivity)内添加一个<intent-filter>,比如你的回调URL设为myapp://dropbox-auth(Scheme可以自定义,建议用你的应用包名避免冲突):
<intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <!-- 这里的scheme和host要和你Dropbox应用后台设置的redirect_uri完全一致 -->
    <data android:scheme="myapp" android:host="dropbox-auth" />
</intent-filter>
  1. 同步去Dropbox开发者后台,把这个redirect_uri(比如myapp://dropbox-auth)添加到应用允许的回调地址列表里,不然Dropbox会拒绝跳转请求。
二、用Intent打开系统浏览器发起授权

替换原来TWebBrowser的代码,用系统Intent唤起浏览器打开授权URL,注意redirect_uri要和上面配置的完全匹配:

procedure TfrmmDropBox.OpenOAuthInBrowser;
var
  LIntent: JIntent;
  LAuthURL: string;
begin
  // 替换成你的Dropbox Client ID,redirect_uri和Manifest里的一致
  LAuthURL := 'https://www.dropbox.com/oauth2/authorize?client_id=YOUR_DROPBOX_CLIENT_ID&response_type=code&redirect_uri=myapp://dropbox-auth';
  
  LIntent := TJIntent.JavaClass.init(TJIntent.JavaClass.ACTION_VIEW,
    TJUri.JavaClass.parse(StringToJString(LAuthURL)));
  
  // 可选:指定用Chrome打开,也可以不指定让用户自行选择浏览器
  LIntent.setPackage('com.android.chrome');
  
  if TAndroidHelper.Context.getPackageManager.queryIntentActivities(LIntent, 0).size > 0 then
    TAndroidHelper.Activity.startActivity(LIntent)
  else
    ShowMessage('未找到可用浏览器!');
end;
三、接收浏览器回调并获取授权数据

现在要处理浏览器跳回应用时的Intent,从中解析出授权码(code)。这里有两种实现方式:

方法1:重写主Activity(推荐,更稳定)

  1. 新建一个继承自TFMXNativeActivity的类:
unit MainActivity;

interface

uses
  Androidapi.JNI.App, Androidapi.JNI.Os, Androidapi.Helpers, FMX.Platform.Android;

type
  TMainActivity = class(TFMXNativeActivity)
  protected
    procedure OnNewIntent(Intent: JIntent); override;
  end;

implementation

uses
  frmmDropBox, Androidapi.JNI.Net;

procedure TMainActivity.OnNewIntent(Intent: JIntent);
var
  LUri: JUri;
  LCode: string;
begin
  inherited;
  if Intent.getData <> nil then
  begin
    LUri := Intent.getData;
    // 验证回调URL的Scheme和Host是否匹配
    if LUri.getScheme.equals(StringToJString('myapp')) and
       LUri.getHost.equals(StringToJString('dropbox-auth')) then
    begin
      LCode := JStringToString(LUri.getQueryParameter(StringToJString('code')));
      if LCode <> '' then
        // 拿到授权码,去换Access Token
        frmmDropBox.HandleOAuthCode(LCode)
      else
        // 用户主动取消授权
        frmmDropBox.HandleOAuthCancelled;
    end;
  end;
end;

initialization
  // 替换默认的Activity类
  RegisterActivity(TMainActivity);

end.
  1. 在你的主窗体中添加对应的处理方法:
procedure TfrmmDropBox.HandleOAuthCode(const ACode: string);
begin
  // 这里调用Dropbox API,用授权码换取Access Token
  ShowMessage('拿到授权码:' + ACode);
end;

procedure TfrmmDropBox.HandleOAuthCancelled;
begin
  ShowMessage('授权已取消');
end;

方法2:用消息管理器监听(适合不想重写Activity的场景)

在主窗体的OnCreate事件中添加监听:

uses
  FMX.Platform, Androidapi.JNI.App;

procedure TfrmmDropBox.FormCreate(Sender: TObject);
begin
  TMessageManager.DefaultManager.SubscribeToMessage(TMessageReceivedNotification,
    procedure(const Sender: TObject; const M: TMessage)
    var
      LMsg: TMessageReceivedNotification;
      LIntent: JIntent;
      LUri: JUri;
      LCode: string;
    begin
      LMsg := TMessageReceivedNotification(M);
      if LMsg.Message is JIntent then
      begin
        LIntent := TJIntent(LMsg.Message);
        if LIntent.getData <> nil then
        begin
          LUri := LIntent.getData;
          if LUri.getScheme.equals(StringToJString('myapp')) and
             LUri.getHost.equals(StringToJString('dropbox-auth')) then
          begin
            LCode := JStringToString(LUri.getQueryParameter(StringToJString('code')));
            if LCode <> '' then
              HandleOAuthCode(LCode)
            else
              HandleOAuthCancelled;
          end;
        end;
      end;
    end);
end;
四、关于「关闭浏览器」的说明

其实在标准OAuth流程里,授权完成后浏览器会自动跳回你的应用,此时浏览器会留在后台,不需要你手动关闭——用户如果需要可以自行关闭,强行关闭反而会影响体验。你之前只有关闭浏览器才收到Cancelled消息,正是因为回调配置没做好,导致浏览器无法跳回应用,用户只能手动关闭,系统因此判定为授权取消。

排查当前问题的小技巧
  1. 检查Dropbox后台的redirect_uri和代码、Manifest里的内容是否完全一致(大小写、符号都不能错)
  2. 确认Manifest里的<intent-filter>确实添加到了主Activity标签内
  3. 尽量用真实设备测试(模拟器可能存在回调跳转的兼容性问题)
  4. 测试时观察授权完成后,浏览器是否会自动唤起你的应用

内容的提问来源于stack exchange,提问作者Parodius

火山引擎 最新活动