Android Custom URL Scheme Refuses To Work / How to Navigate Back to Android App After OAuth
Asked Answered
E

4

12

edit, please see my 2nd answer below this question for a much more concise, and simple display of the problem


I finally have hit my wits end. I'm working on a cross platform app (IOS and Android) using titanium and connects to salesforce api via rest calls. I've spent 3 days and 20 some hours on this issue and am still stuck. Any support would be extremely appreciated! So lets dive right in.

What I Want It To Do: The app starts, checks if the user has been given a token using oauth2 authorization, if not gives the user the salesforce login screen. User enters credentials and hits "allow" button and then we are redirected back to the app.

The Issue: From the description of what I want it to do above everything works except for the last line in bold. Once the user clicks "allow" button we receive this unknown url scheme error:

error

The code: The code pretty much consists of 3 different important areas

  • my index.js file which is where the salesforce login call is made
  • my AuthService.js file which is where the salesforce url info is called in the index.js
  • my tiapp.xml where configuration stuff is held for android custom url scheme

index.js

var Auth = require('AuthService');

Auth.openLogin();

AuthService.js Only "openLogin" function is really important here.

var loginWindow;

module.exports = {

  get: function () {
    console.log("made it into the auth.get function");
    return Ti.App.Properties.getObject('auth');
  },

  set: function (data) {
    Ti.App.Properties.setObject('auth', data);
    console.log("auth set function was executed!!!");
  },

  erase: function () {
    Ti.App.Properties.removeProperty('auth');
  },

  openLogin: function () {

    console.log("made it into openLogin!!");


   var webview = Titanium.UI.createWebView({url:'https://login.salesforce.com/services/oauth2/authorize' +
      '?response_type=token&display=touch' +
      '&redirect_uri=testapp://app.open' +
      '&client_id=' + Ti.App.Properties.getString('salesforce_client_id')});


      //console.log("Webview URL: " + webview.getUrl());

    loginWindow = Titanium.UI.createWindow();
    loginWindow.add(webview);
    loginWindow.open({modal:true});



    // attempt to log url to console here
    //console.log("this is the webview URL during callback: " +  webview.getUrl());

  },

  closeLogin: function () {
    loginWindow.close();
  }

};

tiapp.xml I am only including my android section of this for the sake of brevity

<android xmlns:android="http://schemas.android.com/apk/res/android"> 
        <manifest android:versionCode="1" android:versionName="1.00">
            <uses-permission android:name="android.permission.INTERNET"></uses-permission>
            <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
            <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"></uses-permission>
            <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"></uses-permission>
            <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission>
        </manifest>


        <activity
      android:name=".TesterActivity"
      android:exported="true"
      android:label="@string/app_name" >
      <intent-filter>
          <data android:host="app.open" android:scheme="testapp" />
          <action android:name="android.intent.action.VIEW" />
          <category android:name="android.intent.category.BROWSABLE" />
          <category android:name="android.intent.category.DEFAULT" />
      </intent-filter>
  </activity>

    </android>

Callback Url registered with Salesforce: enter image description here

What I have tried: I have googled myself blue on this issue. Most of my tinkering has been making adjustments to my "tiapp.xml" in changing the properties and values of those properties within the android tag as well as many adjustments to the "redirect_rui" value in my "AuthService.js" file and in salesforce "callback url" field (since these values have to match or you get a uri mismatch error).

Here are some solutions I have looked at and attempted in order to fix my problem: https://gist.github.com/jasonkneen/5736738

android custom url scheme..?

How to implement my very own URI scheme on Android

Just to name a few.

console log on app execution:

[INFO] :   Emulator is booted
[INFO] :   SD card not required, skipping mount check
[INFO] :   Emulator ready!
[INFO] :   Creating unsigned apk
[INFO] :   Processing /Users/michael.kellogg/Documents/Appcelerator_Studio_Workspace/tester/build/android/src
[INFO] :   Writing unsigned apk: /Users/michael.kellogg/Documents/Appcelerator_Studio_Workspace/tester/build/android/bin/app-unsigned.apk
[INFO] :   Using MD5withRSA signature algorithm
[INFO] :   Signing apk: /Library/Java/JavaVirtualMachines/jdk1.8.0_72.jdk/Contents/Home/bin/jarsigner "-sigalg" "MD5withRSA" "-digestalg" "SHA1" "-keystore" "/Users/michael.kellogg/Library/Application Support/Titanium/mobilesdk/osx/5.1.2.GA/android/dev_keystore" "-storepass" "*******" "-signedjar" "/Users/michael.kellogg/Documents/Appcelerator_Studio_Workspace/tester/build/android/bin/tester.apk" "/Users/michael.kellogg/Documents/Appcelerator_Studio_Workspace/tester/build/android/bin/app-unsigned.apk" "tidev"
[INFO] :   Aligning zip file: /Users/michael.kellogg/android-sdk/build-tools/23.0.2/zipalign "-v" "4" "/Users/michael.kellogg/Documents/Appcelerator_Studio_Workspace/tester/build/android/bin/tester.apk" "/Users/michael.kellogg/Documents/Appcelerator_Studio_Workspace/tester/build/android/bin/tester.apkz"
[INFO] :   Writing build manifest: /Users/michael.kellogg/Documents/Appcelerator_Studio_Workspace/tester/build/android/build-manifest.json
[INFO] :   Making sure the adb server is running
[INFO] :   Installing apk: /Users/michael.kellogg/Documents/Appcelerator_Studio_Workspace/tester/build/android/bin/tester.apk
[INFO] :   App successfully installed
[INFO] :   Starting app: com.test.test/.TesterActivity
[INFO] :   Application pid: 1160
[INFO] :   Project built successfully in 3m 8s 295ms
[INFO] :   art: Late-enabling JIT
[INFO] :   art: JIT created with code_cache_capacity=2MB compile_threshold=1000
[INFO] :   TiApplication: (main) [2,2] checkpoint, app created.
[INFO] :   TiApplication: (main) [1067,1069] Titanium 5.1.2 (2015/12/16 19:00 ca822b2)
[INFO] :   art: Background sticky concurrent mark sweep GC freed 15623(973KB) AllocSpace objects, 11(384KB) LOS objects, 41% free, 2MB/3MB, paused 2.142ms total 105.631ms
[INFO] :   TiApplication: (main) [383,1452] Titanium Javascript runtime: v8
[INFO] :   TiRootActivity: (main) [0,0] checkpoint, on root activity create, savedInstanceState: null
[WARN] :   TiTempFileHelper: (main) [189,189] The external temp directory doesn't exist, skipping cleanup
[INFO] :   TiRootActivity: (main) [0,0] checkpoint, on root activity resume. activity = com.test.test.TesterActivity@a298e02
[WARN] :   V8Object: (KrollRuntimeThread) [1039,1228] Runtime disposed, cannot set property 'userAgent'
[INFO] :   made it into openLogin!!
[INFO] :   OpenGLRenderer: Initialized EGL, version 1.4
[WARN] :   EGL_emulation: eglSurfaceAttrib not implemented
[WARN] :   OpenGLRenderer: Failed to set EGL_SWAP_BEHAVIOR on surface 0xb4d32b20, error=EGL_SUCCESS
[INFO] :   WebViewFactory: Loading com.android.webview version 44.0.2403.119 (code 246011900)
[WARN] :   System: ClassLoader referenced unknown path: /system/app/webview/lib/arm
[INFO] :   LibraryLoader: Time to load native libraries: 36 ms (timestamps 588-624)
[INFO] :   LibraryLoader: Expected native library version number "",actual native library version number ""
[INFO] :   LibraryLoader: Expected native library version number "",actual native library version number ""
[INFO] :   chromium: [INFO:library_loader_hooks.cc(120)] Chromium logging enabled: level = 0, default verbosity = 0
[INFO] :   BrowserStartupController: Initializing chromium process, singleProcess=true
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[ERROR] :  SysUtils: ApplicationContext is null in ApplicationStatus
[WARN] :   chromium: [WARNING:resource_bundle.cc(285)] locale_file_path.empty()
[ERROR] :  libEGL: validate_display:255 error 3008 (EGL_BAD_DISPLAY)
[ERROR] :  libEGL: validate_display:255 error 3008 (EGL_BAD_DISPLAY)
[ERROR] :  eglCodecCommon: glUtilsParamSize: unknow param 0x00008d57
[WARN] :   AudioManagerAndroid: Requires BLUETOOTH permission
[ERROR] :  DataReductionProxySettingListener: No DRP key due to exception:java.lang.ClassNotFoundException: com.android.webview.chromium.Drp
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   AwContents: onDetachedFromWindow called when already detached. Ignoring
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   chromium: [WARNING:data_reduction_proxy_config.cc(423)] SPDY proxy OFF at startup
[WARN] :   EGL_emulation: eglSurfaceAttrib not implemented
[WARN] :   OpenGLRenderer: Failed to set EGL_SWAP_BEHAVIOR on surface 0xb4d32b40, error=EGL_SUCCESS
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[INFO] :   art: Background sticky concurrent mark sweep GC freed 7564(860KB) AllocSpace objects, 25(988KB) LOS objects, 0% free, 11MB/11MB, paused 1.603ms total 308.766ms
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[INFO] :   art: Background partial concurrent mark sweep GC freed 1948(144KB) AllocSpace objects, 3(8MB) LOS objects, 22% free, 13MB/17MB, paused 1.658ms total 271.298ms
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[INFO] :   art: Background partial concurrent mark sweep GC freed 148(6KB) AllocSpace objects, 13(36MB) LOS objects, 17% free, 19MB/23MB, paused 1.205ms total 327.978ms
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[INFO] :   art: Background partial concurrent mark sweep GC freed 91(3KB) AllocSpace objects, 5(14MB) LOS objects, 22% free, 13MB/17MB, paused 1.228ms total 201.595ms
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Suspending all threads took: 15.737ms
[INFO] :   art: Background sticky concurrent mark sweep GC freed 261(13KB) AllocSpace objects, 2(5MB) LOS objects, 0% free, 25MB/25MB, paused 18.832ms total 174.330ms
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[INFO] :   art: Background partial concurrent mark sweep GC freed 98(20KB) AllocSpace objects, 7(19MB) LOS objects, 26% free, 11MB/15MB, paused 3.244ms total 302.681ms
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   BindingManager: Cannot call determinedVisibility() - never saw a connection for the pid: 1160
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[INFO] :   art: Background sticky concurrent mark sweep GC freed 11512(602KB) AllocSpace objects, 0(0B) LOS objects, 4% free, 14MB/15MB, paused 1.739ms total 174.806ms
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[INFO] :   art: Background partial concurrent mark sweep GC freed 301(32KB) AllocSpace objects, 2(5MB) LOS objects, 25% free, 11MB/15MB, paused 2.370ms total 274.103ms
[WARN] :   EGL_emulation: eglSurfaceAttrib not implemented
[WARN] :   OpenGLRenderer: Failed to set EGL_SWAP_BEHAVIOR on surface 0x9d719f80, error=EGL_SUCCESS
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[INFO] :   art: Background sticky concurrent mark sweep GC freed 358(19KB) AllocSpace objects, 0(0B) LOS objects, 0% free, 17MB/17MB, paused 6.593ms total 277.832ms
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[INFO] :   art: Background partial concurrent mark sweep GC freed 148(5KB) AllocSpace objects, 3(8MB) LOS objects, 18% free, 17MB/21MB, paused 7.547ms total 414.524ms
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[INFO] :   art: Background sticky concurrent mark sweep GC freed 30(608B) AllocSpace objects, 1(2MB) LOS objects, 0% free, 23MB/23MB, paused 2.321ms total 159.615ms
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[INFO] :   art: Background partial concurrent mark sweep GC freed 36(1168B) AllocSpace objects, 5(14MB) LOS objects, 21% free, 14MB/18MB, paused 1.626ms total 197.175ms
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[INFO] :   art: Background sticky concurrent mark sweep GC freed 21(448B) AllocSpace objects, 0(0B) LOS objects, 0% free, 20MB/20MB, paused 37.280ms total 96.680ms
[INFO] :   art: Background partial concurrent mark sweep GC freed 34(960B) AllocSpace objects, 4(11MB) LOS objects, 30% free, 8MB/12MB, paused 1.475ms total 106.381ms
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[INFO] :   art: Background partial concurrent mark sweep GC freed 56(3KB) AllocSpace objects, 3(8MB) LOS objects, 21% free, 14MB/18MB, paused 3.001ms total 243.577ms
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   BindingManager: Cannot call determinedVisibility() - never saw a connection for the pid: 1160
[WARN] :   BindingManager: Cannot call determinedVisibility() - never saw a connection for the pid: 1160
[WARN] :   BindingManager: Cannot call determinedVisibility() - never saw a connection for the pid: 1160
[INFO] :   I/TiWebChromeClient.console: (main) [21796,21796] Not allowed to load local resource: file:///android_asset/webkit/android-weberror.png (0:data:text/html,chromewebdata)
[INFO] :   art: Background sticky concurrent mark sweep GC freed 337(53KB) AllocSpace objects, 1(2MB) LOS objects, 6% free, 17MB/18MB, paused 43.249ms total 82.260ms
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[INFO] :   art: Background partial concurrent mark sweep GC freed 82(11KB) AllocSpace objects, 3(8MB) LOS objects, 21% free, 14MB/18MB, paused 1.613ms total 159.713ms
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[INFO] :   art: Background sticky concurrent mark sweep GC freed 27(544B) AllocSpace objects, 1(2MB) LOS objects, 6% free, 17MB/18MB, paused 21.912ms total 68.965ms
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[INFO] :   art: Background partial concurrent mark sweep GC freed 39(1056B) AllocSpace objects, 3(8MB) LOS objects, 21% free, 14MB/18MB, paused 1.523ms total 136.698ms
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[WARN] :   art: Attempt to remove non-JNI local reference, dumping thread
[ERROR] :  Surface: getSlotFromBufferLocked: unknown buffer: 0xaad48dc0
[INFO] :   art: Background partial concurrent mark sweep GC freed 40(1104B) AllocSpace objects, 5(14MB) LOS objects, 25% free, 11MB/15MB, paused 3.871ms total 157.623ms
[INFO] :   APSAnalyticsService: Analytics Service Started
[INFO] :   APSAnalyticsService: Stopping Analytics Service
-- End application log -------------------------------------------------------
Extractor answered 16/2, 2016 at 20:44 Comment(1)
Did this question help anyone else or are people pity upvoting? hahaExtractor
E
7

After something like 64 hours of scouring the internet and countless attempts I have finally found a solution to this issue. Yay me! I must admit I feel a bit of shame as to how long this issue took to resolve.

The Issue: Could not find a way to navigate back to android app after salesforce authorization (custom url scheme was extremely painful with android).

Solution: After searching around I found a code example from salesforce that did what I was attempting to do here:

https://developer.salesforce.com/page/Developing_Mobile_Applications_with_Force.com_and_Appcelerator_Titanium

Here is the github link provided in the above article that has the much needed code:

https://github.com/appcelerator-developer-relations/Force

TLDR: The real key here was to NOT use a custom url scheme with android and titanium. Instead the github code listed above opens the auth page in a web view and then, using listeners, closes that webview after auth is done and opens whatever view you need.

Explanation: to navigate back to my app on IOS all I had to do was add a couple of lines to my tiapp.xml file in order to use a custom url scheme. It worked very quickly and simply without any headache. Naturally I assumed a custom url scheme was the way to go with android, simple right? false. I spent many hours as listed above trying to make a custom url scheme on android to no avail. There seems to be lots of info online about android custom url schemes but none of them worked for me and believe me I tried so many things (after all it was approx. 64 hours). Eventually I just tried to find an example online of code that did what I wanted. This was the one that I found.

Extractor answered 24/2, 2016 at 16:1 Comment(7)
I use custom URL schemes and deeplinking in my recent project. I found it very straighforward and it was detected whether I used the URL in a browser, another app or a webview. What exactly wasn't working for you?Chasidychasing
That is, I'm sure you did some debugging, were you unable to intercept the url from a webview, or was the issue it expecting a response? Was this an external browser?Chasidychasing
@NickCardoso I had tried many times to get the custom url working, but never worked. Initially, I had my custom url scheme set as the "redirect_uri" in the link to salesforce auth page so that when I was done, it would redirect back to my app. Always got the uknown url scheme issue. Then I simply tried hitting a web page with a custom url scheme. Also didn't work. I would try typing the url scheme into the browser. Nothing. I'm pretty new to this kind of dev and haven't been able to get titanium debugging working. Maybe you could link so custom url scheme code in this discussion?Extractor
I will write an answer if I can figure out your exact issue. Did you try different schemas in the manifest? to see if it just didnt like your host for instance?Chasidychasing
@NickCardoso I did. you can imagine how many things I must have tried trying to resolve this for ~64 hours lol. I'm not saying I am without error. I'm new to phone app development and titanium so there is a lot of room for error, but I was working solely on this issue for a very long time. Also my first answer is pretty exhaustive in explaining the things I had done. Do you have any titanium/android code on github? looking at others' code so far has been extremely helpful for me. Or do you not use titanium?Extractor
we are trying to move away from webview based auth as many services are blocking them due to security concerns eg. Google developers.googleblog.com/2016/08/…Christiano
@Christiano alternative would be to authorize using rest api's I assume? that is better especially if you can configure SSOExtractor
C
11

In terms of setting up the manifest for a schema recognition you should end up with something resembling the following

<manifest ...>
    ...
    <application ...>
        ...
        <activity ...
            android:exported="true"> <!-- Make it exported -->
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <intent-filter android:label="...">
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="testapp" />
                <data android:host="app" /> <!-- cover all your bases -->
                <data android:host="app.open" />
            </intent-filter>
        </activity>
    </application>
</manifest>

I would then have a method in that activity which could check if your schema is in the intent. Something like the following:

protected void onCreate(final Bundle savedInstanceState) {
    ...
    checkIntent( getIntent() );
} 

protected void onNewIntent(final Intent intent) {
    ...
    checkIntent( intent );
}

private void checkIntent(Intent intent) {
    if ( intent.getDataString() != null && Intent.ACTION_VIEW.equals(intent.getAction()) ) {
        //If we reached here we have some kind of deep link or custom schema
        //so lets identify which schema
        if ( "testapp".equals(data.getScheme().toLowerCase()) ) {
            //my tests were able to reach here
        }
    }
}

One quirk of androids is that with later versions the user has to show intent. What that means realistically is that typing in a url in the browser and pressing go won't work. It's only triggered when a user clicks on a link or when you fire an intent. When you only want to detect the redirect from your own WebView (and not external browsers) then you are able to get around that:

myWebView.setWebViewClient( new WebViewClient() {

    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        if (url.startsWith("testapp://")) {
            Intent intent = new Intent(Intent.ACTION_VIEW);
            intent.setData( Uri.parse(url) );
            view.getContext().startActivity( intent );
            return true; //with return true, the webview wont try rendering the url
        }
        return false;
    }

} );
Chasidychasing answered 25/2, 2016 at 14:6 Comment(9)
wow. this is so in depth! Why could I not find this information online anywhere else? Haven't tried this to see if it works for me yetExtractor
Let me know if its unclear, but I think that should be all the steps you needChasidychasing
@NickCardosa would you like to be my mentor? I can't get images to show on titanium android side now..starting to wonder if there is something wrong with my configuration.Extractor
No idea why this attracted a downvote. You should open another question if you have a new issue and then lots of people will helpChasidychasing
@NickCardosa I'm not sure either. If there is some input as to why this code isn't correct or something I'd be very interested to see reasoning as to whyExtractor
@NickCardosa Can't get bounty back anyway so here you are sir hahaExtractor
Thanks, hope you find a way to work with that framework, doesn't sound like fun!Chasidychasing
This is working but some android browsers will return Unknown_url_scheme wathever you doUnderlet
didn't solve for me, perhaps this is a Chrome bug bugs.chromium.org/p/chromium/issues/detail?id=738724Christiano
E
7

After something like 64 hours of scouring the internet and countless attempts I have finally found a solution to this issue. Yay me! I must admit I feel a bit of shame as to how long this issue took to resolve.

The Issue: Could not find a way to navigate back to android app after salesforce authorization (custom url scheme was extremely painful with android).

Solution: After searching around I found a code example from salesforce that did what I was attempting to do here:

https://developer.salesforce.com/page/Developing_Mobile_Applications_with_Force.com_and_Appcelerator_Titanium

Here is the github link provided in the above article that has the much needed code:

https://github.com/appcelerator-developer-relations/Force

TLDR: The real key here was to NOT use a custom url scheme with android and titanium. Instead the github code listed above opens the auth page in a web view and then, using listeners, closes that webview after auth is done and opens whatever view you need.

Explanation: to navigate back to my app on IOS all I had to do was add a couple of lines to my tiapp.xml file in order to use a custom url scheme. It worked very quickly and simply without any headache. Naturally I assumed a custom url scheme was the way to go with android, simple right? false. I spent many hours as listed above trying to make a custom url scheme on android to no avail. There seems to be lots of info online about android custom url schemes but none of them worked for me and believe me I tried so many things (after all it was approx. 64 hours). Eventually I just tried to find an example online of code that did what I wanted. This was the one that I found.

Extractor answered 24/2, 2016 at 16:1 Comment(7)
I use custom URL schemes and deeplinking in my recent project. I found it very straighforward and it was detected whether I used the URL in a browser, another app or a webview. What exactly wasn't working for you?Chasidychasing
That is, I'm sure you did some debugging, were you unable to intercept the url from a webview, or was the issue it expecting a response? Was this an external browser?Chasidychasing
@NickCardoso I had tried many times to get the custom url working, but never worked. Initially, I had my custom url scheme set as the "redirect_uri" in the link to salesforce auth page so that when I was done, it would redirect back to my app. Always got the uknown url scheme issue. Then I simply tried hitting a web page with a custom url scheme. Also didn't work. I would try typing the url scheme into the browser. Nothing. I'm pretty new to this kind of dev and haven't been able to get titanium debugging working. Maybe you could link so custom url scheme code in this discussion?Extractor
I will write an answer if I can figure out your exact issue. Did you try different schemas in the manifest? to see if it just didnt like your host for instance?Chasidychasing
@NickCardoso I did. you can imagine how many things I must have tried trying to resolve this for ~64 hours lol. I'm not saying I am without error. I'm new to phone app development and titanium so there is a lot of room for error, but I was working solely on this issue for a very long time. Also my first answer is pretty exhaustive in explaining the things I had done. Do you have any titanium/android code on github? looking at others' code so far has been extremely helpful for me. Or do you not use titanium?Extractor
we are trying to move away from webview based auth as many services are blocking them due to security concerns eg. Google developers.googleblog.com/2016/08/…Christiano
@Christiano alternative would be to authorize using rest api's I assume? that is better especially if you can configure SSOExtractor
E
2

Could not put it in comment, so creating an answer. Would request you to please try it and see if it helps.

<android xmlns:android="http://schemas.android.com/apk/res/android">
<manifest>
    <application android:icon="@drawable/appicon" android:label="MyApp" android:name="MyApplication">
        <activity android:label="MyApp" android:name=".MyApplicationActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="mytestapplication" />
            </intent-filter>
        </activity>
    </application>
</manifest>

Can you give this a try and see if this works. You can also check out the TiApp.xml entry mentioned in this Git Post.

Request you to try and let us know, if this does not work out, then may be we can try something else.

Everybody answered 23/2, 2016 at 21:42 Comment(2)
thank you for your post! I tried this code but I'm getting a "unfortunately, myapp has stopped" error. I'm not sure if it is the code you provided or my small application. Did you try this code with a simple webview to see if it works?Extractor
It looks like the manifest tags encasing all of application and activity, etc was what was breaking the application on execution. I thought that the manifest tags looked strange where they were based off of what I've seen online, but I thought maybe it was the golden ticket haha. Apparently not :). Please try using your tiapp.xml with this code and see what results you get: var webview = Titanium.UI.createWebView({url:"mytestapp://"}); loginWindow = Titanium.UI.createWindow(); loginWindow.add(webview); loginWindow.open({modal:true});Extractor
V
0

There's some workaround which looks legit to me. So, along with shouldOverrideUrlLoading() the WebViewClient allows to override onErrorRecieved() and actually it passes failingUrl as an argument.

private val webViewClient = object : WebViewClient() {

        @Suppress("OverridingDeprecatedMember")
        override fun onReceivedError(view: WebView?, errorCode: Int, description: String?, failingUrl: String?) {
            if (failingUrl != null) {
                handleCallbackUrl(Uri.parse(failingUrl))
            }
        }

        override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?) = when {
            handleCallbackUrl(request?.url) -> true
            else -> super.shouldOverrideUrlLoading(view, request)
        }
}
Vouge answered 29/7, 2018 at 9:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.