// I have a refresh token, I need an access token.
        public IEnumerator <object> Reauthorize(Action successCallback, Action <string> failureCallback)
        {
            m_AccessToken = null;
            if (!String.IsNullOrEmpty(m_RefreshToken))
            {
                Dictionary <string, string> parameters = new Dictionary <string, string>();
                parameters.Add("client_id", m_ClientId);
                parameters.Add("client_secret", m_ClientSecret);
                parameters.Add("refresh_token", m_RefreshToken);
                parameters.Add("grant_type", "refresh_token");
                using (UnityWebRequest www = UnityWebRequest.Post(m_AccessTokenUri, parameters)) {
                    yield return(UnityCompat.SendWebRequest(www));

                    if (UnityCompat.IsNetworkError(www))
                    {
                        failureCallback("Network error");
                        yield break;
                    }

                    if (www.responseCode == 400 || www.responseCode == 401)
                    {
                        // Refresh token revoked or expired - forget it
                        m_RefreshToken = null;
                        PlayerPrefs.DeleteKey(m_PlayerPrefRefreshKey);
                        failureCallback("No valid refresh token, could not reauthorize");
                    }
                    else
                    {
                        JObject json = JObject.Parse(www.downloadHandler.text);
                        m_AccessToken = json["access_token"].ToString();
                        successCallback();
                    }
                }
            }
        }
        private IEnumerator LoadProfileIcon(string uri)
        {
            if (Profile == null)
            {
                yield break;
            }
            using (UnityWebRequest www = UnityCompat.GetTexture(uri + kIconSizeSuffix)) {
                yield return(UnityCompat.SendWebRequest(www));

                if (UnityCompat.IsNetworkError(www) || www.responseCode >= 400)
                {
                    Debug.LogErrorFormat("Error downloading {0}, error {1}", uri, www.responseCode);
                    Profile.icon = null;
                }
                else
                {
                    // Convert the texture to a circle and set it as the user's avatar in the UI.
                    Texture2D profileImage = DownloadHandlerTexture.GetContent(www);
                    Profile.icon = Sprite.Create(CropSquareTextureToCircle(profileImage),
                                                 new Rect(0, 0, profileImage.width, profileImage.height), new Vector2(0.5f, 0.5f),
                                                 USER_AVATAR_PIXELS_PER_UNIT);
                }
                if (m_OnProfileUpdated != null)
                {
                    m_OnProfileUpdated();
                }
            }
        }
        private IEnumerator GetUserInfo()
        {
            if (String.IsNullOrEmpty(m_RefreshToken))
            {
                yield break;
            }

            UserInfo user = new UserInfo();

            for (int i = 0; i < 2; i++)
            {
                using (UnityWebRequest www = UnityWebRequest.Get(m_UserInfoUri))
                {
                    Authenticate(www);
                    yield return(UnityCompat.SendWebRequest(www));

                    if (www.responseCode == 200)
                    {
                        JObject json = JObject.Parse(www.downloadHandler.text);
                        user.id   = json["resourceName"].ToString();
                        user.name = json["names"][0]["displayName"].ToString();
                        string iconUri = json["photos"][0]["url"].ToString();
                        if (json["residences"] != null)
                        {
                            user.location = json["residences"][0]["value"].ToString();
                        }
                        if (json["emailAddresses"] != null)
                        {
                            foreach (var email in json["emailAddresses"])
                            {
                                var primary = email["metadata"]["primary"];
                                if (primary != null && primary.Value <bool>())
                                {
                                    user.email = email["value"].ToString();
                                    break;
                                }
                            }
                        }
                        Profile = user;
                        yield return(LoadProfileIcon(iconUri));

                        yield break;
                    }
                    else if (www.responseCode == 401)
                    {
                        yield return(Reauthorize(() => { }, (error) => { Debug.LogError(error); }));
                    }
                    else
                    {
                        Debug.Log(www.responseCode);
                        Debug.Log(www.error);
                        Debug.Log(www.downloadHandler.text);
                    }
                }
            }
            Profile = null;
        }
Пример #4
0
        /// <summary>
        /// Sends a hit to Google Analytics.
        /// </summary>
        /// <param name="fields">Key-value pairs that make up the properties of the hit. These
        /// are assumed to be in the appropriate Google Analytics format.</param>
        private void SendHit(Dictionary <string, string> fields)
        {
            StringBuilder sb = new StringBuilder(prefix);

            foreach (KeyValuePair <string, string> pair in fields)
            {
                sb.AppendFormat("&{0}={1}", UnityWebRequest.EscapeURL(pair.Key), UnityWebRequest.EscapeURL(pair.Value));
            }
            string payload = sb.ToString();

            try {
                UnityWebRequest request = new UnityWebRequest("https://www.google-analytics.com/collect", "POST");
                request.uploadHandler             = new UploadHandlerRaw(Encoding.UTF8.GetBytes(payload));
                request.uploadHandler.contentType = "application/x-www-form-urlencoded";
                UnityCompat.SendWebRequest(request);
                PtDebug.LogFormat("ANALYTICS: sent hit: {0}", payload);
            } catch (Exception ex) {
                // Reporting these as errors would be noisy and annoying. We don't want to do that -- maybe the user is
                // offline. Not being able to send analytics isn't a big deal. So only log this error if
                // PtDebug verbose logging is on.
                PtDebug.LogFormat("*** Error sending analytics: {0}", ex);
            }
        }
        /// I have nothing.  Open browser to authorize permissions then get refresh and access tokens.
        public IEnumerator <object> Authorize(System.Action onSuccess, System.Action onFailure,
                                              bool launchSignInFlowIfNeeded)
        {
            if (String.IsNullOrEmpty(m_RefreshToken))
            {
                if (!launchSignInFlowIfNeeded)
                {
                    // We need to launch the sign-in flow. If we are not allowed to, then we have failed.
                    onFailure();
                    yield break;
                }

                if (m_HttpListener == null)
                {
                    // Listener is not yet running. Let's try to start it.
                    if (!StartHttpListener(out m_HttpPort, out m_HttpListener))
                    {
                        // Failed to start HTTP listener.
                        Debug.LogError("Failed to start HTTP listener for authentication flow.");
                        if (onFailure != null)
                        {
                            onFailure();
                        }
                        yield break;
                    }
                    // HTTP listener successfully started.
                }

                StringBuilder sb = new StringBuilder(m_RequestTokenUri)
                                   .Append("?client_id=").Append(Uri.EscapeDataString(m_ClientId))
                                   .Append("&redirect_uri=").Append("http://localhost:").Append(m_HttpPort).Append(m_CallbackPath)
                                   .Append("&response_type=code")
                                   .Append("&scope=").Append(m_OAuthScope);

                // Something about the url makes OpenURL() not work on OSX, so use a workaround
                if (Application.platform == RuntimePlatform.OSXEditor || Application.platform == RuntimePlatform.OSXPlayer)
                {
                    System.Diagnostics.Process.Start(sb.ToString());
                }
                else
                {
                    Application.OpenURL(sb.ToString());
                }

                if (m_WaitingOnAuthorization)
                {
                    // A previous attempt is already waiting
                    yield break;
                }
                m_WaitingOnAuthorization = true;
                m_VerificationCode       = null;
                m_VerificationError      = false;

                // Wait for verification
                while (m_VerificationCode == null && !m_VerificationError)
                {
                    yield return(null);
                }

                if (m_VerificationError)
                {
                    Debug.LogError("Account verification failed");
                    Debug.LogFormat("Verification error {0}", m_VerificationCode);
                    m_WaitingOnAuthorization = false;
                    yield break;
                }

                // Exchange for tokens
                var parameters = new Dictionary <string, string>();
                parameters.Add("code", m_VerificationCode);
                parameters.Add("client_id", m_ClientId);
                parameters.Add("client_secret", m_ClientSecret);
                parameters.Add("redirect_uri", String.Format("http://localhost:{0}{1}", m_HttpPort, m_CallbackPath));
                parameters.Add("grant_type", "authorization_code");

                UnityWebRequest www = UnityWebRequest.Post(m_AccessTokenUri, parameters);

                yield return(UnityCompat.SendWebRequest(www));

                if (UnityCompat.IsNetworkError(www))
                {
                    Debug.LogError("Network error");
                    m_WaitingOnAuthorization = false;
                    yield break;
                }
                else if (www.responseCode >= 400)
                {
                    Debug.LogError("Authorization failed");
                    Debug.LogFormat("Authorization error {0}", www.downloadHandler.text);
                    m_WaitingOnAuthorization = false;
                    yield break;
                }

                JObject json = JObject.Parse(www.downloadHandler.text);
                if (json != null)
                {
                    SetTokens(json["access_token"].ToString(), json["refresh_token"].ToString());
                }
                m_WaitingOnAuthorization = false;
            }

            yield return(GetUserInfo());

            if (LoggedIn)
            {
                onSuccess();
            }
            else
            {
                onFailure();
            }
        }
Пример #6
0
        /// <summary>
        /// Co-routine that services one PendingRequest. This method must be called with StartCoroutine.
        /// </summary>
        /// <param name="request">The request to service.</param>
        private IEnumerator HandleWebRequest(PendingRequest request, BufferHolder bufferHolder)
        {
            // NOTE: This method runs on the main thread, but never blocks -- the blocking part of the work is
            // done by yielding the UnityWebRequest, which releases the main thread for other tasks while we
            // are waiting for the web request to complete (by the miracle of coroutines).

            // Let the caller create the UnityWebRequest, configuring it as they want. The caller can set the URL,
            // method, headers, anything they want. The only thing they can't do is call Send(), as we're in charge
            // of doing that.
            UnityWebRequest webRequest = request.creationCallback();

            PtDebug.LogVerboseFormat("Web request: {0} {1}", webRequest.method, webRequest.url);

            bool cacheAllowed = cache != null && webRequest.method == "GET" && request.maxAgeMillis != CACHE_NONE;

            // Check the cache (if it's a GET request and cache is enabled).
            if (cacheAllowed)
            {
                bool   cacheHit      = false;
                byte[] cacheData     = null;
                bool   cacheReadDone = false;
                cache.RequestRead(webRequest.url, request.maxAgeMillis, (bool success, byte[] data) =>
                {
                    cacheHit      = success;
                    cacheData     = data;
                    cacheReadDone = true;
                });
                while (!cacheReadDone)
                {
                    yield return(null);
                }
                if (cacheHit)
                {
                    PtDebug.LogVerboseFormat("Web request CACHE HIT: {0}, response: {1} bytes",
                                             webRequest.url, cacheData.Length);
                    request.completionCallback(PolyStatus.Success(), /* responseCode */ 200, cacheData);

                    // Return the buffer to the pool for reuse.
                    CleanUpAfterWebRequest(bufferHolder);

                    yield break;
                }
                else
                {
                    PtDebug.LogVerboseFormat("Web request CACHE MISS: {0}.", webRequest.url);
                }
            }

            DownloadHandlerBuffer handler = new DownloadHandlerBuffer();

            webRequest.downloadHandler = handler;

            // We need to asset that we actually succeeded in setting the download handler, because this can fail
            // if, for example, the creation callback mistakenly called Send().
            PolyUtils.AssertTrue(webRequest.downloadHandler == handler,
                                 "Couldn't set download handler. It's either disposed of, or the creation callback mistakenly called Send().");

            // Start the web request. This will suspend this coroutine until the request is done.
            PtDebug.LogVerboseFormat("Sending web request: {0}", webRequest.url);
            yield return(UnityCompat.SendWebRequest(webRequest));

            // Request is finished. Call user-supplied callback.
            PtDebug.LogVerboseFormat("Web request finished: {0}, HTTP response code {1}, response: {2}",
                                     webRequest.url, webRequest.responseCode, webRequest.downloadHandler.text);
            PolyStatus status = UnityCompat.IsNetworkError(webRequest) ? PolyStatus.Error(webRequest.error) : PolyStatus.Success();

            request.completionCallback(status, (int)webRequest.responseCode, webRequest.downloadHandler.data);

            // Cache the result, if applicable.
            if (!UnityCompat.IsNetworkError(webRequest) && cacheAllowed)
            {
                byte[] data = webRequest.downloadHandler.data;
                if (data != null && data.Length > 0)
                {
                    byte[] copy = new byte[data.Length];
                    Buffer.BlockCopy(data, 0, copy, 0, data.Length);
                    cache.RequestWrite(webRequest.url, copy);
                }
            }

            // Clean up.
            webRequest.Dispose();
            CleanUpAfterWebRequest(bufferHolder);
        }
        public static void ConfigureTestCases()
        {
            // The default application name is different in different versions of Unity. Set the value
            // in the beginning of the test to ensure all AndroidManifest.xml are using the same
            // application name across different versions of Unity.
            UnityCompat.SetApplicationId(UnityEditor.BuildTarget.Android, "com.Company.ProductName");

            // Set of files to ignore (relative to the Assets/Plugins/Android directory) in all tests
            // that do not use the Gradle template.
            var nonGradleTemplateFilesToIgnore = new HashSet <string>()
            {
                Path.GetFileName(GRADLE_TEMPLATE_DISABLED),
                Path.GetFileName(GRADLE_TEMPLATE_LIBRARY_DISABLED)
            };

            UnityEngine.Debug.Log("Setting up test cases for execution.");
            IntegrationTester.Runner.ScheduleTestCases(new [] {
                // This *must* be the first test case as other test cases depend upon it.
                new IntegrationTester.TestCase {
                    Name   = "ValidateAndroidTargetSelected",
                    Method = ValidateAndroidTargetSelected,
                },
                new IntegrationTester.TestCase {
                    Name   = "SetupDependencies",
                    Method = (testCase, testCaseComplete) => {
                        ClearAllDependencies();
                        SetupDependencies();

                        var testCaseResult = new IntegrationTester.TestCaseResult(testCase);
                        ValidateDependencies(testCaseResult);
                        testCaseComplete(testCaseResult);
                    }
                },
                new IntegrationTester.TestCase {
                    Name   = "ResolveForGradleBuildSystemWithTemplate",
                    Method = (testCase, testCaseComplete) => {
                        ClearAllDependencies();
                        SetupDependencies();

                        ResolveWithGradleTemplate(
                            GRADLE_TEMPLATE_DISABLED,
                            "ExpectedArtifacts/NoExport/GradleTemplate",
                            testCase, testCaseComplete,
                            otherExpectedFiles: new [] {
                            "Assets/GeneratedLocalRepo/Firebase/m2repository/com/google/" +
                            "firebase/firebase-app-unity/5.1.1/firebase-app-unity-5.1.1.aar"
                        },
                            filesToIgnore: new HashSet <string> {
                            Path.GetFileName(GRADLE_TEMPLATE_LIBRARY_DISABLED)
                        });
                    }
                },
                new IntegrationTester.TestCase {
                    Name   = "ResolverForGradleBuildSystemWithTemplateUsingJetifier",
                    Method = (testCase, testCaseComplete) => {
                        ClearAllDependencies();
                        SetupDependencies();
                        GooglePlayServices.SettingsDialog.UseJetifier = true;

                        ResolveWithGradleTemplate(
                            GRADLE_TEMPLATE_DISABLED,
                            "ExpectedArtifacts/NoExport/GradleTemplateJetifier",
                            testCase, testCaseComplete,
                            otherExpectedFiles: new [] {
                            "Assets/GeneratedLocalRepo/Firebase/m2repository/com/google/" +
                            "firebase/firebase-app-unity/5.1.1/firebase-app-unity-5.1.1.aar"
                        },
                            filesToIgnore: new HashSet <string> {
                            Path.GetFileName(GRADLE_TEMPLATE_LIBRARY_DISABLED)
                        });
                    }
                },
                new IntegrationTester.TestCase {
                    Name   = "ResolveForGradleBuildSystemLibraryWithTemplate",
                    Method = (testCase, testCaseComplete) => {
                        ClearAllDependencies();
                        SetupDependencies();

                        ResolveWithGradleTemplate(
                            GRADLE_TEMPLATE_LIBRARY_DISABLED,
                            "ExpectedArtifacts/NoExport/GradleTemplateLibrary",
                            testCase, testCaseComplete,
                            otherExpectedFiles: new [] {
                            "Assets/GeneratedLocalRepo/Firebase/m2repository/com/google/" +
                            "firebase/firebase-app-unity/5.1.1/firebase-app-unity-5.1.1.aar"
                        },
                            filesToIgnore: new HashSet <string> {
                            Path.GetFileName(GRADLE_TEMPLATE_DISABLED)
                        });
                    }
                },
                new IntegrationTester.TestCase {
                    Name   = "ResolveForGradleBuildSystemWithTemplateEmpty",
                    Method = (testCase, testCaseComplete) => {
                        string enabledDependencies =
                            "Assets/ExternalDependencyManager/Editor/TestDependencies.xml";
                        string disabledDependencies =
                            "Assets/ExternalDependencyManager/Editor/TestDependenciesDISABLED.xml";
                        Action enableDependencies = () => {
                            UnityEditor.AssetDatabase.MoveAsset(disabledDependencies,
                                                                enabledDependencies);
                        };
                        try {
                            // Disable all XML dependencies.
                            var error = UnityEditor.AssetDatabase.MoveAsset(enabledDependencies,
                                                                            disabledDependencies);
                            if (!String.IsNullOrEmpty(error))
                            {
                                testCaseComplete(new IntegrationTester.TestCaseResult(testCase)
                                {
                                    ErrorMessages = new List <string>()
                                    {
                                        error
                                    }
                                });
                                return;
                            }
                            ClearAllDependencies();
                            ResolveWithGradleTemplate(
                                GRADLE_TEMPLATE_DISABLED,
                                "ExpectedArtifacts/NoExport/GradleTemplateEmpty",
                                testCase, (testCaseResult) => {
                                enableDependencies();
                                testCaseComplete(testCaseResult);
                            },
                                filesToIgnore: new HashSet <string> {
                                Path.GetFileName(GRADLE_TEMPLATE_LIBRARY_DISABLED)
                            });
                        } finally {
                            enableDependencies();
                        }
                    }
                },
                new IntegrationTester.TestCase {
                    Name   = "ResolveForGradleBuildSystem",
                    Method = (testCase, testCaseComplete) => {
                        ClearAllDependencies();
                        SetupDependencies();
                        Resolve("Gradle", false, "ExpectedArtifacts/NoExport/Gradle",
                                null, nonGradleTemplateFilesToIgnore, testCase, testCaseComplete);
                    }
                },
                new IntegrationTester.TestCase {
                    Name   = "ResolveForGradleBuildSystemSync",
                    Method = (testCase, testCaseComplete) => {
                        ClearAllDependencies();
                        SetupDependencies();
                        Resolve("Gradle", false, "ExpectedArtifacts/NoExport/Gradle",
                                null, nonGradleTemplateFilesToIgnore, testCase, testCaseComplete,
                                synchronous: true);
                    }
                },
                new IntegrationTester.TestCase {
                    Name   = "ResolveForGradleBuildSystemAndExport",
                    Method = (testCase, testCaseComplete) => {
                        ClearAllDependencies();
                        SetupDependencies();
                        Resolve("Gradle", true, "ExpectedArtifacts/Export/Gradle",
                                null, nonGradleTemplateFilesToIgnore, testCase, testCaseComplete);
                    }
                },
                new IntegrationTester.TestCase {
                    Name   = "ResolveAddedDependencies",
                    Method = (testCase, testCaseComplete) => {
                        ClearAllDependencies();
                        SetupDependencies();
                        UpdateAdditionalDependenciesFile(true);
                        Resolve("Gradle", true, "ExpectedArtifacts/Export/GradleAddedDeps",
                                null, nonGradleTemplateFilesToIgnore, testCase, testCaseComplete);
                    }
                },
                new IntegrationTester.TestCase {
                    Name   = "ResolveRemovedDependencies",
                    Method = (testCase, testCaseComplete) => {
                        ClearAllDependencies();
                        SetupDependencies();
                        // Add the additional dependencies file then immediately remove it.
                        UpdateAdditionalDependenciesFile(true);
                        UpdateAdditionalDependenciesFile(false);
                        Resolve("Gradle", true, "ExpectedArtifacts/Export/Gradle",
                                null, nonGradleTemplateFilesToIgnore, testCase, testCaseComplete);
                    }
                },
                new IntegrationTester.TestCase {
                    Name   = "DeleteResolvedLibraries",
                    Method = (testCase, testCaseComplete) => {
                        ClearAllDependencies();
                        SetupDependencies();
                        Resolve("Gradle", true, "ExpectedArtifacts/Export/Gradle",
                                null, nonGradleTemplateFilesToIgnore,
                                testCase, (testCaseResult) => {
                            PlayServicesResolver.DeleteResolvedLibrariesSync();
                            var unexpectedFilesMessage = new List <string>();
                            var resolvedFiles          = ListFiles("Assets/Plugins/Android",
                                                                   nonGradleTemplateFilesToIgnore);
                            if (resolvedFiles.Count > 0)
                            {
                                unexpectedFilesMessage.Add("Libraries not deleted!");
                                foreach (var filename in resolvedFiles.Values)
                                {
                                    unexpectedFilesMessage.Add(filename);
                                }
                            }
                            testCaseResult.ErrorMessages.AddRange(unexpectedFilesMessage);
                            testCaseComplete(testCaseResult);
                        },
                                synchronous: true);
                    }
                },
                new IntegrationTester.TestCase {
                    Name   = "ResolveForGradleBuildSystemWithTemplateDeleteLibraries",
                    Method = (testCase, testCaseComplete) => {
                        ClearAllDependencies();
                        SetupDependencies();
                        var filesToIgnore = new HashSet <string> {
                            Path.GetFileName(GRADLE_TEMPLATE_LIBRARY_DISABLED)
                        };

                        ResolveWithGradleTemplate(
                            GRADLE_TEMPLATE_DISABLED,
                            "ExpectedArtifacts/NoExport/GradleTemplate",
                            testCase, (testCaseResult) => {
                            PlayServicesResolver.DeleteResolvedLibrariesSync();
                            testCaseResult.ErrorMessages.AddRange(CompareDirectoryContents(
                                                                      "ExpectedArtifacts/NoExport/GradleTemplateEmpty",
                                                                      "Assets/Plugins/Android", filesToIgnore));
                            if (File.Exists(GRADLE_TEMPLATE_ENABLED))
                            {
                                File.Delete(GRADLE_TEMPLATE_ENABLED);
                            }
                            testCaseComplete(testCaseResult);
                        },
                            deleteGradleTemplate: false,
                            filesToIgnore: filesToIgnore);
                    }
                },
            });

            // Internal build system for Android is removed in Unity 2019, even
            // UnityEditor.AndroidBuildSystem.Internal still exist.
            if (IntegrationTester.Runner.UnityVersion < 2019.0f)
            {
                IntegrationTester.Runner.ScheduleTestCases(new [] {
                    new IntegrationTester.TestCase {
                        Name   = "ResolveForInternalBuildSystem",
                        Method = (testCase, testCaseComplete) => {
                            ClearAllDependencies();
                            SetupDependencies();
                            Resolve("Internal", false, AarsWithNativeLibrariesSupported ?
                                    "ExpectedArtifacts/NoExport/InternalNativeAars" :
                                    "ExpectedArtifacts/NoExport/InternalNativeAarsExploded",
                                    null, nonGradleTemplateFilesToIgnore, testCase,
                                    testCaseComplete);
                        }
                    },
                    new IntegrationTester.TestCase {
                        Name   = "ResolveForInternalBuildSystemUsingJetifier",
                        Method = (testCase, testCaseComplete) => {
                            ClearAllDependencies();
                            SetupDependencies();
                            GooglePlayServices.SettingsDialog.UseJetifier = true;
                            Resolve("Internal", false, AarsWithNativeLibrariesSupported ?
                                    "ExpectedArtifacts/NoExport/InternalNativeAarsJetifier" :
                                    "ExpectedArtifacts/NoExport/InternalNativeAarsExplodedJetifier",
                                    null, nonGradleTemplateFilesToIgnore, testCase,
                                    testCaseComplete);
                        }
                    },
                });
            }

            // Test resolution with Android ABI filtering.
            if (IntegrationTester.Runner.UnityVersion >= 2018.0f)
            {
                IntegrationTester.Runner.ScheduleTestCase(
                    new IntegrationTester.TestCase {
                    Name   = "ResolverForGradleBuildSystemUsingAbisArmeabiv7aAndArm64",
                    Method = (testCase, testCaseComplete) => {
                        ClearAllDependencies();
                        Resolve("Gradle", false,
                                "ExpectedArtifacts/NoExport/GradleArmeabiv7aArm64",
                                "armeabi-v7a, arm64-v8a", nonGradleTemplateFilesToIgnore,
                                testCase, testCaseComplete);
                    }
                });
            }
            else if (IntegrationTester.Runner.UnityVersion >= 5.0f)
            {
                IntegrationTester.Runner.ScheduleTestCase(
                    new IntegrationTester.TestCase {
                    Name   = "ResolverForGradleBuildSystemUsingAbisArmeabiv7a",
                    Method = (testCase, testCaseComplete) => {
                        ClearAllDependencies();
                        Resolve("Gradle", false,
                                "ExpectedArtifacts/NoExport/GradleArmeabiv7a",
                                "armeabi-v7a", nonGradleTemplateFilesToIgnore,
                                testCase, testCaseComplete);
                    }
                });
            }
        }