Page MenuHomeGitPull.it

D74.1729597523.diff
No OneTemporary

Size
24 KB
Referenced Files
None
Subscribers
None

D74.1729597523.diff

diff --git a/res/layout/fragment_favorites.xml b/res/layout/fragment_favorites.xml
--- a/res/layout/fragment_favorites.xml
+++ b/res/layout/fragment_favorites.xml
@@ -28,13 +28,14 @@
android:layout_marginRight="@dimen/activity_horizontal_margin"
android:visibility="invisible" />
- <ListView
- android:id="@+id/favoriteListView"
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/favoritesRecyclerView"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_height="match_parent"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
- android:layout_alignParentTop="true" />
+ android:layout_alignParentTop="true"
+ />
</RelativeLayout>
\ No newline at end of file
diff --git a/src/it/reyboz/bustorino/adapters/AdapterListener.java b/src/it/reyboz/bustorino/adapters/AdapterListener.java
new file mode 100644
--- /dev/null
+++ b/src/it/reyboz/bustorino/adapters/AdapterListener.java
@@ -0,0 +1,7 @@
+package it.reyboz.bustorino.adapters;
+
+import it.reyboz.bustorino.backend.Stop;
+
+public interface AdapterListener {
+ void onTappedStop(Stop stop);
+}
diff --git a/src/it/reyboz/bustorino/adapters/StopRecyclerAdapter.java b/src/it/reyboz/bustorino/adapters/StopRecyclerAdapter.java
new file mode 100644
--- /dev/null
+++ b/src/it/reyboz/bustorino/adapters/StopRecyclerAdapter.java
@@ -0,0 +1,157 @@
+package it.reyboz.bustorino.adapters;
+
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.LayoutInflater;
+import android.view.MenuInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import java.util.List;
+
+import it.reyboz.bustorino.R;
+import it.reyboz.bustorino.backend.Stop;
+
+public class StopRecyclerAdapter extends RecyclerView.Adapter<StopRecyclerAdapter.ViewHolder> {
+ private List<Stop> stops;
+ private static final int row_layout = R.layout.entry_bus_stop;
+ private static final int busIcon = R.drawable.bus;
+ private static final int trainIcon = R.drawable.subway;
+ private static final int tramIcon = R.drawable.tram;
+ private static final int cityIcon = R.drawable.city;
+
+ private AdapterListener listener;
+ private int position;
+
+
+
+ protected static class ViewHolder extends RecyclerView.ViewHolder implements View.OnCreateContextMenuListener{
+ TextView busStopIDTextView;
+ TextView busStopNameTextView;
+ //TextView busLineVehicleIcon;
+ TextView busStopLinesTextView;
+ TextView busStopLocaLityTextView;
+ Stop mStop;
+
+ public ViewHolder(@NonNull View itemView, AdapterListener listener) {
+ super(itemView);
+ busStopIDTextView = (TextView) itemView.findViewById(R.id.busStopID);
+ busStopNameTextView = (TextView) itemView.findViewById(R.id.busStopName);
+ busStopLinesTextView = (TextView) itemView.findViewById(R.id.routesThatStopHere);
+ busStopLocaLityTextView = (TextView) itemView.findViewById(R.id.busStopLocality);
+
+ mStop = new Stop("");
+
+ itemView.setOnClickListener(view -> {
+ listener.onTappedStop(mStop);
+ });
+ }
+ //many thanks to https://stackoverflow.com/questions/26466877/how-to-create-context-menu-for-recyclerview
+ @Override
+ public void onCreateContextMenu(ContextMenu contextMenu, View view, ContextMenu.ContextMenuInfo contextMenuInfo) {
+ MenuInflater inflater = new MenuInflater(view.getContext());
+ inflater.inflate(R.menu.menu_favourites_entry, contextMenu);
+ }
+ }
+
+ public StopRecyclerAdapter(List<Stop> stops,AdapterListener listener) {
+ this.stops = stops;
+ this.listener = listener;
+ }
+
+ public void setStops(List<Stop> stops){
+ this.stops = stops;
+ notifyDataSetChanged();
+ }
+
+ public List<Stop> getStops() {
+ return stops;
+ }
+
+ public int getPosition() {
+ return position;
+ }
+
+ public void setPosition(int position) {
+ this.position = position;
+ }
+
+ @NonNull
+ @Override
+ public StopRecyclerAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ View view = LayoutInflater.from(parent.getContext())
+ .inflate(row_layout, parent, false);
+
+ return new StopRecyclerAdapter.ViewHolder(view, listener);
+ }
+
+ @Override
+ public void onViewRecycled(@NonNull StopRecyclerAdapter.ViewHolder holder) {
+ holder.itemView.setOnLongClickListener(null);
+ super.onViewRecycled(holder);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull StopRecyclerAdapter.ViewHolder vh, int position) {
+ Log.d("StopRecyclerAdapter", "Called for position "+position);
+ Stop stop = stops.get(position);
+ vh.busStopIDTextView.setText(stop.ID);
+ vh.mStop = stop;
+ Log.d("StopRecyclerAdapter", "Stop: "+stop.ID);
+
+ // NOTE: intentionally ignoring stop username in search results: if it's in the favorites, why are you searching for it?
+ vh.busStopNameTextView.setText(stop.getStopDisplayName());
+ String whatStopsHere = stop.routesThatStopHereToString();
+ if(whatStopsHere == null) {
+ vh.busStopLinesTextView.setVisibility(View.GONE);
+ } else {
+ vh.busStopLinesTextView.setText(whatStopsHere);
+ vh.busStopLinesTextView.setVisibility(View.VISIBLE); // might be GONE due to View Holder Pattern
+ }
+
+ if(stop.type == null) {
+ vh.busStopLinesTextView.setCompoundDrawablesWithIntrinsicBounds(busIcon, 0, 0, 0);
+ } else {
+ switch(stop.type) {
+ case BUS:
+ default:
+ vh.busStopLinesTextView.setCompoundDrawablesWithIntrinsicBounds(busIcon, 0, 0, 0);
+ break;
+ case METRO:
+ case RAILWAY:
+ vh.busStopLinesTextView.setCompoundDrawablesWithIntrinsicBounds(trainIcon, 0, 0, 0);
+ break;
+ case TRAM:
+ vh.busStopLinesTextView.setCompoundDrawablesWithIntrinsicBounds(tramIcon, 0, 0, 0);
+ break;
+ case LONG_DISTANCE_BUS:
+ // è l'opposto della città ma va beh, dettagli.
+ vh.busStopLinesTextView.setCompoundDrawablesWithIntrinsicBounds(cityIcon, 0, 0, 0);
+ }
+ }
+
+ if (stop.location == null) {
+ vh.busStopLocaLityTextView.setVisibility(View.GONE);
+ } else {
+ vh.busStopLocaLityTextView.setText(stop.location);
+ vh.busStopLocaLityTextView.setVisibility(View.VISIBLE); // might be GONE due to View Holder Pattern
+ }
+ //trick to set the position
+ vh.itemView.setOnLongClickListener(new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View view) {
+ setPosition(vh.getAdapterPosition());
+ return false;
+ }
+ });
+ }
+
+ @Override
+ public int getItemCount() {
+ return stops.size();
+ }
+}
diff --git a/src/it/reyboz/bustorino/fragments/CommonScrollListener.java b/src/it/reyboz/bustorino/fragments/CommonScrollListener.java
--- a/src/it/reyboz/bustorino/fragments/CommonScrollListener.java
+++ b/src/it/reyboz/bustorino/fragments/CommonScrollListener.java
@@ -17,6 +17,7 @@
*/
package it.reyboz.bustorino.fragments;
+import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import android.util.Log;
import android.widget.AbsListView;
@@ -35,10 +36,8 @@
this.enableRefreshLayout = enableRefreshLayout;
}
@Override
- public void onScrollStateChanged(AbsListView view, int scrollState) {
- /*
- * This seems to be a totally useless method
- */
+ public void onScrollStateChanged(AbsListView absListView, int i) {
+
}
@Override
@@ -76,7 +75,7 @@
}}
@Override
- public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
+ public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
FragmentListenerMain listener = listenerWeakReference.get();
if(newState!=SCROLL_STATE_IDLE) listener.showFloatingActionButton(false);
else listener.showFloatingActionButton(true);
diff --git a/src/it/reyboz/bustorino/fragments/FavoritesFragment.java b/src/it/reyboz/bustorino/fragments/FavoritesFragment.java
--- a/src/it/reyboz/bustorino/fragments/FavoritesFragment.java
+++ b/src/it/reyboz/bustorino/fragments/FavoritesFragment.java
@@ -38,29 +38,40 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.ViewModelProvider;
+import androidx.recyclerview.widget.DividerItemDecoration;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
import it.reyboz.bustorino.*;
+import it.reyboz.bustorino.adapters.AdapterListener;
import it.reyboz.bustorino.adapters.StopAdapter;
+import it.reyboz.bustorino.adapters.StopRecyclerAdapter;
import it.reyboz.bustorino.backend.Stop;
import it.reyboz.bustorino.data.FavoritesViewModel;
import it.reyboz.bustorino.middleware.AsyncStopFavoriteAction;
public class FavoritesFragment extends BaseFragment {
- private ListView favoriteListView;
+ private RecyclerView favoriteRecyclerView;
private EditText busStopNameText;
private TextView favoriteTipTextView;
private ImageView angeryBusImageView;
+ private LinearLayoutManager llManager;
@Nullable
private CommonFragmentListener mListener;
public static final String FRAGMENT_TAG = "BusTOFavFragment";
-
+ private final AdapterListener adapterListener = new AdapterListener() {
+ @Override
+ public void onTappedStop(Stop stop) {
+ mListener.requestArrivalsForStopID(stop.ID);
+ }
+ };
public static FavoritesFragment newInstance() {
@@ -87,12 +98,14 @@
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_favorites, container, false);
- favoriteListView = root.findViewById(R.id.favoriteListView);
- favoriteListView.setOnItemClickListener((parent, view, position, id) -> {
- /**
+ favoriteRecyclerView = root.findViewById(R.id.favoritesRecyclerView);
+ //favoriteListView = root.findViewById(R.id.favoriteListView);
+ /*favoriteRecyclerView.setOn((parent, view, position, id) -> {
+ /*
* Casting because of Javamerda
* @url http://stackoverflow.com/questions/30549485/androids-list-view-parameterized-type-in-adapterview-onitemclicklistener
*/
+ /*
Stop busStop = (Stop) parent.getItemAtPosition(position);
if(mListener!=null){
@@ -100,9 +113,19 @@
}
});
+
+ */
+
+ llManager = new LinearLayoutManager(getContext());
+ llManager.setOrientation(LinearLayoutManager.VERTICAL);
+ favoriteRecyclerView.setLayoutManager(llManager);
+ DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(favoriteRecyclerView.getContext(),
+ llManager.getOrientation());
+ favoriteRecyclerView.addItemDecoration(dividerItemDecoration);
+
angeryBusImageView = root.findViewById(R.id.angeryBusImageView);
favoriteTipTextView = root.findViewById(R.id.favoriteTipTextView);
- registerForContextMenu(favoriteListView);
+ registerForContextMenu(favoriteRecyclerView);
FavoritesViewModel model = new ViewModelProvider(this).get(FavoritesViewModel.class);
model.getFavorites().observe(getViewLifecycleOwner(), this::showStops);
@@ -127,14 +150,18 @@
super.onDetach();
mListener = null;
}
-
+ /*
+ This method is apparently NOT CALLED ANYMORE
+ */
@Override
public void onCreateContextMenu(@NonNull ContextMenu menu, @NonNull View v,
ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
- if (v.getId() == R.id.favoriteListView) {
+ Log.d("Favorites Fragment", "Creating context menu on "+v);
+ if (v.getId() == R.id.favoritesRecyclerView) {
// if we aren't attached to activity, return null
if (getActivity()==null) return;
+
MenuInflater inflater = getActivity().getMenuInflater();
inflater.inflate(R.menu.menu_favourites_entry, menu);
}
@@ -151,7 +178,11 @@
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item
.getMenuInfo();
- Stop busStop = (Stop) favoriteListView.getItemAtPosition(info.position);
+ if(!(favoriteRecyclerView.getAdapter() instanceof StopRecyclerAdapter))
+ return false;
+
+ StopRecyclerAdapter adapter = (StopRecyclerAdapter) favoriteRecyclerView.getAdapter();
+ Stop busStop = (Stop) adapter.getStops().get(adapter.getPosition());
switch (item.getItemId()) {
case R.id.action_favourite_entry_delete:
@@ -189,14 +220,14 @@
if(BuildConfig.DEBUG)
Log.d("BusTO - Favorites", "We have "+busStops.size()+" favorites in the list");
if (busStops.size() == 0) {
- favoriteListView.setVisibility(View.INVISIBLE);
+ favoriteRecyclerView.setVisibility(View.INVISIBLE);
// TextView favoriteTipTextView = (TextView) findViewById(R.id.favoriteTipTextView);
//assert favoriteTipTextView != null;
favoriteTipTextView.setVisibility(View.VISIBLE);
//ImageView angeryBusImageView = (ImageView) findViewById(R.id.angeryBusImageView);
angeryBusImageView.setVisibility(View.VISIBLE);
} else {
- favoriteListView.setVisibility(View.VISIBLE);
+ favoriteRecyclerView.setVisibility(View.VISIBLE);
favoriteTipTextView.setVisibility(View.INVISIBLE);
angeryBusImageView.setVisibility(View.INVISIBLE);
}
@@ -210,7 +241,7 @@
* redrwaing everything.
*/
// Show results
- favoriteListView.setAdapter(new StopAdapter(getContext(), busStops));
+ favoriteRecyclerView.setAdapter(new StopRecyclerAdapter(busStops,adapterListener));
}
public void showBusStopUsernameInputDialog(final Stop busStop) {
diff --git a/src/it/reyboz/bustorino/fragments/FragmentHelper.java b/src/it/reyboz/bustorino/fragments/FragmentHelper.java
--- a/src/it/reyboz/bustorino/fragments/FragmentHelper.java
+++ b/src/it/reyboz/bustorino/fragments/FragmentHelper.java
@@ -89,7 +89,7 @@
*/
public void createOrUpdateStopFragment(Palina p, boolean addToBackStack){
boolean sameFragment;
- ArrivalsFragment arrivalsFragment;
+ ArrivalsFragment arrivalsFragment = null;
if(managerWeakRef.get()==null || shouldHaltAllActivities) {
//SOMETHING WENT VERY WRONG
@@ -102,19 +102,22 @@
if(fm.findFragmentById(primaryFrameLayout) instanceof ArrivalsFragment) {
arrivalsFragment = (ArrivalsFragment) fm.findFragmentById(primaryFrameLayout);
//Log.d(DEBUG_TAG, "Arrivals are for fragment with same stop?");
- assert arrivalsFragment != null;
- sameFragment = arrivalsFragment.isFragmentForTheSameStop(p);
+ if (arrivalsFragment == null) sameFragment = false;
+ else sameFragment = arrivalsFragment.isFragmentForTheSameStop(p);
} else {
sameFragment = false;
Log.d(DEBUG_TAG, "We aren't showing an ArrivalsFragment");
}
setLastSuccessfullySearchedBusStop(p);
-
+ if (sameFragment){
+ Log.d("BusTO", "Same bus stop, accessing existing fragment");
+ arrivalsFragment = (ArrivalsFragment) fm.findFragmentById(primaryFrameLayout);
+ if (arrivalsFragment == null) sameFragment = false;
+ }
if(!sameFragment) {
//set the String to be displayed on the fragment
String displayName = p.getStopDisplayName();
- String displayStuff;
if (displayName != null && displayName.length() > 0) {
arrivalsFragment = ArrivalsFragment.newInstance(p.ID,displayName);
@@ -123,9 +126,6 @@
}
String probableTag = ResultListFragment.getFragmentTag(p);
attachFragmentToContainer(fm,arrivalsFragment,new AttachParameters(probableTag, true, addToBackStack));
- } else {
- Log.d("BusTO", "Same bus stop, accessing existing fragment");
- arrivalsFragment = (ArrivalsFragment) fm.findFragmentById(primaryFrameLayout);
}
// DO NOT CALL `setListAdapter` ever on arrivals fragment
arrivalsFragment.updateFragmentData(p);
@@ -197,11 +197,11 @@
this.shouldHaltAllActivities = shouldI;
}
- public void stopLastRequestIfNeeded(){
+ public void stopLastRequestIfNeeded(boolean interruptIfRunning){
if(lastTaskRef == null) return;
AsyncDataDownload task = lastTaskRef.get();
if(task!=null){
- task.cancel(false);
+ task.cancel(interruptIfRunning);
}
}
diff --git a/src/it/reyboz/bustorino/fragments/MainScreenFragment.java b/src/it/reyboz/bustorino/fragments/MainScreenFragment.java
--- a/src/it/reyboz/bustorino/fragments/MainScreenFragment.java
+++ b/src/it/reyboz/bustorino/fragments/MainScreenFragment.java
@@ -101,7 +101,8 @@
ArrivalsFragment fragment = (ArrivalsFragment) fragMan.findFragmentById(R.id.resultFrame);
if (fragment == null){
//we create a new fragment, which is WRONG
- new AsyncDataDownload(fragmentHelper, arrivalsFetchers,getContext()).execute();
+ Log.e("BusTO-RefreshStop", "Asking for refresh when there is no fragment");
+ // AsyncDataDownload(fragmentHelper, arrivalsFetchers,getContext()).execute();
} else{
String stopName = fragment.getStopID();
@@ -168,6 +169,10 @@
if(result==null || result.get(Manifest.permission.ACCESS_COARSE_LOCATION) == null
||result.get(Manifest.permission.ACCESS_FINE_LOCATION) ) return;
+ if(result.get(Manifest.permission.ACCESS_COARSE_LOCATION) == null ||
+ result.get(Manifest.permission.ACCESS_FINE_LOCATION) == null)
+ return;
+
if(result.get(Manifest.permission.ACCESS_COARSE_LOCATION) && result.get(Manifest.permission.ACCESS_FINE_LOCATION)){
locationPermissionGranted = true;
Log.w(DEBUG_TAG, "Starting position");
@@ -326,7 +331,7 @@
if(getContext()==null) return; //we are not attached
//Fragment fr = getChildFragmentManager().findFragmentById(R.id.resultFrame);
- fragmentHelper.stopLastRequestIfNeeded();
+ fragmentHelper.stopLastRequestIfNeeded(true);
toggleSpinner(false);
}
@@ -418,7 +423,7 @@
locationManager.removeLocationRequestFor(requester);
super.onPause();
fragmentHelper.setBlockAllActivities(true);
- fragmentHelper.stopLastRequestIfNeeded();
+ fragmentHelper.stopLastRequestIfNeeded(true);
}
@@ -448,6 +453,7 @@
final StopsFinderByName[] stopsFinderByNames = new StopsFinderByName[]{new GTTStopsFetcher(), new FiveTStopsFetcher()};
if (searchMode == SEARCH_BY_ID) {
String busStopID = busStopSearchByIDEditText.getText().toString();
+ fragmentHelper.stopLastRequestIfNeeded(true);
requestArrivalsForStopID(busStopID);
} else { // searchMode == SEARCH_BY_NAME
String query = busStopSearchByNameEditText.getText().toString();
@@ -458,8 +464,10 @@
} else if(query.length()< 3){
Toast.makeText(getContext(), R.string.query_too_short, Toast.LENGTH_SHORT).show();
}
- else
+ else {
+ fragmentHelper.stopLastRequestIfNeeded(true);
new AsyncDataDownload(fragmentHelper, stopsFinderByNames, getContext()).execute(query);
+ }
}
}
}
@@ -561,13 +569,19 @@
void showNearbyStopsFragment(){
swipeRefreshLayout.setVisibility(View.VISIBLE);
- NearbyStopsFragment fragment = NearbyStopsFragment.newInstance(NearbyStopsFragment.TYPE_STOPS);
- Fragment oldFrag = fragMan.findFragmentById(R.id.resultFrame);
- FragmentTransaction ft = fragMan.beginTransaction();
- if (oldFrag != null)
- ft.remove(oldFrag);
- ft.add(R.id.resultFrame, fragment, NearbyStopsFragment.FRAGMENT_TAG);
- ft.commit();
+ final Fragment existingFrag = fragMan.findFragmentById(R.id.resultFrame);
+ NearbyStopsFragment fragment;
+ if (!(existingFrag instanceof NearbyStopsFragment)){
+ //there is no fragment showing
+ fragment = NearbyStopsFragment.newInstance(NearbyStopsFragment.TYPE_STOPS);
+
+ FragmentTransaction ft = fragMan.beginTransaction();
+ //if (oldFrag != null)
+ // ft.remove(oldFrag);
+
+ ft.replace(R.id.resultFrame, fragment, NearbyStopsFragment.FRAGMENT_TAG);
+ ft.commit();
+ }
}
diff --git a/src/it/reyboz/bustorino/fragments/ResultListFragment.java b/src/it/reyboz/bustorino/fragments/ResultListFragment.java
--- a/src/it/reyboz/bustorino/fragments/ResultListFragment.java
+++ b/src/it/reyboz/bustorino/fragments/ResultListFragment.java
@@ -23,6 +23,8 @@
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
@@ -55,13 +57,11 @@
protected static final String MESSAGE_TEXT_VIEW = "message_text_view";
private FragmentKind adapterKind;
- private boolean adapterSet = false;
protected FragmentListenerMain mListener;
protected TextView messageTextView;
protected ListView resultsListView;
- private FloatingActionButton fabutton;
private ListAdapter mListAdapter = null;
boolean listShown;
private Parcelable mListInstanceState = null;
@@ -211,7 +211,7 @@
switch (adapterKind) {
case ARRIVALS:
resultsListView.setOnScrollListener(new CommonScrollListener(mListener, true));
- fabutton.show();
+ mListener.showFloatingActionButton(true);
break;
case STOPS:
resultsListView.setOnScrollListener(new CommonScrollListener(mListener, false));
@@ -223,6 +223,7 @@
}
+
@Override
public void onPause() {
if (adapterKind.equals(FragmentKind.ARRIVALS)) {
@@ -234,11 +235,10 @@
}
@Override
- public void onAttach(Context context) {
+ public void onAttach(@NonNull Context context) {
super.onAttach(context);
if (context instanceof FragmentListenerMain) {
mListener = (FragmentListenerMain) context;
- fabutton = (FloatingActionButton) getActivity().findViewById(R.id.floatingActionButton);
} else {
throw new RuntimeException(context.toString()
+ " must implement ResultFragmentListener");
@@ -249,9 +249,8 @@
@Override
public void onDetach() {
+ mListener.showFloatingActionButton(false);
mListener = null;
- if (fabutton != null)
- fabutton.show();
super.onDetach();
}

File Metadata

Mime Type
text/plain
Expires
Tue, Oct 22, 13:45 (7 h, 23 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
637545
Default Alt Text
D74.1729597523.diff (24 KB)

Event Timeline