I wrote my first Android UI application for a test job, but it was declined as employer said that he did't like "the code quality". He didn't specify what he meant. But I'm very interesting to know what's wrong with the code.
Here is the link to the Android Studio project.
Here is the text of this test:
Design and build android activity for serving full screen html ads.
When Activity is shown request for ad by sending HTTP POST request with parameter id with value of sim card IMSI (subscriber id) to http://www.505.rs/adviator/index.php
Example:
POST /adviator/index.php HTTP/1.1 Host: www.505.rs Cache-Control: no-cache Postman-Token: abd93bb8-2857-2fd0-7679-0b25087e1d35 Content-Type: application/x-www-form-urlencoded id=85950205030644900 { "status":"OK", "message":"display full screen ad", "url":"http://www.505.rs/adviator/ad.html" }
If status is equal "OK" use returned "url" and load ad into Activity webView.
If status is not equal "OK" show dialog with 'message' text and OK button. Clicking OK button will dismiss both dialog and activity.
While requesting for ad and loading ad html show spinner in center of screen and transparent background.
Once html ad is loaded hide spinner and show ad.
When user clicks ad link close activity and open native android browser with clicked url.
Activity should work in both portrait and landscape mode.
AdActivity.java:
package ru.cityads.test.activities;
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.StringRes;
import android.telephony.TelephonyManager;
import android.view.View;
import android.webkit.WebResourceError;
import android.webkit.WebResourceRequest;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ProgressBar;
import com.trello.rxlifecycle.components.RxActivity;
import ru.cityads.test.R;
import ru.cityads.test.services.AdResponse;
import ru.cityads.test.services.AdsService;
import rx.Observable;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
/**
* Activity that shows ads.
*/
public class AdActivity extends RxActivity
{
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ad);
setupWebView();
requestAd();
getDeviceID();
}
//region Private behaviour methods
private void finishDisplayAd()
{
this.finish();
}
//endregion
//region Private ad request methods
private void requestAd()
{
showProgress();
final AdsService adsService = new AdsService();
final Observable<AdResponse> adRequest = adsService.requestAd(getDeviceID());
adRequest.compose(bindToLifecycle())
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(adResponse -> { acceptAdResponse(adResponse); }, error -> handleAdRequestError(error));
}
private void handleAdRequestError(Throwable e)
{
showErrorMessage(R.string.error_dialog_unknown_error_message);
}
private void acceptAdResponse(AdResponse adResponse)
{
if(adResponse.checkStatus())
{
loadUrl(adResponse.getUrl());
}
else
{
showErrorMessage(adResponse.getMessage());
}
}
//endregion
//region Private view helpers
private void setupWebView()
{
getWebView().setWebViewClient(new WebViewClient()
{
@Override
public void onPageFinished(WebView view, String url)
{
AdActivity.this.showWebView();
}
@Override
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error)
{
AdActivity.this.showErrorMessage(R.string.error_dialog_web_load_fail_message);
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url)
{
final Uri uri = Uri.parse(url);
final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
finishDisplayAd();
return true;
}
});
}
private void showProgress()
{
getProgressBar().setVisibility(View.VISIBLE);
getWebView().setVisibility(View.GONE);
}
private void showErrorMessage(@StringRes int errorMessageId)
{
final String message = getResources().getString(errorMessageId);
showErrorMessage(message);
}
private void showErrorMessage(String errorMessage)
{
final AlertDialog.Builder dialog = new AlertDialog.Builder(this);
dialog.setMessage(errorMessage);
dialog.setTitle(R.string.error_dialog_title);
dialog.setPositiveButton(R.string.error_dialog_button_text, (a, b) -> finishDisplayAd());
dialog.create();
dialog.show();
}
private void loadUrl(String url)
{
getWebView().loadUrl(url);
}
private void showWebView()
{
getProgressBar().setVisibility(View.GONE);
getWebView().setVisibility(View.VISIBLE);
}
//endregion
//region Private view getters
private ProgressBar getProgressBar()
{
return (ProgressBar)findViewById(R.id.progressBar);
}
private WebView getWebView()
{
return (WebView)findViewById(R.id.webView);
}
//endregion
//region Private device id getter method
public String getDeviceID()
{
String deviceID = null;
try
{
String serviceName = Context.TELEPHONY_SERVICE;
TelephonyManager m_telephonyManager = (TelephonyManager) getSystemService(serviceName);
deviceID = m_telephonyManager.getDeviceId();
}
catch(Throwable e)
{
}
if(deviceID == null)
{
deviceID = "000000000000000";
}
return deviceID;
}
//endregion
}
AdsService.java:
package ru.cityads.test.services;
import com.squareup.okhttp.OkHttpClient;
import java.util.concurrent.TimeUnit;
import retrofit.GsonConverterFactory;
import retrofit.Retrofit;
import retrofit.RxJavaCallAdapterFactory;
import retrofit.http.Field;
import retrofit.http.FormUrlEncoded;
import retrofit.http.POST;
import ru.cityads.test.BuildConfig;
import rx.Observable;
/**
* Service used to request ads.
*
* @see AdResponse
*/
public class AdsService
{
public AdsService()
{
final OkHttpClient httpClient = new OkHttpClient();
httpClient.setConnectTimeout(BuildConfig.HTTP_CLIENT_CONNECT_TIMEOUT, TimeUnit.SECONDS);
httpClient.setWriteTimeout(BuildConfig.HTTP_CLIENT_WRITE_TIMEOUT, TimeUnit.SECONDS);
httpClient.setReadTimeout(BuildConfig.HTTP_CLIENT_READ_TIMEOUT, TimeUnit.SECONDS);
final Retrofit retrofit = new Retrofit.Builder()
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(BuildConfig.API_BASE_URL)
.client(httpClient)
.build();
mRemoteInterface = retrofit.create(RemoteInterface.class);
}
public Observable<AdResponse> requestAd(String requesterId)
{
return mRemoteInterface.requestAd(requesterId);
}
//region Interface representing remote ad service. Implemented by Retrofit.
private interface RemoteInterface
{
@FormUrlEncoded
@POST("/adviator/index.php")
Observable<AdResponse> requestAd(@Field("id") String id);
}
//endregion
//region Private data
private final RemoteInterface mRemoteInterface;
//endregion
}
AdResponse.java:
package ru.cityads.test.services;
import com.google.gson.annotations.SerializedName;
/**
* Represents response to ad request, made by {@link AdsService}
*
* @see AdsService
*/
public class AdResponse
{
public String getStatus()
{
return mStatus;
}
public String getMessage()
{
return mMessage;
}
public String getUrl()
{
return mUrl;
}
public boolean checkStatus()
{
return getStatus().equals("OK");
}
//region Private data
@SerializedName("status")
private String mStatus;
@SerializedName("message")
private String mMessage;
@SerializedName("url")
private String mUrl;
//endregion
}