Home Connect SDK测试版:CLIENT_NOT_INITIALIZED错误

时间:2016-12-11 14:54:11

标签: java android api sdk authorization

根据提供的示例项目的文档和模拟,我未能成功将HomeConnect SDK Beta v1.2.0(https://developer.home-connect.com)实施到Android应用中。 不幸的是,我无法完成授权流程,并且遇到错误“CLIENT_NOT_INITIALIZED”。我知道SDK仍然处于测试阶段,但也许有人已经有了一些经验,可以帮助我解决这个问题。

出于测试目的并隔离问题,我创建了以下应用程序。 我正在应用程序覆盖中初始化HomeConnect类,在onCreate()中传递我的凭据并设置getter方法:

import android.app.Application;
import com.home_connect.sdk.Environment;
import com.home_connect.sdk.services.HomeConnect;

public class HCSignInDemoApplication extends Application {
private static HomeConnect mHomeConnect;

    @Override
    public void onCreate() {
        super.onCreate();
        mHomeConnect = new HomeConnect("signInDemo", this, "<MY_API_KEY>", Environment.SIMULATOR);
    }
    public static HomeConnect getHomeConnect() {
        return mHomeConnect;
    }
}

在MainActivity的onCreate()中,如果尚未授权HomeConnect类,则按钮的OnClickListener正在调用AuthorizationDialogFragment:

public class MainActivity extends AppCompatActivity implements AuthorizationDialogFragment.OnAuthorizedCallback {

    Button loginButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);
        loginButton = (Button) findViewById(R.id.loginBtn);

        loginButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Log.d("MainActivity", "LoginButton clicked");
                if(HCSignInDemoApplication.getHomeConnect().isAuthorized()){
                Log.d("MainActivity", "Is authorized");
                } else  {
                    Log.d("MainActivity", "Open HC AuthorizationDialogFragment");
                    new AuthorizationDialogFragment().show(getSupportFragmentManager(), "");
                }
            }
        });
    }

    @Override
    public void onAuthorized() {
        Log.d("MainActivity", "onAuthorized called");
    }
}

在按钮上单击以下DialogFragment应该打开一个WebView,用户可以在其中输入他/她的HomeConnect帐户的电子邮件和密码并获取访问令牌。

 import com.home_connect.sdk.exceptions.HomeConnectException;
 import com.home_connect.sdk.internal.util.TextUtil;
 import com.home_connect.sdk.model.Permission;
 import com.home_connect.sdk.property.RxBinder;
 import com.home_connect.sdk.services.HomeConnect;
 import java.util.Set;
 import rx.functions.Action1;

 /**
 * {@link DialogFragment} which is shown at the authorization process
 */
public class AuthorizationDialogFragment extends DialogFragment {

    private WebView mWebView;
    private OnAuthorizedCallback onAuthorizedCallback;

    public interface OnAuthorizedCallback {
        void onAuthorized();
    }
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setStyle(DialogFragment.STYLE_NO_FRAME, R.style.AppTheme);
    }
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        mWebView = new WebView(inflater.getContext());
        return mWebView;
    }
    @Override
    public void onDestroyView() {
        super.onDestroyView();
        mWebView = null;
    }
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        if (context instanceof OnAuthorizedCallback) {
            onAuthorizedCallback = (OnAuthorizedCallback) context;
        }
    }
    @Override
    public void onDetach() {
        super.onDetach();
        onAuthorizedCallback = null;
    }
    /**
     * set the {@link Permission} scopes and make a Token request via {@link HomeConnect}
     */
    @Override
    public void onResume() {
        super.onResume();

        Set<Permission> scopes = Permission.createScope(
            Permission.IDENTIFY_APPLIANCE
        );

        RxBinder.bind(this
            , HCSignInDemoApplication.getHomeConnect().requestToken(mWebView, scopes)
            , new Action1<Void>() {
                @Override
                public void call(Void aVoid) {
                    Toast.makeText(getContext(), "token request successful!", Toast.LENGTH_LONG).show();
                    if (onAuthorizedCallback != null) {
                        onAuthorizedCallback.onAuthorized();
                    }
                }
            }
            , new Action1<Throwable>() {
                @Override
                public void call(Throwable throwable) {
                    if (throwable instanceof HomeConnectException) {
                        HomeConnectException exception = (HomeConnectException) throwable;
                        showErrorDialog(exception);
                        dismiss();
                    }
                }
            }
        );
    }

    /**
     * Necessary call to unbind the {@link RxBinder} to avoid memory leaks
     */
    @Override
    public void onPause() {
        super.onPause();
        RxBinder.unbind(this);
    }

    protected void showErrorDialog(@Nullable HomeConnectException error) {
        Log.i("HCAuthDialogFragment", "Error: " + error);
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setTitle(R.string.error);
        if (error != null && error.apiError != null && !TextUtil.isEmpty(error.apiError.description)) {
            builder.setMessage(error.apiError.description);
        } else if (error != null && !TextUtil.isEmpty(error.description)) {
            builder.setMessage(error.description);
        } else if (error != null && !TextUtil.isEmpty(error.httpMessage)) {
            builder.setMessage(error.httpMessage);
        } else {
            builder.setMessage(R.string.unknown_error);
        }
        builder.setPositiveButton(R.string.ok, null);
        AlertDialog alertDialog = builder.create();
        alertDialog.show();
    }
}

好的,所以会发生什么而不是预期的WebView对话框是一个AlertDialog,错误代码为“CLIENT_NOT_INITIALIZED”,描述“客户端尚未初始化。请确保您的API密钥和重定向URL正确”。<登记/> API密钥肯定是正确的,但我不会在任何时候主动传递重定向URL,也没有选项。 完整日志如下所示:

Connected to process 3108 on device Nexus_5_API_24 [emulator-5554]
I/art: Not late-enabling -Xcheck:jni (already on)
W/art: Unexpected CPU variant for X86 using defaults: x86_64
E/art: Failed sending reply to debugger: Broken pipe
I/art: Debugger is no longer active
I/art: Starting a blocking GC Instrumentation
W/System: ClassLoader referenced unknown path: /data/app/android.test.net.hcsignindemo-1/lib/x86_64
I/InstantRun: Instant Run Runtime started. Android package is android.test.net.hcsignindemo, real application class is android.test.net.hcsignindemo.HCSignInDemoApplication.
W/System: ClassLoader referenced unknown path: /data/app/android.test.net.hcsignindemo-1/lib/x86_64
D/NetworkSecurityConfig: No Network Security Config specified, using platform default
W/art: Before Android 4.1, method android.graphics.PorterDuffColorFilter android.support.graphics.drawable.VectorDrawableCompat.updateTintFilter(android.graphics.PorterDuffColorFilter, android.content.res.ColorStateList, android.graphics.PorterDuff$Mode) would have incorrectly overridden the package-private method in android.graphics.drawable.Drawable
W/art: Verification of java.lang.Object android.support.v7.appcompat.R$attr.access$super(android.support.v7.appcompat.R$attr, java.lang.String, java.lang.Object[]) took 708.872ms
I/art: Background sticky concurrent mark sweep GC freed 20350(3MB) AllocSpace objects, 18(348KB) LOS objects, 39% free, 6MB/10MB, paused 5.598ms total 1.051s
I/OpenGLRenderer: Initialized EGL, version 1.4
D/OpenGLRenderer: Swap behavior 1
E/EGL_emulation: tid 3224: eglSurfaceAttrib(1146): error 0x3009 (EGL_BAD_MATCH)
W/OpenGLRenderer: Failed to set EGL_SWAP_BEHAVIOR on surface 0x72c1e3a56900, error=EGL_BAD_MATCH
I/Choreographer: Skipped 88 frames!  The application may be doing too much work on its main thread.
D/MainActivity: LoginButton clicked
D/MainActivity: Open HC AuthorizationDialogFragment
W/System: ClassLoader referenced unknown path: /system/app/webview/lib/x86_64
I/WebViewFactory: Loading com.android.webview version 52.0.2743.100 (code 275610060)
I/cr_LibraryLoader: Time to load native libraries: 59 ms (timestamps 4797-4856)
I/cr_LibraryLoader: Expected native library version number "52.0.2743.100", actual native library version number "52.0.2743.100"
V/WebViewChromiumFactoryProvider: Binding Chromium to main looper Looper (main, tid 1) {a14c6d}
I/cr_LibraryLoader: Expected native library version number "52.0.2743.100", actual native library version number "52.0.2743.100"
I/chromium: [INFO:library_loader_hooks.cc(143)] Chromium logging enabled: level = 0, default verbosity = 0
I/cr_BrowserStartup: Initializing chromium process, singleProcess=true
W/cr_media: Requires BLUETOOTH permission
I/cr_DRP: No DRP key due to exception:java.lang.ClassNotFoundException: com.android.webview.chromium.Drp
W/cr_AwContents: onDetachedFromWindow called when already detached. Ignoring
I/ANDROID.TEST.NET.HCSIGNINDEMO: (InitializeCall.java:40) createRequest: main creating init request...
I/Choreographer: Skipped 108 frames!  The application may be doing too much work on its main thread.
E/EGL_emulation: tid 3224: eglSurfaceAttrib(1146): error 0x3009 (EGL_BAD_MATCH)
W/OpenGLRenderer: Failed to set EGL_SWAP_BEHAVIOR on surface 0x72c1e3a56c00, error=EGL_BAD_MATCH
I/Choreographer: Skipped 34 frames!  The application may be doing too much work on its main thread.
W/art: Attempt to remove non-JNI local reference, dumping thread
I/art: Background partial concurrent mark sweep GC freed 14247(1514KB) AllocSpace objects, 6(120KB) LOS objects, 21% free, 14MB/18MB, paused 2.204ms total 127.182ms
I/ANDROID.TEST.NET.HCSIGNINDEMO: (InitializeCall.java:57) onJsonServerResponse: RxCachedThreadScheduler-1 handling init...
I/ANDROID.TEST.NET.HCSIGNINDEMO: (RequestSimulatorAuthorizationCall.java:36) createRequest: RxCachedThreadScheduler-1 creating simulator token request...
I/ANDROID.TEST.NET.HCSIGNINDEMO: (HttpApiCallClient.java:197) addInitializationInfo: RxCachedThreadScheduler-2 adding init info...
I/ANDROID.TEST.NET.HCSIGNINDEMO: (HCUtils.java:79) call: main retry attempt
I/HCAuthDialogFragment: Error: com.home_connect.sdk.exceptions.HomeConnectException: Description: The client hasn't been initialized. Please make sure your API key and redirect URL are correct
                    Error code: CLIENT_NOT_INITIALIZED
E/EGL_emulation: tid 3224: eglSurfaceAttrib(1146): error 0x3009 (EGL_BAD_MATCH)
W/OpenGLRenderer: Failed to set EGL_SWAP_BEHAVIOR on surface 0x72c1e3a56c00, error=EGL_BAD_MATCH
Application terminated.

HomeConnect SDK文档有一个故障排除部分,但遗憾的是不包括发生的错误。 我错过了一些必要的东西,或者可能是,当前版本的SDK还不能运行吗?

有什么用? HomeConnect提供了一个带有swagger UI(https://apiclient.home-connect.com/)的API客户端。在Chrome开发工具和Postman的帮助下,我能够找到client_secret(我在文档中找不到任何地方)。 有了这个,就可以通过HttpURLconnection获得一个有效的访问令牌,它允许使用一组示例设备获得JSON响应。但这忽略了使用SDK的重点。

//!!!!!! TEST !!!!!!
//get access_token through client_secret
private class PostRequestForToken extends AsyncTask<String, Void, HCAccessTokenModel> {

    @Override
    protected HCAccessTokenModel doInBackground(String... params) {

        HttpURLConnection connection = null;
        BufferedReader reader = null;

        try{
            // Open connection and handle request (OutputStream)
            URL url = new URL(params[0]);           // ParamsArray[0] is url address, needs to be "https://developer.home-connect.com/security/oauth/token"
            connection = (HttpURLConnection) url.openConnection();
            connection.setReadTimeout(10000);
            connection.setConnectTimeout(15000);
            connection.setRequestMethod("POST");
            connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            connection.setDoInput(true);
            connection.setDoOutput(true);

            Uri.Builder builder = new Uri.Builder()
                    .appendQueryParameter("client_id", "<MY_API_KEY>")          // API key from https://developer.home-connect.com/?q=user/my_apps
                    .appendQueryParameter("code", "96b5673f5e56eb9efba9424f94d5c0958377185767d6f2711f53d72c7ba4bb7f")               // redirect from https://developer.home-connect.com/security/oauth/authorize?client_id=<YOUR API KEY HERE>&redirect_uri=https://apiclient.home-connect.com/o2c.html&response_type=code&scope=IdentifyAppliance
                    .appendQueryParameter("grant_type", "authorization_code")
                    .appendQueryParameter("redirect_uri", "https://apiclient.home-connect.com/o2c.html")                            // redirect URL set in https://developer.home-connect.com/?q=user/my_apps
                    .appendQueryParameter("client_secret", "C8DED1367FEEFDE94C1390B6B3EC3EE9397A78943A6BA18CB416A430A0DE696F");      // retrieved through URL log on Swagger UI

            String query = builder.build().getEncodedQuery();

            OutputStream os = connection.getOutputStream();
            BufferedWriter writer = new BufferedWriter(
                    new OutputStreamWriter(os, "UTF-8"));
            writer.write(query);
            writer.flush();
            writer.close();
            os.close();

            connection.connect();

            // Handle response (InputStream)
            InputStream stream = connection.getInputStream();
            reader = new BufferedReader(new InputStreamReader(stream));
            StringBuffer buffer = new StringBuffer();

            String line = "";
            while ((line = reader.readLine()) != null){
                buffer.append(line);
            }

            String finalJson = buffer.toString();
            // Parse to JSON Object
            JSONObject parentObject = new JSONObject(finalJson);

            HCAccessTokenModel tokenModel = new HCAccessTokenModel();
            tokenModel.setAccess_token(parentObject.getString("access_token"));
            tokenModel.setExpires_in(parentObject.getString("expires_in"));
            tokenModel.setId_token(parentObject.getString("id_token"));
            tokenModel.setRefresh_token(parentObject.getString("refresh_token"));
            tokenModel.setScope(parentObject.getString("scope"));
            tokenModel.setToken_type(parentObject.getString("token_type"));


            return tokenModel;
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (JSONException e) {
            e.printStackTrace();
        } finally {
            if (connection != null) {
                connection.disconnect();
            }
        } try {
            if (reader != null){
                reader.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    protected void onPostExecute(HCAccessTokenModel tokenModel) {
        super.onPostExecute(tokenModel);
        Log.d(TAG, "token_type: " + tokenModel.getToken_type() + "\naccess_token: " + tokenModel.getAccess_token());
        new GetRequestForApplianceList().execute("https://developer.home-connect.com/api/homeappliances", tokenModel.getToken_type(), tokenModel.getAccess_token());
    }
}

private class GetRequestForApplianceList extends AsyncTask<String, Void, List<HCApplianceModel>> {

    @Override
    protected List<HCApplianceModel> doInBackground(String... params) {

        HttpURLConnection connection = null;
        BufferedReader reader = null;

        try {
            // Open connection
            URL url = new URL(params[0]);           // ParamsArray[0] is url address, needs to be "https://developer.home-connect.com/api/homeappliances"
            String token_type = params[1];          // ParamsArray[1] is token type, needs to be "Bearer"
            String access_token = params[2];        // ParamsArray[2]
            connection = (HttpURLConnection) url.openConnection();
            connection.setReadTimeout(10000);
            connection.setConnectTimeout(15000);
            connection.setRequestProperty("Authorization", token_type + " " + access_token);
            connection.setRequestProperty("Content-Type", "application/vnd.bsh.sdk.v1+json");
            connection.setRequestProperty("User-Agent", "Mozilla/5.0 ( compatible ) ");
            connection.setRequestProperty("Accept", "*/*");

            //connection.connect();
            Log.d(TAG, "Connected");

            // Handle response (InputStream)
            InputStream stream = new BufferedInputStream(connection.getInputStream());
            reader = new BufferedReader(new InputStreamReader(stream));
            StringBuffer buffer = new StringBuffer();

            String line = "";
            while ((line = reader.readLine()) != null) {
                buffer.append(line);
            }

            String finalJson = buffer.toString();
            // Parse to JSON Array
            JSONObject parentObject = new JSONObject(finalJson);
            JSONArray parentArray = parentObject.getJSONObject("data").getJSONArray("homeappliances");         //"homeappliances" is key of JSONArray

            List<HCApplianceModel> applianceModelList = new ArrayList<>();
            for (int i = 0; i < parentArray.length(); i++) {

                JSONObject finalObject = parentArray.getJSONObject(i);

                HCApplianceModel applianceModel = new HCApplianceModel();
                applianceModel.setHaId(finalObject.getString("haId"));
                applianceModel.setVib(finalObject.getString("vib"));
                applianceModel.setBrand(finalObject.getString("brand"));
                applianceModel.setType(finalObject.getString("type"));
                applianceModel.setName(finalObject.getString("name"));
                applianceModel.setEnumber(finalObject.getString("enumber"));
                applianceModel.setConnected(finalObject.getBoolean("connected"));

                applianceModelList.add(applianceModel);
            }

            return applianceModelList;           // This is result passed to onPostExecute(List<HCApplianceModel> applianceModels)

        } catch (ProtocolException e) {
            e.printStackTrace();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (JSONException e) {
            e.printStackTrace();
        } finally {
            if (connection != null) {
                connection.disconnect();
            }
        }
        try {
            if (reader != null) {
                reader.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }

    @Override
    protected void onPostExecute(List<HCApplianceModel> applianceModels) {
        super.onPostExecute(applianceModels);
        // Set TextView for testing
        String message = "";
        for (int j = 0; j < applianceModels.size(); j++){
            message += "haId: " + applianceModels.get(j).getHaId() + "\n";
            message += "vib: " + applianceModels.get(j).getVib() + "\n";
            message += "brand: " + applianceModels.get(j).getBrand() + "\n";
            message += "type: " + applianceModels.get(j).getType() + "\n";
            message += "name: " + applianceModels.get(j).getName() + "\n";
            message += "enumber: " + applianceModels.get(j).getEnumber() + "\n";
            message += "connected: " + applianceModels.get(j).isConnected() + "\n" + "\n";

        }

        textView.setText(message);

    }

}

0 个答案:

没有答案