Commit 054906cf authored by Roger Barton's avatar Roger Barton
Browse files

Refined job detail activity, download pdf, job list improvements

parent 6703ac22
Pipeline #4609 canceled with stages
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
<uses-permission android:name="android.permission.GET_ACCOUNTS" /> <uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.READ_PROFILE" /> <uses-permission android:name="android.permission.READ_PROFILE" />
<uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application <application
android:name=".core.MyApplication" android:name=".core.MyApplication"
......
...@@ -365,6 +365,8 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On ...@@ -365,6 +365,8 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
public PagerAdapter(FragmentManager fm) { public PagerAdapter(FragmentManager fm) {
super(fm); super(fm);
for(int i = 0; i< ListFragment.PageType.COUNT; i++)
pages[i] = ListFragment.NewInstance(i);
} }
@Override @Override
...@@ -390,7 +392,13 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On ...@@ -390,7 +392,13 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
if(pages[position] != null) if(pages[position] != null)
pages[position].RefreshList(animate); pages[position].RefreshList(animate);
else else
Log.e("pageview", "RefreshPage(), Page does not exist, will not refresh: " + position); Log.e("pageview", "RefreshPage(), Page does not exist will not refresh: " + position);
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
pages[position] = null;
super.destroyItem(container, position, object);
} }
} }
......
...@@ -35,6 +35,7 @@ import ch.amiv.android_app.jobs.Jobs; ...@@ -35,6 +35,7 @@ import ch.amiv.android_app.jobs.Jobs;
public final class Requests { public final class Requests {
private static RequestQueue requestQueue; private static RequestQueue requestQueue;
private static ImageLoader imageLoader; private static ImageLoader imageLoader;
private static final int MAX_CACHED_IMAGES = 50;
public static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); public static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
...@@ -76,7 +77,9 @@ public final class Requests { ...@@ -76,7 +77,9 @@ public final class Requests {
return; return;
} }
String url = Settings.API_URL + "events" + (eventId.isEmpty() ? "" : "/" + eventId); String url = Settings.API_URL + "events" + (eventId.isEmpty() ?
(Settings.showHiddenFeatures ? "" : "?where={\"show_website\":true}")
: "/" + eventId) ;
Log.e("request", "url: " + url); Log.e("request", "url: " + url);
StringRequest request = new StringRequest(Request.Method.GET, url,null, null) StringRequest request = new StringRequest(Request.Method.GET, url,null, null)
...@@ -239,7 +242,9 @@ public final class Requests { ...@@ -239,7 +242,9 @@ public final class Requests {
return; return;
} }
String url = Settings.API_URL + "joboffers" + (jobId.isEmpty() ? "" : "/" + jobId); String url = Settings.API_URL + "joboffers" + (jobId.isEmpty() ?
(Settings.showHiddenFeatures ? "" : "?where={\"show_website\":true}")
: "/" + jobId);
Log.e("request", "url: " + url); Log.e("request", "url: " + url);
StringRequest request = new StringRequest(Request.Method.GET, url,null, null) StringRequest request = new StringRequest(Request.Method.GET, url,null, null)
...@@ -469,7 +474,7 @@ public final class Requests { ...@@ -469,7 +474,7 @@ public final class Requests {
requestQueue = Volley.newRequestQueue(context); requestQueue = Volley.newRequestQueue(context);
imageLoader = new ImageLoader(requestQueue, new ImageLoader.ImageCache() { imageLoader = new ImageLoader(requestQueue, new ImageLoader.ImageCache() {
private final LruCache<String, Bitmap> mCache = new LruCache<String, Bitmap>(10); private final LruCache<String, Bitmap> mCache = new LruCache<>(MAX_CACHED_IMAGES);
public void putBitmap(String url, Bitmap bitmap) { public void putBitmap(String url, Bitmap bitmap) {
mCache.put(url, bitmap); mCache.put(url, bitmap);
} }
......
...@@ -18,6 +18,9 @@ public class Settings { ...@@ -18,6 +18,9 @@ public class Settings {
//public static final String API_URL = "http://192.168.1.105:5000/"; //public static final String API_URL = "http://192.168.1.105:5000/";
public static final String API_URL = "https://api-dev.amiv.ethz.ch/"; public static final String API_URL = "https://api-dev.amiv.ethz.ch/";
//Whether to show hidden events, where the adverts should not have started yet, should later be set by user access group
public static final boolean showHiddenFeatures = true;
//Vars for saving/reading the url from shared prefs, to allow saving between sessions. For each variable, have a key to access it and a default value //Vars for saving/reading the url from shared prefs, to allow saving between sessions. For each variable, have a key to access it and a default value
private static SharedPreferences sharedPrefs; private static SharedPreferences sharedPrefs;
private static final String SHARED_PREFS_KEY = "ch.amiv.android_app"; private static final String SHARED_PREFS_KEY = "ch.amiv.android_app";
......
...@@ -41,7 +41,7 @@ public final class Events { ...@@ -41,7 +41,7 @@ public final class Events {
public static void UpdateEventInfos(JSONArray json) public static void UpdateEventInfos(JSONArray json)
{ {
boolean isInitialising = eventInfos.size() == 0; boolean isInitialising = eventInfos.size() == 0;
if(eventInfos.size() == 0){ if(isInitialising){
for (int k = 0; k < EventGroup.SIZE; k++) for (int k = 0; k < EventGroup.SIZE; k++)
sortedEvents.add(new ArrayList<EventInfo>()); sortedEvents.add(new ArrayList<EventInfo>());
} }
......
...@@ -18,13 +18,12 @@ import ch.amiv.android_app.core.BaseRecyclerAdapter; ...@@ -18,13 +18,12 @@ import ch.amiv.android_app.core.BaseRecyclerAdapter;
import ch.amiv.android_app.core.ListHelper; import ch.amiv.android_app.core.ListHelper;
import ch.amiv.android_app.core.MainActivity; import ch.amiv.android_app.core.MainActivity;
import static ch.amiv.android_app.core.Settings.showHiddenFeatures;
public class EventsListAdapter extends BaseRecyclerAdapter { public class EventsListAdapter extends BaseRecyclerAdapter {
private List<ListHelper.Pair> dataList = new ArrayList<>(); private List<ListHelper.Pair> dataList = new ArrayList<>();
private Activity activity; private Activity activity;
//Whether to show hidden events, where the adverts should not have started yet, should later be set by user access group
private boolean showHidden = true;
private static final class ViewType { private static final class ViewType {
private static final int HEADER = 0; private static final int HEADER = 0;
private static final int SPACE = 1; private static final int SPACE = 1;
...@@ -59,19 +58,17 @@ public class EventsListAdapter extends BaseRecyclerAdapter { ...@@ -59,19 +58,17 @@ public class EventsListAdapter extends BaseRecyclerAdapter {
@Override @Override
public void BuildDataset () public void BuildDataset ()
{ {
if(Events.sortedEvents.size() == 0)
return;
dataList.clear(); dataList.clear();
List<Integer> headers = new ArrayList<>(); int[] headers = new int[] {R.string.hidden_events_title, R.string.all_events_title, R.string.closed_events_title, R.string.past_events_title};
if(showHidden)
headers.add(R.string.hidden_events_title);
headers.add(R.string.all_events_title);
headers.add(R.string.closed_events_title);
headers.add(R.string.past_events_title);
//Debug: Start at 0 to show hidden events, headers will be offset though //Debug: Start at 0 to show hidden events, headers will be offset though
for (int i = (showHidden ? 0 : 1); i < Events.sortedEvents.size(); i++) { for (int i = (showHiddenFeatures ? 0 : 1); i < Events.EventGroup.SIZE; i++) {
if(i < headers.size()) if(i < headers.length)
dataList.add(new ListHelper.Pair(ViewType.HEADER, activity.getResources().getString(headers.get(i)))); dataList.add(new ListHelper.Pair(ViewType.HEADER, activity.getResources().getString(headers[i])));
//invert order on the specified groups //invert order on the specified groups
if(i >= Events.invertEventGroupSorting.length || !Events.invertEventGroupSorting[i]) { if(i >= Events.invertEventGroupSorting.length || !Events.invertEventGroupSorting[i]) {
......
package ch.amiv.android_app.jobs; package ch.amiv.android_app.jobs;
import android.Manifest;
import android.app.DownloadManager;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.graphics.Bitmap; import android.content.pm.PackageManager;
import android.graphics.ColorFilter;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment;
import android.support.design.widget.Snackbar; import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar; import android.support.v7.widget.Toolbar;
import android.util.Base64;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.widget.Button; import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ScrollView; import android.widget.ScrollView;
import android.widget.TextView; import android.widget.TextView;
import com.android.volley.NetworkResponse;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.ImageRequest;
import com.android.volley.toolbox.NetworkImageView;
import com.android.volley.toolbox.StringRequest;
import org.json.JSONException;
import org.json.JSONObject;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import ch.amiv.android_app.R; import ch.amiv.android_app.R;
import ch.amiv.android_app.core.LoginActivity;
import ch.amiv.android_app.core.Requests; import ch.amiv.android_app.core.Requests;
import ch.amiv.android_app.core.Settings;
import ch.amiv.android_app.core.UserInfo;
import ch.amiv.android_app.util.CustomNetworkImageView; import ch.amiv.android_app.util.CustomNetworkImageView;
/** /**
...@@ -67,7 +50,7 @@ public class JobDetailActivity extends AppCompatActivity { ...@@ -67,7 +50,7 @@ public class JobDetailActivity extends AppCompatActivity {
private CustomNetworkImageView logoImage; private CustomNetworkImageView logoImage;
private ScrollView scrollView; private ScrollView scrollView;
private Button openPdfButton; private Button downloadPdfButton;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
...@@ -115,14 +98,15 @@ public class JobDetailActivity extends AppCompatActivity { ...@@ -115,14 +98,15 @@ public class JobDetailActivity extends AppCompatActivity {
//Link up variables with UI elements from the layout xml //Link up variables with UI elements from the layout xml
scrollView = findViewById(R.id.scrollView_event); scrollView = findViewById(R.id.scrollView_event);
logoImage = findViewById(R.id.companyLogo); logoImage = findViewById(R.id.companyLogo);
openPdfButton = findViewById(R.id.openPdf); downloadPdfButton = findViewById(R.id.openPdf);
((TextView) findViewById(R.id.companyTitle)).setText(job().company); ((TextView) findViewById(R.id.companyTitle)).setText(job().company);
((TextView) findViewById(R.id.jobTitle)).setText(job().GetTitle(getResources())); ((TextView) findViewById(R.id.jobTitle)).setText(job().GetTitle(getResources()));
((TextView) findViewById(R.id.jobDescription)).setText(job().GetDescription(getResources())); ((TextView) findViewById(R.id.jobDescription)).setText(job().GetDescription(getResources()));
DateFormat dateFormat = new SimpleDateFormat("dd - MMM - yyyy HH:mm", getResources().getConfiguration().locale); DateFormat dateFormat = new SimpleDateFormat("dd-MMM-yyyy", getResources().getConfiguration().locale);
((TextView) findViewById(R.id.dateCreated)).setText(getResources().getString(R.string.date_created) + ": " + dateFormat.format(job().time_created).toString()); ((TextView) findViewById(R.id.dateCreated)).setText(getResources().getString(R.string.date_created) + ": " + dateFormat.format(job().time_created).toString());
((TextView) findViewById(R.id.dateEnd)).setText(getResources().getString(R.string.date_available_until) + ": " + dateFormat.format(job().time_end).toString());
//LoadEventImage(); //LoadEventImage();
logoImage.setImageUrl(job().GetLogoUrl(), Requests.GetImageLoader(getApplicationContext())); logoImage.setImageUrl(job().GetLogoUrl(), Requests.GetImageLoader(getApplicationContext()));
...@@ -143,18 +127,55 @@ public class JobDetailActivity extends AppCompatActivity { ...@@ -143,18 +127,55 @@ public class JobDetailActivity extends AppCompatActivity {
private void UpdateOpenPdfButton() { private void UpdateOpenPdfButton() {
if(job().pdf_url != null && !job().pdf_url.isEmpty()) if(job().pdf_url != null && !job().pdf_url.isEmpty())
{ {
openPdfButton.setEnabled(true); downloadPdfButton.setEnabled(true);
openPdfButton.setText(R.string.open_pdf); downloadPdfButton.setText(R.string.open_pdf);
} }
else { else {
openPdfButton.setEnabled(false); downloadPdfButton.setEnabled(false);
openPdfButton.setText(R.string.no_pdf_found); downloadPdfButton.setText(R.string.no_pdf_found);
} }
} }
public void OpenJobPdf(View view){ public void OpenJobPdf(View view) {
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(job().GetPdfUrl())); if (job().pdf_url.isEmpty()) {
startActivity(browserIntent); UpdateOpenPdfButton();
return;
}
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { //Get permission to write to downloads
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
//Add popup
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0);
}
else
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0);
}
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)
return;
Uri uri = Uri.parse(job().GetPdfUrl());
String savePath = job().GetTitle(getResources());
savePath = savePath.replace(' ', '-');
savePath = "/amiv/" + savePath;
if(!savePath.substring(savePath.length() -4, savePath.length()).equalsIgnoreCase( ".pdf"))
savePath += ".pdf";
savePath = savePath.replaceAll("[^a-zA-Z0-9\\.\\-]", "_"); //remove illegal characters
DownloadManager.Request request = new DownloadManager.Request(uri)
.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI | DownloadManager.Request.NETWORK_MOBILE)
.setAllowedOverRoaming(false)
.setTitle(job().GetTitle(getResources()) + ".pdf")
.setDescription(getResources().getString(R.string.job_pdf_description))
.setVisibleInDownloadsUi(true)
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, Uri.parse(savePath).toString());
Log.e("download", "Download Job PDF url: " + uri.toString() + " with filepath: " + Uri.parse(savePath));
((DownloadManager)getSystemService(Context.DOWNLOAD_SERVICE)).enqueue(request);
Snackbar.make(downloadPdfButton, R.string.see_notification, Snackbar.LENGTH_SHORT).show();
} }
public void ScrollToTop (View view) { public void ScrollToTop (View view) {
......
...@@ -20,13 +20,12 @@ import ch.amiv.android_app.core.ListHelper; ...@@ -20,13 +20,12 @@ import ch.amiv.android_app.core.ListHelper;
import ch.amiv.android_app.core.MainActivity; import ch.amiv.android_app.core.MainActivity;
import ch.amiv.android_app.core.Requests; import ch.amiv.android_app.core.Requests;
import static ch.amiv.android_app.core.Settings.showHiddenFeatures;
public class JobListAdapter extends BaseRecyclerAdapter { public class JobListAdapter extends BaseRecyclerAdapter {
private List<ListHelper.Pair> dataList = new ArrayList<>(); private List<ListHelper.Pair> dataList = new ArrayList<>();
private Activity activity; private Activity activity;
//Whether to show hidden jobs, where the adverts should not have started yet, should later be set by user access group
private boolean showHidden = true;
private static final class ViewType { private static final class ViewType {
private static final int HEADER = 0; private static final int HEADER = 0;
private static final int SPACE = 1; private static final int SPACE = 1;
...@@ -59,15 +58,17 @@ public class JobListAdapter extends BaseRecyclerAdapter { ...@@ -59,15 +58,17 @@ public class JobListAdapter extends BaseRecyclerAdapter {
@Override @Override
public void BuildDataset () public void BuildDataset ()
{ {
if(Jobs.sortedJobs.size() == 0)
return;
dataList.clear(); dataList.clear();
List<Integer> headers = new ArrayList<>(); List<Integer> headers = new ArrayList<>();
if(showHidden) headers.add(R.string.hidden_jobs_title);
headers.add(R.string.hidden_jobs_title);
headers.add(R.string.all_jobs_title); headers.add(R.string.all_jobs_title);
headers.add(R.string.past_jobs_title); headers.add(R.string.past_jobs_title);
for (int i = (showHidden ? 0 : 1); i < Jobs.sortedJobs.size(); i++) { for (int i = (showHiddenFeatures ? 0 : 1); i < Jobs.JobGroup.SIZE; i++) {
if(i < headers.size()) if(i < headers.size())
dataList.add(new ListHelper.Pair(JobListAdapter.ViewType.HEADER, activity.getResources().getString(headers.get(i)))); dataList.add(new ListHelper.Pair(JobListAdapter.ViewType.HEADER, activity.getResources().getString(headers.get(i))));
......
...@@ -34,7 +34,7 @@ public class Jobs { ...@@ -34,7 +34,7 @@ public class Jobs {
public static void UpdateJobInfos(JSONArray json) public static void UpdateJobInfos(JSONArray json)
{ {
boolean isInitialising = jobInfos.size() == 0; boolean isInitialising = jobInfos.size() == 0;
if(sortedJobs.size() == 0){ if(isInitialising){
for (int k = 0; k < JobGroup.SIZE; k++) for (int k = 0; k < JobGroup.SIZE; k++)
sortedJobs.add(new ArrayList<JobInfo>()); sortedJobs.add(new ArrayList<JobInfo>());
} }
...@@ -65,7 +65,7 @@ public class Jobs { ...@@ -65,7 +65,7 @@ public class Jobs {
comparator = new Comparator<JobInfo>() { comparator = new Comparator<JobInfo>() {
@Override @Override
public int compare(JobInfo a, JobInfo b) { public int compare(JobInfo a, JobInfo b) {
return b.time_end.compareTo(a.time_end); return a.time_end.compareTo(b.time_end);
} }
}; };
......
...@@ -34,12 +34,15 @@ ...@@ -34,12 +34,15 @@
android:id="@+id/scrollView_event" android:id="@+id/scrollView_event"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:scrollIndicators="right"
android:scrollbars="vertical"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"> app:layout_constraintStart_toStartOf="parent">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:focusableInTouchMode="true"
android:orientation="vertical"> android:orientation="vertical">
<View <View
...@@ -47,6 +50,8 @@ ...@@ -47,6 +50,8 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/company_logo_height_detail" android:layout_height="@dimen/company_logo_height_detail"
android:adjustViewBounds="true" android:adjustViewBounds="true"
android:clickable="true"
android:focusable="auto"
android:onClick="ScrollToTop" android:onClick="ScrollToTop"
android:visibility="visible" android:visibility="visible"
custom:layout_constraintTop_toBottomOf="@id/toolbar_include" /> custom:layout_constraintTop_toBottomOf="@id/toolbar_include" />
...@@ -88,16 +93,30 @@ ...@@ -88,16 +93,30 @@
android:textColor="@color/textLightGray" android:textColor="@color/textLightGray"
android:textStyle="italic" /> android:textStyle="italic" />
<TextView
android:id="@+id/dateEnd"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:text="Available Until: Tomorrow"
android:textAppearance="@style/Body"
android:textColor="@color/textLightGray"
android:textStyle="italic" />
<TextView <TextView
android:id="@+id/jobDescription" android:id="@+id/jobDescription"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="16dp" android:layout_marginLeft="16dp"
android:layout_marginRight="16dp" android:layout_marginRight="16dp"
android:autoLink="all"
android:linksClickable="true"
android:paddingBottom="12dp" android:paddingBottom="12dp"
android:paddingTop="8dp" android:paddingTop="8dp"
android:text="Description" android:text="Description"
android:textAppearance="@style/Body" /> android:textAppearance="@style/Body"
android:textIsSelectable="true" />
<Button <Button
android:id="@+id/openPdf" android:id="@+id/openPdf"
......
...@@ -180,7 +180,9 @@ ...@@ -180,7 +180,9 @@
<!-- Unsorted/Translated Strings--> <!-- Unsorted/Translated Strings-->
<string name="pref_title_system_sync_settings">System sync settings</string> <string name="pref_title_system_sync_settings">System sync settings</string>
<string name="date_created">Hinzugefügt</string> <string name="date_created">Hinzugefügt</string>
<string name="date_available_until">Verfügbar bis</string>
<string name="job_pdf_description">Weitere details über Ihre Jobanzeige</string>
<string name="see_notification">Siehe Benachrichtigung&#8230;</string>
</resources> </resources>
...@@ -176,9 +176,12 @@ ...@@ -176,9 +176,12 @@
<item>-1</item> <item>-1</item>
</string-array> </string-array>
<!-- Unsorted/Translated Strings-->
<string name="pref_title_system_sync_settings">System sync settings</string> <string name="pref_title_system_sync_settings">System sync settings</string>
<string name="date_created">Added</string> <string name="date_created">Added</string>
<string name="date_available_until">Available until</string>
<string name="job_pdf_description">More details on your job offer</string>
<string name="see_notification">See Notification&#8230;</string>