package fi.bitrite.android.ws.activity; import android.app.Dialog; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Color; import android.graphics.PorterDuff; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.os.Parcelable; import android.text.Spanned; import android.text.util.Linkify; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.*; import com.google.android.gms.analytics.GoogleAnalytics; import fi.bitrite.android.ws.R; import fi.bitrite.android.ws.WSAndroidApplication; import fi.bitrite.android.ws.activity.model.HostInformation; import fi.bitrite.android.ws.api.RestClient; import fi.bitrite.android.ws.host.impl.HttpHostFeedback; import fi.bitrite.android.ws.host.impl.HttpHostInformation; import fi.bitrite.android.ws.model.Feedback; import fi.bitrite.android.ws.model.Host; import fi.bitrite.android.ws.persistence.StarredHostDao; import fi.bitrite.android.ws.persistence.impl.StarredHostDaoImpl; import fi.bitrite.android.ws.util.GlobalInfo; import fi.bitrite.android.ws.util.Tools; import fi.bitrite.android.ws.view.FeedbackTable; import java.io.InputStream; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; import static java.util.Collections.sort; /** * Activity that fetches host information and shows it to the user. * The information is retrieved either from the device storage (for starred hosts) * or downloaded from the WarmShowers web service. */ public class HostInformationActivity extends WSBaseActivity implements android.widget.AdapterView.OnItemClickListener { public static final String TAG = "HostInformationActivity"; public static final int RESULT_SHOW_HOST_ON_MAP = RESULT_FIRST_USER + 1; LinearLayout layoutHostDetails; ImageView imgMemberPhoto; TextView lblMemberName; TextView txtAvailability; TextView txtLoginInfo; TextView txtHostLimitations; CheckBox checkBoxFavorite; ImageView iconFavorite; TextView txtHostLocation; TextView txtPhone; TextView txtHostComments; TextView txtFeedbackLabel; ImageView iconAvailableStatus; FeedbackTable feedbackTable; TextView comments; TextView txtHostServices; TextView txtNearbyServices; StarredHostDao starredHostDao = new StarredHostDaoImpl(); private HostInformation hostInfo; private HostInformationTask hostInfoTask; private DialogHandler dialogHandler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.host_information); // Tell the BaseActivity to use a back action in the toolbar instead of the hamburger mHasBackIntent = true; if (!initView()) { finish(); return; } initUI(); dialogHandler = new DialogHandler(HostInformationActivity.this); boolean inProgress = DialogHandler.inProgress(); // TODO: Looks like the idea is to make sure the db connection is // closed so we can open it, but this seems like a hack. starredHostDao.close(); starredHostDao.open(); // set HostDetails to INVISIBLE to reduce 'pop in' effect layoutHostDetails.setVisibility(View.INVISIBLE); if (savedInstanceState != null) { // recovering from e.g. screen rotation change hostInfo = HostInformation.fromSavedInstanceState(savedInstanceState, starredHostDao); if (inProgress) { // if we were in the process of downloading host info, retry downloadHostInformation(); } else { updateViewContent(); } } else { // returning from another activity Intent i = getIntent(); hostInfo = HostInformation.fromIntent(i, starredHostDao); // Don't download if we have no network connection if (!Tools.isNetworkConnected(this)) { // Try loading from db if starred if (starredHostDao.isHostStarred(hostInfo.getId())) { Host host = starredHostDao.getHost(hostInfo.getId()); ArrayList<Feedback> feedback = starredHostDao.getFeedback(host.getId(), host.getName()); hostInfo = new HostInformation(host, feedback, host.getId(), true); updateViewContent(); } } else { downloadHostInformation(); } } starredHostDao.close(); } /* * Grab the UI components from R and init event listeners */ private void initUI() { layoutHostDetails = (LinearLayout) findViewById(R.id.layoutHostDetails); imgMemberPhoto = (ImageView) findViewById(R.id.memberPhoto); lblMemberName = (TextView) findViewById(R.id.lblMemberName); iconAvailableStatus = (ImageView) findViewById(R.id.iconAvailableStatus); txtAvailability = (TextView) findViewById(R.id.txtAvailability); txtLoginInfo = (TextView) findViewById(R.id.txtLoginInfo); txtHostLimitations = (TextView) findViewById(R.id.txtHostLimitations); checkBoxFavorite = (CheckBox) findViewById(R.id.checkBoxFavorite); iconFavorite = (ImageView) findViewById(R.id.iconFavorite); txtHostLocation = (TextView) findViewById(R.id.txtHostLocation); txtPhone = (TextView) findViewById(R.id.txtPhone); txtHostComments = (TextView) findViewById(R.id.txtHostComments); txtFeedbackLabel = (TextView) findViewById(R.id.txtFeedbackLabel); feedbackTable = (FeedbackTable) findViewById(R.id.tblFeedback); comments = (TextView) findViewById(R.id.txtHostComments); txtHostServices = (TextView) findViewById(R.id.txtHostServices); txtNearbyServices = (TextView) findViewById(R.id.txtNearbyServices); checkBoxFavorite.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (isChecked && !hostInfo.getStarred()) { setStarredHost(true); showStarredHostToast(); } else if (!isChecked) { setStarredHost(false); } updateStarredHostIcon(isChecked); } } ); } @Override protected void onSaveInstanceState(Bundle outState) { hostInfo.onSaveInstanceState(outState); if (hostInfoTask != null) { hostInfoTask.cancel(false); } super.onSaveInstanceState(outState); } // Starred Host // I tried to put variables here to access them only once, but it crashes the app //private int StarredHost_normalColor = getResources().getColor(R.color.primaryTextColor); //private int StarredHost_starredColor = getResources().getColor(R.color.primaryColorAccent); private void updateStarredHostIcon(boolean isStarred) { iconFavorite.setColorFilter(isStarred ? getResources().getColor(R.color.primaryColorAccent) : getResources().getColor(R.color.primaryTextColor), PorterDuff.Mode.SRC_ATOP); } public void showStarredHostToast() { int msgId = (hostInfo.isStarred() ? R.string.host_starred : R.string.host_unstarred); Toast.makeText(this, getResources().getString(msgId), Toast.LENGTH_SHORT).show(); } private void setStarredHost(boolean saveHost) { starredHostDao.open(); if (!saveHost && starredHostDao.isHostStarred(hostInfo.getId())) { starredHostDao.delete(hostInfo.getId(), hostInfo.getHost().getName()); } else { starredHostDao.insert(hostInfo.getId(), hostInfo.getHost().getName(), hostInfo.getHost(), hostInfo.getFeedback()); } hostInfo.setStarred(saveHost); starredHostDao.close(); } public void contactHost(View view) { Intent i = new Intent(HostInformationActivity.this, HostContactActivity.class); i.putExtra("host", hostInfo.getHost()); i.putExtra("id", hostInfo.getId()); startActivity(i); } public void sendFeedback(View view) { Intent i = new Intent(HostInformationActivity.this, FeedbackActivity.class); i.putExtra("host", hostInfo.getHost()); i.putExtra("id", hostInfo.getId()); startActivity(i); } /** * Show host in context on our own Maps2Activity * * @param view */ public void showHostOnMap(View view) { Intent intent = new Intent(this, Maps2Activity.class); intent.putExtra("target_map_latlng", (Parcelable) hostInfo.getHost().getLatLng()); startActivity(intent); } /** * Send a geo intent so that we can view the host on external maps application * * @param view */ public void sendGeoIntent(View view) { Intent intent = new Intent(Intent.ACTION_VIEW); String lat = hostInfo.getHost().getLatitude(); String lng = hostInfo.getHost().getLongitude(); String query = Uri.encode(lat + "," + lng + "(" + hostInfo.getHost().getFullname() + ")"); String uri = "geo:" + lat + "," + lng + "?q=" + query; intent.setData(Uri.parse(uri)); if (intent.resolveActivity(getPackageManager()) != null) { startActivity(intent); } } @Override protected Dialog onCreateDialog(int id, Bundle args) { if (DialogHandler.inProgress()) { return dialogHandler.createDialog(id, getResources().getString(R.string.host_info_in_progress)); } else { return null; } } private void downloadHostInformation() { dialogHandler.showDialog(DialogHandler.HOST_INFORMATION); hostInfoTask = new HostInformationTask(); hostInfoTask.execute(); } private void updateViewContent() { final Host host = hostInfo.getHost(); String fullname = hostInfo.getHost().getFullname(); lblMemberName.setText(fullname); getSupportActionBar().setTitle(fullname); checkBoxFavorite.setChecked(hostInfo.getStarred()); updateStarredHostIcon(hostInfo.getStarred()); // Host Availability: // TODO: Copied from HostListAdapter.java, needs to be refactored // Set the host icon to black if they're available, otherwise gray if (host.isNotCurrentlyAvailable()) { iconAvailableStatus.setImageResource(R.drawable.ic_home_variant_grey600_24dp); iconAvailableStatus.setAlpha(0.5f); txtAvailability.setText(R.string.not_currently_available); } else { iconAvailableStatus.setImageResource(R.drawable.ic_home_variant_black_24dp); iconAvailableStatus.setAlpha(1.0f); txtAvailability.setText(R.string.currently_available); } DateFormat simpleDate = DateFormat.getDateInstance(); String activeDate = simpleDate.format(host.getLastLoginAsDate()); String createdDate = new SimpleDateFormat("yyyy").format(host.getCreatedAsDate()); String memberString = getString(R.string.search_host_summary, createdDate, activeDate); txtLoginInfo.setText(memberString); String limitations = getString(R.string.host_limitations, host.getMaxCyclists(), host.getLanguagesSpoken()); txtHostLimitations.setText(limitations); // Host location section txtHostLocation.setText(host.getLocation()); String phones = ""; if (!host.getMobilePhone().isEmpty()) { phones += getString(R.string.mobile_phone_abbrev, host.getMobilePhone()) + " "; } if (!host.getHomePhone().isEmpty()) { phones += "\n" + getString(R.string.home_phone_abbrev, host.getHomePhone()) + " "; } if (!host.getWorkPhone().isEmpty()) { phones += "\n" + getString(R.string.work_phone_abbrev, host.getWorkPhone()) + " "; } if (!phones.isEmpty()) { txtPhone.setText(phones); Linkify.addLinks(txtPhone, Linkify.ALL); txtPhone.setVisibility(View.VISIBLE); } else { txtPhone.setVisibility(View.GONE); } // Profile/Comments section // Allow such TextView html as it will; but Drupal's text assumes linefeeds break lines Spanned comments = Tools.siteHtmlToHtml(host.getComments()); txtHostComments.setText(comments); // Host Services String hostServices = host.getHostServices(this); if (!hostServices.isEmpty()) { txtHostServices.setText(getString(R.string.host_services_description, hostServices)); } else { txtHostServices.setVisibility(View.GONE); } // Nearby Services String nearbyServices = host.getNearbyServices(this); if (!nearbyServices.isEmpty()) { txtNearbyServices.setText(getString(R.string.nearby_services_description, nearbyServices)); } else { txtNearbyServices.setVisibility(View.GONE); } List<Feedback> feedback = hostInfo.getFeedback(); sort(feedback); feedbackTable.addRows(feedback); if (feedback.size() > 0) { txtFeedbackLabel.setText(getString(R.string.feedback, feedback.size())); } else { txtFeedbackLabel.setText(R.string.no_feedback_yet); } // If we're connected and there is a picture, get host picture. // TODO: Consider saving the picture on disk, with uid. if (Tools.isNetworkConnected(this)) { String url = host.getProfilePictureLarge(); if (!url.isEmpty()) { new DownloadImageTask(imgMemberPhoto) .execute(url); } else { layoutHostDetails.setVisibility(View.VISIBLE); } } else { layoutHostDetails.setVisibility(View.VISIBLE); } } /** * Download an image into a bitmap in an AsyncTask * <p/> * From http://stackoverflow.com/questions/2471935/how-to-load-an-imageview-by-url-in-android */ private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> { ImageView bmImage; public DownloadImageTask(ImageView bmImage) { this.bmImage = bmImage; } protected Bitmap doInBackground(String... urls) { String urldisplay = urls[0]; Bitmap mIcon11 = null; try { InputStream in = new java.net.URL(urldisplay).openStream(); mIcon11 = BitmapFactory.decodeStream(in); } catch (Exception e) { Log.e("Error", e.getMessage()); } return mIcon11; } protected void onPostExecute(Bitmap result) { if (result != null) { bmImage.setImageBitmap(result); Tools.scaleImage(bmImage, bmImage.getWidth()); // Attempt to now force the name on top of the picture lblMemberName.setTextColor(Color.WHITE); } layoutHostDetails.setVisibility(View.VISIBLE); } } public void viewOnSite() { final Host host = hostInfo.getHost(); String url = GlobalInfo.warmshowersBaseUrl + "/user/" + hostInfo.getId(); Intent i = new Intent(Intent.ACTION_VIEW); i.setData(Uri.parse(url)); startActivity(i); // WebViewActivity.viewOnSite(HostInformationActivity.this, url, host.getFullname()); } private class HostInformationTask extends AsyncTask<Void, Void, Object> { @Override protected Object doInBackground(Void... params) { Object retObj = null; try { HttpHostInformation httpHostInfo = new HttpHostInformation(); HttpHostFeedback hostFeedback = new HttpHostFeedback(); int uid = hostInfo.getId(); boolean starred = hostInfo.getStarred(); Host host = httpHostInfo.getHostInformation(uid); ArrayList<Feedback> feedback = hostFeedback.getFeedback(uid); hostInfo = new HostInformation(host, feedback, uid, starred); } catch (Exception e) { Log.e(WSAndroidApplication.TAG, e.getMessage(), e); retObj = e; } return retObj; } @Override protected void onPostExecute(Object result) { dialogHandler.dismiss(); if (result instanceof Exception) { RestClient.reportError(HostInformationActivity.this, result); return; } updateViewContent(); if (hostInfo.isStarred()) { starredHostDao.open(); starredHostDao.update(hostInfo.getId(), hostInfo.getHost().getName(), hostInfo.getHost(), hostInfo.getFeedback()); starredHostDao.close(); } } } @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); getMenuInflater().inflate(R.menu.host_information_actions, menu); return true; // Causes menu to be displayed } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menuSendMessage: contactHost(null); return true; case R.id.menuViewOnMap: showHostOnMap(null); return true; case R.id.menuMapApplication: sendGeoIntent(null); return true; case R.id.menuLeaveFeedback: sendFeedback(null); return true; case R.id.menuViewOnSite: viewOnSite(); return true; default: return super.onOptionsItemSelected(item); } } @Override protected void onStop() { GoogleAnalytics.getInstance(this).reportActivityStop(this); super.onStop(); } @Override protected void onStart() { super.onStart(); GoogleAnalytics.getInstance(this).reportActivityStart(this); } }