Commit a6207602 authored by Javier Guerra Melgares's avatar Javier Guerra Melgares

Merge branch 'feature/phase-3-work-item-1' into develop

parents 5e14bd7e ec737526
Subproject commit f4ae603d09e81a3673e395d7382450b52e6be2cb
Subproject commit bb53715d5e4dbe30360685690bac61f2e4546f6b
......@@ -30,7 +30,7 @@ android {
minSdkVersion 21
targetSdkVersion 28
versionCode 13
versionName "2.12.0"
versionName "2.13.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
compileOptions {
......
......@@ -101,6 +101,11 @@
android:configChanges="orientation|screenSize"
android:label="@string/trust_anchor_title"
android:parentActivityName=".view.devicelist.DeviceListActivity" />
<activity
android:name=".view.cloud.CloudActivity"
android:configChanges="orientation|screenSize"
android:label="@string/cloud_title"
android:parentActivityName=".view.devicelist.DeviceListActivity" />
<activity
android:name=".view.trustanchor.CertificateActivity"
android:configChanges="orientation|screenSize"
......
package org.openconnectivity.otgc.data.repository;
import org.iotivity.OCCloud;
import org.iotivity.OCCloudContext;
import org.openconnectivity.otgc.domain.model.resource.cloud.OcCloudConfiguration;
import javax.inject.Inject;
import javax.inject.Singleton;
import io.reactivex.Completable;
import io.reactivex.Single;
@Singleton
public class CloudRepository {
@Inject
public CloudRepository() {
}
public Single<Integer> retrieveState() {
return Single.create(emitter -> {
OCCloudContext ctx = OCCloud.getContext(0);
short status = ctx.getStore().getStatus();
emitter.onSuccess((int)status);
});
}
public Single<OcCloudConfiguration> retrieveCloudConfiguration() {
return Single.create(emitter -> {
OCCloudContext ctx = OCCloud.getContext(0);
String apn = ctx.getStore().getAuth_provider();
String url = ctx.getStore().getCi_server();
String at = ctx.getStore().getAccess_token();
String uuid = ctx.getStore().getSid();
emitter.onSuccess(new OcCloudConfiguration(apn, url, at, uuid));
});
}
public Completable provisionCloudConfiguration(String authProvider, String cloudUrl, String accessToken, String cloudUuid) {
return Completable.create(emitter -> {
OCCloudContext ctx = OCCloud.getContext(0);
OCCloud.provisionConfResource(ctx, authProvider, cloudUrl, accessToken, cloudUuid);
emitter.onComplete();
});
}
}
\ No newline at end of file
package org.openconnectivity.otgc.domain.model.resource.cloud;
public class OcCloudConfiguration {
private String authProvider;
private String cloudUrl;
private String accessToken;
private String cloudUuid;
public OcCloudConfiguration() { }
public OcCloudConfiguration(String apn, String url, String at, String uuid) {
this.authProvider = apn;
this.cloudUrl = url;
this.accessToken = at;
this.cloudUuid = uuid;
}
public String getAuthProvider() {
return authProvider;
}
public void setAuthProvider(String authProvider) {
this.authProvider = authProvider;
}
public String getCloudUrl() {
return cloudUrl;
}
public void setCloudUrl(String cloudUrl) {
this.cloudUrl = cloudUrl;
}
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public String getCloudUuid() {
return cloudUuid;
}
public void setCloudUuid(String cloudUuid) {
this.cloudUuid = cloudUuid;
}
}
package org.openconnectivity.otgc.domain.usecase.cloud;
import org.openconnectivity.otgc.data.repository.CloudRepository;
import javax.inject.Inject;
import io.reactivex.Completable;
public class ProvisionCloudConfUseCase {
private final CloudRepository cloudRepository;
@Inject
public ProvisionCloudConfUseCase(CloudRepository cloudRepository) {
this.cloudRepository = cloudRepository;
}
public Completable execute(String authProvider, String cloudUrl, String accessToken, String cloudUuid) {
return cloudRepository.provisionCloudConfiguration(authProvider, cloudUrl, accessToken, cloudUuid);
}
}
package org.openconnectivity.otgc.domain.usecase.cloud;
import org.openconnectivity.otgc.data.repository.CloudRepository;
import org.openconnectivity.otgc.domain.model.resource.cloud.OcCloudConfiguration;
import javax.inject.Inject;
import io.reactivex.Single;
public class RetrieveCloudConfigurationUseCase {
private final CloudRepository cloudRepository;
@Inject
public RetrieveCloudConfigurationUseCase(CloudRepository cloudRepository) {
this.cloudRepository = cloudRepository;
}
public Single<OcCloudConfiguration> execute() {
return cloudRepository.retrieveCloudConfiguration();
}
}
package org.openconnectivity.otgc.domain.usecase.cloud;
import org.openconnectivity.otgc.data.repository.CloudRepository;
import javax.inject.Inject;
import io.reactivex.Single;
public class RetrieveStatusUseCase {
private final CloudRepository cloudRepository;
@Inject
public RetrieveStatusUseCase(CloudRepository cloudRepository) {
this.cloudRepository = cloudRepository;
}
public Single<Integer> execute() {
return cloudRepository.retrieveState();
}
}
......@@ -25,6 +25,7 @@ import org.openconnectivity.otgc.view.accesscontrol.AccessControlActivity;
import org.openconnectivity.otgc.view.accesscontrol.AceActivity;
import org.openconnectivity.otgc.view.client.ClientBuildersModule;
import org.openconnectivity.otgc.view.client.GenericClientActivity;
import org.openconnectivity.otgc.view.cloud.CloudActivity;
import org.openconnectivity.otgc.view.credential.CredActivity;
import org.openconnectivity.otgc.view.credential.CredentialsActivity;
import org.openconnectivity.otgc.view.devicelist.DeviceListBuildersModule;
......@@ -77,4 +78,7 @@ public interface BuildersModule {
@ContributesAndroidInjector
abstract CertificateActivity bindCertificateActivity();
@ContributesAndroidInjector
abstract CloudActivity bindCloudActivity();
}
......@@ -28,6 +28,7 @@ import androidx.lifecycle.ViewModelProvider;
import org.openconnectivity.otgc.viewmodel.AccessControlViewModel;
import org.openconnectivity.otgc.viewmodel.AceViewModel;
import org.openconnectivity.otgc.viewmodel.CertificateViewModel;
import org.openconnectivity.otgc.viewmodel.CloudViewModel;
import org.openconnectivity.otgc.viewmodel.ResourceViewModel;
import org.openconnectivity.otgc.utils.viewmodel.ViewModelFactory;
import org.openconnectivity.otgc.viewmodel.GenericClientViewModel;
......@@ -125,4 +126,9 @@ abstract class ViewModelModule {
@IntoMap
@ViewModelKey(CertificateViewModel.class)
abstract ViewModel bindCertificateViewModel(CertificateViewModel certificateViewModel);
@Binds
@IntoMap
@ViewModelKey(CloudViewModel.class)
abstract ViewModel bindCloudViewModel(CloudViewModel cloudViewModel);
}
package org.openconnectivity.otgc.view.cloud;
import android.os.Bundle;
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelProviders;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import org.iotivity.OCCloudStatusMask;
import org.openconnectivity.otgc.R;
import org.openconnectivity.otgc.domain.model.resource.cloud.OcCloudConfiguration;
import org.openconnectivity.otgc.utils.di.Injectable;
import org.openconnectivity.otgc.utils.viewmodel.ViewModelError;
import org.openconnectivity.otgc.viewmodel.CloudViewModel;
import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class CloudActivity extends AppCompatActivity implements Injectable {
@Inject
ViewModelProvider.Factory mViewModelFactory;
@BindView(R.id.progress_bar)
ProgressBar mProgressBar;
@BindView(R.id.toolbar)
Toolbar mToolbar;
@BindView(R.id.cloud_label_status)
TextView mLabelStatus;
@BindView(R.id.cloud_text_auth_provider)
EditText mTextAuthProvider;
@BindView(R.id.cloud_text_url)
EditText mTextUrl;
@BindView(R.id.cloud_text_access_token)
EditText mTextAccessToken;
@BindView(R.id.cloud_text_uuid)
EditText mTextUuid;
@BindView(R.id.floating_button_cloud_add)
FloatingActionButton mFloatingActionButton;
@OnClick(R.id.floating_button_cloud_add)
protected void onAddPressed() {
// TODO: Retrieve configuration from view
String authProvider = mTextAuthProvider.getText().toString();
String cloudUrl = mTextUrl.getText().toString();
String accessToken = mTextAccessToken.getText().toString();
String cloudUuid = mTextUuid.getText().toString();
mViewModel.provisionCloudConfiguration(authProvider, cloudUrl, accessToken, cloudUuid);
}
private CloudViewModel mViewModel;
@Override
protected void onCreate(Bundle savedInstance) {
super.onCreate(savedInstance);
setContentView(R.layout.activity_cloud);
ButterKnife.bind(this);
initViews();
initViewModel();
}
@Override
protected void onResume() {
super.onResume();
mViewModel.retrieveStatus();
mViewModel.retrieveCloudConfiguration();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == android.R.id.home) {
onBackPressed();
return true;
}
return super.onOptionsItemSelected(item);
}
private void initViews() {
setSupportActionBar(mToolbar);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setDisplayShowHomeEnabled(true);
}
}
private void initViewModel() {
mViewModel = ViewModelProviders.of(this, mViewModelFactory).get(CloudViewModel.class);
mViewModel.isProcessing().observe(this, this::handleProcessing);
mViewModel.getError().observe(this, this::handleError);
mViewModel.getStatus().observe(this, this::processStatus);
mViewModel.getCloudConfiguration().observe(this, this::processCloudConfiguration);
mViewModel.getSuccess().observe(this, this::processSuccess);
}
private void handleProcessing(@NonNull Boolean isProcessing) {
mProgressBar.setVisibility(isProcessing ? View.VISIBLE : View.GONE);
}
private void handleError(@NonNull ViewModelError error) {
int errorId = -1;
switch ((CloudViewModel.Error)error.getType()) {
case RETRIEVE_STATUS:
errorId = R.string.cloud_retrieve_status_error;
break;
case RETRIEVE_CONFIGURATION:
errorId = R.string.cloud_retrieve_configuration_error;
break;
case PROVISION_CONFIGURATION:
errorId = R.string.cloud_provision_configuration_error;
break;
}
if (errorId != -1) {
Toast.makeText(this, errorId, Toast.LENGTH_SHORT).show();
}
}
private void processStatus(@NonNull Integer status) {
// Set value of status
switch(status) {
case OCCloudStatusMask.OC_CLOUD_INITIALIZED:
mLabelStatus.setText("Initialized");
break;
case OCCloudStatusMask.OC_CLOUD_REGISTERED:
mLabelStatus.setText("Registered");
break;
case OCCloudStatusMask.OC_CLOUD_LOGGED_IN:
mLabelStatus.setText("Logged in");
break;
case OCCloudStatusMask.OC_CLOUD_TOKEN_EXPIRY:
mLabelStatus.setText("Token expiry");
break;
case OCCloudStatusMask.OC_CLOUD_REFRESHED_TOKEN:
mLabelStatus.setText("Refresh token");
break;
case OCCloudStatusMask.OC_CLOUD_LOGGED_OUT:
mLabelStatus.setText("Logged out");
break;
case OCCloudStatusMask.OC_CLOUD_FAILURE:
mLabelStatus.setText("Failure");
break;
case OCCloudStatusMask.OC_CLOUD_DEREGISTERED:
mLabelStatus.setText("Deregistered");
break;
default:
mLabelStatus.setText("Unknown");
break;
}
}
private void processCloudConfiguration(@NonNull OcCloudConfiguration cloudConf) {
mTextAuthProvider.setText(cloudConf.getAuthProvider());
mTextUrl.setText(cloudConf.getCloudUrl());
mTextAccessToken.setText(cloudConf.getAccessToken());
mTextUuid.setText(cloudConf.getCloudUuid());
}
private void processSuccess(@NonNull Boolean success) {
if (success) {
Toast.makeText(this, R.string.cloud_store_config_success, Toast.LENGTH_SHORT).show();
}
}
}
......@@ -46,6 +46,7 @@ import org.openconnectivity.otgc.utils.viewmodel.CommonError;
import org.openconnectivity.otgc.utils.viewmodel.Response;
import org.openconnectivity.otgc.utils.viewmodel.Status;
import org.openconnectivity.otgc.utils.viewmodel.ViewModelError;
import org.openconnectivity.otgc.view.cloud.CloudActivity;
import org.openconnectivity.otgc.view.trustanchor.TrustAnchorActivity;
import org.openconnectivity.otgc.viewmodel.DeviceListViewModel;
import org.openconnectivity.otgc.viewmodel.SharedViewModel;
......@@ -206,6 +207,9 @@ public class DeviceListActivity extends AppCompatActivity implements HasSupportF
case R.id.menu_item_trust_anchor:
onTrustAnchorManagement();
break;
case R.id.menu_item_cloud_configuration:
onCloudConfiguration();
break;
case R.id.menu_item_log:
onLogPressed();
break;
......@@ -357,6 +361,11 @@ public class DeviceListActivity extends AppCompatActivity implements HasSupportF
startActivity(trustAnchorIntent);
}
private void onCloudConfiguration() {
Intent cloudIntent = new Intent().setClass(DeviceListActivity.this, CloudActivity.class);
startActivity(cloudIntent);
}
private void onLogPressed() {
Intent intent = new Intent(this, LogViewerActivity.class);
startActivity(intent);
......
......@@ -22,7 +22,6 @@ import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelProviders;
import org.openconnectivity.otgc.R;
import org.openconnectivity.otgc.domain.model.resource.secure.cred.OcCredential;
import org.openconnectivity.otgc.utils.di.Injectable;
import org.openconnectivity.otgc.utils.viewmodel.ViewModelError;
import org.openconnectivity.otgc.viewmodel.CertificateViewModel;
......
package org.openconnectivity.otgc.viewmodel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import org.openconnectivity.otgc.domain.model.resource.cloud.OcCloudConfiguration;
import org.openconnectivity.otgc.domain.usecase.cloud.ProvisionCloudConfUseCase;
import org.openconnectivity.otgc.domain.usecase.cloud.RetrieveCloudConfigurationUseCase;
import org.openconnectivity.otgc.domain.usecase.cloud.RetrieveStatusUseCase;
import org.openconnectivity.otgc.utils.rx.SchedulersFacade;
import org.openconnectivity.otgc.utils.viewmodel.ViewModelError;
import org.openconnectivity.otgc.utils.viewmodel.ViewModelErrorType;
import javax.inject.Inject;
import io.reactivex.disposables.CompositeDisposable;
public class CloudViewModel extends ViewModel {
private CompositeDisposable disposable = new CompositeDisposable();
private final SchedulersFacade schedulersFacade;
private final MutableLiveData<Boolean> mProcessing = new MutableLiveData<>();
private final MutableLiveData<ViewModelError> mError = new MutableLiveData<>();
private final MutableLiveData<Boolean> mSuccess = new MutableLiveData<>();
// Use cases
private final RetrieveStatusUseCase retrieveStatusUseCase;
private final RetrieveCloudConfigurationUseCase retrieveCloudConfigurationUseCase;
private final ProvisionCloudConfUseCase provisionCloudConfUseCase;
// Observable values
private final MutableLiveData<Integer> status = new MutableLiveData<>();
private final MutableLiveData<OcCloudConfiguration> cloudConf = new MutableLiveData<>();
@Inject
public CloudViewModel(SchedulersFacade schedulersFacade,
RetrieveStatusUseCase retrieveStatusUseCase,
RetrieveCloudConfigurationUseCase retrieveCloudConfigurationUseCase,
ProvisionCloudConfUseCase provisionCloudConfUseCase) {
this.schedulersFacade = schedulersFacade;
this.retrieveStatusUseCase = retrieveStatusUseCase;
this.retrieveCloudConfigurationUseCase = retrieveCloudConfigurationUseCase;
this.provisionCloudConfUseCase = provisionCloudConfUseCase;
}
@Override
protected void onCleared() {
disposable.clear();
}
public LiveData<Boolean> isProcessing() {
return mProcessing;
}
public LiveData<ViewModelError> getError() {
return mError;
}
public LiveData<Boolean> getSuccess() {
return mSuccess;
}
public LiveData<Integer> getStatus() {
return status;
}
public LiveData<OcCloudConfiguration> getCloudConfiguration() {
return cloudConf;
}
public void retrieveStatus() {
disposable.add(retrieveStatusUseCase.execute()
.subscribeOn(schedulersFacade.io())
.observeOn(schedulersFacade.ui())
.subscribe(
status::setValue,
throwable -> mError.setValue(new ViewModelError(Error.RETRIEVE_STATUS, throwable.getMessage()))
));
}
public void retrieveCloudConfiguration() {
disposable.add(retrieveCloudConfigurationUseCase.execute()
.subscribeOn(schedulersFacade.io())
.observeOn(schedulersFacade.ui())
.subscribe(
cloudConf::setValue,
throwable -> mError.setValue(new ViewModelError(Error.RETRIEVE_CONFIGURATION, throwable.getMessage()))
));
}
public void provisionCloudConfiguration(String authProvider, String cloudUrl, String accessToken, String cloudUuid) {
disposable.add(provisionCloudConfUseCase.execute(authProvider, cloudUrl, accessToken, cloudUuid)
.subscribeOn(schedulersFacade.io())
.observeOn(schedulersFacade.ui())
.subscribe(
() -> mSuccess.setValue(true),
throwable -> mError.setValue(new ViewModelError(Error.PROVISION_CONFIGURATION, throwable.getMessage()))
));
}
public enum Error implements ViewModelErrorType {
RETRIEVE_STATUS,
RETRIEVE_CONFIGURATION,
PROVISION_CONFIGURATION
}
}
<?xml version="1.0" encoding="utf-8"?>
<!--
~ *****************************************************************
~
~ Copyright 2018 DEKRA Testing and Certification, S.A.U. All Rights Reserved.
~
~ *****************************************************************
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
~
~ *****************************************************************
-->
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white1"
android:keepScreenOn="true">
<com.google.android.material.appbar.AppBarLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="top">
<include
layout="@layout/progress_bar" />