Expert: Integration of HMS Core Kits in MVVM and RxAndroid based Android App Part-1
Overview
In this article, I will create a Movie Show android application in which I will integrate HMS Core kits such as Huawei ID, Analytics, Huawei Ads and much more.
In this series of article I will cover all the kits with real life usages in Movie Show application. This is the part-1 article of this series.
Huawei ID Service Introduction
Huawei ID login provides you with simple, secure, and quick sign-in and authorization functions. Instead of entering accounts and passwords and waiting for authentication, users can just tap the Sign in with HUAWEI ID button to quickly and securely sign in to your app with their HUAWEI IDs.
Prerequisite
- Huawei Phone EMUI 3.0 or later.
- Non-Huawei phones Android 4.4 or later (API level 19 or higher).
- HMS Core APK 4.0.0.300 or later
- Android Studio
- AppGallery Account
App Gallery Integration process
- Sign In and Create or Choose a project on AppGallery Connect portal.
2. Navigate to Project settings and download the configuration file.
3. Navigate to General Information, and then provide Data Storage location.
App Development
- Create A New Project.
2. Configure Project Gradle.
buildscript {
repositories {
google()
jcenter()
maven { url 'http://developer.huawei.com/repo/' }
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.1'
classpath 'com.huawei.agconnect:agcp:1.2.1.301'
}
}
3. Configure App Gradle.
// Android MVVM Components
implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0'
// Android Support Library
implementation 'androidx.recyclerview:recyclerview:1.0.0'
implementation 'com.google.android.material:material:1.1.0-alpha04'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
//Huawei Dynamic ability
api 'com.huawei.hms:dynamicability:1.0.11.302'
implementation 'com.huawei.agconnect:agconnect-auth:1.4.1.300'
implementation 'com.huawei.hms:hwid:5.3.0.302'
// Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.6.2'
implementation "com.squareup.retrofit2:converter-gson:2.6.2"
implementation 'com.squareup.okhttp3:logging-interceptor:4.2.2'
implementation "com.squareup.retrofit2:adapter-rxjava2:2.6.2"
// RxAndroid
implementation 'io.reactivex.rxjava2:rxjava:2.2.8'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
//Glide
implementation 'com.github.bumptech.glide:glide:4.9.0'
4. Configure AndroidManifest.xml.
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
ShowsActivity:
package com.hms.manoj.aab.ui;import android.content.Intent;
import android.content.IntentSender;
import android.os.Bundle;
import android.util.Log;
import android.view.View;import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import androidx.lifecycle.ViewModelProviders;
import androidx.recyclerview.widget.GridLayoutManager;import com.hms.manoj.aab.R;
import com.hms.manoj.aab.apiconnector.response.Show;
import com.hms.manoj.aab.databinding.ActivityShowsBinding;
import com.hms.manoj.aab.ui.adapter.ShowAdapter;
import com.hms.manoj.aab.viewmodel.ShowsViewModel;
import com.huawei.hms.feature.install.FeatureInstallManager;
import com.huawei.hms.feature.install.FeatureInstallManagerFactory;
import com.huawei.hms.feature.listener.InstallStateListener;
import com.huawei.hms.feature.model.FeatureInstallException;
import com.huawei.hms.feature.model.FeatureInstallRequest;
import com.huawei.hms.feature.model.FeatureInstallSessionStatus;
import com.huawei.hms.feature.model.InstallState;
import com.huawei.hms.feature.tasks.FeatureTask;
import com.huawei.hms.feature.tasks.listener.OnFeatureCompleteListener;
import com.huawei.hms.feature.tasks.listener.OnFeatureFailureListener;
import com.huawei.hms.feature.tasks.listener.OnFeatureSuccessListener;import java.util.List;import static com.hms.manoj.aab.utils.Utils.KEY_SHOW_ID;public class ShowsActivity extends AppCompatActivity { public static final String TAG=ShowsActivity.class.getName(); private FeatureInstallManager mFeatureInstallManager; private int sessionId = 10086; private ActivityShowsBinding mBinding;
private ShowsViewModel mShowsViewModel; private InstallStateListener mStateUpdateListener = new InstallStateListener() {
@Override
public void onStateUpdate(InstallState state) {
Log.d(TAG, "install session state " + state);
if (state.status() == FeatureInstallSessionStatus.REQUIRES_USER_CONFIRMATION) {
try {
mFeatureInstallManager.triggerUserConfirm(state, ShowsActivity.this, 1);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
return;
} if (state.status() == FeatureInstallSessionStatus.REQUIRES_PERSON_AGREEMENT) {
try {
mFeatureInstallManager.triggerUserConfirm(state, ShowsActivity.this, 1);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
return;
} if (state.status() == FeatureInstallSessionStatus.INSTALLED) {
Log.i(TAG, "installed success ,can use new feature");
return;
} if (state.status() == FeatureInstallSessionStatus.UNKNOWN) {
Log.e(TAG, "installed in unknown status");
return;
} if (state.status() == FeatureInstallSessionStatus.DOWNLOADING) {
long process = state.bytesDownloaded() * 100 / state.totalBytesToDownload();
Log.d(TAG, "downloading percentage: " + process);
return;
} if (state.status() == FeatureInstallSessionStatus.FAILED) {
Log.e(TAG, "installed failed, errorcode : " + state.errorCode());
return;
} }
}; @Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getSupportActionBar().setTitle("All Shows");
mFeatureInstallManager = FeatureInstallManagerFactory.create(this);
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_shows);
mShowsViewModel = ViewModelProviders.of(this).get(ShowsViewModel.class); wait(true);
getShowList();
} public void installFeature(View view) {
if (mFeatureInstallManager == null) {
return;
}
FeatureInstallRequest request = FeatureInstallRequest.newBuilder()
.addModule("repository")
.addModule("retrofit")
.addModule("viewmodel")
.build();
final FeatureTask<Integer> task = mFeatureInstallManager.installFeature(request);
task.addOnListener(new OnFeatureSuccessListener<Integer>() {
@Override
public void onSuccess(Integer integer) {
Log.d(TAG, "load feature onSuccess.session id:" + integer);
}
});
task.addOnListener(new OnFeatureFailureListener<Integer>() {
@Override
public void onFailure(Exception exception) {
if (exception instanceof FeatureInstallException) {
int errorCode = ((FeatureInstallException) exception).getErrorCode();
Log.d(TAG, "load feature onFailure.errorCode:" + errorCode);
} else {
exception.printStackTrace();
}
}
});
task.addOnListener(new OnFeatureCompleteListener<Integer>() {
@Override
public void onComplete(FeatureTask<Integer> featureTask) {
if (featureTask.isComplete()) {
Log.d(TAG, "complete to start install.");
if (featureTask.isSuccessful()) {
Integer result = featureTask.getResult();
sessionId = result;
Log.d(TAG, "succeed to start install. session id :" + result);
} else {
Log.d(TAG, "fail to start install.");
Exception exception = featureTask.getException();
exception.printStackTrace();
}
}
}
});
Log.d(TAG, "start install func end");
}
private void wait(boolean isLoading) {
if (isLoading) {
mBinding.loaderLayout.rootLoader.setVisibility(View.VISIBLE);
mBinding.layout.shows.setVisibility(View.GONE);
} else {
mBinding.loaderLayout.rootLoader.setVisibility(View.GONE);
mBinding.layout.shows.setVisibility(View.VISIBLE);
}
} private void getShowList() {
mShowsViewModel.getShowsLiveData().observeForever(showList -> {
if (showList != null) {
wait(false);
String eventName = "Show Success";
Bundle bundle = new Bundle();
bundle.putInt("Show Size", showList.size());
setDataIntoAdapter(showList);
} else {
wait(false);
}
});
} private void setDataIntoAdapter(List<Show> list) {
mBinding.layout.shows.setLayoutManager(new GridLayoutManager(this, 2));
mBinding.layout.shows.setAdapter(new ShowAdapter(list, (item) -> {
Intent intent = new Intent(getBaseContext(), ShowDetailActivity.class);
intent.putExtra(KEY_SHOW_ID, String.valueOf(item.getId()));
String eventName = "Choosed Show";
Bundle bundle = new Bundle();
bundle.putInt("Show ID", item.getId());
startActivity(intent);
}));
} @Override
protected void onResume() {
super.onResume();
if (mFeatureInstallManager != null) {
mFeatureInstallManager.registerInstallListener(mStateUpdateListener);
}
} @Override
protected void onPause() {
super.onPause();
if (mFeatureInstallManager != null) {
mFeatureInstallManager.unregisterInstallListener(mStateUpdateListener);
}
}
}
LoginActivity:
package com.hms.manoj.aab;import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;import androidx.appcompat.app.AppCompatActivity;import com.hms.manoj.aab.ui.ShowsActivity;
import com.huawei.hmf.tasks.Task;
import com.huawei.hms.common.ApiException;
import com.huawei.hms.support.hwid.HuaweiIdAuthManager;
import com.huawei.hms.support.hwid.request.HuaweiIdAuthParams;
import com.huawei.hms.support.hwid.request.HuaweiIdAuthParamsHelper;
import com.huawei.hms.support.hwid.result.AuthHuaweiId;
import com.huawei.hms.support.hwid.service.HuaweiIdAuthService;
public class LoginActivity extends AppCompatActivity implements View.OnClickListener { private static final int REQUEST_SIGN_IN_LOGIN = 1002;
private static String TAG = LoginActivity.class.getName();
private HuaweiIdAuthService mAuthManager;
private HuaweiIdAuthParams mAuthParam; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
Button view = findViewById(R.id.btn_sign);
view.setOnClickListener(this); } private void signIn() {
mAuthParam = new HuaweiIdAuthParamsHelper(HuaweiIdAuthParams.DEFAULT_AUTH_REQUEST_PARAM)
.setIdToken()
.setAccessToken()
.createParams();
mAuthManager = HuaweiIdAuthManager.getService(this, mAuthParam);
startActivityForResult(mAuthManager.getSignInIntent(), REQUEST_SIGN_IN_LOGIN);
} @Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_sign:
signIn();
break;
}
} @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_SIGN_IN_LOGIN) {
Task<AuthHuaweiId> authHuaweiIdTask = HuaweiIdAuthManager.parseAuthResultFromIntent(data);
if (authHuaweiIdTask.isSuccessful()) {
AuthHuaweiId huaweiAccount = authHuaweiIdTask.getResult();
Log.i(TAG, huaweiAccount.getDisplayName() + " signIn success ");
Log.i(TAG, "AccessToken: " + huaweiAccount.getAccessToken()); Intent intent = new Intent(this, ShowsActivity.class);
intent.putExtra("user", huaweiAccount.getDisplayName());
startActivity(intent);
this.finish(); } else {
Log.i(TAG, "signIn failed: " + ((ApiException) authHuaweiIdTask.getException()).getStatusCode());
}
} }
}
ShowViewModel:
package com.hms.manoj.aab.viewmodel;import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;import com.hms.manoj.aab.apiconnector.response.Cast;
import com.hms.manoj.aab.apiconnector.response.Show;
import com.hms.manoj.aab.apiconnector.response.pojo.ShowDetail;
import com.hms.manoj.aab.repository.ShowRepository;import java.util.List;import io.reactivex.SingleObserver;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;public class ShowsViewModel extends ViewModel { private final CompositeDisposable disposables = new CompositeDisposable(); private MutableLiveData<List<Show>> mShowsLiveData = new MutableLiveData<>();
private MutableLiveData<ShowDetail> mShowByIdLiveData = new MutableLiveData<>();
private MutableLiveData<List<Cast>> mCastLiveData = new MutableLiveData<>(); private ShowRepository mShowRepository = new ShowRepository(); public LiveData<ShowDetail> getShowByIdLiveData(String showId) { disposables.add(mShowRepository.executeShowByIdApi(showId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> mShowByIdLiveData.setValue(result),
throwable -> mShowByIdLiveData.setValue(null)));
return mShowByIdLiveData;
} public LiveData<List<Show>> getShowsLiveData() {
mShowRepository.executeShowsApi()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SingleObserver<List<Show>>() {
@Override
public void onSubscribe(Disposable d) {
disposables.add(d);
} @Override
public void onSuccess(List<Show> showList) {
mShowsLiveData.setValue(showList);
} @Override
public void onError(Throwable e) {
mShowsLiveData.setValue(null);
}
});
return mShowsLiveData;
}
public LiveData<List<Cast>> getCastsLiveData(String showId) {
disposables.add(mShowRepository.executeCastApi(showId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> mCastLiveData.setValue(result),
throwable -> mCastLiveData.setValue(null)));
return mCastLiveData;
} @Override
protected void onCleared() {
disposables.clear();
}
}
ShowRepository:
package com.hms.manoj.aab.repository;import com.hms.manoj.aab.apiconnector.Client;
import com.hms.manoj.aab.apiconnector.response.Cast;
import com.hms.manoj.aab.apiconnector.response.Show;
import com.hms.manoj.aab.apiconnector.response.pojo.ShowDetail;import java.util.List;import io.reactivex.Single;
public class ShowRepository { private Client.Service mService; public ShowRepository() {
this.mService = Client.getClient();
} public Single<List<Show>> executeShowsApi() {
return mService.getShows();
} public Single<ShowDetail> executeShowByIdApi(String showId) {
return mService.getShowById(showId);
} public Single<List<Cast>> executeCastApi(String showId) {
return mService.getShowCast(showId);
}}
Client.Service:
package com.hms.manoj.aab.apiconnector;import com.hms.manoj.aab.apiconnector.response.Cast;
import com.hms.manoj.aab.apiconnector.response.Show;
import com.hms.manoj.aab.apiconnector.response.pojo.ShowDetail;
import com.hms.manoj.aab.utils.Utils;import java.util.List;
import java.util.concurrent.TimeUnit;import io.reactivex.Single;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.http.GET;
import retrofit2.http.Path;public class Client { private final static HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
private static OkHttpClient okHttpClient; public static Service getClient() {
interceptor.level(HttpLoggingInterceptor.Level.BODY);
interceptor.level(HttpLoggingInterceptor.Level.BASIC);
interceptor.level(HttpLoggingInterceptor.Level.HEADERS); if (okHttpClient == null) {
okHttpClient = new OkHttpClient.Builder()
.addInterceptor(interceptor)
.connectTimeout(90, TimeUnit.SECONDS)
.readTimeout(90, TimeUnit.SECONDS)
.build();
}
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Utils.BASE_URL)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build(); return retrofit.create(Service.class);
} public interface Service { @GET("/schedule/full")
Single<List<Show>> getShows(); @GET("/shows/{showId}")
Single<ShowDetail> getShowById(@Path("showId") String showId); @GET("/shows/{showId}/cast")
Single<List<Cast>> getShowCast(@Path("showId") String showId); }
}
App Build Result
Tips and Tricks
Identity Kit displays the HUAWEI ID registration or sign-in page first. The user can use the functions provided by Identity Kit only after signing in using a registered HUAWEI ID.
Conclusion
In this article, we have learned how to integrate Huawei ID in Android application. After completely read this article user can easily implement Huawei ID in the MovieShow application.
Thanks for reading this article. Be sure to like and comment to this article, if you found it helpful. It means a lot to me.
References
HMS Docs: