package org.commcare.activities;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ListView;
import android.widget.Switch;
import android.widget.TextView;
import android.widget.ToggleButton;
import org.commcare.CommCareApplication;
import org.commcare.core.interfaces.HttpResponseProcessor;
import org.commcare.dalvik.R;
import org.commcare.logging.AndroidLogger;
import org.commcare.models.database.SqlStorage;
import org.commcare.modern.util.Pair;
import org.commcare.android.database.global.models.AppAvailableToInstall;
import org.commcare.tasks.SimpleHttpTask;
import org.commcare.tasks.templates.CommCareTaskConnector;
import org.commcare.xml.AvailableAppsParser;
import org.javarosa.core.services.Logger;
import org.javarosa.core.services.locale.Localization;
import org.javarosa.xml.ElementParser;
import org.javarosa.xml.util.InvalidStructureException;
import org.javarosa.xml.util.UnfullfilledRequirementsException;
import org.kxml2.io.KXmlParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* Created by amstone326 on 2/3/17.
*/
public class InstallFromListActivity<T> extends CommCareActivity<T> implements HttpResponseProcessor {
private static final String TAG = InstallFromListActivity.class.getSimpleName();
public static final String PROFILE_REF = "profile-ref-selected";
private static final String REQUESTED_FROM_PROD_KEY = "have-requested-from-prod";
private static final String REQUESTED_FROM_INDIA_KEY = "have-requested-from-india";
private static final String ERROR_MESSAGE_KEY = "error-message-key";
private static final String PROD_URL = "https://www.commcarehq.org/phone/list_apps";
private static final String INDIA_URL = "https://india.commcarehq.org/phone/list_apps";
private static final int RETRIEVE_APPS_FOR_DIFF_USER = Menu.FIRST;
private boolean inMobileUserAuthMode;
private boolean requestedFromProd;
private boolean requestedFromIndia;
private String urlCurrentlyRequestingFrom;
private String errorMessage;
private View authenticateView;
private TextView errorMessageBox;
private View appsListContainer;
private ListView appsListView;
private List<AppAvailableToInstall> availableApps = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
loadStateFromSavedInstance(savedInstanceState);
setupUI();
loadPreviouslyRetrievedAvailableApps();
}
private void setupUI() {
setContentView(R.layout.user_get_available_apps);
errorMessageBox = (TextView)findViewById(R.id.error_message);
authenticateView = findViewById(R.id.authenticate_view);
setUpGetAppsButton();
setUpAppsList();
setUpToggle();
}
private void setUpGetAppsButton() {
Button getAppsButton = (Button)findViewById(R.id.get_apps_button);
getAppsButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
errorMessageBox.setVisibility(View.INVISIBLE);
if (inputIsValid()) {
authenticateView.setVisibility(View.GONE);
requestedFromIndia = false;
requestedFromProd = false;
requestAppList();
}
}
});
}
private void setUpAppsList() {
appsListContainer = findViewById(R.id.apps_list_container);
appsListView = (ListView)findViewById(R.id.apps_list_view);
appsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (position < availableApps.size()) {
AppAvailableToInstall app = availableApps.get(position);
Intent i = new Intent(getIntent());
i.putExtra(PROFILE_REF, app.getMediaProfileRef());
setResult(RESULT_OK, i);
finish();
}
}
});
}
private void setUpToggle() {
FrameLayout toggleContainer = (FrameLayout)findViewById(R.id.toggle_button_container);
CompoundButton userTypeToggler;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
Switch switchButton = new Switch(this);
switchButton.setTextOff(Localization.get("toggle.web.user.mode"));
switchButton.setTextOn(Localization.get("toggle.mobile.user.mode"));
userTypeToggler = switchButton;
} else {
ToggleButton toggleButton = new ToggleButton(this);
toggleButton.setTextOff(Localization.get("toggle.web.user.mode"));
toggleButton.setTextOn(Localization.get("toggle.mobile.user.mode"));
userTypeToggler = toggleButton;
}
final View mobileUserView = findViewById(R.id.mobile_user_view);
final View webUserView = findViewById(R.id.web_user_view);
userTypeToggler.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
inMobileUserAuthMode = isChecked;
if (inMobileUserAuthMode) {
mobileUserView.setVisibility(View.VISIBLE);
webUserView.setVisibility(View.GONE);
} else {
mobileUserView.setVisibility(View.GONE);
webUserView.setVisibility(View.VISIBLE);
}
errorMessageBox.setVisibility(View.INVISIBLE);
((EditText)findViewById(R.id.edit_password)).setText("");
}
});
userTypeToggler.setChecked(true);
toggleContainer.addView(userTypeToggler);
}
private boolean inputIsValid() {
String enteredPassword = ((EditText)findViewById(R.id.edit_password)).getText().toString();
if ("".equals(enteredPassword)) {
enterErrorState(Localization.get("missing.fields"));
return false;
}
if (inMobileUserAuthMode) {
String enteredMobileUser = ((EditText)findViewById(R.id.edit_username)).getText().toString();
String enteredDomain = ((EditText)findViewById(R.id.edit_domain)).getText().toString();
if ("".equals(enteredMobileUser) || "".equals(enteredDomain)) {
enterErrorState(Localization.get("missing.fields"));
return false;
}
} else {
String enteredEmail = ((EditText)findViewById(R.id.edit_email)).getText().toString();
if ("".equals(enteredEmail)) {
enterErrorState(Localization.get("missing.fields"));
return false;
}
if (!enteredEmail.contains("@")) {
enterErrorState(Localization.get("email.address.invalid"));
return false;
}
}
return true;
}
private void loadStateFromSavedInstance(Bundle savedInstanceState) {
if (savedInstanceState != null) {
requestedFromIndia = savedInstanceState.getBoolean(REQUESTED_FROM_INDIA_KEY);
requestedFromProd = savedInstanceState.getBoolean(REQUESTED_FROM_PROD_KEY);
errorMessage = savedInstanceState.getString(ERROR_MESSAGE_KEY);
}
}
@Override
protected void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putBoolean(REQUESTED_FROM_INDIA_KEY, requestedFromIndia);
savedInstanceState.putBoolean(REQUESTED_FROM_PROD_KEY, requestedFromProd);
savedInstanceState.putString(ERROR_MESSAGE_KEY, errorMessage);
}
/**
*
* @return whether a request was initiated
*/
private boolean requestAppList() {
URL urlToTry = getURLToTry();
if (urlToTry != null) {
final View processingRequestView = findViewById(R.id.processing_request_view);
SimpleHttpTask task = new SimpleHttpTask(this, urlToTry, new HashMap<String, String>(),
false, new Pair<>(getUsernameForAuth(),
((EditText)findViewById(R.id.edit_password)).getText().toString())) {
@Override
protected void onPreExecute() {
super.onPreExecute();
processingRequestView.setVisibility(View.VISIBLE);
}
@Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
processingRequestView.setVisibility(View.GONE);
}
};
task.connect((CommCareTaskConnector)this);
task.setResponseProcessor(this);
setAttemptedRequestFlag();
task.executeParallel();
return true;
}
return false;
}
private String getUsernameForAuth() {
if (inMobileUserAuthMode) {
String username = ((EditText)findViewById(R.id.edit_username)).getText().toString();
String domain = ((EditText)findViewById(R.id.edit_domain)).getText().toString();
return username + "@" + domain + ".commcarehq.org";
} else {
return ((EditText)findViewById(R.id.edit_email)).getText().toString();
}
}
private void setAttemptedRequestFlag() {
if (PROD_URL.equals(urlCurrentlyRequestingFrom)) {
requestedFromProd = true;
} else {
requestedFromIndia = true;
}
}
private URL getURLToTry() {
if (!requestedFromProd) {
urlCurrentlyRequestingFrom = PROD_URL;
} else if (!requestedFromIndia) {
urlCurrentlyRequestingFrom = INDIA_URL;
} else {
return null;
}
try {
return new URL(urlCurrentlyRequestingFrom);
} catch (MalformedURLException e) {
// This shouldn't ever happen because the URL is static
Logger.log(AndroidLogger.TYPE_ERROR_WORKFLOW, "Encountered exception while creating " +
"URL for apps list request");
return null;
}
}
private void enterErrorState(String message) {
errorMessage = message;
enterErrorState();
}
private void enterErrorState() {
authenticateView.setVisibility(View.VISIBLE);
errorMessageBox.setVisibility(View.VISIBLE);
errorMessageBox.setText(errorMessage);
}
@Override
public void processSuccess(int responseCode, InputStream responseData) {
processResponseIntoAppsList(responseData);
repeatRequestOrShowResults(false);
}
private void processResponseIntoAppsList(InputStream responseData) {
try {
KXmlParser baseParser = ElementParser.instantiateParser(responseData);
List<AppAvailableToInstall> apps = (new AvailableAppsParser(baseParser)).parse();
availableApps.addAll(apps);
} catch (IOException | InvalidStructureException | XmlPullParserException | UnfullfilledRequirementsException e) {
Logger.log(AndroidLogger.TYPE_RESOURCES, "Error encountered while parsing apps available for install");
}
}
@Override
public void processRedirection(int responseCode) {
handleRequestError(responseCode);
}
@Override
public void processClientError(int responseCode) {
handleRequestError(responseCode);
}
@Override
public void processServerError(int responseCode) {
handleRequestError(responseCode);
}
@Override
public void processOther(int responseCode) {
handleRequestError(responseCode);
}
@Override
public void handleIOException(IOException exception) {
repeatRequestOrShowResults(true);
}
private void handleRequestError(int responseCode) {
Log.e(TAG,
"Request to " + urlCurrentlyRequestingFrom + " in get available apps request " +
"had error code response: " + responseCode);
repeatRequestOrShowResults(true);
}
private void repeatRequestOrShowResults(final boolean responseWasError) {
if (!requestAppList()) {
// Means we've tried requesting to both endpoints
this.runOnUiThread(new Runnable() {
@Override
public void run() {
if (availableApps.size() == 0) {
if (responseWasError) {
enterErrorState(Localization.get("invalid.fields.entered." +
(inMobileUserAuthMode ? "mobile" : "web")));
} else {
enterErrorState(Localization.get("no.apps.available"));
}
} else {
showResults();
}
}
});
}
}
private void showResults() {
appsListContainer.setVisibility(View.VISIBLE);
authenticateView.setVisibility(View.GONE);
appsListView.setAdapter(new ArrayAdapter<AppAvailableToInstall>(this,
android.R.layout.simple_list_item_1, availableApps) {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
Context context = InstallFromListActivity.this;
if (v == null) {
v = View.inflate(context, R.layout.single_available_app_view, null);
}
AppAvailableToInstall app = this.getItem(position);
TextView appName = (TextView)v.findViewById(R.id.app_name);
appName.setText(app.getAppName());
TextView domain = (TextView)v.findViewById(R.id.domain);
domain.setText(app.getDomainName());
return v;
}
});
rebuildOptionsMenu();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
menu.add(0, RETRIEVE_APPS_FOR_DIFF_USER, 0,
Localization.get("menu.admin.install.other.user"));
return true;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
menu.findItem(RETRIEVE_APPS_FOR_DIFF_USER)
.setVisible(appsListContainer.getVisibility() == View.VISIBLE);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == RETRIEVE_APPS_FOR_DIFF_USER) {
clearPreviouslyRetrievedApps();
availableApps.clear();
appsListContainer.setVisibility(View.GONE);
authenticateView.setVisibility(View.VISIBLE);
rebuildOptionsMenu();
return true;
}
return super.onOptionsItemSelected(item);
}
private void loadPreviouslyRetrievedAvailableApps() {
for (AppAvailableToInstall availableApp : storage()) {
this.availableApps.add(availableApp);
}
if (this.availableApps.size() > 0) {
showResults();
}
}
private void clearPreviouslyRetrievedApps() {
storage().removeAll();
}
private SqlStorage<AppAvailableToInstall> storage() {
return CommCareApplication.instance()
.getGlobalStorage(AppAvailableToInstall.STORAGE_KEY, AppAvailableToInstall.class);
}
}