52 Commits

Author SHA1 Message Date
Niels Zwemmer
f8265ac3aa Wrong dirname... whoops. 2017-06-30 10:05:27 +02:00
Niels Zwemmer
d0bd10a854 Added personal log file. 2017-06-30 10:04:05 +02:00
Niels Zwemmer
250ee529fe Added comments for the files. Needs reviewing from Paul. 2017-06-29 16:46:16 +02:00
Niels Zwemmer
1f4fa4c768 Fixed sepia filter uploading black pictures. 2017-06-29 16:21:18 +02:00
Niels Zwemmer
32f39a35db Profile timeline button remade. 2017-06-29 15:51:00 +02:00
Niels Zwemmer
98f986fc0a Merge conflict fixed 2017-06-29 15:45:22 +02:00
Felix Atsma
5a6abe85f1 Timeline layout now with imagebutton 2017-06-29 15:41:16 +02:00
Niels Zwemmer
ab54ffb9ec Merge conflict resolved. 2017-06-29 15:23:36 +02:00
Niels Zwemmer
6258c2a91a Fixed the refresh combined with the progressBar. 2017-06-29 15:20:40 +02:00
Paul Lagerweij
0a7c49b3a3 added animation 2017-06-29 15:18:12 +02:00
Felix Atsma
63236c5895 Merge remote-tracking branch 'origin/niels-profile' into felix 2017-06-29 14:43:34 +02:00
Felix Atsma
d8c04c78ea Fix keyboard hiding crash + prettify findviewbyid's in camerafragment 2017-06-29 14:32:03 +02:00
Niels Zwemmer
9d016b8c65 Added a progressBar to the profilepage aswell. 2017-06-29 14:01:27 +02:00
Niels Zwemmer
7895054743 Merge remote-tracking branch 'origin/felix' into niels-profile 2017-06-29 13:44:08 +02:00
Felix Atsma
c38cc026fc Add progress bar for timeline 2017-06-29 13:42:44 +02:00
Niels Zwemmer
bd4149ccb1 Added rotate restriction. Merge-prep with Felix. 2017-06-29 13:41:23 +02:00
Marijn Jansen
27b005930f Merge branch 'marijn-appje' into 'master'
Fix!

See merge request !29
2017-06-29 13:36:15 +02:00
Marijn Jansen
7936350ede Fix! 2017-06-29 13:35:49 +02:00
Marijn Jansen
c517c2ef10 Merge branch 'marijn-appje' into 'master'
Marijn appje

See merge request !28
2017-06-29 13:34:34 +02:00
Marijn Jansen
c9eb4b62a1 Merge remote-tracking branch 'origin/master' into marijn-appje 2017-06-29 13:34:20 +02:00
Marijn Jansen
71d412beac Date works 2017-06-29 13:28:19 +02:00
Marijn Jansen
af47900288 Better name for niet slechts. 2017-06-29 13:21:58 +02:00
Marijn Jansen
5ae1d1f964 Added NietSlechts! 2017-06-29 13:19:12 +02:00
Niels Zwemmer
4eed601b7d WIP: for Felix 2017-06-29 12:50:29 +02:00
Niels Zwemmer
26a7239caa Much cleaner code. Known bugs seem to be fixed. Added extra progressDialog for posts downloading. 2017-06-29 00:35:55 +02:00
Niels Zwemmer
1fdd21440b Potentially fixed Felix' bug in the camera. Also made some changes for better readability of my own code. 2017-06-28 19:46:38 +02:00
Niels Zwemmer
2315d58598 Changed some appearances of the personal posts. 2017-06-28 18:02:28 +02:00
Niels Zwemmer
ab79fa7d7c Changed some appearances of the personal posts. 2017-06-28 17:58:32 +02:00
Niels Zwemmer
52d15a80ca Working refresh, working user-specific posts in profile. 2017-06-28 17:44:27 +02:00
Felix Atsma
c8f65c0dca Merge branch 'felix' into 'master'
Fix front facing + layout profile

See merge request !27
2017-06-28 16:04:01 +02:00
Felix Atsma
0560e6a0fb Fix front facing camera 2017-06-28 16:03:16 +02:00
Felix Atsma
93434a981f Layout profile changes + half of a reloader 2017-06-28 15:28:40 +02:00
Niels Zwemmer
2621062c60 Merge branch 'niels-profile' into 'master'
Niels profile

See merge request !26
2017-06-28 13:16:12 +02:00
Niels Zwemmer
43a1fcd52c Merge remote-tracking branch 'origin/master' into niels-profile 2017-06-28 13:15:19 +02:00
Niels Zwemmer
982d7d4279 Merge remote-tracking branch 'origin/master' into niels-profile 2017-06-28 13:14:10 +02:00
Felix Atsma
a04866ba3f Merge branch 'felix' into 'master'
Timeline improvements

See merge request !25
2017-06-28 13:14:09 +02:00
Felix Atsma
f1befdd21c Merge remote-tracking branch 'origin/master' into felix 2017-06-28 13:13:34 +02:00
Felix Atsma
cab6728a54 Improve timeline layout 2017-06-28 13:12:48 +02:00
Niels Zwemmer
017b5890c2 Neated up the code. 2017-06-28 13:11:30 +02:00
Marijn Jansen
fe15764d2b Merge branch 'marijn-appje' into 'master'
Option to get own posts.

See merge request !24
2017-06-28 12:47:33 +02:00
Marijn Jansen
bfd018ad4a Option to get own posts. 2017-06-28 12:46:57 +02:00
Felix Atsma
ba832682bf Merge branch 'felix' into 'master'
Image preview + cancel + change icons

See merge request !23
2017-06-28 12:05:34 +02:00
Felix Atsma
f0719c51f9 Add cancel button, fix button switching, change icons to vector 2017-06-28 12:01:05 +02:00
Niels Zwemmer
3aab463143 Added error message-shows and mail_successful/mail_failed to the error messages. 2017-06-28 11:13:39 +02:00
Felix Atsma
2db1254750 Image preview working 2017-06-28 10:25:54 +02:00
Niels Zwemmer
16c3b727fc Working profile page with Adapter. Need Marijn to fix the downloadclass to show user-only content instead of entire timeline. 2017-06-27 16:12:42 +02:00
Felix Atsma
d3395f3e9b Add negative filter + rotate half working 2017-06-27 15:06:20 +02:00
Niels Zwemmer
7ed5c36884 Merge remote-tracking branch 'origin/paul-profile' into niels-profile 2017-06-27 13:22:40 +02:00
Niels Zwemmer
744ab1a9c0 Merge remote-tracking branch 'origin/paul-profile' into niels-profile 2017-06-27 13:20:29 +02:00
Niels Zwemmer
cebc1bd59b Merge remote-tracking branch 'origin/paul-profile' into niels-profile 2017-06-27 13:16:26 +02:00
Niels Zwemmer
1d3537cdde First attempt at ProfileAdapter. Not in working state. 2017-06-27 13:15:55 +02:00
Felix Atsma
08849521e1 Fix comment box clickable + prettify 2017-06-27 12:37:23 +02:00
30 changed files with 1146 additions and 484 deletions

View File

@@ -0,0 +1,102 @@
The Return of the MyHyvesBook+ : MyHyvesBookPlusTagram
=======================================
### _Vak 'Multimedia' van Bsc Informatica leerjaar 1_
Niels Zwemmer | 11025980 | UvA | Persoonlijk logboek | Begeleider Youri Voet
## Introductie
De volgende logboek-entries zijn die van mij persoonlijk per dag genoteerd. Ik heb gekozen voor markdown omdat dit een eenvoudig te updaten document is, zonder de noodzaak tot recompilen en dan her-uploaden van het PDF bestand zoals het geval is bij LaTeX. Zodra dit logboek klaar is, zal ik proberen ook een LaTeX versie erbij te doen. Onderstaand logboek wordt verwerkt in het uiteindelijke individuele rapport. (LaTeX).
## Logboek per dag
**19-6-2017**
De dag verliep anders dan verwacht door een onverwachte opdracht die de TA's ons oplegde. Deze bestond uit een pitch van een paar minuten die ons plan en project duidelijk moesten maken. Dit plan werd goedgekeurd door Youri. Voor de pitches stond de tijd van 13:00 tot 14:00 ingeplanned. Door deze pitch is onze vergadering verplaatst naar morgen van 11:00 tot 12:00 (zie hieronder).
Naast de pitch heb ik vandaag ook nog gewerkt aan het projectplan en deze afgemaakt voor het inleveren op Blackboard. Hierbij is de planning dan het enige nog ontbrekende onderdeel. Deze zal tijdens de vergadering morgen worden afgerond.
Wij hebben daarnaast gewerkt aan onze code, met hulp van zowel Marijn als de referenties hieronder.
Eenmaal weer thuis aangekomen, heb ik de laatste hand gelegd aan het projectplan en deze klaargemaakt voor het inleveren.
_Referenties_
* [Firebase-documentatie](https://firebase.google.com/docs/guides/)
* [Android-documentatie](https://developer.android.com/guide/index.html)
**20-6-2017**
Allereerst zijn de laatste oefenopgaven nagekeken. Daarna zijn Felix en Marijn direct begonnen aan hun onderdelen implementeren waarna Paul en ik zich bij hen voegden.
Paul is begonnen aan de profielpagina en ik heb hem daarmee geholpen. Daarnaast heb ik de notulen voor de vergadering gemaakt die vandaag van 11:00 tot 12:00 plaatsvond. Tijdens deze vergadering zijn vooral een aantal ontwerpkeuzes besproken, zoals het vernieuwde logo, hoe ziet de planning eruit, wat hebben we tot nu toe al werkend en hoe kunnen we dit aan elkaar gelijk houden qua ontwerp.
Tot slot heb ik een LaTeX-template gemaakt voor de logboeken van ieder persoon, zodat productiviteit zo veel mogelijk ongehinderd kan blijven door het achteraf moeten stroomlijnen van dit soort zaken.
_Note: Dit template had ik reeds gemaakt voor mijn keuze over te stappen op markdown. Ik houd nu voor mijzelf een markdown-bestand aan maar hou daarnaast per dag een algemeen logboek bij wat de overige leden kunnen gebruiken om hun persoonlijke logboek te updaten. Dit LaTeX bestand staat op gitlab en is ter beschikking gesteld voor alle TA's._
Thuis heb ik gewerkt aan mijn Firebase-kennis en heb ik het projectplan ingeleverd na de laatste wijzigingen doorgevoerd te hebben. Ook heb ik zelf even een blik geworpen op het wachtwoord-wijzig systeem. Dit leek mij niet al te moeilijk voor een beginner en dus zou ik hier morgen zelf mee aan de slag gaan.
**21-6-2017**
De dag begon om 11:00 met een bijeenkomst van Marijn, Paul en mijzelf. Felix kon niet aanwezig zijn vandaag maar heeft dat gecompenseerd door veel thuis gewerkt te hebben aan de opdracht. Paul en ik hebben de profielpagina nagenoeg afgemaakt en we wachten nu tot Marijn en Felix hun eerste deel hebben geïmplementeerd zodat wij verder kunnen. Dit zijn de onderdelen foto uploaden en wachtwoord wijzigen.
Om 13:00 ging Marijn naar zijn Minor Programmeren groep om daar TA te zijn. Paul en ik hebben tussen 13:00 en 15:00 gewerkt aan de interface opleuken van de profielpagina naast de functionaliteiten die eerder al waren verwerkt.
Om 15:00 ging iedereen naar huis, met uitzondering van Marijn die pas om 16:00 klaar was.
Wederom heb ik thuis verder gewerkt, waar ik deze keer ideëen voor de poster en flyers heb bekeken en genoteerd. Daarnaast heb ik het logboek bijgewerkt en de wachtwoord-wijzig methode geïmplementeerd. Dit beviel goed en zorgde voor een beter begrip van Fragments, Activities en Firebase.
_Referenties_
* [Fragments](https://developer.android.com/guide/components/fragments.html)
* [Activities](https://developer.android.com/guide/components/activities/index.html)
**22-6-2017**
Vandaag hebben wij om 10 uur afgesproken om alvast een eerste versie van onze poster te maken. We zijn over het algemeen al tevreden over dat resultaat maar de definitieve versie zal minder tekst en meer, grotere plaatjes moeten bevatten.
Nadat de poster klaar was, hebben wij geprogrammeerd tot 15:00; de tijd waarop de PAV bijeenkomst begon.
Marijn was vanaf 13:00 weer te vinden in het Minor Programmeren lokaal. We hebben allemaal wat progressie geboekt. Paul heeft Felix ondersteunt met zijn camera implementatie waardoor Felix nu bijna toe is aan de filterimplementatie.
Marijn is bezig geweest aan de poster ontwerpen onder het toeziend oog van Felix als hoofd-design en ik heb een eerste implementatie gedaan voor het updaten van de profielfoto. Na de PAV-bijeenkomst is iedereen in de stromende regen naar huis gegaan. Ik heb daarbij in de trein naar huis gewerkt aan het logboek en heb de eerste versie van de profielfoto-wijziging op gitlab gezet. Marijn zal nu de upload-class moeten afmaken zodat ik deze kan gebruiken voor de profielfoto.
Voor deze eerste versie heb ik wederom alleen gebruik gemaakt van de reeds genoemde documentaties. Echter heb ik voor de "timeline" interface die wij willen implementeren, een mogelijk goede bron gevonden. Deze zal terug te vinden zijn in de referentielijst hieronder. Of wij deze bron uiteindelijk gaan gebruiken staat nog niet vast, maar voor de logboek-documentatie leek het mij een goed idee om deze bron alvast op te nemen voor toekomstige referentie.
_Referenties_
* [Android-documentatie](https://developer.android.com/guide/index.html)
* [Timeline-bron](http://www.androidhive.info/2014/06/android-facebook-like-custom-listview-feed-using-volley/)
* [Camera](https://developer.android.com/training/camera/photobasics.html)
* [Upload to Firebase](https://firebase.google.com/docs/storage/android/upload-files)
**23-6-2017**
De dag begon deze keer met een korte programmeersessie tussen Marijn en mij. Felix was iets later aanwezig. Marijn heeft mij geholpen met een aantal laatste functionaliteiten toevoegen voor het uploaden van een foto naar de Firebase storage. Felix heeft zich vooral bezig gehouden met het implementeren van de filters. Hij heeft zijn camera-deel waarbij zowel een upload -als opslagfunctie is ingebouwd nu bijna af.
Paul kon vandaag niet aanwezig zijn. Hij heeft zich echter van huis uit bezig gehouden met zijn gedeelte van de profielpagina en heeft een aantal wijzigingen met mij besproken. We hebben een vergadering gehouden van 11:30 tot ongeveer 12:30. Uit deze vergadering zijn een aantal dingen gebleken. Zo hebben wij bijvoorbeeld geconstateerd dat een bitmap niet de beste methode is om de profielfoto op te slaan, omdat deze een slechte kwaliteit biedt.
Marijn was vandaag niet nodig bij de Minor Programmeren. Hierdoor waren wij de gehele dag gezamelijk bezig aan de opdracht wat ervoor zorgde dat er een grote productiviteit was.
Op de terugweg naar huis heb ik de notulen van de vergadering van vandaag verwerkt. Thuis aangekomen heb ik tot laat op de avond gewerkt aan het omschrijven van de profielfoto functionaliteit van een bitmap naar een foto-bestand. Dit kon echter niet vervolledigd worden en hier zal dan ook in het weekend of maandag met andere leden naar gekeken moeten worden. Voor het omschrijven van deze functionaliteit heb ik dezelfde bronnen gebruikt als gisteren, waar men iets verder door moet scrollen om op de juiste onderdelen uit te komen.
_Referenties_
* [Camera](https://developer.android.com/training/camera/photobasics.html)
* [Upload to Firebase](https://firebase.google.com/docs/storage/android/upload-files)
**24-6-2017 (zaterdag)**
Een ieder is van huis uit bezig geweest met zijn eigen onderdelen t.b.v. Trello.
Communicatieverkeer was er nauwelijks omdat iedereen nu goed gefocused kon zijn op zijn eigen werk.
Ik ben vandaag zelf wat verder gaan inlezen in de documentatie maar heb voor nu de implementatie en upload naar Firebase even links laten liggen, zodat dit knelpunt morgen met de overige groepsleden kan worden aangepakt.
**25-6-2017 (zondag)**
Vandaag ben ik begonnen met het maken van dit markdown-bestand aan de hand van de reeds aangemaakte LaTeX-template. Daarnaast ben ik begonnen aan de finetuning van de poster en flyer inhoud. Ik heb met Marijn samen overlegt over de stickers en hij gaat ze vandaag bestellen. Als het goed is komen deze aan voordat de presentaties zijn de 30e, dus daar gaan we dan ook vanuit. Voor het opstellen van de poster en de flyers gebruiken wij het programma InDesign van Adobe. Maandag zullen wij gezamelijk onze onderdelen van de poster bij elkaar voegen zodat wij als het goed is een mooi resultaat zullen hebben. Dit komt goed uit in combinatie met onze geplande vergadering. Hier zal een en ander ook besproken worden over onze knelpunten en zal de voortgang opnieuw worden geëvalueerd.
Ik heb in de avond vooral gewerkt aan de algemene poster -en flyertekst samen met Paul. Ik had met hem overleg via Skype.
**26-7-2017**
Vandaag was een zeer productieve dag. Om 10 uur hebben wij de werkzaamheden weer opgepakt en heb ik opnieuw
gekeken naar de uploadfunctionaliteit van de profielfoto. Deze keer is het mij wel gelukt het voor elkaar te krijgen; het bleek om een simpele toevoeging van metadata te gaan wat ik was vergeten te implementeren.
Naast deze simpele toevoeging heb ik ook een stukje extra veiligheid weten in te bouwen, namelijk het gebruiken van een FileProvider voor de URI van de afbeelding. Dit zorgt ervoor dat de app ook kan blijven draaien op een Android 7.x + toestel, wat anders een
FileUriExposedException op zou gooien. (In combinatie met API level > 23).
Vandaag stond bij ons op de planning een gesprek te voeren met onze begeleider. Echter kwamen wij iets te laat met het aanvragen van dit gesprek; Youri kon vandaag namelijk niet aanwezig zijn. Deze meeting zal dus worden verplaatst naar morgen.
Ik heb naast de implementatie van de uploadfunctie wat ondersteunende taken verricht bij zowel Marijn als Felix. Paul is thuis aan het werk gegaan aan het project omdat hij niet aanwezig kon zijn vandaag.
Ook hadden wij vanaf 11:00 t/m 12:00 een vergadering gehad en hier is uit gekomen dat we eigenlijk nog aardig op schema liggen. Bij iedereen beginnen de individuele onderdelen langzaam tot een goed einde gebracht te worden en dus verwachten wij binnekort een goede versie samen te kunnen brengen. Het enige wat nu nog resteert is bugtesting en een paar laatste layout tweaks.
Voor de ietwat meer volledige versie van onze vergadering, kunnen de notulen van vandaag worden geraadpleegd. Deze zijn wederom te vinden op gitlab.
_Referenties_
* [FileProvider](https://developer.android.com/reference/android/support/v4/content/FileProvider.html)
**27-6-2017**
Vandaag begonnen wij om 10:00. Ik was echter iets later, net als Paul. Felix en Marijn waren al bezig toen wij aankwamen.
Ik ben eerst begonnen met bugtesten van de code van Felix. Deze had nog een aantal memory-problemen en ook was Felix nog niet toegekomen aan de correcte rotatie.
Nu Felix weer aan het werk kon, heb ik samen met Paul gekeken naar de ListView voor de profielpagina. Hier worden dan de posts van de gebruiker zelf weergegeven. Dit was in het begin nog wat lastig, maar gelukkig had Marijn al een implementatie van de ListView gemaakt in de Timeline-class. Hier konden Paul en ik een voorbeeld aan nemen en dat heeft uiteindelijk geresulteert in een goed werkende personal-timeline.
Naast de implementaties die geslaagd waren vandaag, hebben wij ook van 13:00 tot 13:30 een vergadering gehad met Youri vandaag. Wij hebben uitgelegd hoe ver wij waren en hebben hem onze voorlopige app laten zien. Hij was tevreden over het mogelijke eindresultaat waar wij naartoe leven en over de manier waarop wij hebben samengewerkt en onze voortgang hebben gedocumenteerd.
Voor het implementeren van ListViews aan de hand van Adapters, zijn onderstaande bronnen gebruikt.
_Referenties_
* [ListView](https://developer.android.com/reference/android/support/v4/content/FileProvider.html)
* [Adapter](https://developer.android.com/reference/android/widget/Adapter.html)

View File

@@ -3,9 +3,8 @@
package="nl.myhyvesbookplus.tagram">
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<application
android:allowBackup="true"
@@ -27,6 +26,7 @@
<activity
android:screenOrientation="portrait"
android:name=".MainActivity"
android:label="@string/app_name">
<intent-filter>
@@ -35,7 +35,9 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".LoginActivity">
<activity
android:screenOrientation="portrait"
android:name=".LoginActivity">
</activity>
</application>

View File

@@ -1,13 +1,10 @@
package nl.myhyvesbookplus.tagram;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.hardware.Camera;
import android.hardware.Camera.PictureCallback;
import android.net.Uri;
import android.os.Bundle;
import android.app.Fragment;
import android.support.design.widget.FloatingActionButton;
@@ -18,98 +15,51 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import nl.myhyvesbookplus.tagram.controller.PostUploader;
import nl.myhyvesbookplus.tagram.model.BitmapPost;
/**
* A simple {@link Fragment} subclass.
* Activities that contain this fragment must implement the
* {@link CameraFragment.OnFragmentInteractionListener} interface
* to handle interaction events.
* Use the {@link CameraFragment#newInstance} factory method to
* create an instance of this fragment.
*/
public class CameraFragment extends Fragment implements PostUploader.PostUploadListener{
// TODO: Rename parameter arguments, choose names that match
private static final String TAG = "CameraFragment";
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
// TODO: Rename and change types of parameters
private String mParam1;
private String mParam2;
private OnFragmentInteractionListener mListener;
private Camera mCamera;
private CameraPreview mPreview;
private byte[] mPhotoRaw;
private Bitmap mPhoto;
private int facing = Camera.CameraInfo.CAMERA_FACING_BACK;
public CameraFragment() {
// Required empty public constructor
}
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param param1 Parameter 1.
* @param param2 Parameter 2.
* @return A new instance of fragment CameraFragment.
*/
// TODO: Rename and change types and number of parameters
public static CameraFragment newInstance(String param1, String param2) {
CameraFragment fragment = new CameraFragment();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
}
/* Required empty public constructor */
public CameraFragment() { }
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
((AppCompatActivity)getActivity()).getSupportActionBar().hide();
getActivity().findViewById(R.id.content).setPadding(0,0,0,0);
final View view = inflater.inflate(R.layout.fragment_camera, container, false);
mCamera = getCameraInstance(facing);
Camera.Parameters params = mCamera.getParameters();
params.setRotation(0);
mCamera.setParameters(params);
mPreview = new CameraPreview(getActivity().getBaseContext(), mCamera);
final RelativeLayout pictureButtons = (RelativeLayout) view.findViewById(R.id.picture_taken_buttons);
final RelativeLayout filterButtons = (RelativeLayout) view.findViewById(R.id.filter_buttons);
final RelativeLayout mCameraLayout = (RelativeLayout) view.findViewById(R.id.camera_preview);
// final RelativeLayout mImageTaken = (RelativeLayout) view.findViewById(R.id.picture_view);
final LinearLayout commentBox = (LinearLayout) view.findViewById(R.id.comment_box);
final ImageButton pictureButton = (ImageButton) view.findViewById(R.id.picture_button);
final ImageButton switchButton = (ImageButton) view.findViewById(R.id.switch_camera_button);
// Hide the action bar
((AppCompatActivity)getActivity()).getSupportActionBar().hide();
mCamera = getCameraInstance(facing);
mPreview = new CameraPreview(getActivity().getBaseContext(), mCamera);
mCameraLayout.addView(mPreview);
// Draw buttons over preview
view.findViewById(R.id.picture_button).bringToFront();
view.findViewById(R.id.switch_camera_button).bringToFront();
pictureButtons.bringToFront();
// Draw initial buttons over preview
pictureButton.bringToFront();
switchButton.bringToFront();
filterButtons.bringToFront();
(view.findViewById(R.id.switch_camera_button)).setOnClickListener(new View.OnClickListener() {
/* Upon pressing the switch camera facing button: */
switchButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
switchFacing();
@@ -120,22 +70,21 @@ public class CameraFragment extends Fragment implements PostUploader.PostUploadL
mPreview = new CameraPreview(getActivity().getBaseContext(), mCamera);
mCameraLayout.addView(mPreview);
view.findViewById(R.id.picture_button).bringToFront();
view.findViewById(R.id.switch_camera_button).bringToFront();
pictureButton.bringToFront();
switchButton.bringToFront();
}
});
(view.findViewById(R.id.picture_button)).setOnClickListener(new View.OnClickListener() {
/* Upon pressing the take photo button: */
pictureButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCamera.takePicture(null, null, new PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
// Bitmap bmp = rotate(BitmapFactory.decodeByteArray(data, 0, data.length, null), 90);
// mPhoto = bmp;
mPhoto = BitmapFactory.decodeByteArray(data, 0, data.length, null);
PicturePreview mPicPreview = new PicturePreview(getActivity().getBaseContext(), mPhoto);
PicturePreview mPicPreview = new PicturePreview(getActivity().getBaseContext(), mPhoto, facing);
mPicPreview.setId(R.id.pic_preview);
mCameraLayout.addView(mPicPreview);
@@ -143,20 +92,25 @@ public class CameraFragment extends Fragment implements PostUploader.PostUploadL
filterButtons.setVisibility(View.VISIBLE);
filterButtons.bringToFront();
// mPicPreview.invalidate();
switchButtons(view);
}
});
}
});
/* Upon pressing the upload button: */
(view.findViewById(R.id.upload_button)).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
view.findViewById(R.id.comment_box).bringToFront();
commentBox.setClickable(true);
commentBox.setVisibility(View.VISIBLE);
commentBox.bringToFront();
filterButtons.setVisibility(View.GONE);
((FloatingActionButton)view.findViewById(R.id.upload_button)).hide();
}
});
/* Upon pressing the enter button on the virtual keyboard: */
(view.findViewById(R.id.comment_submit)).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -169,7 +123,6 @@ public class CameraFragment extends Fragment implements PostUploader.PostUploadL
upload.uploadPicture(new BitmapPost(((PicturePreview)view.findViewById(R.id.pic_preview)).getPicture(), comment));
mPhoto.recycle();
mPhoto = null;
filterButtons.setVisibility(View.GONE);
switchButtons(view);
@@ -177,21 +130,45 @@ public class CameraFragment extends Fragment implements PostUploader.PostUploadL
mCameraLayout.removeView(mPreview);
mCamera = getCameraInstance(facing);
Camera.Parameters params = mCamera.getParameters();
params.setRotation(90);
mCamera.setParameters(params);
mPreview = new CameraPreview(getActivity().getBaseContext(), mCamera);
mCameraLayout.addView(mPreview);
view.findViewById(R.id.picture_button).bringToFront();
view.findViewById(R.id.switch_camera_button).bringToFront();
pictureButton.bringToFront();
switchButton.bringToFront();
mCameraLayout.removeView(view.findViewById(R.id.pic_preview));
hideKeyboard();
}
});
/* Upon pressing the cancel button: */
(view.findViewById(R.id.comment_cancel)).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
((EditText) view.findViewById(R.id.comment_text)).setText("");
mPhoto.recycle();
filterButtons.setVisibility(View.GONE);
switchButtons(view);
mCameraLayout.removeView(mPreview);
mCamera = getCameraInstance(facing);
mPreview = new CameraPreview(getActivity().getBaseContext(), mCamera);
mCameraLayout.addView(mPreview);
pictureButton.bringToFront();
switchButton.bringToFront();
mCameraLayout.removeView(view.findViewById(R.id.pic_preview));
hideKeyboard();
}
});
/* Upon pressing the left arrow filter change button: */
(view.findViewById(R.id.filter_left)).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -199,17 +176,18 @@ public class CameraFragment extends Fragment implements PostUploader.PostUploadL
PicturePreview.filterPrev();
PicturePreview mPicPreview = new PicturePreview(getActivity().getBaseContext(), mPhoto);
PicturePreview mPicPreview = new PicturePreview(getActivity().getBaseContext(), mPhoto, facing);
mPicPreview.setId(R.id.pic_preview);
mCameraLayout.addView(mPicPreview);
view.findViewById(R.id.picture_taken_buttons).bringToFront();
view.findViewById(R.id.upload_button).bringToFront();
filterButtons.setVisibility(View.VISIBLE);
filterButtons.bringToFront();
}
});
/* Upon pressing the right arrow filter change button: */
(view.findViewById(R.id.filter_right)).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -217,76 +195,42 @@ public class CameraFragment extends Fragment implements PostUploader.PostUploadL
PicturePreview.filterNext();
PicturePreview mPicPreview = new PicturePreview(getActivity().getBaseContext(), mPhoto);
PicturePreview mPicPreview = new PicturePreview(getActivity().getBaseContext(), mPhoto, facing);
mPicPreview.setId(R.id.pic_preview);
mCameraLayout.addView(mPicPreview);
view.findViewById(R.id.picture_taken_buttons).bringToFront();
view.findViewById(R.id.upload_button).bringToFront();
filterButtons.setVisibility(View.VISIBLE);
filterButtons.bringToFront();
}
});
(view.findViewById(R.id.comment_text)).setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus) {
hideKeyboard(v);
}
}
});
return view;
}
public void hideKeyboard(View view) {
InputMethodManager inputMethodManager =(InputMethodManager) getActivity().getSystemService(Activity.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
// TODO: Rename method, update argument and hook method into UI event
public void onButtonPressed(Uri uri) {
if (mListener != null) {
mListener.onFragmentInteraction(uri);
}
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnFragmentInteractionListener) {
mListener = (OnFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
}
}
@Override
public void onDetach() {
super.onDetach();
mListener = null;
/**
* Hides keyboard after submit, upload or cancel button gets pressed.
*/
public void hideKeyboard() {
((InputMethodManager) getActivity().getSystemService(Activity.INPUT_METHOD_SERVICE))
.toggleSoftInput(InputMethodManager.SHOW_IMPLICIT, 0);
}
/**
* Restores the action bar when exiting the fragment.
*/
@Override
public void onDestroyView() {
super.onDestroyView();
int padding = 16; // 6 dps
float scale = getResources().getDisplayMetrics().density;
int dp = (int) (padding * scale + 0.5f);
((AppCompatActivity)getActivity()).getSupportActionBar().show();
getActivity().findViewById(R.id.content).setPadding(dp,dp,dp,dp);
}
public static Bitmap rotate(Bitmap bmp, int degree) {
Matrix mtx = new Matrix();
mtx.setRotate(degree);
return Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), mtx, true);
}
/**
* Start the camera.
* @param facing The direction in which the camera should be initialized (back by default).
* @return the result of the opened camera, if successful.
*/
public static Camera getCameraInstance(int facing) {
Camera c = null;
try {
@@ -297,62 +241,47 @@ public class CameraFragment extends Fragment implements PostUploader.PostUploadL
return c;
}
/**
* Switch between front facing camera and the back camera.
*/
public void switchFacing() {
if (facing == Camera.CameraInfo.CAMERA_FACING_FRONT)
facing = Camera.CameraInfo.CAMERA_FACING_BACK;
else
facing = Camera.CameraInfo.CAMERA_FACING_FRONT;
facing = facing == Camera.CameraInfo.CAMERA_FACING_FRONT ?
Camera.CameraInfo.CAMERA_FACING_BACK :
Camera.CameraInfo.CAMERA_FACING_FRONT;
}
/**
* Change which buttons are visible during the different stages on the camera fragment.
*
* @param view The current view upon which the buttons need to be placed or removed.
*/
public void switchButtons(View view) {
RelativeLayout pictureButtons = (RelativeLayout) view.findViewById(R.id.picture_taken_buttons);
FloatingActionButton upload = (FloatingActionButton) view.findViewById(R.id.upload_button);
// FloatingActionButton save = (FloatingActionButton) view.findViewById(R.id.save_button);
ImageButton picButton = (ImageButton) view.findViewById(R.id.picture_button);
ImageButton switchButton = (ImageButton) view.findViewById(R.id.switch_camera_button);
if (((Integer)upload.getVisibility()).equals(View.VISIBLE)) {
if (((Integer)picButton.getVisibility()).equals(View.GONE)) {
Log.d(TAG, "switchButtons: GONE");
upload.hide();
// save.hide();
view.findViewById(R.id.picture_button).setVisibility(View.VISIBLE);
view.findViewById(R.id.switch_camera_button).setVisibility(View.VISIBLE);
picButton.setVisibility(View.VISIBLE);
switchButton.setVisibility(View.VISIBLE);
picButton.bringToFront();
switchButton.bringToFront();
} else {
pictureButtons.bringToFront();
Log.d(TAG, "switchButtons: VISIBLE");
upload.bringToFront();
upload.show();
// save.show();
view.findViewById(R.id.picture_button).setVisibility(View.GONE);
view.findViewById(R.id.switch_camera_button).setVisibility(View.GONE);
picButton.setVisibility(View.GONE);
switchButton.setVisibility(View.GONE);
}
}
@Override
public void onPause() {
super.onPause();
}
@Override
public void onResume() {
super.onResume();
}
@Override
public void PostUploadComplete(Boolean success) {
}
/**
* This interface must be implemented by activities that contain this
* fragment to allow an interaction in this fragment to be communicated
* to the activity and potentially other fragments contained in that
* activity.
* <p>
* See the Android Training lesson <a href=
* "http://developer.android.com/training/basics/fragments/communicating.html"
* >Communicating with Other Fragments</a> for more information.
*/
public interface OnFragmentInteractionListener {
// TODO: Update argument type and name
void onFragmentInteraction(Uri uri);
}
}

View File

@@ -34,21 +34,14 @@ public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if (mHolder.getSurface() == null){
// preview surface does not exist
return;
}
// stop preview before making changes
try {
mCamera.stopPreview();
} catch (Exception e){
// ignore: tried to stop a non-existent preview
}
// set preview size and make any resize, rotate or
// reformatting changes here
// start preview with new settings
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
@@ -60,6 +53,7 @@ public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(TAG, "surfaceDestroyed: CAMERA DESTROYED");
mCamera.stopPreview();
mCamera.release();
}

View File

@@ -181,7 +181,7 @@ public class LoginActivity extends AppCompatActivity implements View.OnClickList
* @param passwordString the entered password
*/
protected void logIn(String emailString, String passwordString) {
progressDialog = ProgressDialog.show(LoginActivity.this, getString(R.string.please_wait), "Logging in", true, false);
progressDialog = ProgressDialog.show(LoginActivity.this, getString(R.string.please_wait), getString(R.string.logging_in), true, false);
mAuth.signInWithEmailAndPassword(emailString, passwordString)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
@@ -211,7 +211,7 @@ public class LoginActivity extends AppCompatActivity implements View.OnClickList
* @param password the entered password
*/
protected void registerUser(String email, String password) {
this.progressDialog = ProgressDialog.show(LoginActivity.this, getString(R.string.please_wait), "Registering", true, false);
this.progressDialog = ProgressDialog.show(LoginActivity.this, getString(R.string.please_wait), getString(R.string.registering), true, false);
mAuth.createUserWithEmailAndPassword(email, password)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
@Override

View File

@@ -20,9 +20,10 @@ import nl.myhyvesbookplus.tagram.controller.PostUploader;
import nl.myhyvesbookplus.tagram.controller.ProfilePictureUploader;
public class MainActivity extends AppCompatActivity implements
CameraFragment.OnFragmentInteractionListener,
ProfilePictureUploader.ProfilePictureUpdatedListener,
DownloadClass.PostDownloadListener, PostUploader.PostUploadListener {
DownloadClass.PostDownloadListener,
PostUploader.PostUploadListener {
final static private String TAG = "MainScreen";
FirebaseAuth mAuth;
@@ -89,11 +90,6 @@ public class MainActivity extends AppCompatActivity implements
finish();
}
@Override
public void onFragmentInteraction(Uri uri) {
}
public void logOutOnClick(View view) {
FirebaseAuth.getInstance().signOut();
goToLogin();
@@ -107,7 +103,6 @@ public class MainActivity extends AppCompatActivity implements
@Override
public void ProfilePictureUpdated(Boolean success) {
Log.d(TAG, "ProfilePictureUpdated: Ja ik luister naar je!");
FragmentManager man = getFragmentManager();
ProfileFragment frag = (ProfileFragment) man.findFragmentById(R.id.content);
FragmentTransaction transaction = man.beginTransaction();
@@ -123,7 +118,12 @@ public class MainActivity extends AppCompatActivity implements
FragmentManager fragmentManager = getFragmentManager();
Fragment frag = fragmentManager.findFragmentById(R.id.content);
if (frag instanceof TimelineFragment) {
if (frag instanceof ProfileFragment) {
((ProfileFragment) frag).startList();
} else if (frag instanceof TimelineFragment) {
// ((TimelineFragment) frag).progressDialog.dismiss();
((TimelineFragment) frag).startList();
}
}

View File

@@ -2,11 +2,14 @@ package nl.myhyvesbookplus.tagram;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.hardware.Camera;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
@@ -16,20 +19,33 @@ import android.view.SurfaceView;
*/
public class PicturePreview extends SurfaceView implements SurfaceHolder.Callback {
private static final String TAG = "PicturePreveiew";
private static final int FILTER_NONE = 0;
private static final int FILTER_SEPIA = 1;
private static final int FILTER_BW = 2;
private static final int FILTER_NEG = 3;
private static int currentFilter = FILTER_NONE;
int facing;
int rotate;
Bitmap picture;
Bitmap filterPicture;
public PicturePreview(Context context, Bitmap bmp) {
public PicturePreview(Context context, Bitmap bmp, int facing) {
super(context);
picture = Bitmap.createScaledBitmap(bmp, bmp.getWidth() / 2, bmp.getHeight() / 2, false);
// picture = Bitmap.createBitmap(bmp);
setWillNotDraw(false);
this.facing = facing;
if (((Integer)facing).equals(Camera.CameraInfo.CAMERA_FACING_FRONT)) {
picture = Bitmap.createBitmap(bmp);
rotate = 270;
} else {
picture = Bitmap.createScaledBitmap(bmp, bmp.getWidth() / 2, bmp.getHeight() / 2, false);
rotate = 90;
}
Log.d(TAG, "PicturePreview: " + bmp.getWidth() + " " + bmp.getHeight());
}
@Override
@@ -38,38 +54,63 @@ public class PicturePreview extends SurfaceView implements SurfaceHolder.Callbac
ColorMatrix cm = new ColorMatrix();
Paint paint = new Paint();
ColorMatrixColorFilter filter;
Canvas saveCanvas = new Canvas();
switch (currentFilter) {
case FILTER_NONE:
canvas.drawBitmap(picture, 0, 0, null);
canvas.rotate(90);
filterPicture = picture;
canvas.drawBitmap(rotate(picture, rotate), 0, 0, null);
filterPicture = rotate(picture, rotate);
break;
case FILTER_SEPIA:
canvas.drawBitmap(toSepia(picture), 0, 0, null);
canvas.rotate(90);
filterPicture = toSepia(picture);
break;
case FILTER_BW:
Canvas bw = new Canvas();
// filterPicture = Bitmap.createBitmap(1920, 1440, null);
filterPicture = Bitmap.createBitmap(picture.getWidth() / 2, picture.getHeight() / 2, Bitmap.Config.ARGB_8888);
cm.setSaturation(0);
filterPicture = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888);
Log.d(TAG, "onDraw: " + Integer.toString(canvas.getWidth()));
float[] sepia = {0.393f,0.769f,0.189f,0f,0f,
0.349f,0.686f,0.168f,0f,0f,
0.272f,0.534f,0.131f,0f,0f,
0f, 0f, 0f, 1f, 0f};
cm.set(sepia);
filter = new ColorMatrixColorFilter(cm);
paint.setColorFilter(filter);
bw.setBitmap(filterPicture);
bw.drawBitmap(picture, 0, 0, paint);
bw.rotate(90);
canvas.drawBitmap(picture, 0, 0, paint);
canvas.rotate(90);
saveCanvas.setBitmap(filterPicture);
saveCanvas.drawBitmap(rotate(picture, rotate), 0, 0, paint);
canvas.drawBitmap(rotate(picture, rotate), 0, 0, paint);
break;
case FILTER_BW:
filterPicture = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888);
cm.setSaturation(0);
filter = new ColorMatrixColorFilter(cm);
paint.setColorFilter(filter);
saveCanvas.setBitmap(filterPicture);
saveCanvas.drawBitmap(rotate(picture, rotate), 0, 0, paint);
canvas.drawBitmap(rotate(picture, rotate), 0, 0, paint);
break;
case FILTER_NEG:
filterPicture = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888);
float[] neg = {-1f,0f,0f,0f,255f,
0f,-1f,0f,0f,255f,
0f,0f,-1f,0f,255f,
0f,0f,0f,1f,0f};
cm.set(neg);
filter = new ColorMatrixColorFilter(cm);
paint.setColorFilter(filter);
saveCanvas.setBitmap(filterPicture);
saveCanvas.drawBitmap(rotate(picture, rotate), 0, 0, paint);
canvas.drawBitmap(rotate(picture, rotate), 0, 0, paint);
break;
}
}
public static void filterPrev() {
switch (currentFilter) {
case FILTER_NONE:
currentFilter = FILTER_BW;
currentFilter = FILTER_NEG;
break;
case FILTER_SEPIA:
currentFilter = FILTER_NONE;
@@ -77,6 +118,9 @@ public class PicturePreview extends SurfaceView implements SurfaceHolder.Callbac
case FILTER_BW:
currentFilter = FILTER_SEPIA;
break;
case FILTER_NEG:
currentFilter = FILTER_BW;
break;
}
}
@@ -89,44 +133,23 @@ public class PicturePreview extends SurfaceView implements SurfaceHolder.Callbac
currentFilter = FILTER_BW;
break;
case FILTER_BW:
currentFilter = FILTER_NEG;
break;
case FILTER_NEG:
currentFilter = FILTER_NONE;
break;
}
}
public Bitmap toSepia(Bitmap color) {
int red, green, blue, pixel;
int height = color.getHeight();
int width = color.getWidth();
int depth = 20;
public static Bitmap rotate(Bitmap bmp, int degree) {
Matrix mtx = new Matrix();
mtx.postRotate(degree);
Bitmap sepia = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
int[] pixels = new int[width * height];
color.getPixels(pixels, 0, width, 0, 0, width, height);
for (int i = 0; i < pixels.length; i++) {
pixel = pixels[i];
red = (pixel >> 16) & 0xFF;
green = (pixel >> 8) & 0xFF;
blue = pixel & 0xFF;
red = green = blue = (red + green + blue) / 3;
red += (depth * 2);
green += depth;
if (red > 255)
red = 255;
if (green > 255)
green = 255;
pixels[i] = (0xFF << 24) | (red << 16) | (green << 8) | blue;
}
sepia.setPixels(pixels, 0, width, 0, 0, width, height);
return sepia;
return Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), mtx, true);
}
public Bitmap getPicture() {
picture.recycle();
return filterPicture;
}
@@ -140,5 +163,8 @@ public class PicturePreview extends SurfaceView implements SurfaceHolder.Callbac
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(TAG, "surfaceDestroyed: PICTURE DESTROYED");
picture.recycle();
filterPicture.recycle();
}
}

View File

@@ -0,0 +1,239 @@
package nl.myhyvesbookplus.tagram;
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import com.bumptech.glide.Glide;
import com.firebase.ui.storage.images.FirebaseImageLoader;
import com.google.firebase.storage.FirebaseStorage;
import com.google.firebase.storage.StorageReference;
import java.util.ArrayList;
import nl.myhyvesbookplus.tagram.model.UriPost;
/**
* Class which creates views for the profile-page timeline. This is done with a ListView.
*/
public class ProfileAdapter extends BaseAdapter {
private LayoutInflater mInflater;
private Context mContext;
private ArrayList<UriPost> mData;
private TextView comment;
private TextView nietSlechts;
private ImageView photo;
private Animator mCurrentAnimator;
/* ProfileAdapter constructor */
ProfileAdapter(Context context, ArrayList<UriPost> data) {
mContext = context;
mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mData = data;
}
@Override
public int getCount() {
return mData.size();
}
@Override
public Object getItem(int position) {
return mData.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
/**
* Initiate a new view to be part of the ListView.
* @param position The position at which the view should start.
* @param convertView The viewconverter.
* @param parent The parent of the view.
* @return
*/
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View rowView = mInflater.inflate(R.layout.list_item_timeline_profile, parent, false);
View newRowView = findViews(rowView);
UriPost post = (UriPost) getItem(position);
comment.setText(post.getComment());
final StorageReference ref = FirebaseStorage.getInstance().getReferenceFromUrl(post.getUri());
Glide.with(mContext)
.using(new FirebaseImageLoader())
.load(ref)
.into(photo);
photo.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
zoomImageFromThumb(photo, ref);
}
});
return newRowView;
}
/**
* Find all views from within the row.
* @param rowView The row from which views must be found.
* @return The rowView which contains the necessary views.
*/
private View findViews(View rowView) {
comment = (TextView) rowView.findViewById(R.id.comment_timeline_profile);
nietSlechts = (TextView) rowView.findViewById(R.id.niet_slecht_count_profile);
photo = (ImageView) rowView.findViewById(R.id.timeline_image_profile);
return rowView;
}
/**
* https://developer.android.com/training/animation/zoom.html
* "Zooms" in a thumbnail view by assigning the high resolution image to a hidden "zoomed-in"
* image view and animating its bounds to fit the entire activity content area.
*
* @param thumbView The thumbnail view to zoom in.
* @param imageRef The high-resolution version of the image represented by the thumbnail.
*/
private void zoomImageFromThumb(final View thumbView, StorageReference imageRef) {
// If there's an animation in progress, cancel it immediately and proceed with this one.
if (mCurrentAnimator != null) {
mCurrentAnimator.cancel();
}
// Load the high-resolution "zoomed-in" image.
final ImageView hiddenView = (ImageView) ((MainActivity) mContext).findViewById(R.id.expanded_image_profile);
Glide.with(mContext)
.using(new FirebaseImageLoader())
.load(imageRef)
.into(hiddenView);
// Calculate the starting and ending bounds for the zoomed-in image. This step
// involves lots of math. Yay, math.
final Rect startBounds = new Rect();
final Rect finalBounds = new Rect();
final Point globalOffset = new Point();
// The start bounds are the global visible rectangle of the thumbnail, and the
// final bounds are the global visible rectangle of the container view. Also
// set the container view's offset as the origin for the bounds, since that's
// the origin for the positioning animation properties (X, Y).
thumbView.getGlobalVisibleRect(startBounds);
((MainActivity) mContext).findViewById(R.id.relative_layout_timeline_profile).getGlobalVisibleRect(finalBounds, globalOffset);
startBounds.offset(-globalOffset.x, -globalOffset.y);
finalBounds.offset(-globalOffset.x, -globalOffset.y);
// Adjust the start bounds to be the same aspect ratio as the final bounds using the
// "center crop" technique. This prevents undesirable stretching during the animation.
// Also calculate the start scaling factor (the end scaling factor is always 1.0).
float startScale;
if ((float) finalBounds.width() / finalBounds.height()
> (float) startBounds.width() / startBounds.height()) {
// Extend start bounds horizontally
startScale = (float) startBounds.height() / finalBounds.height();
float startWidth = startScale * finalBounds.width();
float deltaWidth = (startWidth - startBounds.width()) / 2;
startBounds.left -= deltaWidth;
startBounds.right += deltaWidth;
} else {
// Extend start bounds vertically
startScale = (float) startBounds.width() / finalBounds.width();
float startHeight = startScale * finalBounds.height();
float deltaHeight = (startHeight - startBounds.height()) / 2;
startBounds.top -= deltaHeight;
startBounds.bottom += deltaHeight;
}
// Hide the thumbnail and show the zoomed-in view. When the animation begins,
// it will position the zoomed-in view in the place of the thumbnail.
thumbView.setAlpha(0f);
hiddenView.setVisibility(View.VISIBLE);
// Set the pivot point for SCALE_X and SCALE_Y transformations to the top-left corner of
// the zoomed-in view (the default is the center of the view).
hiddenView.setPivotX(0f);
hiddenView.setPivotY(0f);
// Construct and run the parallel animation of the four translation and scale properties
// (X, Y, SCALE_X, and SCALE_Y).
AnimatorSet set = new AnimatorSet();
set
.play(ObjectAnimator.ofFloat(hiddenView, View.X, startBounds.left,
finalBounds.left))
.with(ObjectAnimator.ofFloat(hiddenView, View.Y, startBounds.top,
finalBounds.top))
.with(ObjectAnimator.ofFloat(hiddenView, View.SCALE_X, startScale, 1f))
.with(ObjectAnimator.ofFloat(hiddenView, View.SCALE_Y, startScale, 1f));
set.setDuration(200);
set.setInterpolator(new DecelerateInterpolator());
set.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mCurrentAnimator = null;
}
@Override
public void onAnimationCancel(Animator animation) {
mCurrentAnimator = null;
}
});
set.start();
mCurrentAnimator = set;
// Upon clicking the zoomed-in image, it should zoom back down to the original bounds
// and show the thumbnail instead of the expanded image.
final float startScaleFinal = startScale;
hiddenView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (mCurrentAnimator != null) {
mCurrentAnimator.cancel();
}
// Animate the four positioning/sizing properties in parallel, back to their
// original values.
AnimatorSet set = new AnimatorSet();
set
.play(ObjectAnimator.ofFloat(hiddenView, View.X, startBounds.left))
.with(ObjectAnimator.ofFloat(hiddenView, View.Y, startBounds.top))
.with(ObjectAnimator
.ofFloat(hiddenView, View.SCALE_X, startScaleFinal))
.with(ObjectAnimator
.ofFloat(hiddenView, View.SCALE_Y, startScaleFinal));
set.setDuration(200);
set.setInterpolator(new DecelerateInterpolator());
set.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
thumbView.setAlpha(1f);
hiddenView.setVisibility(View.GONE);
mCurrentAnimator = null;
}
@Override
public void onAnimationCancel(Animator animation) {
thumbView.setAlpha(1f);
hiddenView.setVisibility(View.GONE);
mCurrentAnimator = null;
}
});
set.start();
mCurrentAnimator = set;
}
});
}
}

View File

@@ -15,6 +15,8 @@ import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.bumptech.glide.Glide;
@@ -29,43 +31,66 @@ import com.google.firebase.storage.StorageReference;
import java.io.File;
import java.io.IOException;
import nl.myhyvesbookplus.tagram.controller.DownloadClass;
import nl.myhyvesbookplus.tagram.controller.ProfilePictureUploader;
import static android.app.Activity.RESULT_OK;
/**
* Profilefragment which holds the personal info of the user.
* Makes use of ProfileAdapter in order to load in the items for ListView.
*/
public class ProfileFragment extends Fragment implements View.OnClickListener {
static final int REQUEST_TAKE_PHOTO = 1;
ProgressDialog progressDialog;
/// Views, buttons and other protected declarations ///
/* Views, buttons and other protected and private inits */
protected Button changePwdButton;
protected ImageButton profilePicButton;
protected StorageReference httpsReference;
protected TextView profileName;
protected ImageView profilePicture;
protected FirebaseUser user;
protected File photoFile = null;
protected File photoFile;
ProgressDialog progressDialog;
/// Required empty public constructor ///
private ListView listView;
private DownloadClass downloadClass;
private View headerInflater;
private View timeLineInflater;
private ProgressBar progressBar;
/* Required empty public constructor */
public ProfileFragment() {}
/**
* Overridden onCreate which initializes a user and sets the default photoFile to null.
* @param savedInstanceState The standard return of the onCreate method.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
user = FirebaseAuth.getInstance().getCurrentUser();
photoFile = null;
}
/**
* Assigns all views and buttons for the header.
*/
protected void findHeaderViews() {
profilePicButton = (ImageButton) headerInflater.findViewById(R.id.profile_pic_button);
profilePicture = (ImageView) headerInflater.findViewById(R.id.imageView_profile_picture);
profileName = (TextView) headerInflater.findViewById(R.id.profile_name);
changePwdButton = (Button) headerInflater.findViewById(R.id.change_psw_button);
bindOnClick();
}
/**
* Assigns all views and buttons.
* Assign the ListView and add the header to it.
*/
protected void findViews(View view) {
profilePicButton = (ImageButton) view.findViewById(R.id.profile_pic_button);
profilePicture = (ImageView) view.findViewById(R.id.imageView_profile_picture);
profileName = (TextView) view.findViewById(R.id.profile_name);
changePwdButton = (Button) view.findViewById(R.id.change_psw_button);
bindOnClick();
protected void findTimelineViews() {
listView = (ListView) timeLineInflater.findViewById(R.id.list);
listView.addHeaderView(headerInflater);
}
/**
@@ -76,13 +101,27 @@ public class ProfileFragment extends Fragment implements View.OnClickListener {
changePwdButton.setOnClickListener(this);
}
/// Page setup ///
/**
* Overridden onCreateView which serves as a fragment content creator.
* Checks for user data to be displayed.
*
* @param inflater The inflater used for the fragment.
* @param container The container which holds this fragment.
* @param savedInstanceState The state which was provided by onCreate.
* @return the timeLineInflater View which is required for the ListView to be updated.
*/
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_profile, container, false);
findViews(view);
timeLineInflater = inflater.inflate(R.layout.fragment_profile_timeline, container, false);
headerInflater = inflater.inflate(R.layout.fragment_profile_header, listView, false);
progressBar = (ProgressBar) timeLineInflater.findViewById(R.id.progressbar_timeline);
progressBar.setVisibility(View.VISIBLE);
findHeaderViews();
findTimelineViews();
profilePicture.invalidate();
if (user != null) {
if(user.getPhotoUrl() != null) {
@@ -98,19 +137,19 @@ public class ProfileFragment extends Fragment implements View.OnClickListener {
Glide.with(this).using(new FirebaseImageLoader()).load(httpsReference).into(profilePicture);
}
profilePicture.invalidate();
return view;
downloadClass = new DownloadClass(getActivity());
downloadClass.getPostsFromServer();
return timeLineInflater;
}
/**
* Called when a view has been clicked.
*
* @param v The view that was clicked.
* @param view The view that was clicked.
*/
@Override
public void onClick(View v) {
switch (v.getId()) {
public void onClick(View view) {
switch (view.getId()) {
case R.id.profile_pic_button:
profilePicOnClick();
break;
@@ -125,13 +164,13 @@ public class ProfileFragment extends Fragment implements View.OnClickListener {
*/
private void profilePicOnClick() {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
/* Ensure that there's a camera activity to handle the intent */
if (takePictureIntent.resolveActivity(getActivity().getPackageManager()) != null) {
/* Create the File where the photo should go */
try {
photoFile = createImageFile();
} catch (IOException ex) {
Toast.makeText(getActivity(), getString(R.string.image_save_error), Toast.LENGTH_LONG);
Toast.makeText(getActivity(), getString(R.string.image_save_error),
Toast.LENGTH_LONG).show();
}
if (photoFile != null) {
Uri photoURI = FileProvider.getUriForFile(getActivity(),
@@ -143,8 +182,18 @@ public class ProfileFragment extends Fragment implements View.OnClickListener {
}
}
/**
* Start display of the list; uses an adapter and listener in the main activity.
*/
public void startList() {
ProfileAdapter adapter = new ProfileAdapter(getActivity(), downloadClass.getOwnPosts());
listView.setAdapter(adapter);
progressBar.setVisibility(View.GONE);
}
/**
* Grabs the image just taken by the built-in camera and pushes this image to the user account.
*
* @param requestCode The code which corresponds to REQUEST_TAKE_PHOTO. Used as indicator.
* @param resultCode Code should be RESULT_OK to allow camera to proceed.
* @param data The image data from the camera.
@@ -154,25 +203,26 @@ public class ProfileFragment extends Fragment implements View.OnClickListener {
if (requestCode == REQUEST_TAKE_PHOTO && resultCode == RESULT_OK) {
progressDialog = ProgressDialog.show(getActivity(), getString(R.string.please_wait), getString(R.string.upload_profile_pic), false, false);
ProfilePictureUploader profilePictureUploader = new ProfilePictureUploader(getActivity());
profilePictureUploader.uploadProfilePicture(photoFile.getAbsoluteFile());
profilePictureUploader.uploadProfilePicture(photoFile);
}
}
/**
* Create the file which the camera requires to save a proper quality picture to.
*
* @return The new file.
* @throws IOException when insufficient permission or storage available.
*/
private File createImageFile() throws IOException {
// Create an image file name
String imageFileName = "JPEG_" + user.getUid();
File storageDir = getActivity().getExternalFilesDir(Environment.DIRECTORY_PICTURES);
return File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
}
return File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
}
// TODO Make this function into its own class for modularity.
/**
* Performs password reset action.
*/
@@ -183,13 +233,13 @@ public class ProfileFragment extends Fragment implements View.OnClickListener {
@Override
public void onComplete(@NonNull Task<Void> task) {
Toast.makeText(getActivity(), task.isSuccessful()
? "An e-mail was sent, please follow its instructions."
: "An error occurred, please check internet connection.",
? getString(R.string.mail_successful)
: getString(R.string.mail_failed),
Toast.LENGTH_SHORT).show();
}
});
} else {
// TODO Add code here for when there is no currently active user.
}
}
}

View File

@@ -1,18 +1,30 @@
package nl.myhyvesbookplus.tagram;
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
import android.support.annotation.NonNull;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import com.bumptech.glide.Glide;
import com.firebase.ui.storage.images.FirebaseImageLoader;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.storage.FirebaseStorage;
import com.google.firebase.storage.StorageReference;
@@ -21,7 +33,7 @@ import java.util.ArrayList;
import nl.myhyvesbookplus.tagram.model.UriPost;
/**
* Created by marijnjansen on 26/06/2017.
* Class which creates views for the home-page timeline. This is done with a ListView.
*/
public class TimeLineAdapter extends BaseAdapter implements AdapterView.OnItemClickListener {
@@ -29,11 +41,15 @@ public class TimeLineAdapter extends BaseAdapter implements AdapterView.OnItemCl
private LayoutInflater mInflater;
private Context mContext;
private ArrayList<UriPost> mData;
private DatabaseReference mRef;
private Animator mCurrentAnimator;
/* TimeLineAdapter constructor */
TimeLineAdapter(Context context, ArrayList<UriPost> data) {
mContext = context;
mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mData = data;
mRef = FirebaseDatabase.getInstance().getReference();
}
@Override
@@ -51,38 +67,61 @@ public class TimeLineAdapter extends BaseAdapter implements AdapterView.OnItemCl
return position;
}
/**
* Initiate a new view to be part of the ListView.
* @param position The position at which the view should start.
* @param convertView The viewconverter.
* @param parent The parent of the view.
* @return
*/
@Override
public View getView(int position, View convertView, ViewGroup parent) {
public View getView(final int position, View convertView, ViewGroup parent) {
View rowView = mInflater.inflate(R.layout.list_item_timeline, parent, false);
TextView comment = (TextView) rowView.findViewById(R.id.comment_timeline);
TextView nietSlechts = (TextView) rowView.findViewById(R.id.niet_slecht_count);
ImageView photo = (ImageView) rowView.findViewById(R.id.timeline_image);
ImageButton nietSlechtButton = (ImageButton) rowView.findViewById(R.id.niet_slecht_button);
final TextView nietSlechts = (TextView) rowView.findViewById(R.id.niet_slecht_count);
TextView dateTime = (TextView) rowView.findViewById(R.id.timeline_date);
final ImageView photo = (ImageView) rowView.findViewById(R.id.timeline_image);
final ImageButton nietSlechtButton = (ImageButton) rowView.findViewById(R.id.niet_slecht_button);
nietSlechtButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
UriPost post = (UriPost) getItem(position);
final UriPost post = (UriPost) getItem(position);
nietSlechts.setText(Integer.toString(post.getNietSlechts()));
comment.setText(post.getComment());
StorageReference ref = FirebaseStorage.getInstance().getReferenceFromUrl(post.getUri());
nietSlechtButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d(TAG, "onClick: " + position);
mRef.child("posts").child(post.getDatabaseEntryName())
.child("nietSlechts").setValue(post.getNietSlechts() + 1)
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
nietSlechts.setText(Integer.toString(post.getNietSlechts() + 1));
}
});
}
});
dateTime.setText(post.getDate().toString());
final StorageReference ref = FirebaseStorage.getInstance().getReferenceFromUrl(post.getUri());
Glide.with(mContext)
.using(new FirebaseImageLoader())
.load(ref)
.into(photo);
photo.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
zoomImageFromThumb(photo, ref);
}
});
return rowView;
}
/**
* Callback method to be invoked when an item in this AdapterView has
* been clicked.
@@ -101,4 +140,140 @@ public class TimeLineAdapter extends BaseAdapter implements AdapterView.OnItemCl
Log.d(TAG, "onItemClick: rowNumber! "+ position);
}
/**
* https://developer.android.com/training/animation/zoom.html
* "Zooms" in a thumbnail view by assigning the high resolution image to a hidden "zoomed-in"
* image view and animating its bounds to fit the entire activity content area.
*
* @param thumbView The thumbnail view to zoom in.
* @param imageRef The high-resolution version of the image represented by the thumbnail.
*/
private void zoomImageFromThumb(final View thumbView, StorageReference imageRef) {
// If there's an animation in progress, cancel it immediately and proceed with this one.
if (mCurrentAnimator != null) {
mCurrentAnimator.cancel();
}
// Load the high-resolution "zoomed-in" image.
final ImageView hiddenView = (ImageView) ((MainActivity) mContext).findViewById(R.id.expanded_image);
Glide.with(mContext)
.using(new FirebaseImageLoader())
.load(imageRef)
.into(hiddenView);
// Calculate the starting and ending bounds for the zoomed-in image. This step
// involves lots of math. Yay, math.
final Rect startBounds = new Rect();
final Rect finalBounds = new Rect();
final Point globalOffset = new Point();
// The start bounds are the global visible rectangle of the thumbnail, and the
// final bounds are the global visible rectangle of the container view. Also
// set the container view's offset as the origin for the bounds, since that's
// the origin for the positioning animation properties (X, Y).
thumbView.getGlobalVisibleRect(startBounds);
((MainActivity) mContext).findViewById(R.id.relative_layout_timeline).getGlobalVisibleRect(finalBounds, globalOffset);
startBounds.offset(-globalOffset.x, -globalOffset.y);
finalBounds.offset(-globalOffset.x, -globalOffset.y);
// Adjust the start bounds to be the same aspect ratio as the final bounds using the
// "center crop" technique. This prevents undesirable stretching during the animation.
// Also calculate the start scaling factor (the end scaling factor is always 1.0).
float startScale;
if ((float) finalBounds.width() / finalBounds.height()
> (float) startBounds.width() / startBounds.height()) {
// Extend start bounds horizontally
startScale = (float) startBounds.height() / finalBounds.height();
float startWidth = startScale * finalBounds.width();
float deltaWidth = (startWidth - startBounds.width()) / 2;
startBounds.left -= deltaWidth;
startBounds.right += deltaWidth;
} else {
// Extend start bounds vertically
startScale = (float) startBounds.width() / finalBounds.width();
float startHeight = startScale * finalBounds.height();
float deltaHeight = (startHeight - startBounds.height()) / 2;
startBounds.top -= deltaHeight;
startBounds.bottom += deltaHeight;
}
// Hide the thumbnail and show the zoomed-in view. When the animation begins,
// it will position the zoomed-in view in the place of the thumbnail.
thumbView.setAlpha(0f);
hiddenView.setVisibility(View.VISIBLE);
// Set the pivot point for SCALE_X and SCALE_Y transformations to the top-left corner of
// the zoomed-in view (the default is the center of the view).
hiddenView.setPivotX(0f);
hiddenView.setPivotY(0f);
// Construct and run the parallel animation of the four translation and scale properties
// (X, Y, SCALE_X, and SCALE_Y).
AnimatorSet set = new AnimatorSet();
set
.play(ObjectAnimator.ofFloat(hiddenView, View.X, startBounds.left,
finalBounds.left))
.with(ObjectAnimator.ofFloat(hiddenView, View.Y, startBounds.top,
finalBounds.top))
.with(ObjectAnimator.ofFloat(hiddenView, View.SCALE_X, startScale, 1f))
.with(ObjectAnimator.ofFloat(hiddenView, View.SCALE_Y, startScale, 1f));
set.setDuration(200);
set.setInterpolator(new DecelerateInterpolator());
set.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mCurrentAnimator = null;
}
@Override
public void onAnimationCancel(Animator animation) {
mCurrentAnimator = null;
}
});
set.start();
mCurrentAnimator = set;
// Upon clicking the zoomed-in image, it should zoom back down to the original bounds
// and show the thumbnail instead of the expanded image.
final float startScaleFinal = startScale;
hiddenView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (mCurrentAnimator != null) {
mCurrentAnimator.cancel();
}
// Animate the four positioning/sizing properties in parallel, back to their
// original values.
AnimatorSet set = new AnimatorSet();
set
.play(ObjectAnimator.ofFloat(hiddenView, View.X, startBounds.left))
.with(ObjectAnimator.ofFloat(hiddenView, View.Y, startBounds.top))
.with(ObjectAnimator
.ofFloat(hiddenView, View.SCALE_X, startScaleFinal))
.with(ObjectAnimator
.ofFloat(hiddenView, View.SCALE_Y, startScaleFinal));
set.setDuration(200);
set.setInterpolator(new DecelerateInterpolator());
set.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
thumbView.setAlpha(1f);
hiddenView.setVisibility(View.GONE);
mCurrentAnimator = null;
}
@Override
public void onAnimationCancel(Animator animation) {
thumbView.setAlpha(1f);
hiddenView.setVisibility(View.GONE);
mCurrentAnimator = null;
}
});
set.start();
mCurrentAnimator = set;
}
});
}
}

View File

@@ -2,37 +2,89 @@ package nl.myhyvesbookplus.tagram;
import android.app.Fragment;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.widget.SwipeRefreshLayout;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.Toast;
import nl.myhyvesbookplus.tagram.controller.DownloadClass;
public class TimelineFragment extends Fragment {
/* Some protected and private inits */
private ListView listView;
private DownloadClass downloadClass;
private ProgressBar progressBar;
public TimelineFragment() {
// Required empty public constructor
}
/* Required empty public constructor */
public TimelineFragment() {}
/**
* Overridden onCreateView method which creates the ListView and contains a possible refresh
* functionality (swipe down page for result).
*
* https://www.survivingwithandroid.com/2014/05/android-swiperefreshlayout-tutorial-2.html
* Above reference was largely copied from.
* @param inflater The inflater used for the fragment.
* @param container The container which holds this fragment.
* @param savedInstanceState The state which was provided by onCreate.
* @return the timeLineInflater View which is required for the ListView to be updated.
*/
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_timeline, container, false);
listView = (ListView) view.findViewById(R.id.listview);
View timeLineInflater = inflater.inflate(R.layout.fragment_timeline, container, false);
listView = (ListView) timeLineInflater.findViewById(R.id.list);
final SwipeRefreshLayout swipeView = (SwipeRefreshLayout) timeLineInflater.findViewById(R.id.swipe);
progressBar = (ProgressBar) timeLineInflater.findViewById(R.id.progressbar_timeline);
progressBar.setVisibility(View.VISIBLE);
swipeView.setEnabled(false);
downloadClass = new DownloadClass(getActivity());
downloadClass.getPostsFromServer();
return view;
swipeView.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
downloadClass.getPostsFromServer();
Toast.makeText(getActivity(), R.string.refreshing,
Toast.LENGTH_LONG).show();
swipeView.setRefreshing(true);
( new Handler()).postDelayed(new Runnable() {
@Override
public void run() {
swipeView.setRefreshing(false);
}
}, 1000);
}
});
listView.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView absListView, int i) {
}
@Override
public void onScroll(AbsListView absListView, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
swipeView.setEnabled(firstVisibleItem == 0);
}
});
return timeLineInflater;
}
/**
* Start display of the list; uses an adapter and listener in the main activity.
*/
public void startList() {
TimeLineAdapter adapter = new TimeLineAdapter(getActivity(), downloadClass.getmList());
listView.setAdapter(adapter);
progressBar.setVisibility(View.GONE);
}
}

View File

@@ -3,6 +3,7 @@ package nl.myhyvesbookplus.tagram.controller;
import android.content.Context;
import android.util.Log;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
@@ -36,14 +37,17 @@ public class DownloadClass {
}
public void getPostsFromServer() {
Log.d(TAG, "getPostsFromServer: Begin of function");
mDataRef.child("posts").addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot data : dataSnapshot.getChildren()) {
mList.add(data.getValue(UriPost.class));
UriPost tempPost = data.getValue(UriPost.class);
tempPost.setDatabaseEntryName(data.getKey());
mList.add(tempPost);
}
Collections.reverse(mList);
mListener.PostDownloaded();
}
@@ -58,6 +62,19 @@ public class DownloadClass {
return mList;
}
public ArrayList<UriPost> getOwnPosts() {
String currentUid = FirebaseAuth.getInstance().getCurrentUser().getUid();
ArrayList<UriPost> posts = new ArrayList<>();
for (UriPost post : mList) {
if (post.getPoster().equals(currentUid)) {
posts.add(post);
}
}
return posts;
}
public interface PostDownloadListener {
void PostDownloaded();
}

View File

@@ -9,6 +9,7 @@ import java.util.Date;
*/
public class UriPost extends Post {
private String uri;
private String databaseEntryName;
public UriPost() {
// Default constructor required for calls to DataSnapshot.getValue(UriPost.class)
@@ -33,4 +34,12 @@ public class UriPost extends Post {
public void setUri(String uri) {
this.uri = uri;
}
public String getDatabaseEntryName() {
return databaseEntryName;
}
public void setDatabaseEntryName(String databaseEntryName) {
this.databaseEntryName = databaseEntryName;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 199 B

View File

@@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:strokeColor="#AAAAAA" android:strokeWidth="0.5"
android:fillColor="#FFFFFFFF"
android:fillAlpha="0.5"
android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
</vector>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 222 B

View File

@@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:strokeColor="#AAAAAA" android:strokeWidth="0.5"
android:fillColor="#FFFFFFFF"
android:fillAlpha="0.5"
android:pathData="M12,4l-1.41,1.41L16.17,11H4v2h12.17l-5.58,5.59L12,20l8,-8z"/>
</vector>

View File

@@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:strokeColor="#AAAAAA" android:strokeWidth="0.5"
android:fillColor="#FFFFFFFF"
android:fillAlpha="0.5"
android:pathData="M9.4,10.5l4.77,-8.26C13.47,2.09 12.75,2 12,2c-2.4,0 -4.6,0.85 -6.32,2.25l3.66,6.35 0.06,-0.1zM21.54,9c-0.92,-2.92 -3.15,-5.26 -6,-6.34L11.88,9h9.66zM21.8,10h-7.49l0.29,0.5 4.76,8.25C21,16.97 22,14.61 22,12c0,-0.69 -0.07,-1.35 -0.2,-2zM8.54,12l-3.9,-6.75C3.01,7.03 2,9.39 2,12c0,0.69 0.07,1.35 0.2,2h7.49l-1.15,-2zM2.46,15c0.92,2.92 3.15,5.26 6,6.34L12.12,15L2.46,15zM13.73,15l-3.9,6.76c0.7,0.15 1.42,0.24 2.17,0.24 2.4,0 4.6,-0.85 6.32,-2.25l-3.66,-6.35 -0.93,1.6z"/>
</vector>

View File

@@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:strokeColor="#AAAAAA" android:strokeWidth="0.5"
android:fillColor="#FFFFFFFF"
android:fillAlpha="0.5"
android:pathData="M20,4h-3.17L15,2L9,2L7.17,4L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2zM15,15.5L15,13L9,13v2.5L5.5,12 9,8.5L9,11h6L15,8.5l3.5,3.5 -3.5,3.5z"/>
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

@@ -13,7 +13,7 @@
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:padding="16dp">
android:padding="0dp">
</FrameLayout>
<android.support.design.widget.BottomNavigationView

View File

@@ -18,28 +18,23 @@
android:layout_centerHorizontal="true"
android:background="@android:color/transparent"
android:layout_margin="10dp"
android:padding="10dp"
android:padding="15dp"
android:scaleType="center"
android:scaleX="2"
android:scaleY="2"
app:srcCompat="@android:drawable/ic_menu_camera" />
android:scaleX="2.5"
android:scaleY="2.5"
android:src="@drawable/ic_camera_black_24dp"/>
<ImageButton
android:id="@+id/switch_camera_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:padding="10dp"
android:layout_margin="9dp"
android:scaleX="1.5"
android:scaleY="1.5"
android:background="@android:color/transparent"
app:srcCompat="@android:drawable/ic_menu_revert" />
<ProgressBar
android:id="@+id/load_bar"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminate="true"
android:layout_centerInParent="true"/>
android:src="@drawable/ic_switch_camera_black_24dp"/>
<RelativeLayout
android:id="@+id/filter_buttons"
@@ -54,8 +49,10 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:scaleX="0.5"
android:scaleY="0.5"
android:scaleX="2"
android:scaleY="2"
android:padding="10dp"
android:layout_margin="10dp"
android:layout_alignParentLeft="true"
android:src="@drawable/ic_arrow_back_black_24dp"/>
@@ -64,8 +61,10 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:scaleX="0.5"
android:scaleY="0.5"
android:scaleX="2"
android:scaleY="2"
android:padding="10dp"
android:layout_margin="10dp"
android:layout_alignParentRight="true"
android:src="@drawable/ic_arrow_forward_black_24dp"/>
</RelativeLayout>
@@ -74,13 +73,17 @@
android:id="@+id/comment_box"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:clickable="false"
android:layout_margin="10dp"
android:padding="5dp"
android:background="@android:color/background_light"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Comment:"/>
android:text="@string/comment"/>
<EditText
android:id="@+id/comment_text"
@@ -89,47 +92,41 @@
android:lines="4"
android:layout_margin="4dp"
android:padding="5dp"
android:visibility="visible"
android:background="@android:color/darker_gray"/>
<Button
android:id="@+id/comment_submit"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Submit"/>
android:orientation="horizontal">
<Button
android:id="@+id/comment_submit"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/upload"/>
<Button
android:id="@+id/comment_cancel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/cancel"/>
</LinearLayout>
</LinearLayout>
<RelativeLayout
android:id="@+id/picture_taken_buttons"
android:layout_width="match_parent"
<android.support.design.widget.FloatingActionButton
android:id="@+id/upload_button"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginLeft="30dp"
android:layout_marginRight="30dp"
android:layout_marginBottom="20dp">
<android.support.design.widget.FloatingActionButton
android:id="@+id/upload_button"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:scaleType="center"
app:fabSize="normal"
android:src="@android:drawable/ic_menu_upload"/>
<!--<android.support.design.widget.FloatingActionButton
android:id="@+id/save_button"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:scaleType="center"
app:fabSize="normal"
android:src="@android:drawable/ic_menu_save"/>-->
</RelativeLayout>
android:layout_centerHorizontal="true"
android:scaleType="center"
android:layout_margin="15dp"
app:fabSize="normal"
android:src="@android:drawable/ic_menu_upload"/>
</RelativeLayout>

View File

@@ -1,81 +0,0 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="nl.myhyvesbookplus.tagram.ProfileFragment">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical">
<Space
android:layout_width="match_parent"
android:layout_height="5dp" />
<TextView
android:id="@+id/profile_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="Name:"
android:textSize="25sp"
android:textStyle="bold" />
<Space
android:layout_width="match_parent"
android:layout_height="10dp" />
<ImageButton
android:id="@+id/profile_pic_button"
android:layout_width="60dp"
android:layout_height="80dp"
android:layout_marginStart="110dp"
android:background="@android:color/transparent"
android:elevation="1dp"
app:srcCompat="@android:drawable/ic_menu_camera" />
<ImageView
android:id="@+id/imageView_profile_picture"
android:contentDescription="@string/profile_picture_description"
android:layout_width="match_parent"
android:layout_height="300dp"
app:srcCompat="@drawable/avatar_standard"
android:layout_marginTop="-70dp"/>
<Space
android:layout_width="match_parent"
android:layout_height="25dp" />
<Button
android:id="@+id/change_psw_button"
android:layout_width="300dp"
android:layout_height="50dp"
android:background="?attr/colorPrimary"
android:text="@string/change_psw_button"
android:textColor="@android:color/white"
android:textSize="16sp" />
<Space
android:layout_width="match_parent"
android:layout_height="20dp" />
<Button
android:id="@+id/logout_button"
android:layout_width="300dp"
android:layout_height="50dp"
android:background="?attr/colorPrimary"
android:onClick="logOutOnClick"
android:text="@string/logout_button"
android:textColor="@android:color/white"
android:textSize="16sp" />
</LinearLayout>
</ScrollView>
</FrameLayout>

View File

@@ -65,5 +65,12 @@
android:text="@string/logout_button"
android:textColor="@android:color/white"
android:textSize="16sp" />
<Space
android:layout_width="match_parent"
android:layout_height="10dp" />
<Space
android:layout_width="match_parent"
android:layout_height="10dp" />
</LinearLayout>

View File

@@ -1,14 +1,35 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/relative_layout_timeline_profile"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="nl.myhyvesbookplus.tagram.TimelineFragment">
<ListView
android:id="@+id/listview_profile"
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
</ListView>
</LinearLayout>
<ProgressBar
android:id="@+id/progressbar_timeline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:indeterminate="true"
android:visibility="gone"/>
<ListView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ListView>
</RelativeLayout>
<ImageView
android:id="@+id/expanded_image_profile"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible" />
</RelativeLayout>

View File

@@ -1,15 +1,43 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/relative_layout_timeline"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="nl.myhyvesbookplus.tagram.TimelineFragment">
<ListView
android:id="@+id/listview"
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
</ListView>
<ProgressBar
android:id="@+id/progressbar_timeline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:indeterminate="true"
android:visibility="gone"/>
</LinearLayout>
<android.support.v4.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/swipe"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ListView>
</android.support.v4.widget.SwipeRefreshLayout>
</RelativeLayout>
<ImageView
android:id="@+id/expanded_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible" />
</RelativeLayout>

View File

@@ -1,40 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools"
android:layout_height="wrap_content"
android:orientation="vertical">
<Space
android:layout_width="match_parent"
android:layout_height="15dp" />
<TextView
android:id="@+id/username_timeline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
<ImageView
android:id="@+id/timeline_image"
android:layout_width="wrap_content"
android:layout_gravity="center"
android:layout_height="250dp" />
android:layout_height="250dp"
android:layout_gravity="center" />
<TextView
android:id="@+id/timeline_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:paddingBottom="5dp"
android:textColor="#AAAAAA"
android:text="date/time" />
<TextView
android:id="@+id/comment_timeline"
android:layout_width="match_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="Hallo Ik ben een comment!" />
<LinearLayout
android:padding="5dp"
android:layout_width="match_parent"
android:layout_height="40dp">
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="horizontal">
<ImageButton
android:id="@+id/niet_slecht_button"
android:layout_width="200dp"
android:layout_height="match_parent" />
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="@string/niet_slecht" />
android:layout_width="150dp"
android:layout_height="40dp"
android:scaleType="centerInside"
android:background="@android:color/transparent"
android:src="@drawable/niet_slecht"/>
<TextView
android:id="@+id/niet_slecht_count"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:textSize="20sp"
android:text="10" />
</LinearLayout>
<Space
android:layout_width="match_parent"
android:layout_height="10dp" />
</LinearLayout>

View File

@@ -3,6 +3,10 @@
android:layout_height="match_parent"
android:orientation="vertical">
<Space
android:layout_width="match_parent"
android:layout_height="15dp" />
<ImageView
android:id="@+id/timeline_image_profile"
android:layout_width="wrap_content"
@@ -13,21 +17,34 @@
android:id="@+id/comment_timeline_profile"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Hallo Ik ben een comment!" />
android:gravity="center_horizontal"
android:text="@string/comment_placeholder" />
<LinearLayout
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="40dp">
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="@string/niet_slecht" />
<ImageButton
android:id="@+id/niet_slecht_button"
android:layout_width="150dp"
android:layout_height="40dp"
android:scaleType="centerInside"
android:background="@android:color/transparent"
android:src="@drawable/niet_slecht"/>
<TextView
android:id="@+id/niet_slecht_count_profile"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:textSize="20sp"
android:text="10" />
</LinearLayout>
<Space
android:layout_width="match_parent"
android:layout_height="15dp" />
</LinearLayout>

View File

@@ -11,24 +11,28 @@
<string name="title_home">Tijdlijn</string>
<string name="title_notifications">Profiel</string>
<string name="username">Gebruikersnaam</string>
<string name="confirm_password_hint">bevestig wachtwoord</string>
<string name="logo_text">MyHyvesBookPlusTagram logo</string>
<string name="logout_button">Uitloggen</string>
<string name="profile_picture_button">Wijzig Profiel Foto</string>
<string name="profile_picture_description">profiel foto</string>
<string name="change_psw_button">Wachtwoord wijzigen</string>
<string name="please_wait">Momentje</string>
<string name="hello_blank_fragment">Hallo leeg fragment</string>
<string name="hello_camera">Hallo Camera fragment</string>
<string name="login_error">Voer alstublieft email en wachtwoord in</string>
<string name="mail_failed">Er is een fout opgetreden. Controleer internetverbinding.</string>
<string name="mail_successful">Er is een e-mail verzonden. Volg a.u.b. de instructies.</string>
<string name="password_match_error">Wachtwoorden komen niet overeen</string>
<string name="password_match_error">Wachtwoorden komoen niet overeen</string>
<string name="register_error">Vul alstublieft alle velden in</string>
<string name="save">Opslaan</string>
<string name="upload">Uploaden</string>
<string name="image_save_error">Foto opslaan mislukt. Zorg a.u.b. dat er genoeg ruimte op uw telefoon beschikbaar is.</string>
<string name="update_profile_pic_error">Het updaten van de profielfoto is mislukt. Controleer uw internetverbinding.</string>
<string name="upload_profile_pic">Profielfoto aan het uploaden…</string>
<string name="niet_slecht">"\"Niet slecht.\"s "</string>
<string name="niet_slecht">Niet Slecht.</string>
<string name="comment">Bijschrift:</string>
<string name="cancel">Annuleer</string>
<string name="comment_placeholder">Hallo Ik ben een comment!</string>
<string name="refreshing">Verversen…</string>
<string name="downloading_posts">Posts worden gedownload…</string>
<string name="logging_in">Aan het inloggen</string>
<string name="registering">Aan het registreren</string>
</resources>

View File

@@ -6,7 +6,6 @@
<string name="username">Username</string>
<string name="password">Password</string>
<string name="confirm_password">Confirm Password</string>
<string name="confirm_password_hint">confirm password</string>
<string name="login_button">Login</string>
<string name="register">Register</string>
<string name="back_to_login">back to Login</string>
@@ -14,20 +13,25 @@
<string name="login_error">Please fill in email and password</string>
<string name="password_match_error">Passwords do not match</string>
<string name="register_error">Please fill in all the fields</string>
<string name="hello_blank_fragment">Hello blank fragment</string>
<string name="hello_camera">Hello Camera</string>
<string name="logo_text">MyHyvesBookPlusTagram logo</string>
<string name="logout_button">Logout</string>
<string name="profile_picture_button">Change Profile Picture</string>
<string name="profile_picture_description">profile picture</string>
<string name="change_psw_button">Change Password</string>
<string name="please_wait">Please Wait</string>
<string name="niet_slecht">"\"Niet slecht.\"s "</string>
<string name="niet_slecht">\"\"Niet slecht.\"s: \"</string>
<string name="upload">Upload</string>
<string name="save">Save</string>
<string name="mail_successful">An e-mail was sent. Please follow its instructions.</string>
<string name="mail_failed">An error occurred. Please check internet connection.</string>
<string name="image_save_error">Saving image to storage failed. Please make sure there is space available on the device.</string>
<string name="update_profile_pic_error">Updating the profile picture failed. Please check network connection.</string>
<string name="upload_profile_pic">Uploading profile picture...</string>
<string name="upload_profile_pic">Uploading profile picture</string>
<string name="comment">Comment:</string>
<string name="cancel">Cancel</string>
<string name="comment_placeholder">Hello, I am a comment!</string>
<string name="refreshing">Refreshing…</string>
<string name="downloading_posts">Downloading posts…</string>
<string name="registering">Registering</string>
<string name="logging_in">Logging in</string>
</resources>