# Grid Lists Image Gallery



## darman96 (22. Nov 2014)

Hallo ich möchte in meiner App ein Bilder Gallerie einbauen wie die Grid Lists hier in der Android Doc: GridLists
Da ist wohl leider nichts zu erklärt und das Grid View beispiel (ist bei den Grid Lists verlinkt) funktioniert bei mir nicht (heißt es ist einfach nur das leere Fragment zu sehen).

Meine Fragment Klasse für die Menüpunkte im Navigation  Drawer:

```
public class ImageViewFragment extends Fragment {

	private int orderBy;
    private Vector<Bitmap> thumbs = new Vector<Bitmap>();
	
	// Constructor
	public ImageViewFragment() {
		
	}
	
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		
	}
	
	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
		
		orderBy = (Integer) getArguments().get("wall_order");

		return inflater.inflate(R.layout.image_layout, container, false);
	}
	
	@Override
	public void onViewCreated(View view, Bundle savedInstanceState) {

        GetPreviewTask task = new GetPreviewTask();
        task.execute("http://alpha.wallhaven.cc/latest?page=1");

        GridView gridview = (GridView) getActivity().findViewById(R.id.gridview);
        gridview.setAdapter(new ImageAdapter(getActivity(), thumbs));

		switch (orderBy) {
		case 0:
			view.setBackgroundColor(Color.YELLOW);
			break;
		case 1:
			view.setBackgroundColor(Color.RED);
			break;
		case 2:
			view.setBackgroundColor(Color.GRAY);

		default:
			break;
		}
	}
	
	@Override
	public void onStart() {
		super.onStart();
		


    }
	
	@Override
	public void onResume() {
		super.onResume();
		// Do stuff
	}



    public class GetPreviewTask extends AsyncTask<String, Void, Void> {

        private String sourceCode = "";
        private Vector<String> IDs = new Vector<String>();
        private Vector<Bitmap> thumbNails = new Vector<Bitmap>();

        @Override
        protected Void doInBackground(String... urls) {
            try {
                getSourceCode(urls[0]);
                getIDsFromSource();
                getThumbNails();
            } catch (IOException e) {
                e.printStackTrace();
            }

            return null;
        }

        // Get the source code from Wallhaven
        private void getSourceCode(String url) throws IOException {
            HttpClient client = new DefaultHttpClient();
            HttpGet request = new HttpGet(url);
            HttpResponse response = null;
            try {
                response = client.execute(request);
            } catch (IOException e) {
                e.printStackTrace();
            }
            HttpEntity entity = response.getEntity();
            InputStream in = null;
            try {
                in = entity.getContent();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (IllegalStateException e) {
                e.printStackTrace();
            }
            String line = "";
            StringBuilder builder = new StringBuilder();
            BufferedReader reader = new BufferedReader(new InputStreamReader(in));
            while ((line = reader.readLine()) != null ) {
                builder.append(line);
            }

            sourceCode = builder.toString();
        }

        // Get all IDs from the source code
        private void getIDsFromSource() {
            int startIndex = 0;
            // find all wallpaper ID's
            if(sourceCode.indexOf("http://alpha.wallhaven.cc/wallpapers/thumb/small/", startIndex) == -1)
                Log.d("DEBUG", "sourceCode is wrong");
            while(sourceCode.indexOf("http://alpha.wallhaven.cc/wallpapers/thumb/small/", startIndex) != -1) {
                int currentIndex = sourceCode.indexOf("http://alpha.wallhaven.cc/wallpapers/thumb/small/", startIndex);
                int indexID = sourceCode.indexOf("-", currentIndex) + 1;
                int endIndexID = sourceCode.indexOf(".", indexID);
                // Get the ID
                String ID = sourceCode.substring(indexID, endIndexID);
                IDs.add(ID);
                // Display the ID for debugging
                Log.d("ID CHECK", ID);
                // Set new start Index
                startIndex = endIndexID;
            }
        }

        // Download the preview thumb nails from Wallhaven
        private void getThumbNails() {
            for(String ID : IDs) {
                thumbs.add(getImage(ID));
                Log.d("DEBUG", "Thumnail " + ID + " downloaded!");
            }
        }

        private Bitmap getImage(String ID) {

            Bitmap thumb = null;

            InputStream in = null;
            try {
                in = (InputStream) new URL("http://alpha.wallhaven.cc/wallpapers/thumb/small/th-" + ID + ".jpg").getContent();
            } catch (MalformedURLException e) {
                e.printStackTrace();
                Log.e("URL ERROR", e.getMessage());
            } catch (IOException e) {
                e.printStackTrace();
                Log.e("IO ERROR", e.getMessage());
            }
            thumb = BitmapFactory.decodeStream(in);
            try {
                in.close();
            } catch (IOException e) {
                Log.e("IO ERROR", e.getMessage());
                e.printStackTrace();
            }

            return thumb;

        }

    }

}
```

Und die ImageAdapter Klasse:

```
public class ImageAdapter extends BaseAdapter {
    private Context mContext;
    private Vector<Bitmap> thumbs;

    public ImageAdapter(Context c, Vector<Bitmap> thumbs) {
        this.thumbs = thumbs;
        mContext = c;
    }

    @Override
    public int getCount() {
        return thumbs.size();
    }

    @Override
    public Object getItem(int i) {
        return thumbs.get(i);
    }

    @Override
    public long getItemId(int i) {
        return 0;
    }

    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        ImageView imageView;
        if(view == null) {
            imageView = new ImageView(mContext);
            imageView.setLayoutParams(new GridView.LayoutParams(85, 85));
            imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
            imageView.setPadding(8, 8, 8, 8);
        } else {
            imageView = (ImageView) view;
        }

        imageView.setImageBitmap(thumbs.get(i));

        return null;
    }
}
```


----------



## dzim (22. Nov 2014)

Bitte ein paar Sachen beachten: Android-Apps sind keine Java-Konsolen-Anwendung, daher System.out oder .err oder e.printStrackTrace() (was nach System.err schreibt) vermeiden, dafür aber ordentliches Logging verwenden

*e.printStackTrace(); --> Log.e(<tag>, <msg>, e);*

Was sind die Berechtigungen deiner App? Vielleicht fehlt z.B. die Permission für's Internet (siehe GetPreviewTask)...
Gibt es Fehler auf der LogCat (du catched ja einfach nur und machst den e.printStackTrace()-Call).
Ohne den Code in einem Test-Projekt einzubauen, sehe ich sonst spontan auch kein Problem. Aber da es recht viel Code ist, habe ich auch jetzt auf die schnelle auch nur grob drüber geschaut...


----------



## darman96 (22. Nov 2014)

Also Internet ist nicht das Problem, ich hab die Permission gesetzt und der Lädt die Bilder auch runter.
Nur die die Grid View funktioniert nicht.


----------



## dzim (22. Nov 2014)

Gibt es denn im LogCat Exceptions?


----------



## darman96 (22. Nov 2014)

Nö nichts


----------



## darman96 (25. Nov 2014)

Wirklich keiner ne Idee?


----------



## dzim (25. Nov 2014)

Sorry, aber ohne es nach zu implementieren und ein laufendes Bsp. zu proggn, nein. (Und die Zeit habe ich gerade nicht dazu...)
Vielleicht versuchst du erst mal einen etwas simpleren Ansatz, und lädst ein oder zwei fixe Bilder aus dem Speicher des Telefons - nur um sicherzustellen, dass es wirklich Daten hat, die du anzeigen kannst.
Bemühe vielleicht auch mal den Debugger - idealerweise im Adapter - und schau mal, ob Daten im Vector enthalten sind (warum nutzt du eigentlich einen Vector? Wegen der Zugriffssicherheit durch mehrere Threads? Ich frage, weil Vectoren wohl etwas langsamer sein sollen, als z.B. eine ArrayList...)  und wenn der nicht leer ist, was deine #getView-Methode macht... Und überlege mal, ob das ViewHolder-Pattern bei dir Sinn bringen würde.


Android ViewHolder Pattern Example | Java Code Geeks
Making ListView Scrolling Smooth | Android Developers


----------



## dzim (25. Nov 2014)

Argh! Ich seh gerade deinen Fehler in der #getView Merthode

```
@Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        ImageView imageView;
        if(view == null) {
            imageView = new ImageView(mContext);
            imageView.setLayoutParams(new GridView.LayoutParams(85, 85));
            imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
            imageView.setPadding(8, 8, 8, 8);
        } else {
            imageView = (ImageView) view;
        }
 
        imageView.setImageBitmap(thumbs.get(i));
 
        return null;
    }
```

Änder das mal ab, das es z.B. so hier aussieht:

```
@Override
    public View getView(int i, View view, ViewGroup viewGroup) {

        ImageView imageView = null;

        if (view == null) {
            // verwende hie vielleicht eher einen View aus einer XML den du inflatest...
            ImageView imageView = new ImageView(mContext);
            imageView.setLayoutParams(new GridView.LayoutParams(85, 85));
            imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
            imageView.setPadding(8, 8, 8, 8);
            vh.imageView = imageView;
            imageView.setTag(vh);
        } else {
            imageView = (ImageView) view;
        }

        Bitmap bm = thumbs.get(i);
        if (bm != null) {
            imageView.setImageBitmap(bm);
        }

        return imageView;
    }
```

#getView sollte am Ende auch einen View zurückgeben.... Das ist mir erst eben gerade aufgefallen und erklärt eigentlich alles.


----------



## darman96 (25. Nov 2014)

ah ja das war es aber ein Problem war auch, das die Gridview erstellt wird bevor der Async Task fertig wird.
Danke für die Hilfe


----------



## darman96 (25. Nov 2014)

Noch ne frage.
bei:


```
imageView.setLayoutParams(new GridView.LayoutParams(500, 500));
```

gibt es da auch ne möglichkeit einzustellen, das die image view die komplette gridview spalte ausfüllt?

Edit:

Achja und weißt du wie man das hier: Grid Lists
mit den Balken machen könnte die die bilder hier etwas überlappen?


----------



## dzim (26. Nov 2014)

Sorry, aber ich hab den GridView nie benutzt, also kann ich dir das leider nicht sagen...


----------



## darman96 (27. Nov 2014)

So ich habe ein weiteres Problem...
Wenn ich im Image Adapter bei den LayoutParams die größe höher stelle (300, 300 reicht schon) dann schmiert die App ab und gibt mir als Fehler meldung Fatal Signal 16 zurück was bedeutet das zu viel speicher verbraucht wird anscheinend.

also:

```
imageView.setLayoutParams(new GridView.LayoutParams(100, 100));
```

das funktioniert


```
imageView.setLayoutParams(new GridView.LayoutParams(300, 300));
```

das hier aber nicht.

Ich speicher die Bitmaps für die GridView ja in nem Vector, gibt es da irgendwelche möglichkeiten speicher zu sparen weil es sollten eigentlich auch noch mehr Bilder dazu kommen.


----------



## dzim (28. Nov 2014)

Das Problem wird sein, das eben alle Bilder vorab schon als Bitmaps geladen werden. Vielleicht noch über den Umweg anderer Listen, die dann noch nicht vom GC weggeputzt wurden (beispielsweise in einem AsyncTask). Ich weiss ja nicht, was du mit den Bildern anstellst. machst du wirklich Thumbnails draus, oder lädst du die je in voller Grösse? Wenn letzteres, dann wandel die Bilder vorher so um, das sie auch nur deine maximale Grösse im Grid haben (Aspect Ratio).
Falls du es schon gemacht hast, musst du überlegen, die Daten asynchron, bzw. auf Bedarf hin, zu laden. Das sollte am Einfachsten gehen, indem du nicht eine List der Bitmaps vorhälst, sondern nur die File Deskriptoren. Dann kannst du sie im #getView nachladen (AsnycTask, dessen Ergebnis dann den View im #getView updated - so würde ich es zumindest einmal probieren).


----------



## darman96 (28. Nov 2014)

Öhm wie funktioniert das mit File Deskriptoren?
Ich hab da noch nie von gehört.


----------



## dzim (28. Nov 2014)

FileDescriptor | Android Developers

:-D

Nimm das nicht so wörtlich. Die meisten File-bezogenen Sachen in Android laufen auch nur über die File-Klasse. Nimm einfach die.
Also dein Ziel: Der Vector<Bitmap> wird z.B. zu einer ArrayList<File> (oder auch String mit dem Pfad zum File auf dem Telefonspeicher). Und lade die Bilder dann asynchron: Processing Bitmaps Off the UI Thread | Android Developers
#onPostExecute muss dann bei dir im View (idealerweise ein einfaches Layout in das der ImageView eingefügt werden kann; der sollte also übergeben werden) das Bild einfügen.

Ich würde ein möglichst simples Layout im #getView erstellen, das dem AsycTask aus dem Link oben übergeben und am Ende halt den erstellen ImageView da einfügen.

Das war jetzt alles etwas doppelt-gemoppelt, aber ich hoffe, du wird in etwa daraus schlau.


----------



## dzim (28. Nov 2014)

Wobei. Bei einem zweiten Blick: Übernimm das Beispiel. Das sieht hinreichend für deinen Fall aus. Eventuell könnte man halt nur einen Spinner oder so anzeigen, das der Nutzer sieht, das etwas passiert. Habe leider mir selbst auch noch nie so sehr Gedanken um Speicher sparen gemacht und hab z.B. mit WeakReference auch noch nicht gearbeitet.


----------



## darman96 (28. Nov 2014)

Also soll ich die Bilder auf dem Telefonspeicher zwischenspeichern und die bei bedarf mit der Fileklasse in die ImageView laden?


----------



## dzim (30. Nov 2014)

Ja so ungefähr. Es gibt den Cache-Berich für deine App ( Context | Android Developers --> kann vom System "jederzeit" gelöscht werden), oder den "Files"-Bereicht (Context | Android Developers --> werden nur beim Deinstallieren der App entfernt).
Du müsstest dir also idealerweise Thumbnails (weil klein, spart Speicher - auch für die Galerie) herunterladen und vielleicht ein paar Metainformationen zusätzlich, wo man die richtigen Bilder findet. Deine App zeigt nun diese (asynchron geladenen!) Thumbnails an (indem die File-Objekte in den ArrayListAdapter übergeben werden) und lädt beim Klick auf ein Thumbnail da richtige Bild herunter (speichert es, damit es nicht jedes mal heruntergeladen werden muss!) und lädt es ebenfalls asynchron. Das Ziel ist, das deine UI *nie* blockiert. Eher zeigt es Spinner oder ähnliche Warteanimationen an, bis das eigentliche Bild da ist. Vergiss nur nicht, dass auch bei einem Fehler dieser irgendwie deutlich werden sollte (Spinner verschwindet und/oder Toast wird angezeigt, bzw. ein dauerhafter Text auf der UI).

In etwa klar?


----------



## darman96 (8. Dez 2014)

Ja eigentlich schon nur ich krieg immer noch ne meldung: 



> 12-08 22:22:22.970  19910-19923/com.example.erik.wallhavenviewer W/dalvikvm﹕ threadid=11: spin on suspend #1 threadid=1 (pcf=0)
> 12-08 22:22:23.721  19910-19923/com.example.erik.wallhavenviewer W/dalvikvm﹕ threadid=11: spin on suspend #2 threadid=1 (pcf=0)
> 12-08 22:22:23.721  19910-19923/com.example.erik.wallhavenviewer I/dalvikvm﹕ "AsyncTask #1" prio=5 tid=11 RUNNABLE
> 12-08 22:22:23.721  19910-19923/com.example.erik.wallhavenviewer I/dalvikvm﹕ | group="main" sCount=0 dsCount=0 obj=0x41b91ca0 self=0x73d97768
> ...


...


> 12-08 22:22:30.108  19910-19923/com.example.erik.wallhavenviewer I/dalvikvm﹕ | group="system" sCount=1 dsCount=0 obj=0x41b45fa8 self=0x726d3748
> 12-08 22:22:30.108  19910-19923/com.example.erik.wallhavenviewer I/dalvikvm﹕ | sysTid=19914 nice=0 sched=0/0 cgrp=apps handle=1941033032
> 12-08 22:22:30.108  19910-19923/com.example.erik.wallhavenviewer I/dalvikvm﹕ | state=S schedstat=( 0 0 0 ) utm=0 stm=0 core=0
> 12-08 22:22:30.108  19910-19923/com.example.erik.wallhavenviewer I/dalvikvm﹕ #00  pc 00022624  /system/lib/libc.so (__futex_syscall3+8)
> ...



Ich hab mal nach diesem FATAL Signal16 gegooglet und nur gefunden das das scheinbar speicher probleme sind. 
kann das daran liegen das ich den main thread während dem Download in ner while schleife laufen lasse damit die Grid View erst gefüllt wird wenn der download angeschlossen ist?

Edit:

Okay ich hab jetzt herausgefunden das man den Adapter der View updaten kann mit notifyDataSetChanged() das Problem ist nur mein Adapter hat die funktion nicht :/ da ist nur notify() und notifyAll()


----------



## dzim (9. Dez 2014)

Was ist denn der "main thread" bei dir? Da ich deine Implementierung nicht kenne, kann ich dir diese Frage nicht genau beantworten, sorry.

*#notifyDataSetChanged()*
Wenn du einen ArrayAdapter verwendest, hat er diese Methode sehr wohl: ArrayAdapter | Android Developers
Oder verwendest du einen anderen? (Bin mittlerweile etwas raus aus dem Thread hier und möchte nicht alles noch einmal lesen).


----------



## darman96 (9. Dez 2014)

Also meine Image Adapter Klasse erbt von BaseAdapter:

```
public class ImageAdapter extends BaseAdapter {
        private Context mContext;

        public ImageAdapter(Context context) {
            mContext = context;
        }

        @Override
        public int getCount() {
            return thumbnails.size();
        }

        @Override
        public Object getItem(int position) {
            return null;
        }

        @Override
        public long getItemId(int position) {
            return 0;
        }

        public void updateView() {
            notifyDataSetChanged();
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ImageView imageView;
            if(convertView == null) {
                imageView = new ImageView(mContext);
                imageView.setLayoutParams(new GridView.LayoutParams(85, 85));
                imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
                imageView.setPadding(8, 8, 8, 8);
            } else {
                imageView = (ImageView) convertView;
            }
            Log.d("DEBUG", thumbnails.get(position).getAbsolutePath());
            Bitmap img = BitmapFactory.decodeFile(thumbnails.get(position).getAbsolutePath());
            imageView.setImageBitmap(img);

            return imageView;
        }
    }
```

Ich will ja einfach nur immer wenn neue Bilder geladen werden die Gridview Updaten


----------



## dzim (9. Dez 2014)

BaseAdapter | Android Developers
Known Direct Subclasses: (unter anderen)
ArrayAdapter | Android Developers

Erbt also von BaseAdapter. Ich würde dir empfehlen, einfach den zu nehmen.
Nichtsdestotrotz sollte es die von dir gesuchte Methode geben:


BTW: Wie kommen die *"thumbnails"* in deinen Adapter?


----------



## darman96 (9. Dez 2014)

ImageAdapter ist in der Main Activity Klasse und die ArrayList mit den File Objekten liegt in der Activity klasse.


----------



## darman96 (10. Dez 2014)

Also ich meine damit, dass ImageAdapter eine InnerClass von der Main Activity ist und die ArrayList ist auch in der Main Activity Klasse so das der Image Adapter direkt auf die ArrayList zugreifen kann.


----------



## dzim (10. Dez 2014)

Hm. Ich würde es halt nicht (mehr) als innere Klasse machen. Ist aber vielleicht nur eine Geschmackssache.
Erklär mir bitte noch einmal kurz dein momentanes Problem. Denn rein vom Code-Schnipsel oben her, kann ich da nichts sehen, was problematisch wäre...


----------



## darman96 (10. Dez 2014)

Das mit der inneren klasse ist nicht das Problem, macht es in dem Fall sogar leichter.
Mein Problem ist, das da ja der download über nen Async Task läuft der noch nicht fertig ist wenn die Gridview erstellt wird.
Deswegen sind noch keine Bilder da wenn die getView() vom Adapter aufgerufen wird. Ich müsste also irgendwie dem Adapter sagen wenn der download fertig ist, das der dann die Bilder laden soll.


----------



## dzim (10. Dez 2014)

Eigentlich muss nur (wie in dem Bsp. mit dem AsyncTask beschrieben) dem Task die Referenz auf den ImageView im Grid gegeben werden und am Ende (in #onPostIrgendwas) müssen die Bilddaten dem ImageView übergeben werden. Der Rest (das Anzeigen) sollte damit automatisch gehen (dafür gibt es den Mechanismus schliesslich).


----------



## darman96 (10. Dez 2014)

Aber es gibt nicht eine bestimmte image view, in der Gridview ist für jedes item eine.
Soweit ich weiß funktioniert das über die notifyDataSetChanged() oder wie die heißt, ich weiß nur nicht wo und wie ich die aufrufen soll.


----------



## dzim (11. Dez 2014)

Ich glaube, wir haben uns hier missverstanden: Du brauchst die AsyncTasks umd die Bilddaten asynchron zu laden. Soweit klar. Aber ich habe nicht gesagt, dass du einen Task am Anfang starten musst und das war es dann.
Dein #getView lädt als erstes einen Stub (daher meinte ich auch, dass es klug wäre, ein via XML definiertes Layout zu nutzen - pro View), z.B. ein FrameLayout deren oberstes Kindelement der ImageView ist, dass aber (da anfangs noch "leer" auf Visibility#INVISIBLE oder gar #GONE gesetzt ist. Darunter liegt z.B. ein einfacher Spinner (der nachher entweder einfach nur vom IV überdeckt wird, oder aber den du explizit entfernst. Und zwar im AsyncTask. Und einen solchen startest du *pro getView-Methoden-Aufruf*. Der bekommt die WeakReference auf den ImageView des o.g. Layouts und füllt ihn in der #onPostIrgendwas-Methode (und versteckt/löscht/whatever den zuvor erwähnten Spinner).

*Achtung:*
Weiss jetzt nicht aus dem Stehgreif, ob sich hier dann ein ThreadPoolExecutor notwendig ist (ThreadPoolExecutor | Android Developers) aber laut dem hier (android - How to use AsyncTask with ThreadPoolExecutor - Stack Overflow) eher nicht (hab den Src-Code vom AsyncTask nicht mehr im Kopf (https://developer.android.com/reference/android/os/AsyncTask.html?hl=de), aber ein kurzes Googlen bringt mich zu der Auffassung, das du dir darum keinen Kopf machen musst (z.B. https://github.com/android/platform_frameworks_base/blob/master/core/java/android/os/AsyncTask.java).


----------



## darman96 (13. Dez 2014)

hmm ich versteh noch nicht so ganz wo da der unterschied ist ob ein Layout in ner XML definiert ist oder nicht weil ich hab die GridView z.B. ja in nem XML File muss die dann aber doch trotzdem im Code erstellen.

Und was ich in der getView() machen soll versteh ich auch nicht...


```
public View getView(int position, View convertView, ViewGroup parent) {

        FrameLayout frameLayout;
        if(convertView == null) {
            frameLayout = new FrameLayout(mContext);
        } else {
            frameLayout = (FrameLayout) convertView;
        }


        return frameLayout;

    }
```
 Sowas?


----------



## dzim (13. Dez 2014)

Unterschied ist, dass du die UI deklarativ erställst und vom Code trennst. Ich würde dir wirklich empfehlen den Code-basierten Ansatz wirkich nur in Ausnahmefällen anzuwenden. Z.B. Wenn es nur ein ImageView ist. Ich hab das mal mit einem Graphen der Lib "GraphView" gemacht. Sonst aber verzichte ich auf die Erstellung von UI im Code und manipulier sie nur von dort. Das Konzept ist das MVC - oder eigentlich noch eher MVVM - vereinfacht: Strikte Trennung von UI-Deklaration und Controller (oder ViewModel - je nachdem). Du verwurschtelst halt beides.
Ich werde mal, wenn ich Zeit finde, etwas Code von Arbeit zusammenstellen und unkritisch machen und posten. Wird aber wohl erst Anfang der Woche werden...


----------



## darman96 (3. Jan 2015)

Hast du denn mal ein beispiel für mich?


----------



## dzim (4. Jan 2015)

Dauert noch... Ferien


----------

