Explorar o código

Merge branch 'master' into newui

seavenois %!s(int64=9) %!d(string=hai) anos
pai
achega
999a03e0ef
Modificáronse 33 ficheiros con 1808 adicións e 628 borrados
  1. 2 7
      .idea/gradle.xml
  2. 8 2
      app/build.gradle
  3. 8 4
      app/src/main/AndroidManifest.xml
  4. 7 5
      app/src/main/java/com/ivalentin/margolariak/ActivityFutureLayout.java
  5. 10 6
      app/src/main/java/com/ivalentin/margolariak/ActivityLayout.java
  6. 8 5
      app/src/main/java/com/ivalentin/margolariak/ActivityPastLayout.java
  7. 1 30
      app/src/main/java/com/ivalentin/margolariak/AlarmReceiver.java
  8. 16 9
      app/src/main/java/com/ivalentin/margolariak/AlbumLayout.java
  9. 1 2
      app/src/main/java/com/ivalentin/margolariak/BlogLayout.java
  10. 13 6
      app/src/main/java/com/ivalentin/margolariak/DownloadImage.java
  11. 198 2
      app/src/main/java/com/ivalentin/margolariak/GM.java
  12. 2 3
      app/src/main/java/com/ivalentin/margolariak/GalleryLayout.java
  13. 14 17
      app/src/main/java/com/ivalentin/margolariak/HomeLayout.java
  14. 2 4
      app/src/main/java/com/ivalentin/margolariak/HomeSectionLocation.java
  15. 9 10
      app/src/main/java/com/ivalentin/margolariak/LablancaLayout.java
  16. 5 12
      app/src/main/java/com/ivalentin/margolariak/LocationLayout.java
  17. 408 291
      app/src/main/java/com/ivalentin/margolariak/MainActivity.java
  18. 1 1
      app/src/main/java/com/ivalentin/margolariak/PhotoLayout.java
  19. 1 1
      app/src/main/java/com/ivalentin/margolariak/PostComment.java
  20. 1 1
      app/src/main/java/com/ivalentin/margolariak/PostLayout.java
  21. 4 3
      app/src/main/java/com/ivalentin/margolariak/ScheduleLayout.java
  22. 4 8
      app/src/main/java/com/ivalentin/margolariak/SettingsActivity.java
  23. 476 171
      app/src/main/java/com/ivalentin/margolariak/Sync.java
  24. 24 12
      app/src/main/res/layout/activity_main.xml
  25. 3 2
      app/src/main/res/layout/fragment_layout_activity_past.xml
  26. 1 0
      app/src/main/res/layout/fragment_layout_location.xml
  27. 21 2
      app/src/main/res/values-en/strings.xml
  28. 21 2
      app/src/main/res/values-eu-rES/strings.xml
  29. 6 6
      app/src/main/res/values/strings.xml
  30. 1 1
      build.gradle
  31. 526 1
      build/intermediates/dex-cache/cache.xml
  32. 4 0
      gradle.properties
  33. 2 2
      gradle/wrapper/gradle-wrapper.properties

+ 2 - 7
.idea/gradle.xml

@@ -5,19 +5,14 @@
       <GradleProjectSettings>
         <option name="distributionType" value="LOCAL" />
         <option name="externalProjectPath" value="$PROJECT_DIR$" />
-        <option name="gradleHome" value="$APPLICATION_HOME_DIR$/gradle/gradle-2.10" />
+        <option name="gradleHome" value="$APPLICATION_HOME_DIR$/gradle/gradle-2.14.1" />
         <option name="modules">
           <set>
             <option value="$PROJECT_DIR$" />
             <option value="$PROJECT_DIR$/app" />
           </set>
         </option>
-        <option name="myModules">
-          <set>
-            <option value="$PROJECT_DIR$" />
-            <option value="$PROJECT_DIR$/app" />
-          </set>
-        </option>
+        <option name="resolveModulePerSourceSet" value="false" />
       </GradleProjectSettings>
     </option>
   </component>

+ 8 - 2
app/build.gradle

@@ -8,6 +8,7 @@ android {
         applicationId "com.ivalentin.margolariak"
         minSdkVersion 12
         targetSdkVersion 23
+        multiDexEnabled true
     }
 
     buildTypes {
@@ -16,11 +17,16 @@ android {
             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
         }
     }
+
+    dexOptions {
+        incremental = true
+        preDexLibraries = false
+        javaMaxHeapSize "2g"
+    }
 }
 
 dependencies {
-//    compile 'com.android.support:support-v4:23.4.0'
-//    compile 'com.android.support:appcompat-v7:23.4.0'
     //noinspection GradleDynamicVersion
     compile 'com.google.android.gms:play-services:+'
+    compile 'com.android.support:multidex:1.0.1'
 }

+ 8 - 4
app/src/main/AndroidManifest.xml

@@ -2,8 +2,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           xmlns:tools="http://schemas.android.com/tools"
           package="com.ivalentin.margolariak"
-    android:versionCode="18"
-    android:versionName="2.0.4" >
+    android:versionCode="19"
+    android:versionName="2.0.5" >
 
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
@@ -28,8 +28,10 @@
         android:supportsRtl="true"
         android:isGame="false"
         android:fullBackupContent="@xml/backup"
+        android:hardwareAccelerated="true"
         android:allowClearUserData="true"
-        tools:ignore="UnusedAttribute">
+        tools:ignore="UnusedAttribute"
+        android:name="android.support.multidex.MultiDexApplication">
         
         <meta-data
     		android:name="com.google.android.gms.version"
@@ -61,7 +63,9 @@
             android:screenOrientation="portrait"
             android:label="@string/app_name">
         </activity>
-        
+        <activity android:name=".AboutActivity"/>
+        <activity android:name=".SponsorActivity"/>
+
         <receiver
             android:process=":remote"
             android:name="com.ivalentin.margolariak.AlarmReceiver"/>

+ 7 - 5
app/src/main/java/com/ivalentin/margolariak/ActivityFutureLayout.java

@@ -2,7 +2,6 @@ package com.ivalentin.margolariak;
 
 import android.annotation.SuppressLint;
 import android.app.Dialog;
-import android.content.Context;
 import android.content.DialogInterface;
 import android.content.pm.PackageManager;
 import android.database.Cursor;
@@ -110,6 +109,7 @@ public class ActivityFutureLayout extends Fragment implements OnMapReadyCallback
 			wvText.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
 		}
 		wvText.loadDataWithBaseURL(null, cursor.getString(2), "text/html", "utf-8", null);
+		//wvText.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
         tvDate.setText(GM.formatDate(cursor.getString(3) + " 00:00:00", lang, false));
         tvPrice.setText(String.format(getString(R.string.price), cursor.getInt(5)));
         tvCity.setText(cursor.getString(4));
@@ -117,8 +117,9 @@ public class ActivityFutureLayout extends Fragment implements OnMapReadyCallback
         //Get images
         Cursor imageCursor = db.rawQuery("SELECT image, idx FROM activity_image WHERE activity = " + id + " ORDER BY idx LIMIT 5;", null);
         int i = 0;
+		String image;
         while (imageCursor.moveToNext()) {
-            String image = imageCursor.getString(0);
+            image = imageCursor.getString(0);
 
             //Check if image exists
             File f;
@@ -134,7 +135,7 @@ public class ActivityFutureLayout extends Fragment implements OnMapReadyCallback
                 fpath = new File(this.getActivity().getFilesDir().toString() + "/img/actividades/preview/");
                 //noinspection ResultOfMethodCallIgnored
                 fpath.mkdirs();
-                new DownloadImage(GM.SERVER + "/img/actividades/preview/" + image, this.getActivity().getFilesDir().toString() + "/img/actividades/preview/" + image, images[i]).execute();
+                new DownloadImage(GM.SERVER + "/img/actividades/preview/" + image, this.getActivity().getFilesDir().toString() + "/img/actividades/preview/" + image, images[i], GM.IMG_PREVIEW).execute();
             }
             images[i].setVisibility(View.VISIBLE);
             i ++;
@@ -190,7 +191,7 @@ public class ActivityFutureLayout extends Fragment implements OnMapReadyCallback
                     tvSchAddress.setText(cursorItinerary.getString(5));
                 }
 
-				//Set clisk listener
+				//Set click listener
 				entry.setOnClickListener(new View.OnClickListener() {
 					@Override
 					public void onClick(View v) {
@@ -217,7 +218,8 @@ public class ActivityFutureLayout extends Fragment implements OnMapReadyCallback
      *
      * @param id The event id
      */
-    private void showDialog(final int id){
+    @SuppressWarnings("ConstantConditions")
+	private void showDialog(final int id){
 
         //Create the dialog
         final Dialog dialog = new Dialog(getActivity());

+ 10 - 6
app/src/main/java/com/ivalentin/margolariak/ActivityLayout.java

@@ -2,7 +2,6 @@ package com.ivalentin.margolariak;
 
 
 import android.annotation.SuppressLint;
-import android.content.Context;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.graphics.Bitmap;
@@ -12,6 +11,7 @@ import android.app.Fragment;
 import android.app.FragmentManager;
 import android.app.FragmentTransaction;
 import android.text.Html;
+import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -139,7 +139,7 @@ public class ActivityLayout extends Fragment{
                         File fpath;
                         fpath = new File(this.getActivity().getFilesDir().toString() + "/img/actividades/miniature/");
                         fpath.mkdirs();
-                        new DownloadImage(GM.SERVER + "/img/actividades/miniature/" + image, this.getActivity().getFilesDir().toString() + "/img/actividades/miniature/" + image, iv).execute();
+                        new DownloadImage(GM.SERVER + "/img/actividades/miniature/" + image, this.getActivity().getFilesDir().toString() + "/img/actividades/miniature/" + image, iv, GM.IMG_MINIATURE).execute();
                     }
                 }
                 cursorImage.close();
@@ -192,11 +192,15 @@ public class ActivityLayout extends Fragment{
 
             //Set text
             String text;
-            if (cursorPast.getString(5).length() < 1){
+            if (cursorPast.getString(5) == null){
                 text = Html.fromHtml(cursorPast.getString(4)).toString();
             }
-            else{
-                text = Html.fromHtml(cursorPast.getString(5)).toString();
+            else {
+                if (cursorPast.getString(5).length() < 1) {
+                    text = Html.fromHtml(cursorPast.getString(4)).toString();
+                } else {
+                    text = Html.fromHtml(cursorPast.getString(5)).toString();
+                }
             }
             if (text.length() > 130) {
                 text = text.substring(0, 130) + "...";
@@ -231,7 +235,7 @@ public class ActivityLayout extends Fragment{
                     File fpath;
                     fpath = new File(this.getActivity().getFilesDir().toString() + "/img/actividades/miniature/");
                     fpath.mkdirs();
-                    new DownloadImage(GM.SERVER + "/img/actividades/miniature/" + image, this.getActivity().getFilesDir().toString() + "/img/actividades/miniature/" + image, iv).execute();
+                    new DownloadImage(GM.SERVER + "/img/actividades/miniature/" + image, this.getActivity().getFilesDir().toString() + "/img/actividades/miniature/" + image, iv, GM.IMG_MINIATURE).execute();
                 }
             }
             cursorImage.close();

+ 8 - 5
app/src/main/java/com/ivalentin/margolariak/ActivityPastLayout.java

@@ -1,7 +1,6 @@
 package com.ivalentin.margolariak;
 
 import android.annotation.SuppressLint;
-import android.content.Context;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.graphics.Bitmap;
@@ -76,11 +75,15 @@ public class ActivityPastLayout extends Fragment {
         //Set fields
         tvTitle.setText(cursor.getString(1));
         ((MainActivity) getActivity()).setSectionTitle(cursor.getString(1));
-        if (cursor.getString(5).length() < 1) {
-            wvText.loadDataWithBaseURL(null, cursor.getString(2), "text/html", "utf-8", null);
+        if (cursor.getString(5) != null) {
+            if (cursor.getString(5).length() < 1) {
+                wvText.loadDataWithBaseURL(null, cursor.getString(2), "text/html", "utf-8", null);
+            } else {
+                wvText.loadDataWithBaseURL(null, cursor.getString(5), "text/html", "utf-8", null);
+            }
         }
         else{
-            wvText.loadDataWithBaseURL(null, cursor.getString(5), "text/html", "utf-8", null);
+            wvText.loadDataWithBaseURL(null, cursor.getString(2), "text/html", "utf-8", null);
         }
         tvDate.setText(GM.formatDate(cursor.getString(3) + " 00:00:00", lang, false));
         tvCity.setText(cursor.getString(4));
@@ -104,7 +107,7 @@ public class ActivityPastLayout extends Fragment {
                 File fpath;
                 fpath = new File(this.getActivity().getFilesDir().toString() + "/img/actividades/preview/");
                 fpath.mkdirs();
-                new DownloadImage(GM.SERVER + "/img/actividades/preview/" + image, this.getActivity().getFilesDir().toString() + "/img/actividades/preview/" + image, images[i]).execute();
+                new DownloadImage(GM.SERVER + "/img/actividades/preview/" + image, this.getActivity().getFilesDir().toString() + "/img/actividades/preview/" + image, images[i], GM.IMG_PREVIEW).execute();
              }
             images[i].setVisibility(View.VISIBLE);
             i ++;

+ 1 - 30
app/src/main/java/com/ivalentin/margolariak/AlarmReceiver.java

@@ -1,9 +1,5 @@
 package com.ivalentin.margolariak;
 
-import java.text.SimpleDateFormat;
-import java.util.Calendar;
-import java.util.Locale;
-
 import android.app.AlarmManager;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
@@ -126,32 +122,6 @@ public class AlarmReceiver extends BroadcastReceiver {
 				Log.e("Notification error", "Error fetching remote file: " + ex.toString());
 			}
     	}
-		
-		//Get location
-		boolean result = false;
-		fu = new FetchURL();
-		fu.Run(GM.SERVER + "/app/location.php");
-		String lat = "", lon = "";
-		String o = fu.getOutput().toString();
-		Log.d("Alarm", "Fetched location");
-		if (o.contains("<location>none</location>"))
-			result = false;
-		if (o.contains("<lat>") && o.contains("<lon>")){
-			lat = o.substring(o.indexOf("<lat>") + 5, o.indexOf("</lat>"));
-			lon = o.substring(o.indexOf("<lon>") + 5, o.indexOf("</lon>"));
-			result = true;
-		}
-
-		SharedPreferences.Editor editor = settings.edit();
-		if (result){
-	    	editor.putLong(GM.PREF_GM_LATITUDE, Double.doubleToLongBits(Double.parseDouble(lat)));
-			editor.putLong(GM.PREF_GM_LONGITUDE, Double.doubleToLongBits(Double.parseDouble(lon)));
-			editor.putString(GM.PREF_GM_LOCATION, new SimpleDateFormat("yyyyMMddHHmmss", Locale.US).format(Calendar.getInstance().getTime()));
-		}
-		else{
-			editor.putString(GM.PREF_GM_LOCATION, GM.DEFAULT_PREF_GM_LOCATION);
-		}
-		editor.apply();
 
 		//Perform a background sync
 		new Sync(context).execute();
@@ -175,4 +145,5 @@ public class AlarmReceiver extends BroadcastReceiver {
         PackageManager pm = context.getPackageManager();
         pm.setComponentEnabledSetting(receiver, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);           
     }
+
 }

+ 16 - 9
app/src/main/java/com/ivalentin/margolariak/AlbumLayout.java

@@ -1,11 +1,8 @@
 package com.ivalentin.margolariak;
 
 import android.annotation.SuppressLint;
-import android.content.Context;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
 import android.os.Build;
 import android.os.Bundle;
 import android.app.Fragment;
@@ -118,15 +115,20 @@ public class AlbumLayout extends Fragment {
             f = new File(this.getActivity().getFilesDir().toString() + "/img/galeria/preview/" + image);
             if (f.exists()){
                 //If the image exists, set it.
-                Bitmap myBitmap = BitmapFactory.decodeFile(this.getActivity().getFilesDir().toString() + "/img/galeria/preview/" + image);
-                ivLeft.setImageBitmap(myBitmap);
+				try{
+					File file = new File(this.getActivity().getFilesDir().toString() + "/img/galeria/preview/" + image);
+					ivLeft.setImageBitmap(GM.decodeSampledBitmapFromFile(file.getAbsolutePath(), GM.IMG_PREVIEW));
+				}
+				catch (Exception ex){
+					Log.e("Bitmap error", "Not loading image " + image + ": " + ex.toString());
+				}
             }
             else {
                 //If not, create directories and download asynchronously
                 File fpath;
                 fpath = new File(this.getActivity().getFilesDir().toString() + "/img/galeria/preview/");
                 fpath.mkdirs();
-				new DownloadImage(GM.SERVER + "/img/galeria/preview/" + image, this.getActivity().getFilesDir().toString() + "/img/galeria/preview/" + image, ivLeft).execute();
+				new DownloadImage(GM.SERVER + "/img/galeria/preview/" + image, this.getActivity().getFilesDir().toString() + "/img/galeria/preview/" + image, ivLeft, GM.IMG_PREVIEW).execute();
             }
 
 			//Count comments
@@ -183,15 +185,20 @@ public class AlbumLayout extends Fragment {
                 f = new File(this.getActivity().getFilesDir().toString() + "/img/galeria/preview/" + image);
                 if (f.exists()){
                     //If the image exists, set it.
-                    Bitmap myBitmap = BitmapFactory.decodeFile(this.getActivity().getFilesDir().toString() + "/img/galeria/preview/" + image);
-                    ivRight.setImageBitmap(myBitmap);
+					try {
+						File file = new File(this.getActivity().getFilesDir().toString() + "/img/galeria/preview/" + image);
+						ivRight.setImageBitmap(GM.decodeSampledBitmapFromFile(file.getAbsolutePath(), GM.IMG_PREVIEW));
+					}
+					catch (Exception ex){
+						Log.e("Bitmap error", "Not loading image " + image + ": " + ex.toString());
+					}
                 }
                 else {
                     //If not, create directories and download asynchronously
                     File fpath;
                     fpath = new File(this.getActivity().getFilesDir().toString() + "/img/galeria/preview/");
                     fpath.mkdirs();
-					new DownloadImage(GM.SERVER + "/img/galeria/preview/" + image, this.getActivity().getFilesDir().toString() + "/img/galeria/preview/" + image, ivRight).execute();
+					new DownloadImage(GM.SERVER + "/img/galeria/preview/" + image, this.getActivity().getFilesDir().toString() + "/img/galeria/preview/" + image, ivRight, GM.IMG_PREVIEW).execute();
                 }
 
 				//Count comments

+ 1 - 2
app/src/main/java/com/ivalentin/margolariak/BlogLayout.java

@@ -2,7 +2,6 @@ package com.ivalentin.margolariak;
 
 
 import android.annotation.SuppressLint;
-import android.content.Context;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.graphics.Bitmap;
@@ -204,7 +203,7 @@ public class BlogLayout extends Fragment{
                     File fpath;
                     fpath = new File(this.getActivity().getFilesDir().toString() + "/img/blog/miniature/");
                     fpath.mkdirs();
-                    new DownloadImage(GM.SERVER + "/img/blog/miniature/" + image, this.getActivity().getFilesDir().toString() + "/img/blog/miniature/" + image, iv).execute();
+                    new DownloadImage(GM.SERVER + "/img/blog/miniature/" + image, this.getActivity().getFilesDir().toString() + "/img/blog/miniature/" + image, iv, GM.IMG_MINIATURE).execute();
                 }
             }
             cursorImage.close();

+ 13 - 6
app/src/main/java/com/ivalentin/margolariak/DownloadImage.java

@@ -1,13 +1,12 @@
 package com.ivalentin.margolariak;
 
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
 import android.os.AsyncTask;
 import android.util.Log;
 import android.view.View;
 import android.widget.ImageView;
 
 import java.io.BufferedInputStream;
+import java.io.File;
 import java.io.FileOutputStream;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -26,6 +25,7 @@ class DownloadImage extends AsyncTask<Void, Void, Void> {
     private final String file;
     private final String path;
     private final ImageView iv;
+	private final int size;
 
 	/**
 	 * Constructor.
@@ -33,14 +33,16 @@ class DownloadImage extends AsyncTask<Void, Void, Void> {
 	 * @param file URL of the remote file.
 	 * @param path Path, including file name, where the image will be saved.
 	 * @param iv ImageView that will hold the image.
+	 * @param size Max size (width or height of the image)
 	 *
 	 * @see android.widget.ImageView
 	 */
-    public DownloadImage(String file, String path, ImageView iv) {
+    public DownloadImage(String file, String path, ImageView iv, int size) {
         super();
         this.file = file;
         this.path = path;
         this.iv = iv;
+		this.size = size;
     }
 
     /**
@@ -92,9 +94,14 @@ class DownloadImage extends AsyncTask<Void, Void, Void> {
     @Override
     protected void onPostExecute(Void v) {
         Log.d("File downloaded", path);
-        Bitmap myBitmap = BitmapFactory.decodeFile(path);
-        iv.setImageBitmap(myBitmap);
-        iv.setVisibility(View.VISIBLE);
+		try {
+			File file = new File(path);
+			iv.setImageBitmap(GM.decodeSampledBitmapFromFile(file.getAbsolutePath(), size));
+			iv.setVisibility(View.VISIBLE);
+		}
+		catch(Exception ex){
+			Log.e("Bitmap error", "Not loading image " + path + ": " + ex.toString());
+		}
     }
 
 }

+ 198 - 2
app/src/main/java/com/ivalentin/margolariak/GM.java

@@ -1,5 +1,7 @@
 package com.ivalentin.margolariak;
 
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
 import android.util.Log;
 
 import java.text.DateFormat;
@@ -15,11 +17,152 @@ import java.util.Locale;
  *
  */
 final class GM {
-	
+
+	final class DB {
+
+		final class COLUMN {
+			static final int INT = 0;
+			static final int VARCHAR = 1;
+			static final int DATETIME = 2;
+		}
+
+		final class QUERY {
+
+			final class CREATE {
+				static final String ACTIVITY = "CREATE TABLE IF NOT EXISTS activity (id INT, permalink VARCHAR, date DATETIME, city VARCHAR, title_es VARCHAR, title_en VARCHAR, title_eu VARCHAR, text_es VARCHAR, text_en VARCHAR, text_eu VARCHAR, after_es VARCHAR, after_en VARCHAR, after_eu VARCHAR, price INT, inscription INT, max_people INT, album INT, dtime DATETIME, comments INT);";
+				static final String ACTIVITY_COMMENT = "CREATE TABLE IF NOT EXISTS activity_comment (id INT, activity INT, text VARCHAR, dtime DATETIME, username VARCHAR, lang VARCHAR);";
+				static final String ACTIVITY_IMAGE = "CREATE TABLE IF NOT EXISTS activity_image (id INT, activity INT, image VARCHAR, idx INT);";
+				static final String ACTIVITY_ITINERARY = "CREATE TABLE IF NOT EXISTS activity_itinerary (id INT, activity INT, name_es VARCHAR, name_en VARCHAR, name_eu VARCHAR, description_es VARCHAR, description_en VARCHAR, description_eu VARCHAR, start DATETIME, end DATETIME, place INT);";
+				static final String ACTIVITY_TAG = "CREATE TABLE IF NOT EXISTS activity_tag (activity INT, tag VARCHAR);";
+				static final String ALBUM = "CREATE TABLE IF NOT EXISTS album (id INT, permalink VARCHAR, title_es VARCHAR, title_en VARCHAR, title_eu VARCHAR, description_es VARCHAR, description_en VARCHAR, description_eu VARCHAR, open INT, time DATETIME);";
+				static final String FESTIVAL = "CREATE TABLE IF NOT EXISTS festival (id INT, year INT, text_es VARCHAR, text_en VARCHAR, text_eu VARCHAR, summary_es VARCHAR, summary_en VARCHAR, summary_eu VARCHAR, img VARCHAR);";
+				static final String FESTIVAL_DAY = "CREATE TABLE IF NOT EXISTS festival_day (id INT, date DATETIME, name_es VARCHAR, name_en VARCHAR, name_eu VARCHAR, price INT);";
+				static final String FESTIVAL_EVENT = "CREATE TABLE IF NOT EXISTS festival_event (id INT, gm INT, title_es VARCHAR, title_en VARCHAR, title_eu VARCHAR, description_es VARCHAR, description_en VARCHAR, description_eu VARCHAR, host INT, place INT, start DATETIME, end DATETIME);";
+				static final String FESTIVAL_EVENT_IMAGE = "CREATE TABLE IF NOT EXISTS festival_event_image (id INT, event INT, image VARCHAR, idx INT);";
+				static final String FESTIVAL_OFFER = "CREATE TABLE IF NOT EXISTS festival_offer (id INT, year INT, name_es VARCHAR, name_en VARCHAR, name_eu VARCHAR, description_es VARCHAR, description_en VARCHAR, description_eu VARCHAR, days INT, price INT);";
+				static final String LOCATION = "CREATE TABLE IF NOT EXISTS location (id INT, dtime DATETIME, lat FLOAT, lon FLOAT, manual INT);";
+				static final String NOTIFICATION = "CREATE TABLE IF NOT EXISTS notification (id INT, dtime DATETIME, duration INT, action VARCHAR, title_es VARCHAR, title_en VARCHAR, title_eu VARCHAR, text_es VARCHAR, text_en VARCHAR, text_eu VARCHAR, internal INT);";
+				static final String PEOPLE = "CREATE TABLE IF NOT EXISTS people (id INT, name_es VARCHAR, name_en VARCHAR, name_eu VARCHAR, link VARCHAR);";
+				static final String PHOTO = "CREATE TABLE IF NOT EXISTS photo (id INT, file VARCHAR, permalink VARCHAR, title_es VARCHAR, title_en VARCHAR, title_eu VARCHAR, description_es VARCHAR, description_en VARCHAR, description_eu VARCHAR, dtime DATETIME, uploaded DATETIME, place INT, width INT, height INT, size INT, username VARCHAR);";
+				static final String PHOTO_ALBUM = "CREATE TABLE IF NOT EXISTS photo_album (photo INT, album INT);";
+				static final String PHOTO_COMMENT = "CREATE TABLE IF NOT EXISTS photo_comment (id INT, photo INT, text VARCHAR, dtime DATETIME, username VARCHAR, lang VARCHAR);";
+				static final String PLACE = "CREATE TABLE IF NOT EXISTS place (id INT, name_es VARCHAR, name_en VARCHAR, name_eu VARCHAR, address_es VARCHAR, address_en VARCHAR, address_eu VARCHAR, cp VARCHAR, lat FLOAT, lon FLOAT);";
+				static final String POST = "CREATE TABLE IF NOT EXISTS post (id INT, permalink VARCHAR, title_es VARCHAR, title_en VARCHAR, title_eu VARCHAR, text_es VARCHAR, text_en VARCHAR, text_eu VARCHAR, comments INT, username VARCHAR, dtime DATETIME);";
+				static final String POST_COMMENT = "CREATE TABLE IF NOT EXISTS post_comment (id INT, post INT, text VARCHAR, dtime DATETIME, username VARCHAR, lang VARCHAR);";
+				static final String POST_IMAGE = "CREATE TABLE IF NOT EXISTS post_image (id INT, post INT, image VARCHAR, idx INT);";
+				static final String POST_TAG = "CREATE TABLE IF NOT EXISTS post_tag (post INT, tag VARCHAR);";
+				static final String SETTINGS = "CREATE TABLE IF NOT EXISTS settings (name VARCHAR, value VARCHAR);";
+				static final String SPONSOR = "CREATE TABLE IF NOT EXISTS sponsor (id INT, name_es VARCHAR, name_en VARCHAR, name_eu VARCHAR, text_es VARCHAR, text_en VARCHAR, text_eu VARCHAR, image VARCHAR, address_es VARCHAR, address_en VARCHAR, address_eu VARCHAR, link VARCHAR, lat FLOAT, lon FLOAT);";
+				static final String VERSION = "CREATE TABLE IF NOT EXISTS version (section VARCHAR, version INT);";
+			}
+
+			final class DROP {
+				static final String ACTIVITY = "DROP TABLE activity;";
+				static final String ACTIVITY_COMMENT = "DROP TABLE activity_comment;";
+				static final String ACTIVITY_IMAGE = "DROP TABLE activity_image;";
+				static final String ACTIVITY_ITINERARY = "DROP TABLE activity_itinerary;";
+				static final String ACTIVITY_TAG = "DROP TABLE activity_tag;";
+				static final String ALBUM = "DROP TABLE album;";
+				static final String FESTIVAL = "DROP TABLE festival;";
+				static final String FESTIVAL_DAY = "DROP TABLE festival_day;";
+				static final String FESTIVAL_EVENT = "DROP TABLE festival_event;";
+				static final String FESTIVAL_EVENT_IMAGE = "DROP TABLE festival_event_image;";
+				static final String FESTIVAL_OFFER = "DROP TABLE festival_offer;";
+				static final String LOCATION = "DROP TABLE location;";
+				static final String NOTIFICATION = "DROP TABLE notification;";
+				static final String PEOPLE = "DROP TABLE people;";
+				static final String PHOTO = "DROP TABLE photo;";
+				static final String PHOTO_ALBUM = "DROP TABLE photo_album;";
+				static final String PHOTO_COMMENT = "DROP TABLE photo_comment;";
+				static final String PLACE = "DROP TABLE place;";
+				static final String POST = "DROP TABLE post;";
+				static final String POST_COMMENT = "DROP TABLE post_comment;";
+				static final String POST_IMAGE = "DROP TABLE post_image;";
+				static final String POST_TAG = "DROP TABLE post_tag;";
+				static final String SETTINGS = "DROP TABLE settings;";
+				static final String SPONSOR = "DROP TABLE sponsor;";
+				static final String VERSION = "DROP TABLE version;";
+			}
+
+			final class EMPTY {
+				static final String ACTIVITY = "DELETE FROM activity;";
+				static final String ACTIVITY_COMMENT = "DELETE FROM activity_comment;";
+				static final String ACTIVITY_IMAGE = "DELETE FROM activity_image;";
+				static final String ACTIVITY_ITINERARY = "DELETE FROM activity_itinerary;";
+				static final String ACTIVITY_TAG = "DELETE FROM activity_tag;";
+				static final String ALBUM = "DELETE FROM album;";
+				static final String FESTIVAL = "DELETE FROM festival;";
+				static final String FESTIVAL_DAY = "DELETE FROM festival_day;";
+				static final String FESTIVAL_EVENT = "DELETE FROM festival_event;";
+				static final String FESTIVAL_EVENT_IMAGE = "DELETE FROM festival_event_image;";
+				static final String FESTIVAL_OFFER = "DELETE FROM festival_offer;";
+				static final String LOCATION = "DELETE FROM location;";
+				static final String NOTIFICATION = "DELETE FROM notification;";
+				static final String PEOPLE = "DELETE FROM people;";
+				static final String PHOTO = "DELETE FROM photo;";
+				static final String PHOTO_ALBUM = "DELETE FROM photo_album;";
+				static final String PHOTO_COMMENT = "DELETE FROM photo_comment;";
+				static final String PLACE = "DELETE FROM place;";
+				static final String POST = "DELETE FROM post;";
+				static final String POST_COMMENT = "DELETE FROM post_comment;";
+				static final String POST_IMAGE = "DELETE FROM post_image;";
+				static final String POST_TAG = "DELETE FROM post_tag;";
+				static final String SETTINGS = "DELETE FROM settings;";
+				static final String SPONSOR = "DELETE FROM sponsor;";
+				static final String VERSION = "DELETE FROM version;";
+			}
+
+			final class RECREATE {
+				static final String ACTIVITY = GM.DB.QUERY.DROP.ACTIVITY + GM.DB.QUERY.CREATE.ACTIVITY;
+				static final String ACTIVITY_COMMENT = GM.DB.QUERY.DROP.ACTIVITY_COMMENT + GM.DB.QUERY.CREATE.ACTIVITY_COMMENT;
+				static final String ACTIVITY_IMAGE = GM.DB.QUERY.DROP.ACTIVITY_IMAGE + GM.DB.QUERY.CREATE.ACTIVITY_IMAGE;
+				static final String ACTIVITY_ITINERARY = GM.DB.QUERY.DROP.ACTIVITY_ITINERARY + GM.DB.QUERY.CREATE.ACTIVITY_ITINERARY;
+				static final String ACTIVITY_TAG = GM.DB.QUERY.DROP.ACTIVITY_TAG + GM.DB.QUERY.CREATE.ACTIVITY_TAG;
+				static final String ALBUM = GM.DB.QUERY.DROP.ALBUM + GM.DB.QUERY.CREATE.ALBUM;
+				static final String FESTIVAL = GM.DB.QUERY.DROP.FESTIVAL + GM.DB.QUERY.CREATE.FESTIVAL;
+				static final String FESTIVAL_DAY = GM.DB.QUERY.DROP.FESTIVAL_DAY + GM.DB.QUERY.CREATE.FESTIVAL_DAY;
+				static final String FESTIVAL_EVENT = GM.DB.QUERY.DROP.FESTIVAL_EVENT + GM.DB.QUERY.CREATE.FESTIVAL_EVENT;
+				static final String FESTIVAL_EVENT_IMAGE = GM.DB.QUERY.DROP.FESTIVAL_EVENT_IMAGE + GM.DB.QUERY.CREATE.FESTIVAL_EVENT_IMAGE;
+				static final String FESTIVAL_OFFER = GM.DB.QUERY.DROP.FESTIVAL_OFFER + GM.DB.QUERY.CREATE.FESTIVAL_OFFER;
+				static final String LOCATION = GM.DB.QUERY.DROP.LOCATION + GM.DB.QUERY.CREATE.LOCATION;
+				static final String NOTIFICATION = GM.DB.QUERY.DROP.NOTIFICATION + GM.DB.QUERY.CREATE.NOTIFICATION;
+				static final String PEOPLE = GM.DB.QUERY.DROP.PEOPLE + GM.DB.QUERY.CREATE.PEOPLE;
+				static final String PHOTO = GM.DB.QUERY.DROP.PHOTO + GM.DB.QUERY.CREATE.PHOTO;
+				static final String PHOTO_ALBUM = GM.DB.QUERY.DROP.PHOTO_ALBUM + GM.DB.QUERY.CREATE.PHOTO_ALBUM;
+				static final String PHOTO_COMMENT = GM.DB.QUERY.DROP.PHOTO_COMMENT + GM.DB.QUERY.CREATE.PHOTO_COMMENT;
+				static final String PLACE = GM.DB.QUERY.DROP.PLACE + GM.DB.QUERY.CREATE.PLACE;
+				static final String POST = GM.DB.QUERY.DROP.POST + GM.DB.QUERY.CREATE.POST;
+				static final String POST_COMMENT = GM.DB.QUERY.DROP.POST_COMMENT + GM.DB.QUERY.CREATE.POST_COMMENT;
+				static final String POST_IMAGE = GM.DB.QUERY.DROP.POST_IMAGE + GM.DB.QUERY.CREATE.POST_IMAGE;
+				static final String POST_TAG = GM.DB.QUERY.DROP.POST_TAG + GM.DB.QUERY.CREATE.POST_TAG;
+				static final String SETTINGS = GM.DB.QUERY.DROP.SETTINGS + GM.DB.QUERY.CREATE.SETTINGS;
+				static final String SPONSOR = GM.DB.QUERY.DROP.SPONSOR + GM.DB.QUERY.CREATE.SPONSOR;
+				static final String VERSION = GM.DB.QUERY.DROP.VERSION + GM.DB.QUERY.CREATE.VERSION;
+			}
+		}
+	}
+
+	static final String CLIENT = "com.ivalentin.margolariak";
+
 	/**
 	 * Address where the web server is
 	 */
-	static final String SERVER = "http://margolariak.es";
+	static final String SERVER = "http://margolariak.com";
+
+	static final String SERVER_SYNC = "/API/v1/sync.php";
+
+	static final String SERVER_SYNC_KEY_CLIENT = "client";
+	static final String SERVER_SYNC_KEY_USER = "user";
+	static final String SERVER_SYNC_KEY_ACTION = "action";
+	static final String SERVER_SYNC_KEY_SECTION = "section";
+	static final String SERVER_SYNC_KEY_VERSION = "version";
+	static final String SERVER_SYNC_KEY_FOREGROUND = "foreground";
+	static final String SERVER_SYNC_KEY_FORMAT = "format";
+	static final String SERVER_SYNC_KEY_LANG = "lang";
+
+	static final String SERVER_SYNC_VALUE_ACTION = "sync";
+	static final String SERVER_SYNC_VALUE_SECTION = "all";
+	static final String SERVER_SYNC_VALUE_FORMAT = "json";
 
 	/**
 	 * Code for the location permission request
@@ -141,6 +284,11 @@ final class GM {
 	 */
 	static final String PREF_DB_VERSION = "prefDbVersion";
 
+	/**
+	 * Name of the preference to store the previous app version.
+	 */
+	static final String PREF_PREVIOUS_VERSION = "prefAppPreviousVersion";
+
 	/**
 	 * Name of the preference to store the setting that allows user to upload comments.
 	 */
@@ -243,6 +391,12 @@ final class GM {
 
 	static final String KEY_PREFERENCE_FEEDBACK = "preference_key_feedback";
 
+	static final int INTERVAL_LOCATION = 240000; //4 min
+
+	static final int INTERVAL_SYNC_FESTIVALS = 600000; //10 min
+
+	static final int INTERVAL_SYNC_NO_FESTIVALS = 3600000; //60 min
+
 	/**
 	 * Name of the database
 	 */
@@ -258,6 +412,14 @@ final class GM {
 	 */
 	static final int LOCATION_ACCURACY_TIME = 10000;
 
+	/**
+	 * Dimensions of the images in the database.
+	 */
+	static final int IMG_MINIATURE = 340;
+	static final int IMG_PREVIEW = 600;
+	static final int IMG_THUMB = 180;
+	static final int IMG_VIEW = 800;
+
 	/**
 	 * Gets the language code for sql queries.
 	 * Only three values can be returned: es, eu, en.
@@ -355,4 +517,38 @@ final class GM {
 
 		return output;
 	}
+
+	public static Bitmap decodeSampledBitmapFromFile(String path, int size) { // BEST QUALITY MATCH
+
+		// First decode with inJustDecodeBounds=true to check dimensions
+		final BitmapFactory.Options options = new BitmapFactory.Options();
+		options.inJustDecodeBounds = true;
+		BitmapFactory.decodeFile(path, options);
+
+		// Calculate inSampleSize
+		// Raw height and width of image
+		final int height = options.outHeight;
+		final int width = options.outWidth;
+		options.inPreferredConfig = Bitmap.Config.RGB_565;
+		int inSampleSize = 1;
+
+		if (height > size) {
+			inSampleSize = Math.round((float)height / (float)size);
+		}
+
+		int expectedWidth = width / inSampleSize;
+
+		if (expectedWidth > size) {
+			//if(Math.round((float)width / (float)reqWidth) > inSampleSize) // If bigger SampSize..
+			inSampleSize = Math.round((float)width / (float)size);
+		}
+
+
+		options.inSampleSize = inSampleSize;
+
+		// Decode bitmap with inSampleSize set
+		options.inJustDecodeBounds = false;
+
+		return BitmapFactory.decodeFile(path, options);
+	}
 }

+ 2 - 3
app/src/main/java/com/ivalentin/margolariak/GalleryLayout.java

@@ -2,7 +2,6 @@ package com.ivalentin.margolariak;
 
 
 import android.annotation.SuppressLint;
-import android.content.Context;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.graphics.Bitmap;
@@ -77,7 +76,7 @@ public class GalleryLayout extends Fragment{
 		String lang = GM.getLang();
 
         //Get data from the database
-        cursor = db.rawQuery("SELECT DISTINCT album.id AS id, album.name_" + lang + " AS name FROM photo, album, photo_album WHERE photo.id = photo AND album.id = album ORDER BY uploaded DESC;", null);
+        cursor = db.rawQuery("SELECT DISTINCT album.id AS id, album.title_" + lang + " AS name FROM photo, album, photo_album WHERE photo.id = photo AND album.id = album ORDER BY uploaded DESC;", null);
 
         //Loop
         while (cursor.moveToNext()){
@@ -126,7 +125,7 @@ public class GalleryLayout extends Fragment{
                     File fpath;
                     fpath = new File(this.getActivity().getFilesDir().toString() + "/img/galeria/miniature/");
                     fpath.mkdirs();
-                    new DownloadImage(GM.SERVER + "/img/galeria/miniature/" + image, this.getActivity().getFilesDir().toString() + "/img/galeria/miniature/" + image, preview[i]).execute();
+                    new DownloadImage(GM.SERVER + "/img/galeria/miniature/" + image, this.getActivity().getFilesDir().toString() + "/img/galeria/miniature/" + image, preview[i], GM.IMG_MINIATURE).execute();
                 }
                 i ++;
             }

+ 14 - 17
app/src/main/java/com/ivalentin/margolariak/HomeLayout.java

@@ -30,14 +30,11 @@ import android.text.Html;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.MenuItem;
-import android.view.MotionEvent;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
-import android.widget.Button;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
-import android.widget.RelativeLayout;
 import android.widget.TextView;
 import android.widget.Toast;
 
@@ -141,7 +138,7 @@ public class HomeLayout extends Fragment implements LocationListener {
 
 		//Asynchronously set up location section
 		//This will trigger onLocationChanged, that will trigger setUpLocation
-		new HomeSectionLocation(view, this.getActivity()).execute();
+		new HomeSectionLocation(view).execute();
 
 		//Set up schedule sections
 		setUpSchedule(1, view);
@@ -296,7 +293,7 @@ public class HomeLayout extends Fragment implements LocationListener {
 				section.setOnClickListener(new OnClickListener() {
 					@Override
 					public void onClick(View v) {
-						((MainActivity) getActivity()).loadSection(GM.SECTION_LABLANCA_GM_SCHEDULE, false);
+						((MainActivity) getActivity()).loadSection(GM.SECTION_LABLANCA_GM_SCHEDULE);
 					}
 				});
 
@@ -304,7 +301,7 @@ public class HomeLayout extends Fragment implements LocationListener {
 				section.setOnClickListener(new OnClickListener() {
 					@Override
 					public void onClick(View v) {
-						((MainActivity) getActivity()).loadSection(GM.SECTION_LABLANCA_SCHEDULE, false);
+						((MainActivity) getActivity()).loadSection(GM.SECTION_LABLANCA_SCHEDULE);
 					}
 				});
 			}
@@ -432,7 +429,7 @@ public class HomeLayout extends Fragment implements LocationListener {
 			llSection.setOnClickListener(new OnClickListener() {
 				@Override
 				public void onClick(View v) {
-					((MainActivity) getActivity()).loadSection(GM.SECTION_LABLANCA, false);
+					((MainActivity) getActivity()).loadSection(GM.SECTION_LABLANCA);
 				}
 			});
 
@@ -478,7 +475,7 @@ public class HomeLayout extends Fragment implements LocationListener {
 						File fpath;
 						fpath = new File(this.getActivity().getFilesDir().toString() + "/img/fiestas/preview/");
 						fpath.mkdirs();
-						new DownloadImage(GM.SERVER + "/img/fiestas/preview/" + image, this.getActivity().getFilesDir().toString() + "/img/fiestas/preview/" + image, ivImage).execute();
+						new DownloadImage(GM.SERVER + "/img/fiestas/preview/" + image, this.getActivity().getFilesDir().toString() + "/img/fiestas/preview/" + image, ivImage, GM.IMG_PREVIEW).execute();
 					}
 				}
 				else {
@@ -500,7 +497,7 @@ public class HomeLayout extends Fragment implements LocationListener {
 	 */
 	private boolean setUpLocation(Location location, View view) {
 		SharedPreferences preferences = view.getContext().getSharedPreferences(GM.PREF, Context.MODE_PRIVATE);
-		if (preferences.getString(GM.PREF_GM_LOCATION, GM.DEFAULT_PREF_GM_LOCATION).equals(GM.DEFAULT_PREF_GM_LOCATION) == false) {
+		if (!preferences.getString(GM.PREF_GM_LOCATION, GM.DEFAULT_PREF_GM_LOCATION).equals(GM.DEFAULT_PREF_GM_LOCATION)) {
 			try {
 				Double lat = Double.longBitsToDouble(preferences.getLong(GM.PREF_GM_LATITUDE, 0));
 				Double lon = Double.longBitsToDouble(preferences.getLong(GM.PREF_GM_LONGITUDE, 0));
@@ -539,7 +536,7 @@ public class HomeLayout extends Fragment implements LocationListener {
 		llGallery.setOnClickListener(new OnClickListener() {
 			@Override
 			public void onClick(View v) {
-				((MainActivity) getActivity()).loadSection(GM.SECTION_GALLERY, false);
+				((MainActivity) getActivity()).loadSection(GM.SECTION_GALLERY);
 			}
 		});
 
@@ -574,7 +571,7 @@ public class HomeLayout extends Fragment implements LocationListener {
 				File fpath;
 				fpath = new File(this.getActivity().getFilesDir().toString() + "/img/galeria/preview/");
 				fpath.mkdirs();
-				new DownloadImage(GM.SERVER + "/img/galeria/preview/" + image, this.getActivity().getFilesDir().toString() + "/img/galeria/preview/" + image, ivPhoto[counter]).execute();
+				new DownloadImage(GM.SERVER + "/img/galeria/preview/" + image, this.getActivity().getFilesDir().toString() + "/img/galeria/preview/" + image, ivPhoto[counter], GM.IMG_PREVIEW).execute();
 			}
 
 			//Set listeners for images
@@ -636,7 +633,7 @@ public class HomeLayout extends Fragment implements LocationListener {
 		llActivitiesPast.setOnClickListener(new OnClickListener() {
 			@Override
 			public void onClick(View v) {
-				((MainActivity) getActivity()).loadSection(GM.SECTION_ACTIVITIES, false);
+				((MainActivity) getActivity()).loadSection(GM.SECTION_ACTIVITIES);
 			}
 		});
 
@@ -709,7 +706,7 @@ public class HomeLayout extends Fragment implements LocationListener {
 					File fpath;
 					fpath = new File(this.getActivity().getFilesDir().toString() + "/img/actividades/miniature/");
 					fpath.mkdirs();
-					new DownloadImage(GM.SERVER + "/img/actividades/miniature/" + image, this.getActivity().getFilesDir().toString() + "/img/actividades/miniature/" + image, iv).execute();
+					new DownloadImage(GM.SERVER + "/img/actividades/miniature/" + image, this.getActivity().getFilesDir().toString() + "/img/actividades/miniature/" + image, iv, GM.IMG_MINIATURE).execute();
 				}
 			}
 			cursorImage.close();
@@ -782,7 +779,7 @@ public class HomeLayout extends Fragment implements LocationListener {
 			llActivitiesFuture.setOnClickListener(new OnClickListener() {
 				@Override
 				public void onClick(View v) {
-					((MainActivity) getActivity()).loadSection(GM.SECTION_ACTIVITIES, false);
+					((MainActivity) getActivity()).loadSection(GM.SECTION_ACTIVITIES);
 				}
 			});
 
@@ -843,7 +840,7 @@ public class HomeLayout extends Fragment implements LocationListener {
 						File fpath;
 						fpath = new File(this.getActivity().getFilesDir().toString() + "/img/actividades/miniature/");
 						fpath.mkdirs();
-						new DownloadImage(GM.SERVER + "/img/actividades/miniature/" + image, this.getActivity().getFilesDir().toString() + "/img/actividades/miniature/" + image, iv).execute();
+						new DownloadImage(GM.SERVER + "/img/actividades/miniature/" + image, this.getActivity().getFilesDir().toString() + "/img/actividades/miniature/" + image, iv, GM.IMG_MINIATURE).execute();
 					}
 				}
 
@@ -898,7 +895,7 @@ public class HomeLayout extends Fragment implements LocationListener {
 		llBlog.setOnClickListener(new OnClickListener() {
 			@Override
 			public void onClick(View v) {
-				((MainActivity) getActivity()).loadSection(GM.SECTION_BLOG, false);
+				((MainActivity) getActivity()).loadSection(GM.SECTION_BLOG);
 			}
 		});
 
@@ -965,7 +962,7 @@ public class HomeLayout extends Fragment implements LocationListener {
 					File fpath;
 					fpath = new File(this.getActivity().getFilesDir().toString() + "/img/blog/miniature/");
 					fpath.mkdirs();
-					new DownloadImage(GM.SERVER + "/img/blog/miniature/" + image, this.getActivity().getFilesDir().toString() + "/img/blog/miniature/" + image, iv).execute();
+					new DownloadImage(GM.SERVER + "/img/blog/miniature/" + image, this.getActivity().getFilesDir().toString() + "/img/blog/miniature/" + image, iv, GM.IMG_MINIATURE).execute();
 				}
 			}
 

+ 2 - 4
app/src/main/java/com/ivalentin/margolariak/HomeSectionLocation.java

@@ -26,7 +26,6 @@ class HomeSectionLocation extends AsyncTask<Void, Void, Void> {
 	private boolean isLocationReported;
 	private LatLng coord;
 	private final View view;
-	private final Activity activity;
 
 	/**
 	 * Constructor.
@@ -35,9 +34,8 @@ class HomeSectionLocation extends AsyncTask<Void, Void, Void> {
 	 *
 	 * @see android.widget.ImageView
 	 */
-	public HomeSectionLocation(View v, Activity a) {
+	public HomeSectionLocation(View v) {
 		super();
-		this.activity = a;
 		this.view = v;
 	}
 
@@ -96,7 +94,7 @@ class HomeSectionLocation extends AsyncTask<Void, Void, Void> {
 			section.setOnClickListener(new View.OnClickListener(){
 				@Override
 				public void onClick(View v) {
-					((MainActivity) v.getContext()).loadSection(GM.SECTION_LOCATION, false);
+					((MainActivity) v.getContext()).loadSection(GM.SECTION_LOCATION);
 				}
 			});
 

+ 9 - 10
app/src/main/java/com/ivalentin/margolariak/LablancaLayout.java

@@ -2,7 +2,6 @@ package com.ivalentin.margolariak;
 
 import android.annotation.SuppressLint;
 import android.app.Fragment;
-import android.content.Context;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.graphics.Bitmap;
@@ -27,7 +26,7 @@ import java.util.Date;
 import java.util.Locale;
 
 /**
- * Fragment openen for La Blanca sections while the festivals are not close.
+ * Fragment opened for La Blanca sections while the festivals are not close.
  *
  * @see Fragment
  *
@@ -39,14 +38,14 @@ public class LablancaLayout extends Fragment {
 	/**
 	 * Run when the fragment is inflated.
 	 *
-	 * @param inflater           A LayoutInflater to manage views
-	 * @param container          The container View
+	 * @param inflater A LayoutInflater to manage views
+	 * @param container The container View
 	 * @param savedInstanceState Bundle containing the state
 	 * @return The fragment view
 	 * @see android.app.Fragment#onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle)
 	 */
 	@Override
-	@SuppressLint("InflateParams")
+	@SuppressLint({"InflateParams", "SwitchIntDef"})
 	@SuppressWarnings("ResultOfMethodCallIgnored")
 	public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
 
@@ -92,7 +91,7 @@ public class LablancaLayout extends Fragment {
 				File fpath;
 				fpath = new File(this.getActivity().getFilesDir().toString() + "/img/fiestas/preview/");
 				fpath.mkdirs();
-				new DownloadImage(GM.SERVER + "/img/fiestas/preview/" + image, this.getActivity().getFilesDir().toString() + "/img/fiestas/preview/" + image, headerImage).execute();
+				new DownloadImage(GM.SERVER + "/img/fiestas/preview/" + image, this.getActivity().getFilesDir().toString() + "/img/fiestas/preview/" + image, headerImage, GM.IMG_PREVIEW).execute();
 			}
 		}
 		else{
@@ -135,10 +134,10 @@ public class LablancaLayout extends Fragment {
 				int month = calendar.get(Calendar.MONTH);
 				switch (month){
 					case 6:
-						tvDate.setText(String.format(getString(R.string.lablanca_date_july), day));
+						tvDate.setText(String.format(getString(R.string.lablanca_date_july), String.valueOf(day)));
 						break;
 					case 7:
-						tvDate.setText(String.format(getString(R.string.lablanca_date_august), day));
+						tvDate.setText(String.format(getString(R.string.lablanca_date_august), String.valueOf(day)));
 						break;
 				}
 			}
@@ -190,14 +189,14 @@ public class LablancaLayout extends Fragment {
 		gmSchedule.setOnClickListener(new View.OnClickListener() {
 			@Override
 			public void onClick(View v) {
-				((MainActivity) getActivity()).loadSection(GM.SECTION_LABLANCA_GM_SCHEDULE, false);
+				((MainActivity) getActivity()).loadSection(GM.SECTION_LABLANCA_GM_SCHEDULE);
 			}
 		});
 
 		citySchedule.setOnClickListener(new View.OnClickListener() {
 			@Override
 			public void onClick(View v) {
-				((MainActivity) getActivity()).loadSection(GM.SECTION_LABLANCA_SCHEDULE, false);
+				((MainActivity) getActivity()).loadSection(GM.SECTION_LABLANCA_SCHEDULE);
 			}
 		});
 

+ 5 - 12
app/src/main/java/com/ivalentin/margolariak/LocationLayout.java

@@ -42,7 +42,7 @@ public class LocationLayout extends Fragment implements OnMapReadyCallback, Loca
 
 	//The map view
 	private MapView mapView;
-	MarkerOptions moGm;
+	private MarkerOptions moGm;
 
 	private LocationManager locationManager;
 
@@ -221,21 +221,14 @@ public class LocationLayout extends Fragment implements OnMapReadyCallback, Loca
 	public void onLocationChanged(Location location) {
 		//TODO: Read again the GM Location.
 		SharedPreferences preferences = v.getContext().getSharedPreferences(GM.PREF, Context.MODE_PRIVATE);
-		if (preferences.getString(GM.PREF_GM_LOCATION, GM.DEFAULT_PREF_GM_LOCATION).equals(GM.DEFAULT_PREF_GM_LOCATION) == false) {
+		if (!preferences.getString(GM.PREF_GM_LOCATION, GM.DEFAULT_PREF_GM_LOCATION).equals(GM.DEFAULT_PREF_GM_LOCATION)) {
 
 			Double lat = Double.longBitsToDouble(preferences.getLong(GM.PREF_GM_LATITUDE, 0));
 			Double lon = Double.longBitsToDouble(preferences.getLong(GM.PREF_GM_LONGITUDE, 0));
 			gmLocation = new LatLng(lat, lon);
-			if (gmLocation != null){
-				calCulateDistance(location);
-				if (moGm != null){
-					moGm.visible(true);
-				}
-			}
-			else{
-				if (moGm != null){
-					moGm.visible(false);
-				}
+						calCulateDistance(location);
+			if (moGm != null){
+				moGm.visible(true);
 			}
 		}
 	}

+ 408 - 291
app/src/main/java/com/ivalentin/margolariak/MainActivity.java

@@ -1,8 +1,10 @@
 package com.ivalentin.margolariak;
 
-import java.io.File;
 import java.math.BigInteger;
 import java.security.SecureRandom;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Locale;
 
 import android.app.Activity;
 import android.app.Fragment;
@@ -15,7 +17,9 @@ import android.content.SharedPreferences;
 import android.database.sqlite.SQLiteDatabase;
 import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
+import android.net.Uri;
 import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.KeyEvent;
@@ -32,22 +36,79 @@ import android.widget.PopupMenu;
 import android.widget.ProgressBar;
 import android.widget.RelativeLayout;
 import android.widget.TextView;
+import android.os.Handler;
+
+import com.google.android.gms.appindexing.Action;
+import com.google.android.gms.appindexing.AppIndex;
+import com.google.android.gms.appindexing.Thing;
+import com.google.android.gms.common.api.GoogleApiClient;
 
 /**
  * The main Activity of the app. It's actually the only activity, and it loads other fragments.
- * 
- * @author Iñigo Valentin
  *
+ * @author Iñigo Valentin
  */
-public class MainActivity extends Activity{
+public class MainActivity extends Activity {
 
 	//Alarm to get location and notifications.
-	private AlarmReceiver alarm;
+	private AlarmReceiver notificationAlarm;
 
 	//The menu entries
-	RelativeLayout menuItem[];
-	TextView menuText[];
-	ImageView menuImage[];
+	private RelativeLayout menuItem[];
+	private TextView menuText[];
+	private ImageView menuImage[];
+
+	//Handler to periodically check for location updates
+	private final Handler locationHandler = new Handler();
+	// Code to check for location updates
+	private final Runnable checkForLocation = new Runnable() {
+		@Override
+		public void run() {
+
+			//Get location page and parse it
+			boolean result = false;
+			FetchURL fu = new FetchURL();
+			//TODO: This needs a simple API too
+			fu.Run(GM.SERVER + "/app/location.php");
+			String lat = "", lon = "";
+			String o = fu.getOutput().toString();
+			Log.d("Location", "Fetched location page");
+			if (o.contains("<location>none</location>"))
+				result = false;
+			if (o.contains("<lat>") && o.contains("<lon>")) {
+				lat = o.substring(o.indexOf("<lat>") + 5, o.indexOf("</lat>"));
+				lon = o.substring(o.indexOf("<lon>") + 5, o.indexOf("</lon>"));
+				result = true;
+			}
+
+			SharedPreferences settings = getSharedPreferences(GM.PREF, Context.MODE_PRIVATE);
+			SharedPreferences.Editor editor = settings.edit();
+			if (result) {
+				editor.putLong(GM.PREF_GM_LATITUDE, Double.doubleToLongBits(Double.parseDouble(lat)));
+				editor.putLong(GM.PREF_GM_LONGITUDE, Double.doubleToLongBits(Double.parseDouble(lon)));
+				editor.putString(GM.PREF_GM_LOCATION, new SimpleDateFormat("yyyyMMddHHmmss", Locale.US).format(Calendar.getInstance().getTime()));
+
+				//Show menu entry
+				if (menuItem[1] != null) {
+					menuItem[1].setVisibility(View.VISIBLE);
+				}
+			} else {
+				editor.putString(GM.PREF_GM_LOCATION, GM.DEFAULT_PREF_GM_LOCATION);
+				if (menuItem[1] != null) {
+					menuItem[1].setVisibility(View.GONE);
+				}
+			}
+			editor.apply();
+
+			//Repeat this the same runnable code block again another th specified interval
+			locationHandler.postDelayed(checkForLocation, GM.INTERVAL_LOCATION);
+		}
+	};
+	/**
+	 * ATTENTION: This was auto-generated to implement the App Indexing API.
+	 * See https://g.co/AppIndexing/AndroidStudio for more information.
+	 */
+	private GoogleApiClient client;
 
 	//Not referenced in code.
 	public void showMenu(View v) {
@@ -55,7 +116,7 @@ public class MainActivity extends Activity{
 		MenuInflater inflater = popup.getMenuInflater();
 		inflater.inflate(R.menu.menu, popup.getMenu());
 
-		popup.getMenu().getItem(0).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener(){
+		popup.getMenu().getItem(0).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
 			@Override
 			public boolean onMenuItemClick(MenuItem item) {
 				Intent intent = new Intent(MainActivity.this, AboutActivity.class);
@@ -64,7 +125,7 @@ public class MainActivity extends Activity{
 			}
 		});
 
-		popup.getMenu().getItem(1).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener(){
+		popup.getMenu().getItem(1).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
 			@Override
 			public boolean onMenuItemClick(MenuItem item) {
 				Intent intent = new Intent(MainActivity.this, SponsorActivity.class);
@@ -73,7 +134,7 @@ public class MainActivity extends Activity{
 			}
 		});
 
-		popup.getMenu().getItem(2).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener(){
+		popup.getMenu().getItem(2).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
 			@Override
 			public boolean onMenuItemClick(MenuItem item) {
 				Intent intent = new Intent(MainActivity.this, SettingsActivity.class);
@@ -86,14 +147,12 @@ public class MainActivity extends Activity{
 	}
 
 
-
 	/**
 	 * Loads a section in the main screen.
-	 * 
+	 *
 	 * @param section Section identifier {@see GM}
-	 * @param fromSliderMenu Indicates if the call has been made from the slider menu, so it can be closed.
 	 */
-	public void loadSection (byte section, boolean fromSliderMenu){
+	public void loadSection(byte section) {
 		FragmentManager fm = MainActivity.this.getFragmentManager();
 		FragmentTransaction ft = fm.beginTransaction();
 		Fragment fragment = null;
@@ -101,13 +160,13 @@ public class MainActivity extends Activity{
 		Bundle bundle = new Bundle();
 
 		//Reset all tabs to it's original state
-		for (int i = 0; i < 6; i ++){
+		for (int i = 0; i < 6; i++) {
 			menuText[i].setTypeface(null, Typeface.NORMAL);
 			menuImage[i].requestLayout();
 			menuImage[i].getLayoutParams().height = 2;
 		}
 
-		switch (section){
+		switch (section) {
 			case GM.SECTION_HOME:
 				fragment = new HomeLayout();
 				title = getString(R.string.menu_home);
@@ -125,10 +184,9 @@ public class MainActivity extends Activity{
 			case GM.SECTION_LABLANCA:
 				//Get settings
 				SharedPreferences preferences = getSharedPreferences(GM.PREF, Context.MODE_PRIVATE);
-				if (preferences.getInt(GM.PREF_DB_FESTIVALS, 0) == 1){
+				if (preferences.getInt(GM.PREF_DB_FESTIVALS, 0) == 1) {
 					fragment = new LablancaLayout();
-				}
-				else{
+				} else {
 					fragment = new LablancaNoFestivalsLayout();
 				}
 				title = getString(R.string.menu_lablanca);
@@ -175,7 +233,7 @@ public class MainActivity extends Activity{
 				menuImage[5].getLayoutParams().height = 8;
 				break;
 		}
-		
+
 		//Replace the fragment.
 		ft.replace(R.id.activity_main_content_fragment, fragment);
 		//ft.addToBackStack(title);
@@ -185,29 +243,28 @@ public class MainActivity extends Activity{
 
 	/**
 	 * Sets the title of the current section.
-	 * 
+	 *
 	 * @param title The title of the current section.
 	 */
-	public void setSectionTitle(String title){
+	public void setSectionTitle(String title) {
 		TextView tvTitle = (TextView) findViewById(R.id.activity_main_content_title);
 		tvTitle.setText(title);
 	}
 
 	/**
-	 * Runs when the activity is created. 
-	 * 
+	 * Runs when the activity is created.
+	 *
 	 * @param savedInstanceState Saved state of the activity.
-	 * 
-	 * @see android.app.Activity#onCreate(android.os.Bundle)
+	 * @see Activity#onCreate(Bundle)
 	 */
-	@SuppressWarnings("deprecation")
+	@SuppressWarnings("deprecation, ConstantConditions")
 	@Override
 	protected void onCreate(Bundle savedInstanceState) {
 
-	    //Get intent extras
-	    String action = getIntent().getStringExtra(GM.EXTRA_ACTION);
-	    String actionText = getIntent().getStringExtra(GM.EXTRA_TEXT);
-	    String actionTitle = getIntent().getStringExtra(GM.EXTRA_TITLE);
+		//Get intent extras
+		String action = getIntent().getStringExtra(GM.EXTRA_ACTION);
+		String actionText = getIntent().getStringExtra(GM.EXTRA_TEXT);
+		String actionTitle = getIntent().getStringExtra(GM.EXTRA_TITLE);
 
 		//Set an alarm for notifications..
 		//Wait a little
@@ -223,9 +280,9 @@ public class MainActivity extends Activity{
 					runOnUiThread(new Runnable() {
 						@Override
 						public void run() {
-							alarm = new AlarmReceiver();
-							alarm.setAlarm(context);
-							alarm.onReceive(context, intent);
+							notificationAlarm = new AlarmReceiver();
+							notificationAlarm.setAlarm(context);
+							notificationAlarm.onReceive(context, intent);
 						}
 					});
 				} catch (InterruptedException e) {
@@ -237,10 +294,10 @@ public class MainActivity extends Activity{
 		t.start();
 
 		//Remove title bar.
-	    this.requestWindowFeature(Window.FEATURE_NO_TITLE);
-	    super.onCreate(savedInstanceState);
+		this.requestWindowFeature(Window.FEATURE_NO_TITLE);
+		super.onCreate(savedInstanceState);
 
-	    //Set layout.
+		//Set layout.
 		setContentView(R.layout.activity_main);
 
 		//Assign menu items
@@ -266,335 +323,341 @@ public class MainActivity extends Activity{
 		menuImage[4] = (ImageView) menuItem[4].findViewById(R.id.iv_menu_blog);
 		menuImage[5] = (ImageView) menuItem[5].findViewById(R.id.iv_menu_gallery);
 
-		//Remove sscrollbars from the sections menu
+		//Remove scrollbars from the sections menu
 		HorizontalScrollView svMenu = (HorizontalScrollView) findViewById(R.id.sv_menu);
 		svMenu.setHorizontalScrollBarEnabled(false);
 
-		//Assign buttons for la blanca submenu
-		TextView menuLablancaItem[] = new TextView[2];
-		//menuLablancaItem[0] = (TextView) findViewById(R.id.menu_lablanca_schedule);
-		//menuLablancaItem[1] = (TextView) findViewById(R.id.menu_lablanca_gm_schedule);
-
 		//Set click listers for menu items
 		menuItem[0].setOnClickListener(new OnClickListener() {
 			@Override
-			public void onClick(View v) { loadSection(GM.SECTION_HOME, true); }
+			public void onClick(View v) {
+				loadSection(GM.SECTION_HOME);
+			}
 		});
 		menuItem[1].setOnClickListener(new OnClickListener() {
 			@Override
-			public void onClick(View v) { loadSection(GM.SECTION_LOCATION, true); }
+			public void onClick(View v) {
+				loadSection(GM.SECTION_LOCATION);
+			}
 		});
 		menuItem[2].setOnClickListener(new OnClickListener() {
 			@Override
-			public void onClick(View v) { loadSection(GM.SECTION_LABLANCA, true); }
+			public void onClick(View v) {
+				loadSection(GM.SECTION_LABLANCA);
+			}
 		});
 		menuItem[3].setOnClickListener(new OnClickListener() {
 			@Override
-			public void onClick(View v) { loadSection(GM.SECTION_ACTIVITIES, true); }
+			public void onClick(View v) {
+				loadSection(GM.SECTION_ACTIVITIES);
+			}
 		});
 		menuItem[4].setOnClickListener(new OnClickListener() {
 			@Override
-			public void onClick(View v) { loadSection(GM.SECTION_BLOG, true); }
+			public void onClick(View v) {
+				loadSection(GM.SECTION_BLOG);
+			}
 		});
 		menuItem[5].setOnClickListener(new OnClickListener() {
 			@Override
-			public void onClick(View v) { loadSection(GM.SECTION_GALLERY, true); }
+			public void onClick(View v) {
+				loadSection(GM.SECTION_GALLERY);
+			}
 		});
 
-
+		//Get preferences
 		SharedPreferences preferences = getSharedPreferences(GM.PREF, Context.MODE_PRIVATE);
-		if (preferences.getInt(GM.PREF_DB_FESTIVALS, 0) == 1) {
-			//Show the entries
-			//for (int i = 0; i < 2; i ++){
-			//	menuLablancaItem[i].setVisibility(View.VISIBLE);
-			//}
-		}
-//		menuLablancaItem[0].setOnClickListener(new OnClickListener() {
-//			@Override
-//			public void onClick(View v) {
-//				loadSection(GM.SECTION_LABLANCA_SCHEDULE, true);
-//			}
-//		});
-//
-//		menuLablancaItem[1].setOnClickListener(new OnClickListener() {
-//			@Override
-//			public void onClick(View v) {
-//				loadSection(GM.SECTION_LABLANCA_GM_SCHEDULE, true);
-//			}
-//		});
+		SharedPreferences.Editor editor = preferences.edit();
+
+		//TODO: Read preferences. If there is a recent location, show the menu entry
 
 		//If the user code is not set, generate one
-		if (preferences.getString(GM.USER_CODE, "").length() == 0){
+		if (preferences.getString(GM.USER_CODE, "").length() == 0) {
 			SecureRandom random = new SecureRandom();
 			String newCode = new BigInteger(130, random).toString(32).substring(0, 16);
-			SharedPreferences.Editor editor = preferences.edit();
 			editor.putString(GM.USER_CODE, newCode);
 			editor.apply();
 		}
-		
-		//If the database doesn't exist, create it.
-		if (!databaseExists()){
-        	Log.i("DB status", "Database not found. Creating it...");
-        	createDatabase();
-        }
-		
-		//If its the first time
-		if (preferences.getInt(GM.PREF_DB_VERSION, GM.DEFAULT_PREF_DB_VERSION) == GM.DEFAULT_PREF_DB_VERSION){
+
+		//Cerate database if it's not already created
+		createDatabase();
+
+		//If the database has just been updated, recreate the database
+		if (preferences.getInt(GM.PREF_PREVIOUS_VERSION, 0) < BuildConfig.VERSION_CODE) {
+			Log.d("UPDATE", "App updated from version " + preferences.getInt(GM.PREF_PREVIOUS_VERSION, 0) + " to " + BuildConfig.VERSION_CODE + ". Forcing a new sync...");
+			deleteDatabase();
+			editor.putInt(GM.PREF_PREVIOUS_VERSION, BuildConfig.VERSION_CODE);
+			editor.putInt(GM.PREF_DB_VERSION, 0);
+			editor.apply();
+			createDatabase();
 			initialSync();
 		}
-		//If t's not the first time
-		else{
-			//Sync db
-			sync();
-			
-			//Load initial section
-			loadSection(GM.SECTION_HOME, false);
-			
-			//If the intent had extras (from notifications), do something
-			if (actionText != null){
-				TextView tvDialogTitle, tvDialogText;
-				Button btDialogClose, btDialogAction;
-				Drawable dialogIcon;
-				if (actionTitle != null){
-					
-					//Create a dialog
-					final Dialog dialog = new Dialog(this);
-					
-					//Set up dialog window
-					dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
-					dialog.setContentView(R.layout.dialog_notification);
-					
-					//Set title
-					tvDialogTitle = (TextView) dialog.findViewById(R.id.tv_dialog_notification_title);
-					tvDialogTitle.setText(actionTitle);
-					
-					//Set text
-					tvDialogText = (TextView) dialog.findViewById(R.id.tv_dialog_notification_text);
-					tvDialogText.setText(actionText);
-					
-					//Set close button
-					btDialogClose = (Button) dialog.findViewById(R.id.bt_dialog_notification_close);
-					btDialogClose.setOnClickListener(new OnClickListener() {
-		    			@Override
-		    			public void onClick(View v) {
-		    				dialog.dismiss();
-		    			}
-		    		});
-
-					//Set the icon
-					dialogIcon = getResources().getDrawable(R.drawable.ic_launcher);
-					if (dialogIcon != null) {
-						dialogIcon.setBounds(0, 0, (int) (tvDialogTitle.getTextSize() * 1.4), (int) (tvDialogTitle.getTextSize() * 1.4));
-					}
-					tvDialogTitle.setCompoundDrawables(dialogIcon, null, null, null);
-					tvDialogTitle.setCompoundDrawablePadding(20);
-
-					//Get preferences
-					int festivals = preferences.getInt(GM.PREF_DB_FESTIVALS, 0);
-
-					//Set the action button
-					btDialogAction = (Button) dialog.findViewById(R.id.bt_dialog_notification_action);
-					if (action != null){
-						switch (action) {
-
-							case GM.EXTRA_ACTION_LABLANCA:
-
-								//Set up the action button
-								btDialogAction.setVisibility(View.VISIBLE);
-								btDialogAction.setText(this.getApplicationContext().getString(R.string.notification_action_lablanca));
-								btDialogAction.setOnClickListener(new OnClickListener() {
-									@Override
-									public void onClick(View v) {
-										dialog.dismiss();
-										loadSection(GM.SECTION_LABLANCA, false);
-									}
-								});
-								break;
+		else {
 
-							case GM.EXTRA_ACTION_LOCATION:
+			//If its the first time
+			if (preferences.getInt(GM.PREF_DB_VERSION, GM.DEFAULT_PREF_DB_VERSION) == GM.DEFAULT_PREF_DB_VERSION) {
+				initialSync();
+			}
+			//If t's not the first time
+			else {
+				//Sync db
+				sync();
+
+				//Load initial section
+				loadSection(GM.SECTION_HOME);
+
+				//If the intent had extras (from notifications), do something
+				if (actionText != null) {
+					TextView tvDialogTitle, tvDialogText;
+					Button btDialogClose, btDialogAction;
+					Drawable dialogIcon;
+					if (actionTitle != null) {
+
+						//Create a dialog
+						final Dialog dialog = new Dialog(this);
+
+						//Set up dialog window
+						dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
+						dialog.setContentView(R.layout.dialog_notification);
+
+						//Set title
+						tvDialogTitle = (TextView) dialog.findViewById(R.id.tv_dialog_notification_title);
+						tvDialogTitle.setText(actionTitle);
+
+						//Set text
+						tvDialogText = (TextView) dialog.findViewById(R.id.tv_dialog_notification_text);
+						tvDialogText.setText(actionText);
+
+						//Set close button
+						btDialogClose = (Button) dialog.findViewById(R.id.bt_dialog_notification_close);
+						btDialogClose.setOnClickListener(new OnClickListener() {
+							@Override
+							public void onClick(View v) {
+								dialog.dismiss();
+							}
+						});
+
+						//Set the icon
+						dialogIcon = getResources().getDrawable(R.drawable.ic_launcher);
+						if (dialogIcon != null) {
+							dialogIcon.setBounds(0, 0, (int) (tvDialogTitle.getTextSize() * 1.4), (int) (tvDialogTitle.getTextSize() * 1.4));
+						}
+						tvDialogTitle.setCompoundDrawables(dialogIcon, null, null, null);
+						tvDialogTitle.setCompoundDrawablePadding(20);
 
-								if (preferences.getString(GM.PREF_GM_LOCATION, GM.DEFAULT_PREF_GM_LOCATION).equals(GM.DEFAULT_PREF_GM_LOCATION) == false) {
-									//Set up the action button if location is reported
+						//Get preferences
+						int festivals = preferences.getInt(GM.PREF_DB_FESTIVALS, 0);
+
+						//Set the action button
+						btDialogAction = (Button) dialog.findViewById(R.id.bt_dialog_notification_action);
+						if (action != null) {
+							switch (action) {
+
+								case GM.EXTRA_ACTION_LABLANCA:
+
+									//Set up the action button
 									btDialogAction.setVisibility(View.VISIBLE);
-									btDialogAction.setText(this.getApplicationContext().getString(R.string.notification_action_location));
+									btDialogAction.setText(this.getApplicationContext().getString(R.string.notification_action_lablanca));
 									btDialogAction.setOnClickListener(new OnClickListener() {
 										@Override
 										public void onClick(View v) {
 											dialog.dismiss();
-											loadSection(GM.SECTION_LOCATION, false);
+											loadSection(GM.SECTION_LABLANCA);
 										}
 									});
+									break;
+
+								case GM.EXTRA_ACTION_LOCATION:
+
+									if (!preferences.getString(GM.PREF_GM_LOCATION, GM.DEFAULT_PREF_GM_LOCATION).equals(GM.DEFAULT_PREF_GM_LOCATION)) {
+										//Set up the action button if location is reported
+										btDialogAction.setVisibility(View.VISIBLE);
+										btDialogAction.setText(this.getApplicationContext().getString(R.string.notification_action_location));
+										btDialogAction.setOnClickListener(new OnClickListener() {
+											@Override
+											public void onClick(View v) {
+												dialog.dismiss();
+												loadSection(GM.SECTION_LOCATION);
+											}
+										});
 
-								}
-								break;
-
-							case GM.EXTRA_ACTION_BLOG:
-
-								//Set up the action button
-								btDialogAction.setVisibility(View.VISIBLE);
-								btDialogAction.setText(this.getApplicationContext().getString(R.string.notification_action_blog));
-								btDialogAction.setOnClickListener(new OnClickListener() {
-									@Override
-									public void onClick(View v) {
-										dialog.dismiss();
-										loadSection(GM.SECTION_BLOG, false);
 									}
-								});
-								break;
-
-							case GM.EXTRA_ACTION_ACTIVITIES:
-
-								//Set up the action button
-								btDialogAction.setVisibility(View.VISIBLE);
-								btDialogAction.setText(this.getApplicationContext().getString(R.string.notification_action_activities));
-								btDialogAction.setOnClickListener(new OnClickListener() {
-									@Override
-									public void onClick(View v) {
-										dialog.dismiss();
-										loadSection(GM.SECTION_ACTIVITIES, false);
-									}
-								});
-								break;
-
-							case GM.EXTRA_ACTION_GALLERY:
-
-								//Set up the action button
-								btDialogAction.setVisibility(View.VISIBLE);
-								btDialogAction.setText(this.getApplicationContext().getString(R.string.notification_action_gallery));
-								btDialogAction.setOnClickListener(new OnClickListener() {
-									@Override
-									public void onClick(View v) {
-										dialog.dismiss();
-										loadSection(GM.SECTION_GALLERY, false);
-									}
-								});
-								break;
+									break;
 
-							case GM.EXTRA_ACTION_GMSCHEDULE:
+								case GM.EXTRA_ACTION_BLOG:
 
-								if (festivals == 1) {
 									//Set up the action button
 									btDialogAction.setVisibility(View.VISIBLE);
-									btDialogAction.setText(this.getApplicationContext().getString(R.string.notification_action_gmschedule));
+									btDialogAction.setText(this.getApplicationContext().getString(R.string.notification_action_blog));
 									btDialogAction.setOnClickListener(new OnClickListener() {
 										@Override
 										public void onClick(View v) {
 											dialog.dismiss();
-											loadSection(GM.SECTION_LABLANCA_GM_SCHEDULE, false);
+											loadSection(GM.SECTION_BLOG);
 										}
 									});
-								}
-								break;
+									break;
+
+								case GM.EXTRA_ACTION_ACTIVITIES:
 
-							case GM.EXTRA_ACTION_CITYSCHEDULE:
-								if (festivals == 1) {
 									//Set up the action button
 									btDialogAction.setVisibility(View.VISIBLE);
-									btDialogAction.setText(this.getApplicationContext().getString(R.string.notification_action_cityschedule));
+									btDialogAction.setText(this.getApplicationContext().getString(R.string.notification_action_activities));
 									btDialogAction.setOnClickListener(new OnClickListener() {
 										@Override
 										public void onClick(View v) {
 											dialog.dismiss();
-											loadSection(GM.SECTION_LABLANCA_SCHEDULE, false);
+											loadSection(GM.SECTION_ACTIVITIES);
 										}
 									});
-								}
-								break;
+									break;
 
-							//If the notification is just text
-							default:
-								//Hide action button
-								btDialogAction.setVisibility(View.GONE);
+								case GM.EXTRA_ACTION_GALLERY:
+
+									//Set up the action button
+									btDialogAction.setVisibility(View.VISIBLE);
+									btDialogAction.setText(this.getApplicationContext().getString(R.string.notification_action_gallery));
+									btDialogAction.setOnClickListener(new OnClickListener() {
+										@Override
+										public void onClick(View v) {
+											dialog.dismiss();
+											loadSection(GM.SECTION_GALLERY);
+										}
+									});
+									break;
+
+								case GM.EXTRA_ACTION_GMSCHEDULE:
+
+									if (festivals == 1) {
+										//Set up the action button
+										btDialogAction.setVisibility(View.VISIBLE);
+										btDialogAction.setText(this.getApplicationContext().getString(R.string.notification_action_gmschedule));
+										btDialogAction.setOnClickListener(new OnClickListener() {
+											@Override
+											public void onClick(View v) {
+												dialog.dismiss();
+												loadSection(GM.SECTION_LABLANCA_GM_SCHEDULE);
+											}
+										});
+									}
+									break;
+
+								case GM.EXTRA_ACTION_CITYSCHEDULE:
+									if (festivals == 1) {
+										//Set up the action button
+										btDialogAction.setVisibility(View.VISIBLE);
+										btDialogAction.setText(this.getApplicationContext().getString(R.string.notification_action_cityschedule));
+										btDialogAction.setOnClickListener(new OnClickListener() {
+											@Override
+											public void onClick(View v) {
+												dialog.dismiss();
+												loadSection(GM.SECTION_LABLANCA_SCHEDULE);
+											}
+										});
+									}
+									break;
+
+								//If the notification is just text
+								default:
+									//Hide action button
+									btDialogAction.setVisibility(View.GONE);
+							}
+						} else {
+							btDialogAction.setVisibility(View.GONE);
 						}
-					}
-					else{
-						btDialogAction.setVisibility(View.GONE);
-					}
 
-					//Set dialog parameters
-					WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
-					lp.copyFrom(dialog.getWindow().getAttributes());
-					lp.width = WindowManager.LayoutParams.MATCH_PARENT;
-					lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
-					lp.gravity = Gravity.CENTER;
-					lp.dimAmount = 0.4f;
-					dialog.getWindow().setAttributes(lp);
-
-					//Show dialog
-					dialog.show();
+						//Set dialog parameters
+						WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
+						lp.copyFrom(dialog.getWindow().getAttributes());
+						lp.width = WindowManager.LayoutParams.MATCH_PARENT;
+						lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
+						lp.gravity = Gravity.CENTER;
+						lp.dimAmount = 0.4f;
+						dialog.getWindow().setAttributes(lp);
+
+						//Show dialog
+						dialog.show();
+					}
 				}
 			}
 		}
+
+		// ATTENTION: This was auto-generated to implement the App Indexing API.
+		// See https://g.co/AppIndexing/AndroidStudio for more information.
+		client = new GoogleApiClient.Builder(this).addApi(AppIndex.API).build();
 	}
-	
+
 	/**
-	 * Checks if the app database exists.
-	 * 
-	 * @return True if the database exists, false otherwise.
+	 * Creates the app database and fills it with the hard coded, default data.
 	 */
-	private boolean databaseExists(){
-    	File database = getApplicationContext().getDatabasePath(GM.DB_NAME);
-		return database.exists();
-    }
-    
-    /**
-     * Creates the app database and fills it with the hard coded, default data.
-     */
-    private void createDatabase(){
-    	SQLiteDatabase db;
-    	try {
-    		//Create database
-    		db = openOrCreateDatabase(GM.DB_NAME, Activity.MODE_PRIVATE, null);
-
-			db.execSQL("CREATE TABLE IF NOT EXISTS activity (id INT, permalink VARCHAR, date DATETIME, city VARCHAR, title_es VARCHAR, title_en VARCHAR, title_eu VARCHAR, text_es VARCHAR, text_en VARCHAR, text_eu VARCHAR, after_es VARCHAR, after_en VARCHAR, after_eu VARCHAR, price INT, inscription INT, max_people INT, album INT, dtime DATETIME, comments INT);");
-			db.execSQL("CREATE TABLE IF NOT EXISTS activity_comment (id INT, activity INT, text VARCHAR, dtime DATETIME, username VARCHAR, lang VARCHAR);");
-			db.execSQL("CREATE TABLE IF NOT EXISTS activity_image (id INT, activity INT, image VARCHAR, idx INT);");
-			db.execSQL("CREATE TABLE IF NOT EXISTS activity_itinerary (id INT, activity INT, name_es VARCHAR, name_en VARCHAR, name_eu VARCHAR, description_es VARCHAR, description_en VARCHAR, description_eu VARCHAR, start DATETIME, end DATETIME, place INT);");
-			db.execSQL("CREATE TABLE IF NOT EXISTS activity_tag (activity INT, tag VARCHAR);");
-			db.execSQL("CREATE TABLE IF NOT EXISTS album (id INT, permalink VARCHAR, name_es VARCHAR, name_en VARCHAR, name_eu VARCHAR, description_es VARCHAR, description_en VARCHAR, description_eu VARCHAR, open INT, time DATETIME);");
-			db.execSQL("CREATE TABLE IF NOT EXISTS festival (id INT, year INT, text_es VARCHAR, text_en VARCHAR, text_eu VARCHAR, img VARCHAR);");
-			db.execSQL("CREATE TABLE IF NOT EXISTS festival_day (id INT, date DATETIME, name_es VARCHAR, name_en VARCHAR, name_eu VARCHAR, price INT);");
-			db.execSQL("CREATE TABLE IF NOT EXISTS festival_event (id INT, gm INT, title_es VARCHAR, title_en VARCHAR, title_eu VARCHAR, description_es VARCHAR, description_en VARCHAR, description_eu VARCHAR, host INT, place INT, start DATETIME, end DATETIME);");
-			db.execSQL("CREATE TABLE IF NOT EXISTS festival_event_image (id INT, event INT, image VARCHAR, idx INT);");
-			db.execSQL("CREATE TABLE IF NOT EXISTS festival_offer (id INT, year INT, name_es VARCHAR, name_en VARCHAR, name_eu VARCHAR, description_es VARCHAR, description_en VARCHAR, description_eu VARCHAR, days INT, price INT);");
-			db.execSQL("CREATE TABLE IF NOT EXISTS location (id INT, dtime DATETIME, lat FLOAT, lon FLOAT, manual INT);");
-			db.execSQL("CREATE TABLE IF NOT EXISTS notification (id INT, dtime DATETIME, duration INT, action VARCHAR, title_es VARCHAR, title_en VARCHAR, title_eu VARCHAR, text_es VARCHAR, text_en VARCHAR, text_eu VARCHAR, internal INT);");
-			db.execSQL("CREATE TABLE IF NOT EXISTS people (id INT, name_es VARCHAR, name_en VARCHAR, name_eu VARCHAR, link VARCHAR);");
-			db.execSQL("CREATE TABLE IF NOT EXISTS photo (id INT, file VARCHAR, permalink VARCHAR, title_es VARCHAR, title_en VARCHAR, title_eu VARCHAR, description_es VARCHAR, description_en VARCHAR, description_eu VARCHAR, dtime DATETIME, uploaded DATETIME, place INT, width INT, height INT, size INT, username VARCHAR);");
-			db.execSQL("CREATE TABLE IF NOT EXISTS photo_album (photo INT, album INT);");
-			db.execSQL("CREATE TABLE IF NOT EXISTS photo_comment (id INT, photo INT, text VARCHAR, dtime DATETIME, username VARCHAR, lang VARCHAR);");
-			db.execSQL("CREATE TABLE IF NOT EXISTS place (id INT, name_es VARCHAR, name_en VARCHAR, name_eu VARCHAR, address_es VARCHAR, address_en VARCHAR, address_eu VARCHAR, cp VARCHAR, lat FLOAT, lon FLOAT);");
-			db.execSQL("CREATE TABLE IF NOT EXISTS post (id INT, permalink VARCHAR, title_es VARCHAR, title_en VARCHAR, title_eu VARCHAR, text_es VARCHAR, text_en VARCHAR, text_eu VARCHAR, dtime DATETIME, comments INT);");
-			db.execSQL("CREATE TABLE IF NOT EXISTS post_comment (id INT, post INT, text VARCHAR, dtime DATETIME, username VARCHAR, lang VARCHAR);");
-			db.execSQL("CREATE TABLE IF NOT EXISTS post_image (id INT, post INT, image VARCHAR, idx INT);");
-			db.execSQL("CREATE TABLE IF NOT EXISTS post_tag (post INT, tag VARCHAR);");
-			db.execSQL("CREATE TABLE IF NOT EXISTS settings (name VARCHAR, value VARCHAR);");
+	private void createDatabase() {
+		SQLiteDatabase db;
+		try {
+			//Create database
+			db = openOrCreateDatabase(GM.DB_NAME, Activity.MODE_PRIVATE, null);
+			db.execSQL(GM.DB.QUERY.CREATE.ACTIVITY);
+			db.execSQL(GM.DB.QUERY.CREATE.ACTIVITY_COMMENT);
+			db.execSQL(GM.DB.QUERY.CREATE.ACTIVITY_IMAGE);
+			db.execSQL(GM.DB.QUERY.CREATE.ACTIVITY_ITINERARY);
+			db.execSQL(GM.DB.QUERY.CREATE.ACTIVITY_TAG);
+			db.execSQL(GM.DB.QUERY.CREATE.ALBUM);
+			db.execSQL(GM.DB.QUERY.CREATE.FESTIVAL);
+			db.execSQL(GM.DB.QUERY.CREATE.FESTIVAL_DAY);
+			db.execSQL(GM.DB.QUERY.CREATE.FESTIVAL_EVENT);
+			db.execSQL(GM.DB.QUERY.CREATE.FESTIVAL_EVENT_IMAGE);
+			db.execSQL(GM.DB.QUERY.CREATE.FESTIVAL_OFFER);
+			db.execSQL(GM.DB.QUERY.CREATE.LOCATION);
+			db.execSQL(GM.DB.QUERY.CREATE.NOTIFICATION);
+			db.execSQL(GM.DB.QUERY.CREATE.PEOPLE);
+			db.execSQL(GM.DB.QUERY.CREATE.PHOTO);
+			db.execSQL(GM.DB.QUERY.CREATE.PHOTO_ALBUM);
+			db.execSQL(GM.DB.QUERY.CREATE.PHOTO_COMMENT);
+			db.execSQL(GM.DB.QUERY.CREATE.PLACE);
+			db.execSQL(GM.DB.QUERY.CREATE.POST);
+			db.execSQL(GM.DB.QUERY.CREATE.POST_COMMENT);
+			db.execSQL(GM.DB.QUERY.CREATE.POST_IMAGE);
+			db.execSQL(GM.DB.QUERY.CREATE.POST_TAG);
+			db.execSQL(GM.DB.QUERY.CREATE.SETTINGS);
+			db.execSQL(GM.DB.QUERY.CREATE.SPONSOR);
+			db.execSQL(GM.DB.QUERY.CREATE.VERSION);
 			db.close();
-    	}
-    	catch (Exception ex){
-    		Log.e("Error creating database", ex.toString());
-    	}
-    }
+		} catch (Exception ex) {
+			Log.e("Error creating database", ex.toString());
+		}
+	}
+
+	/**
+	 * Creates the app database and fills it with the hard coded, default data.
+	 */
+	private void deleteDatabase() {
+		SQLiteDatabase db;
+		try {
+			deleteDatabase(GM.DB_NAME);
+		} catch (Exception ex) {
+			Log.e("Error deleting database", ex.toString());
+		}
+	}
 
 	/**
 	 * Performs a full sync against the remote database.
 	 */
-	private void sync(){
+	private void sync() {
 		ProgressBar pbSync = (ProgressBar) findViewById(R.id.pb_sync);
 		ImageView ivSync = (ImageView) findViewById(R.id.iv_logo);
 		new Sync(this, pbSync, ivSync).execute();
 	}
-	
+
 	/**
-	 * Perform an initial sync before the app can be used. 
-	 * A dialog will block the UI. 
+	 * Perform an initial sync before the app can be used.
+	 * A dialog will block the UI.
 	 * It is intended to be used only when the database is empty.
 	 */
-	private void initialSync(){
+	@SuppressWarnings("ConstantConditions")
+	private void initialSync() {
 		//Create a dialog
 		Dialog dialog = new Dialog(this);
 		dialog.setCancelable(false);
-		
+
 		//Set up the window
 		dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
 		dialog.setContentView(R.layout.dialog_sync);
@@ -607,21 +670,21 @@ public class MainActivity extends Activity{
 		lp.gravity = Gravity.CENTER;
 		lp.dimAmount = 0.4f;
 		dialog.getWindow().setAttributes(lp);
-		
+
 		//Sync
 		ProgressBar pbSync = (ProgressBar) findViewById(R.id.pb_sync);
 		ImageView ivSync = (ImageView) findViewById(R.id.iv_logo);
 		new Sync(this, pbSync, ivSync, dialog, this).execute();
 	}
-	
+
 	/**
-	 * Overrides onBackPressed(). 
+	 * Overrides onBackPressed().
 	 * Used to show the previous fragment instead of finishing the app.
-	 * 
-	 * @see android.support.v7.app.AppCompatActivity#onBackPressed()
+	 *
+	 * @see AppCompatActivity#onBackPressed()
 	 */
 	@Override
-	public void onBackPressed(){
+	public void onBackPressed() {
 		try {
 			FragmentManager fm = getFragmentManager();
 			if (fm.getBackStackEntryCount() > 1) {
@@ -630,20 +693,21 @@ public class MainActivity extends Activity{
 				finish();
 				super.onBackPressed();
 			}
-		}
-		catch (Exception ex){
+		} catch (Exception ex) {
 			Log.e("Error going back", "Finishing activity: " + ex.toString());
+			Log.e("RUNNABLE", "Finishing callback for location");
+			locationHandler.removeCallbacks(checkForLocation);
 			finish();
 			//super.onBackPressed();
 		}
-		
+
 	}
 
 	/**
 	 * Overrides onKeyUp().
 	 * Used to toggle the menu whent the key is pressed.
 	 *
-	 * @see android.app.Activity#onKeyUp(int, KeyEvent)
+	 * @see Activity#onKeyUp(int, KeyEvent)
 	 */
 	@Override
 	public boolean onKeyUp(int keyCode, KeyEvent event) {
@@ -653,5 +717,58 @@ public class MainActivity extends Activity{
 		}
 		return super.onKeyUp(keyCode, event);
 	}
-	
+
+	@Override
+	public void onResume() {
+		locationHandler.post(checkForLocation);
+		super.onResume();
+	}
+
+	@Override
+	public void onPause() {
+		locationHandler.removeCallbacks(checkForLocation);
+		super.onPause();
+	}
+
+	@Override
+	public void onDestroy() {
+		locationHandler.removeCallbacks(checkForLocation);
+		super.onDestroy();
+	}
+
+	/**
+	 * ATTENTION: This was auto-generated to implement the App Indexing API.
+	 * See https://g.co/AppIndexing/AndroidStudio for more information.
+	 */
+	public Action getIndexApiAction() {
+		Thing object = new Thing.Builder()
+				.setName("Main Page") // TODO: Define a title for the content shown.
+				// TODO: Make sure this auto-generated URL is correct.
+				.setUrl(Uri.parse("http://[ENTER-YOUR-URL-HERE]"))
+				.build();
+		return new Action.Builder(Action.TYPE_VIEW)
+				.setObject(object)
+				.setActionStatus(Action.STATUS_TYPE_COMPLETED)
+				.build();
+	}
+
+	@Override
+	public void onStart() {
+		super.onStart();
+
+		// ATTENTION: This was auto-generated to implement the App Indexing API.
+		// See https://g.co/AppIndexing/AndroidStudio for more information.
+		client.connect();
+		AppIndex.AppIndexApi.start(client, getIndexApiAction());
+	}
+
+	@Override
+	public void onStop() {
+		super.onStop();
+
+		// ATTENTION: This was auto-generated to implement the App Indexing API.
+		// See https://g.co/AppIndexing/AndroidStudio for more information.
+		AppIndex.AppIndexApi.end(client, getIndexApiAction());
+		client.disconnect();
+	}
 }

+ 1 - 1
app/src/main/java/com/ivalentin/margolariak/PhotoLayout.java

@@ -208,7 +208,7 @@ public class PhotoLayout extends Fragment {
 			File fpath;
 			fpath = new File(this.getActivity().getFilesDir().toString() + "/img/galeria/view/");
 			fpath.mkdirs();
-			new DownloadImage(GM.SERVER + "/img/galeria/view/" + image, this.getActivity().getFilesDir().toString() + "/img/galeria/view/" + image, imageView).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+			new DownloadImage(GM.SERVER + "/img/galeria/view/" + image, this.getActivity().getFilesDir().toString() + "/img/galeria/view/" + image, imageView, GM.IMG_VIEW).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
 		}
 
 		//Hide or show comments, as needed

+ 1 - 1
app/src/main/java/com/ivalentin/margolariak/PostComment.java

@@ -122,7 +122,7 @@ class PostComment extends AsyncTask<String, String, Integer> {
 
             if (code == 200){
                 //Insert into local db
-                SQLiteDatabase db = SQLiteDatabase.openDatabase(context.getDatabasePath(GM.DB_NAME).getAbsolutePath(), null, SQLiteDatabase.NO_LOCALIZED_COLLATORS | SQLiteDatabase.OPEN_READWRITE);
+                SQLiteDatabase db = SQLiteDatabase.openDatabase(context.getDatabasePath(GM.DB_NAME).getAbsolutePath(), null, SQLiteDatabase.NO_LOCALIZED_COLLATORS);
                 String table = "";
                 String item = "";
                 switch (type){

+ 1 - 1
app/src/main/java/com/ivalentin/margolariak/PostLayout.java

@@ -110,7 +110,7 @@ public class PostLayout extends Fragment {
                 File fpath;
                 fpath = new File(this.getActivity().getFilesDir().toString() + "/img/blog/preview/");
                 fpath.mkdirs();
-				new DownloadImage(GM.SERVER + "/img/blog/preview/" + image, this.getActivity().getFilesDir().toString() + "/img/blog/preview/" + image, images[i]).execute();
+				new DownloadImage(GM.SERVER + "/img/blog/preview/" + image, this.getActivity().getFilesDir().toString() + "/img/blog/preview/" + image, images[i], GM.IMG_PREVIEW).execute();
             }
             images[i].setVisibility(View.VISIBLE);
             i ++;

+ 4 - 3
app/src/main/java/com/ivalentin/margolariak/ScheduleLayout.java

@@ -18,7 +18,6 @@ import com.google.android.gms.maps.model.MarkerOptions;
 
 import android.annotation.SuppressLint;
 import android.app.Dialog;
-import android.content.Context;
 import android.content.DialogInterface;
 import android.content.DialogInterface.OnCancelListener;
 import android.content.DialogInterface.OnShowListener;
@@ -126,7 +125,7 @@ public class ScheduleLayout extends Fragment implements OnMapReadyCallback{
 
 
 		//Get current date in the same format as retrieved from the database
-		DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
+		DateFormat df = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
 		Date today = Calendar.getInstance().getTime();
 		String curDate = df.format(today);
 
@@ -189,7 +188,8 @@ public class ScheduleLayout extends Fragment implements OnMapReadyCallback{
 	 * Populates the list of activities with the ones n the selected day.
 	 * 
 	 */
-	@SuppressLint("InflateParams") //Views are added from a loop: I can't specify the parent when inflating.
+	@SuppressLint({"InflateParams", "SwitchIntDef"})
+	//Views are added from a loop: I can't specify the parent when inflating.
 	private int populateSchedule() {
 
 		int eventCount = 0;
@@ -373,6 +373,7 @@ public class ScheduleLayout extends Fragment implements OnMapReadyCallback{
 	 * 
 	 * @param id The event id
 	 */
+	@SuppressWarnings("ConstantConditions")
 	private void showDialog(final int id){
 		
 		//Create the dialog

+ 4 - 8
app/src/main/java/com/ivalentin/margolariak/SettingsActivity.java

@@ -1,14 +1,10 @@
 package com.ivalentin.margolariak;
 
-import android.app.Notification;
 import android.content.SharedPreferences;
 import android.os.Bundle;
 import android.preference.CheckBoxPreference;
 import android.preference.Preference;
 import android.preference.PreferenceActivity;
-import android.util.Log;
-import android.widget.Toast;
-
 import java.text.DecimalFormat;
 import java.text.NumberFormat;
 
@@ -54,13 +50,13 @@ public class SettingsActivity extends PreferenceActivity {
 
 		//Set up Notification preference
 		CheckBoxPreference prefNotification = (CheckBoxPreference) getPreferenceManager().findPreference(GM.KEY_PREFERENCE_NOTIFICATION);
-		if (prefSync.isChecked() == false){
+		if (!prefSync.isChecked()){
 			prefNotification.setEnabled(false);
 		}
 		else {
 			prefNotification.setEnabled(true);
-			if(prefSync.isChecked() == false){
-
+			if(!prefSync.isChecked()){
+				//TODO
 			}
 			if (prefNotification.isChecked()) {
 				prefNotification.setSummary(getString(R.string.preferences_sync_notifications_on));
@@ -85,7 +81,7 @@ public class SettingsActivity extends PreferenceActivity {
 		//Set up data preference
 		long bgData = sp.getLong(GM.STORAGE_TRAFFIC_BG_RECEIVED, 0) + sp.getLong(GM.STORAGE_TRAFFIC_BG_SENT, 0);
 		long fgData = sp.getLong(GM.STORAGE_TRAFFIC_FG_RECEIVED, 0) + sp.getLong(GM.STORAGE_TRAFFIC_FG_SENT, 0);
-		String value = "";
+		String value;
 		if (bgData < 1500){
 			value = bgData + getString(R.string.Kb);
 		}

+ 476 - 171
app/src/main/java/com/ivalentin/margolariak/Sync.java

@@ -1,12 +1,23 @@
 package com.ivalentin.margolariak;
 
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLEncoder;
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
+import java.util.regex.Pattern;
 
 import android.app.Activity;
 import android.app.Dialog;
 import android.content.Context;
 import android.content.SharedPreferences;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
 import android.database.sqlite.SQLiteDatabase;
 import android.os.AsyncTask;
 import android.util.Log;
@@ -18,6 +29,8 @@ import android.widget.ImageView;
 import android.widget.ProgressBar;
 import android.widget.TextView;
 
+import org.json.*;
+
 /**
  * AsyncTask that synchronizes the online database to the device.
  * Is run every time the app is started, and periodically in the background.
@@ -33,7 +46,6 @@ class Sync extends AsyncTask<Void, Void, Void> {
 	private Dialog dialog;
 	private MainActivity activity;
 	private int fg;
-	private int newVersion;
 	private String strings[];
 	private boolean doProgress = false;
 	private long millis = 0;
@@ -42,15 +54,22 @@ class Sync extends AsyncTask<Void, Void, Void> {
 	
 	/**
 	 * Things to do before sync. Namely, displaying a spinning progress bar.
+	 *
 	 * @see android.os.AsyncTask#onPreExecute()
 	 */
 	@Override
 	protected void onPreExecute(){
+
+		//If pbSync and dialog are not null, it means that the sync is being done on the foreground
 		if (pbSync != null) {
+
+			//Show the spinning bar
 			pbSync.setVisibility(View.VISIBLE);
 			ivSync.setVisibility(View.GONE);
 		}
 		if (dialog != null) {
+
+			//Show the dialog and assign the elements on it
 			dialog.show();
 			strings = new String[10];
 			strings[0] = myContextRef.getString(R.string.dialog_sync_text_0);
@@ -72,21 +91,47 @@ class Sync extends AsyncTask<Void, Void, Void> {
 	}
 	
 	/**
-	 * Things to do after sync. Namely, hiddng the spinning progress bar.
+	 * Things to do after sync. Namely, hiding the spinning progress bar.
+	 *
 	 * @see android.os.AsyncTask#onPreExecute()
 	 */
 	@Override
 	protected void onPostExecute(Void v){
+
+		//If pbSync and dialog are not null, it means that the sync is being done on the foreground
 		if (pbSync != null) {
+
+			//Hide the spinner
 			pbSync.setVisibility(View.GONE);
 			ivSync.setVisibility(View.VISIBLE);
 		}
 		if (dialog != null){
+
+			//Close the dialog
 			dialog.dismiss();
-			//Check db version agan
-			SharedPreferences preferences = myContextRef.getSharedPreferences(GM.PREF, Context.MODE_PRIVATE);
-			if (preferences.getInt(GM.PREF_DB_VERSION, GM.DEFAULT_PREF_DB_VERSION) == GM.DEFAULT_PREF_DB_VERSION){
-				//Create a dialog
+
+			//Check db version again
+			SQLiteDatabase db = myContextRef.openOrCreateDatabase(GM.DB_NAME, Activity.MODE_PRIVATE, null);
+			if (db.isReadOnly()){
+				Log.e("Db ro", "Database is locked and in read only mode. Skipping sync.");
+				return;
+			}
+
+			//Get database version
+			Cursor cursor;
+			int dbVersion;
+			cursor = db.rawQuery("SELECT sum(version) AS v FROM version;", null);
+			cursor.moveToFirst();
+			dbVersion = cursor.getInt(0);
+			cursor.close();
+			db.close();
+
+			//If the database is on it's initial version (i.e: There is no data, new or old)
+			if (dbVersion == GM.DEFAULT_PREF_DB_VERSION){
+
+				Log.e("SYNC", "Full sync failed");
+
+				//Create a message dialog
 				final Dialog dial = new Dialog(activity);
 				dial.setCancelable(false);
 				
@@ -108,14 +153,16 @@ class Sync extends AsyncTask<Void, Void, Void> {
 				dial.show();
 			}
 			else{
-				activity.loadSection(GM.SECTION_HOME, false);
+				Log.d("SYNC", "Full sync finished");
+				activity.loadSection(GM.SECTION_HOME);
 			}
 		}
-		Log.d("Sync", "Full sync finished");
+
 	}
 
 	/**
 	 * Called when the AsyncTask is created.
+	 * This constructor is intended for syncs on the background.
 	 *
 	 * @param myContextRef The Context of the calling activity.
 	 */
@@ -125,6 +172,8 @@ class Sync extends AsyncTask<Void, Void, Void> {
 
     /**
      * Called when the AsyncTask is created.
+	 * This constructor is intended for syncs done from the foreground,
+	 * because it shows and hides a spinner (except for the initial one).
      * 
      * @param myContextRef The Context of the calling activity.
      * @param pb The progress bar that will be shown while the sync goes on.
@@ -139,8 +188,8 @@ class Sync extends AsyncTask<Void, Void, Void> {
     
     /**
      * Called when the AsyncTask is created. 
-     * This constructor is intended to use only in the first sync, 
-     * because a dialog will block the UI.
+     * This constructor is intended to use only in the first sync,
+     * from the foreground, because a dialog will block the UI.
      * 
      * @param myContextRef The Context of the calling activity.
      * @param d Dialog of the initial sync
@@ -171,7 +220,6 @@ class Sync extends AsyncTask<Void, Void, Void> {
 	/**
 	 * Called when the AsyncTask is updated.
 	 * Used to change text in the sync window.
-	 *
 	 */
 	protected void onProgressUpdate(Void...progress) {
 		if (doProgress){
@@ -184,203 +232,460 @@ class Sync extends AsyncTask<Void, Void, Void> {
 	}
 
 	/**
-	 * The sweet stuff. Actually performs the sync. 
-	 * 
-	 * //@see android.os.AsyncTask#doInBackground(Params[])
+	 * Creates the URL required to performa a sync. Uses static data and data passed as arguments.
+	 *
+	 * @param user A unique user identifier.
+	 * @param version The db version of the client.
+	 * @param foreground 1 if the sync is done while the app is running, 0 otherwise.
+	 * @param lang Two letter language identifier.
+	 *
+	 * @return The URL that will be used for syncing.
 	 */
-	@Override
-	protected Void doInBackground(Void... params) {
+	private String buildUrl(String user, int version, int foreground, String lang){
+		String url = "";
+		try {
+			url = GM.SERVER + GM.SERVER_SYNC + "?" +
+					GM.SERVER_SYNC_KEY_CLIENT + "=" + URLEncoder.encode(GM.CLIENT, "UTF-8") + "&" +
+					GM.SERVER_SYNC_KEY_USER + "=" + URLEncoder.encode(user, "UTF-8") + "&" +
+					GM.SERVER_SYNC_KEY_ACTION + "=" + URLEncoder.encode(GM.SERVER_SYNC_VALUE_ACTION, "UTF-8") + "&" +
+					GM.SERVER_SYNC_KEY_SECTION + "=" + URLEncoder.encode(GM.SERVER_SYNC_VALUE_SECTION, "UTF-8") + "&" +
+					GM.SERVER_SYNC_KEY_VERSION + "=" + version + "&" +
+					GM.SERVER_SYNC_KEY_FOREGROUND + "=" + foreground + "&" +
+					GM.SERVER_SYNC_KEY_FORMAT + "=" + URLEncoder.encode(GM.SERVER_SYNC_VALUE_FORMAT, "UTF-8") + "&" +
+					GM.SERVER_SYNC_KEY_LANG + "=" + URLEncoder.encode(lang, "UTF-8");
+		}
+		catch (java.io.UnsupportedEncodingException ex){
+			Log.e("UTF-8", "Error encoding url for sync \"" + url + "\" - " + ex.toString());
+		}
+		return url;
+	}
 
-		//Open the database. f its locked, exit.
-		SQLiteDatabase db = myContextRef.openOrCreateDatabase(GM.DB_NAME, Activity.MODE_PRIVATE, null);
-		if (db.isReadOnly()){
-			Log.e("Db ro", "Database is locked and in read only mode. Skipping sync.");
-			return null;
+	/**
+	 * Recreates the database, dropping and then creating all the tables containing
+	 * data. "location" and "notification" tables are untouched.
+	 *
+	 * @param db A database connection.
+	 *
+	 * @return False if there were errors, true otherwise.
+	 */
+	private boolean recreateDb(SQLiteDatabase db) {
+		boolean result = true;
+		try {
+			db.execSQL(GM.DB.QUERY.DROP.ACTIVITY);
+			db.execSQL(GM.DB.QUERY.DROP.ACTIVITY_COMMENT);
+			db.execSQL(GM.DB.QUERY.DROP.ACTIVITY_IMAGE);
+			db.execSQL(GM.DB.QUERY.DROP.ACTIVITY_ITINERARY);
+			db.execSQL(GM.DB.QUERY.DROP.ACTIVITY_TAG);
+			db.execSQL(GM.DB.QUERY.DROP.ALBUM);
+			db.execSQL(GM.DB.QUERY.DROP.FESTIVAL);
+			db.execSQL(GM.DB.QUERY.DROP.FESTIVAL_DAY);
+			db.execSQL(GM.DB.QUERY.DROP.FESTIVAL_EVENT);
+			db.execSQL(GM.DB.QUERY.DROP.FESTIVAL_EVENT_IMAGE);
+			db.execSQL(GM.DB.QUERY.DROP.FESTIVAL_OFFER);
+			db.execSQL(GM.DB.QUERY.DROP.PEOPLE);
+			db.execSQL(GM.DB.QUERY.DROP.PHOTO);
+			db.execSQL(GM.DB.QUERY.DROP.PHOTO_ALBUM);
+			db.execSQL(GM.DB.QUERY.DROP.PHOTO_COMMENT);
+			db.execSQL(GM.DB.QUERY.DROP.PLACE);
+			db.execSQL(GM.DB.QUERY.DROP.POST);
+			db.execSQL(GM.DB.QUERY.DROP.POST_COMMENT);
+			db.execSQL(GM.DB.QUERY.DROP.POST_IMAGE);
+			db.execSQL(GM.DB.QUERY.DROP.POST_TAG);
+			db.execSQL(GM.DB.QUERY.DROP.SETTINGS);
+			db.execSQL(GM.DB.QUERY.DROP.SPONSOR);
+			db.execSQL(GM.DB.QUERY.DROP.VERSION);
+			//TODO: Is this really necessary?
+			Thread.sleep(1000);
+			db.execSQL(GM.DB.QUERY.CREATE.ACTIVITY);
+			db.execSQL(GM.DB.QUERY.CREATE.ACTIVITY_COMMENT);
+			db.execSQL(GM.DB.QUERY.CREATE.ACTIVITY_IMAGE);
+			db.execSQL(GM.DB.QUERY.CREATE.ACTIVITY_ITINERARY);
+			db.execSQL(GM.DB.QUERY.CREATE.ACTIVITY_TAG);
+			db.execSQL(GM.DB.QUERY.CREATE.ALBUM);
+			db.execSQL(GM.DB.QUERY.CREATE.FESTIVAL);
+			db.execSQL(GM.DB.QUERY.CREATE.FESTIVAL_DAY);
+			db.execSQL(GM.DB.QUERY.CREATE.FESTIVAL_EVENT);
+			db.execSQL(GM.DB.QUERY.CREATE.FESTIVAL_EVENT_IMAGE);
+			db.execSQL(GM.DB.QUERY.CREATE.FESTIVAL_OFFER);
+			db.execSQL(GM.DB.QUERY.CREATE.PEOPLE);
+			db.execSQL(GM.DB.QUERY.CREATE.PHOTO);
+			db.execSQL(GM.DB.QUERY.CREATE.PHOTO_ALBUM);
+			db.execSQL(GM.DB.QUERY.CREATE.PHOTO_COMMENT);
+			db.execSQL(GM.DB.QUERY.CREATE.PLACE);
+			db.execSQL(GM.DB.QUERY.CREATE.POST);
+			db.execSQL(GM.DB.QUERY.CREATE.POST_COMMENT);
+			db.execSQL(GM.DB.QUERY.CREATE.POST_IMAGE);
+			db.execSQL(GM.DB.QUERY.CREATE.POST_TAG);
+			db.execSQL(GM.DB.QUERY.CREATE.SETTINGS);
+			db.execSQL(GM.DB.QUERY.CREATE.SPONSOR);
+			db.execSQL(GM.DB.QUERY.CREATE.VERSION);
+			//TODO: Is this really necessary?
+			Thread.sleep(1000);
+		} catch (InterruptedException e) {
+			Log.e("SYNC", "Couldn't wait for the database to be recreated: " + e.toString());
 		}
+		catch (Exception ex) {
+			result = false;
+			Log.e("SYNC", "Error recreating the database: " + ex.toString());
+		}
+		return result;
+	}
 
-		publishProgress();
+	/**
+	 * Finds out the type of a column of a table in the database.
+	 *
+	 * @param db A database connection.
+	 * @param table The name of the table.
+	 * @param idx Index of the column in the database (starting on 0).
+	 *
+	 * @return GM.DB.COLUMN.INT, GM.DB.COLUMN.DATETIME, GM.DB.COLUMN.VARCHAR (default).
+	 */
+	private int getColumnType(SQLiteDatabase db, String table, int idx) {
+		String type = "";
+		Cursor typeCursor;
+		try {
+			String Query = "PRAGMA table_info(" + table + ")";
+			typeCursor  = db.rawQuery(Query, null);
+			typeCursor.moveToPosition(idx);
+			type = typeCursor.getString(2);
+			typeCursor.close();
+		}
+		catch(Exception ex){
+			Log.e("SYNC", "Unable to reliably determine the type of the column '" + idx + "' of the table " + table + ": " + ex.toString());
+		}
+		if (type.equals("INT")) {
+			return GM.DB.COLUMN.INT;
+		}
+		if (type.equals("DATETIME")) {
+			return GM.DB.COLUMN.DATETIME;
+		}
+		return GM.DB.COLUMN.VARCHAR;
+	}
 
-		//Gets the remote page.
-    	FetchURL fu;
+	/**
+	 * Stores version data for each section in the database.
+	 * Uses a custom JSON parser.
+	 *
+	 * @param db Database store the data.
+	 * @param versions List of strings with data about the versions.
+	 *
+	 * @return False if there were errors, true otherwise.
+	 */
+	private boolean saveVersions(SQLiteDatabase db, String versions){
+		String str = versions;
+		String key;
+		boolean result = true;
+		int value;
+		int totalVersions = 0;
+
+		//If I ever have a database with more than 99 public sections (not tables), I'll have to change this. Also, ask for a raise.
+		int[] values = new int[99];
+		String[] keys = new String[99];
+		try {
+			while (str.indexOf("{") > 0){
+				key = str.substring(str.indexOf("{\"") + 2, str.indexOf("\":"));
+				str = str.substring(str.indexOf("\":\"") + 3);
+				value = Integer.parseInt(str.substring(0, str.indexOf("\"")));
+				str = str.substring(str.indexOf("}") + 1);
+				keys[totalVersions] = key;
+				values[totalVersions] = value;
+				totalVersions ++;
+			}
+			if (totalVersions > 0 && totalVersions < 99){
+				db.execSQL(GM.DB.QUERY.CREATE.VERSION);
+				db.execSQL(GM.DB.QUERY.EMPTY.VERSION);
+				for (int i = 0; i < totalVersions; i ++){
+					db.execSQL("INSERT INTO version VALUES ('" + keys[i] + "', " + values[i] + ");");
+				}
+			}
+			else{
+				result = false;
+			}
 
-		//The lines of the received web page.
-		String o = null;
-		
-		//List of SQL queries to be performed, as received from the web page.
-		List<String> queryList = new ArrayList<>();
-		
-		//Preferences.
-		SharedPreferences preferences = myContextRef.getSharedPreferences(GM.PREF, Context.MODE_PRIVATE);
-		SharedPreferences.Editor prefEditor;
-		prefEditor = preferences.edit();
+		}
+		catch (Exception ex){
+			result = false;
+			Log.e("SYNC", "Error saving the remote db versions: " + ex.toString());
+		}
+		return result;
+	}
 
-		//Boolean that will prevent the process to go on if something goes wrong.
-		boolean success = true;
-		
-		//Get the file
+	/**
+	 * Stores version data for each section in the database.
+	 * Uses a custom JSON parser.
+	 *
+	 * @param db Database store the data.
+	 * @param data List of strings in json format with the tables.
+	 *
+	 * @return False if there were errors, true otherwise.
+	 */
+	private boolean saveData(SQLiteDatabase db, String data){
+		String str;
+		String key;
+		String value;
+		boolean result = true;
 		try{
-			publishProgress();
-
-			String code = preferences.getString(GM.USER_CODE, "");
-			int dbVersion = preferences.getInt(GM.PREF_DB_VERSION, GM.DEFAULT_PREF_DB_VERSION);
-			fu = new FetchURL();
-			fu.Run(GM.SERVER + "/app/sync.php?os=android&code=" + code + "&fg=" + fg + "&v=" + dbVersion + "&lang=" + GM.getLang());
-			//All the info
-			o = fu.getOutput().toString();
-			publishProgress();
+			str = data.substring(data.indexOf("[") + 1, data.lastIndexOf("]"));
+			while (str.indexOf("}") > 0){
+				try {
+					key = str.substring(str.indexOf("\"") + 1, str.indexOf("\"", str.indexOf(":") - 1));
+					value = str.substring(str.indexOf("[") + 1, str.indexOf("]"));
+					str = str.substring(str.indexOf("]") + 1);
+
+					if (!saveTable(db, key, value)){
+						//Finish the loop if there is an error
+						return false;
+					}
+
+					str = str.substring(str.indexOf("}") + 1);
+				}
+				catch (Exception ex){
+					//End of string
+					Log.d("SYNC", "End of data string:" + ex.toString());
+					str = "";
+				}
+			}
 		}
-		catch(Exception ex){
-			publishProgress();
-			Log.e("Sync error", "Error fetching remote file: " + ex.toString());
-			success = false;
+		catch (Exception ex){
+			Log.e("SYNC", "Error saving the remote db data: " + ex.toString());
+			result = false;
 		}
 
-		int errorCount = 0;
+		return result;
+	}
 
-		if (success){
 
-			publishProgress();
 
-			//Parse the contents of the page
-			//Check if the database is synced
-			if (o.contains("<synced>1</synced>")){
-				Log.i("SYNC", "Database is already at the latest version");
-				return null;
-			}
 
-			//Try to separate the file by tables (<table></table>)
-			try{
-				String table, tableName, row, fieldName, fieldValue, line, query, queryFields, queryValues;
+	/**
+	 * Stores a table data into the database.
+	 * Uses a custom JSON parser.
+	 *
+	 * @param db Database to store the data.
+	 * @param table Name of the table.
+	 * @param data String in JSON format with the data of the table.
+	 * @return False if there were errors, true otherwise.
+	 */
+	private boolean saveTable(SQLiteDatabase db, String table, String data) {
+		int type, i;
+		JSONObject jsonObj;
+		String[] values = new String[99];
+		String[] columns = new String[99];
+		String val, row;
+		String str = data;
+		List<String> queries = new ArrayList<>();
+
+		//The first query has to delete all entries
+		queries.add("DELETE FROM " + table + ";");
+
+		//Process each row
+		while (str.contains("{\"") && str.contains("\"}")) {
+			row = str.substring(str.indexOf("{\""), str.indexOf("\"}") + 2);
+			i = 0;
+
+			try {
+
+				//Create a json object with the collumn names and values of the row...
+				jsonObj = new JSONObject(row);
+				Iterator<?> keys = jsonObj.keys();
+
+				//... and loop through it.
+				while (keys.hasNext()) {
+					String key = (String) keys.next();
+					String value = jsonObj.get(key).toString();
+					columns[i] = key;
+					values[i] = value;
+					i++;
+				}
 
-				//Get db version
-				newVersion = Integer.parseInt(o.substring(o.indexOf("<version>") + 9, o.indexOf("</version>")));
+				//Start building the query
+				String q = "INSERT INTO " + table + "(";
 
-				//Get other preferences and store them
-				int prefPhotos = Integer.parseInt(o.substring(o.indexOf("<photos>") + 8, o.indexOf("</photos>")));
-				int prefFestivals = Integer.parseInt(o.substring(o.indexOf("<festivals>") + 11, o.indexOf("</festivals>")));
-				prefEditor.putInt(GM.PREF_DB_PHOTOS, prefPhotos);
-				prefEditor.putInt(GM.PREF_DB_FESTIVALS, prefFestivals);
-				prefEditor.apply();
+				//Add column names
+				for (int j = 0; j < i; j++) {
+					q = q + columns[j] + ", ";
+				}
+				q = q.substring(0, q.length() - 2) + ") VALUES (";
 
+				//Loop trough values
+				for (int j = 0; j < i; j++) {
 
-				while (o.contains("<table>")){
+					//If the value is empty, I dont care what tipe is it, it will be 'null'.
+					if (values[j].length() == 0) {
+						val = "null";
+						q = q + val + ", ";
+					}
 
-					publishProgress();
+					//If the value is not empty, I need the type
+					else {
+						type = getColumnType(db, table, j);
 
-					try {
-						table = o.substring(o.indexOf("<table>"), o.indexOf("</table>") + 8);
-						tableName = table.substring(table.indexOf("<name>") + 6, table.indexOf("</name>"));
-						queryList.add("DELETE FROM " + tableName + ";");
-						while (table.contains("<row>")) {
+						switch (type) {
+							case GM.DB.COLUMN.INT:
 
-							try {
-								if (table.length() < 5) {
-									break;
-								}
-								row = table.substring(table.indexOf("<row>") + 5, table.indexOf("</row>"));
-
-								//if (row.indexOf("Gracias a todos vosotros no hemos hecho") != -1)
-								//	Log.e("Row", row);
-
-								query = "INSERT INTO " + tableName + " ";
-								queryFields = "(";
-								queryValues = "(";
-								while (row.contains(">,")) {
-
-									publishProgress();
-
-									try {
-										if (row.length() < 5) {
-											break;
-										}
-										line = row.substring(0, row.indexOf(">, \t") + 1);
-										line = line.substring(line.indexOf("<"));
-
-										fieldName = line.substring(line.indexOf("<") + 1, line.indexOf(">"));
-										queryFields = queryFields + fieldName + ", ";
-										fieldValue = line.substring(line.indexOf("<" + fieldName + ">") + 2 + fieldName.length());
-										//fieldValue = fieldValue.substring(0, fieldValue.length() - fieldName.length() - 2);
-										fieldValue = fieldValue.substring(0, fieldValue.indexOf("</" + fieldName + ">"));
-										//if (fieldValue.indexOf("Gracias a todos vosotros no hemos hecho") != -1)
-										//Log.e("Fieldvalue", fieldValue);
-
-										if (fieldValue.length() == 0)
-											fieldValue = "null";
-										if (fieldValue.charAt(0) == '\'' && fieldValue.charAt(fieldValue.length() - 1) == '\'') {
-											fieldValue = fieldValue.substring(1, fieldValue.length() - 1);
-											fieldValue = fieldValue.replace("'", "''");
-											fieldValue = "\'" + fieldValue + "\'";
-										}
-										//Log.e(fieldName, fieldValue);
-										queryValues = queryValues + fieldValue + ", ";
-										row = row.substring(row.indexOf(">, \t") + 4);
-									}
-									catch(Exception ex){
-										Log.e("Parsing error", "Error getting values from row: " + ex.toString());
-										errorCount ++;
-										break;
-									}
+								//Check that is really a number to prevent injection.
+								if (Pattern.matches("\\-?\\d+", values[j]))
+									q = q + values[j] + ", ";
+								else{
+									//If its not and actually number, just insert null to avoid problems.
+									q = q + "null, ";
 								}
+								break;
 
-								queryFields = queryFields.substring(0, queryFields.length() - 2) + ")";
-								queryValues = queryValues.substring(0, queryValues.length() - 2) + ")";
-								query = query + queryFields + " VALUES " + queryValues + ";";
-								queryList.add(query);
-								//Log.e("Query", query);
-
-								table = table.substring(table.indexOf("</row>") + 6);
-							}
-							catch(Exception ex){
-								Log.e("Parsing error", "Error getting rows from tables: " + ex.toString());
-								errorCount ++;
+							case GM.DB.COLUMN.DATETIME:
+								//The API formats ('YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS') are good for inserting. Just escape and add quotes.
+								q = q + DatabaseUtils.sqlEscapeString(values[j]) + ", ";
 								break;
-							}
+
+							default: //VARCHAR
+								//Just escape and add quotes.
+								q = q + DatabaseUtils.sqlEscapeString(values[j]) + ", ";
 						}
 					}
-					catch(Exception ex){
-						Log.e("Parsing error", "Error getting tables from the sync file: " + ex.toString());
-						errorCount ++;
-						break;
-					}
-					o = o.substring(o.indexOf("</table>") + 8);
+
 				}
+
+				//End the query string and add it to the array.
+				q = q.substring(0, q.length() - 2) + ")";
+				queries.add(q);
+
 			}
-			catch(Exception ex){
-				Log.e("Sync error", "Error parsing remote info: " + ex.toString());
-				errorCount ++;
+			catch (JSONException e) {
+				Log.e("SYNC", "Error parsing table '" + table + "': " + e.toString());
+				return false;
 			}
+			catch (Exception ex) {
+				Log.e("SYNC", "Error inserting data from remote table " + table + " into the local db: " + ex.toString());
+				return false;
+			}
+
+			//Process str for the next loop pass.
+			str = str.substring(str.indexOf("\"}") + 2);
 		}
-		try{
 
-			for(int i = 0; i < queryList.size(); i++){
-				publishProgress();
-				try {
-					db.execSQL(queryList.get(i));
-				}
-				catch(Exception ex){
-					Log.e("Query Error", "Error on sync query: " + ex.toString());
-					errorCount ++;
-				}
+		//If I get to this point, there were no errors, and I can safely execute the queries
+		try {
+			int totalQueries = queries.size();
+			for (i = 0; i < totalQueries; i++) {
+				db.execSQL(queries.get(i));
 			}
-			db.close();
+		} catch (Exception ex) {
+			Log.e("SYNC", "Error inserting data from remote table " + table + " into the local db: " + ex.toString().substring(18 * ex.toString().length() / 20));
 
-			//Set current database version in preferences.
-			if (errorCount == 0){
-				prefEditor.putInt(GM.PREF_DB_VERSION, newVersion);
-				prefEditor.apply();
-			}
-			else{
-				Log.e("Db update", "Not updating db version because there were errors");
-				if (preferences.getInt(GM.PREF_DB_VERSION, GM.DEFAULT_PREF_DB_VERSION) == GM.DEFAULT_PREF_DB_VERSION) {
-					prefEditor.putInt(GM.PREF_DB_VERSION, GM.DEFAULT_PREF_DB_VERSION + 1);
-					prefEditor.apply();
-				}
+			//I don't put a 'return false;' here because I dont want to loose the whole table for just one row.
+		}
+
+		return true;
+	}
+
+	/**
+	 * The sweet stuff. Actually performs the sync. 
+	 */
+	@Override
+	protected Void doInBackground(Void... params) {
+
+		//Get preferences
+		SharedPreferences preferences = myContextRef.getSharedPreferences(GM.PREF, Context.MODE_PRIVATE);
+
+		//Get useful data for the uri
+		String userCode = preferences.getString(GM.USER_CODE, "");
+
+		//Get database. Stop if it's locked
+		SQLiteDatabase db = myContextRef.openOrCreateDatabase(GM.DB_NAME, Activity.MODE_PRIVATE, null);
+		if (db.isReadOnly()){
+			Log.e("SYNC", "Database is locked and in read only mode. Skipping sync.");
+			return null;
+		}
+
+		//Get database version
+		Cursor cursor;
+		int dbVersion;
+		cursor = db.rawQuery("SELECT sum(version) AS v FROM version;", null);
+		cursor.moveToFirst();
+		dbVersion = cursor.getInt(0);
+		cursor.close();
+
+		URL url;
+		String uri = buildUrl(userCode, dbVersion, fg, GM.getLang());
+		try {
+			url = new URL(uri);
+
+			HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
+
+			int httpCode = urlConnection.getResponseCode();
+			switch (httpCode){
+				case 400:	//Client error: Bad request
+					Log.e("SYNC", "The server returned a 400 code (Client Error: Bad request) for the url \"" + uri + "\"");
+					break;
+				case 403:	//Client error: Forbidden
+					Log.e("SYNC", "The server returned a 403 code (Client Error: Forbidden) for the url \"" + uri + "\"");
+					break;
+				case 204:	//Success: No content
+					Log.d("SYNC", "The server returned a 204 code (Success: No content) for the url \"" + uri + "\". Stopping sync process...");
+					break;
+				case 200:	//Success: OK
+					Log.d("SYNC", "The server returned a 200 code (Success: OK) for the url \"" + uri + "\". Now syncing...");
+					BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
+					StringBuilder sb = new StringBuilder();
+					String o;
+					while ((o = br.readLine()) != null)
+						sb.append(o);
+
+					//Get the string with the sync json. (The whole page)
+					String strSync = sb.toString();
+					strSync = strSync.replace(":null", ":\"\"");
+
+					//Get the JSON object from the string.
+					JSONObject jsonSync = new JSONObject(strSync);
+					jsonSync = new JSONObject(jsonSync.get("sync").toString().substring(1, jsonSync.get("sync").toString().length() - 1));
+
+					//Get the string wit the versions in JSON format.
+					String strVersion = jsonSync.get("version").toString();
+
+					//Handmade parser, because with:
+					//String strData = jsonSync.get("data").toString();
+					//I get an error: "No value for data"
+					String strData = strSync.substring(1, strSync.length() - 1);
+					strData = strData.substring(strData.indexOf("{\"data\":"));
+
+					//If the data is correctly parsed and stored, commit changes to the database.
+					db.beginTransaction();
+
+					//TODO: for save version apps, this is not needed, but I have to check it after an app update.
+					//recreateDb(db);
+
+					if (saveVersions(db, strVersion) && saveData(db, strData)){
+						db.setTransactionSuccessful();
+						Log.d("SYNC", "The sync process finished correctly. Changes to the database will be commited");
+					}
+					else{
+						Log.e("SYNC", "The sync process did not finish correctly. Any changes made to the database will be reverted");
+					}
+					db.endTransaction();
+
+					break;
+				default:
+					Log.e("SYNC", "The server returned an unexpected code (" + httpCode + ") for the url \"" + uri + "\"");
 			}
+			urlConnection.disconnect();
+			return null;
+
 		}
-		catch(Exception ex){
-			Log.e("Sync error", "Error updating info: " + ex.toString());
+		catch (MalformedURLException e) {
+			Log.e("SYNC", "Malformed URL (" + uri + "): " + e.toString());
+			e.printStackTrace();
 		}
+		catch (IOException e) {
+			Log.e("SYNC", "IOException for URL (" + uri + "): " + e.toString());
+			e.printStackTrace();
+		}
+		catch (org.json.JSONException e) {
+			Log.e("SYNC", "JSONException for URL (" + uri + "): " + e.toString());
+			e.printStackTrace();
+		}
+
+		db.close();
 		return null;
     	
 	}
-}
+}

+ 24 - 12
app/src/main/res/layout/activity_main.xml

@@ -1,5 +1,7 @@
 <LinearLayout
 	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="fill_parent"
 	android:orientation="vertical">
@@ -59,13 +61,15 @@
 		</LinearLayout>
 
 		<ImageButton
-			android:layout_width="wrap_content"
-			android:layout_height="wrap_content"
-			android:src="@drawable/abc_ic_menu_moreoverflow_mtrl_alpha"
+			android:layout_width="40dp"
+			android:layout_height="match_parent"
+			android:src="@drawable/ic_expand"
 			android:contentDescription="@string/tutorial_menu"
 			android:onClick="showMenu"
 			android:layout_weight=".2"
-			android:id="@+id/bt_menu"/>
+			android:id="@+id/bt_menu"
+			app:srcCompat="@drawable/ic_expand"
+			android:background="#00000000"/>
 
 		<ProgressBar
 			android:id="@+id/pb_sync"
@@ -87,7 +91,8 @@
 		android:scrollIndicators="none"
 		android:requiresFadingEdge="horizontal"
 		android:fadingEdgeLength="125dp"
-		android:background="@color/background_bar_gradient">
+		android:background="@color/background_bar_gradient"
+		tools:ignore="UnusedAttribute">
 
 		<LinearLayout
 			android:orientation="horizontal"
@@ -125,7 +130,8 @@
 					android:layout_alignParentEnd="false"
 					android:layout_alignParentLeft="false"
 					android:layout_alignParentRight="false"
-					android:layout_alignParentBottom="true"/>
+					android:layout_alignParentBottom="true"
+					tools:ignore="ContentDescription"/>
 			</RelativeLayout>
 
 			<RelativeLayout
@@ -157,7 +163,8 @@
 					android:layout_alignParentEnd="false"
 					android:layout_alignParentLeft="false"
 					android:layout_alignParentRight="false"
-					android:layout_alignParentBottom="true"/>
+					android:layout_alignParentBottom="true"
+					tools:ignore="ContentDescription"/>
 			</RelativeLayout>
 
 			<RelativeLayout
@@ -189,7 +196,8 @@
 					android:layout_alignParentEnd="false"
 					android:layout_alignParentLeft="false"
 					android:layout_alignParentRight="false"
-					android:layout_alignParentBottom="true"/>
+					android:layout_alignParentBottom="true"
+					tools:ignore="ContentDescription"/>
 			</RelativeLayout>
 
 			<RelativeLayout
@@ -221,7 +229,8 @@
 					android:layout_alignParentEnd="false"
 					android:layout_alignParentLeft="false"
 					android:layout_alignParentRight="false"
-					android:layout_alignParentBottom="true"/>
+					android:layout_alignParentBottom="true"
+					tools:ignore="ContentDescription"/>
 			</RelativeLayout>
 
 			<RelativeLayout
@@ -253,7 +262,8 @@
 					android:layout_alignParentEnd="false"
 					android:layout_alignParentLeft="false"
 					android:layout_alignParentRight="false"
-					android:layout_alignParentBottom="true"/>
+					android:layout_alignParentBottom="true"
+					tools:ignore="ContentDescription"/>
 			</RelativeLayout>
 
 			<RelativeLayout
@@ -285,7 +295,8 @@
 					android:layout_alignParentEnd="false"
 					android:layout_alignParentLeft="false"
 					android:layout_alignParentRight="false"
-					android:layout_alignParentBottom="true"/>
+					android:layout_alignParentBottom="true"
+					tools:ignore="ContentDescription"/>
 			</RelativeLayout>
 
 		</LinearLayout>
@@ -299,7 +310,8 @@
 		<FrameLayout
 			android:id="@+id/activity_main_content_fragment"
 			android:layout_width="match_parent"
-			android:layout_height="match_parent">
+			android:layout_height="match_parent"
+			tools:ignore="ScrollViewSize">
 
 		</FrameLayout>
 	</ScrollView>

+ 3 - 2
app/src/main/res/layout/fragment_layout_activity_past.xml

@@ -3,7 +3,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="fill_parent"
-    android:layout_height="wrap_content"
+    android:layout_height="match_parent"
     android:id="@+id/scrollView3"
     android:background="@color/background_app"
     android:padding="7dp"
@@ -27,6 +27,7 @@
 
         <LinearLayout
             android:orientation="vertical"
+            android:transitionGroup="true"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:background="@drawable/entry"
@@ -65,7 +66,7 @@
                 android:maxHeight="70dp" />
 
             <WebView
-                android:layout_width="wrap_content"
+                android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:id="@+id/wv_activity_past_text"
                 android:layout_marginTop="10dp"

+ 1 - 0
app/src/main/res/layout/fragment_layout_location.xml

@@ -24,6 +24,7 @@
 			android:id="@+id/textView2"
 			android:textStyle="bold"
 			android:layout_marginLeft="10dp"
+			android:layout_marginStart="10dp"
 			android:textColor="@color/section_title"/>
 
 		<LinearLayout

+ 21 - 2
app/src/main/res/values-en/strings.xml

@@ -154,12 +154,31 @@
 	<string name="dialog_sync_text_4">Afinando la tuba&#8230;</string>
 	<string name="dialog_sync_text_5">Margolari camina pa\'lante, margolari camina pa\'trás.</string>
 	<string name="dialog_sync_text_6">Ari ari ari!</string>
-	<string name="dialog_sync_text_7">Llegando tarde...</string>
+	<string name="dialog_sync_text_7">Llegando tarde</string>
 	<string name="dialog_sync_text_8">¿Cabe más gente en el Foro Margolari?</string>
 	<string name="dialog_sync_failed_title">Synchronization failed</string>
 	<string name="dialog_sync_failed_text">Couldn\'t download content.\n\nMake sure you have a network connection and try again.</string>
 	<string name="dialog_sync_failed_close">Close</string>
 	<string name="lablanca_nofestivals">Las Fiestas de La Blanca. Esos días, del 4 al 9 de agosto, en los que Vitoria-Gasteiz se transforma. Se llena de color y alegría, y en cada esquina puedes encontrar actividades y espectáculos. Todos las estamos esperando.\n\nPor desgracia, aún hay que esperar más.\n\nNosotros ya estamos trabajando en ello. Tan pronto como vayamos sabiendo más, aquí iremos publicando nuestro programa y otra información sobre las fiestas.</string>
-	<string name="home_section_location_text_calculating">Calculating distance...</string>
+	<string name="home_section_location_text_calculating">Calculating distance…</string>
+	<string name="pop_menu_about">About us</string>
+	<string name="pop_menu_settings">Settings</string>
+	<string name="pop_menu_sponsors">-</string>
+	<string name="preferences">-</string>
+	<string name="preferences_info">-</string>
+	<string name="preferences_info_feedback">-</string>
+	<string name="preferences_info_feedback_summary">-</string>
+	<string name="preferences_info_source">-</string>
+	<string name="preferences_info_source_summary">-</string>
+	<string name="preferences_info_version">-</string>
+	<string name="preferences_sync">-</string>
+	<string name="preferences_sync_notifications">-</string>
+	<string name="preferences_sync_usage">-</string>
+	<string name="preferences_sync_sync_on">-</string>
+	<string name="preferences_sync_sync_off">-</string>
+	<string name="preferences_sync_sync">-</string>
+	<string name="preferences_sync_notifications_on">-</string>
+	<string name="preferences_sync_notifications_off">-</string>
+	<string name="preferences_sync_notifications_disabled">-</string>
 
 </resources>

+ 21 - 2
app/src/main/res/values-eu-rES/strings.xml

@@ -159,12 +159,31 @@
 	<string name="dialog_sync_text_4">Afinando la tuba&#8230;</string>
 	<string name="dialog_sync_text_5">Margolari camina pa\'lante, margolari camina pa\'trás.</string>
 	<string name="dialog_sync_text_6">Ari ari ari!</string>
-	<string name="dialog_sync_text_7">Llegando tarde...</string>
+	<string name="dialog_sync_text_7">Llegando tarde</string>
 	<string name="dialog_sync_text_8">¿Cabe más gente en el Foro Margolari?</string>
 	<string name="dialog_sync_failed_text">No se pudierdon descargar los contenidos\n\nAsegúrate de que dispones de conexión a internet e inténtalo de nuevo.</string>
 	<string name="lablanca_nofestivals">Las Fiestas de La Blanca. Esos días, del 4 al 9 de agosto, en los que Vitoria-Gasteiz se transforma. Se llena de color y alegría, y en cada esquina puedes encontrar actividades y espectáculos. Todos las estamos esperando.\n\nPor desgracia, aún hay que esperar más.\n\nNosotros ya estamos trabajando en ello. Tan pronto como vayamos sabiendo más, aquí iremos publicando nuestro programa y otra información sobre las fiestas.</string>
 	<string name="settings_notification_off">No recibir notificaciones</string>
 	<string name="settings_notification_on">Recibir notificaciones</string>
-	<string name="home_section_location_text_calculating">Calculando distancia...</string>
+	<string name="home_section_location_text_calculating">Calculando distancia…</string>
+	<string name="pop_menu_about">About us</string>
+	<string name="pop_menu_settings">Settings</string>
+	<string name="pop_menu_sponsors">-</string>
+	<string name="preferences">-</string>
+	<string name="preferences_info">-</string>
+	<string name="preferences_info_feedback">-</string>
+	<string name="preferences_info_feedback_summary">-</string>
+	<string name="preferences_info_source">-</string>
+	<string name="preferences_info_source_summary">-</string>
+	<string name="preferences_info_version">-</string>
+	<string name="preferences_sync">-</string>
+	<string name="preferences_sync_notifications">-</string>
+	<string name="preferences_sync_notifications_disabled">-</string>
+	<string name="preferences_sync_notifications_off">-</string>
+	<string name="preferences_sync_notifications_on">-</string>
+	<string name="preferences_sync_sync">-</string>
+	<string name="preferences_sync_sync_off">-</string>
+	<string name="preferences_sync_sync_on">--</string>
+	<string name="preferences_sync_usage">-</string>
 
 </resources>

+ 6 - 6
app/src/main/res/values/strings.xml

@@ -124,7 +124,7 @@
 	<string name="dialog_sync_text_4">Afinando la tuba&#8230;</string>
 	<string name="dialog_sync_text_5">Margolari camina pa\'lante, margolari camina pa\'trás.</string>
 	<string name="dialog_sync_text_6">Ari ari ari!</string>
-	<string name="dialog_sync_text_7">Llegando tarde...</string>
+	<string name="dialog_sync_text_7">Llegando tarde</string>
 	<string name="dialog_sync_text_8">¿Cabe más gente en el Foro Margolari?</string>
 	<string name="gallery_albums">Albums</string>
 	<string name="gallery_upload">Subir fotos</string>
@@ -138,7 +138,7 @@
 	<string name="home_section_gmschedule">Programa Margolari</string>
 	<string name="home_section_location_text_short" tools:ignore="PluralsCandidate">¡Están a solo %1$d metros de ti! (Unos %2$d minutos andando)</string>
 	<string name="home_section_location_text_long">¡Están a solo %1$s kilometros de ti!</string>
-	<string name="home_section_location_text_calculating">Calculando distancia...</string>
+	<string name="home_section_location_text_calculating">Calculando distancia</string>
 	<string name="lablanca_date_august">%1$s de agosto</string>
 	<string name="lablanca_date_july">%1$s de julio</string>
 	<string name="lablanca_prices">Precios</string>
@@ -171,7 +171,7 @@
 	<string name="settings_license">Licencia</string>
 	<string name="settings_license_content"><![CDATA[Copyright 2016 Iñigo Valentin<br/><br/>This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 3 as published by the Free Software Foundation.<br/><br/>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details at http://www.gnu.org/licenses/.<br/><br/>The name "Gasteizko Margolariak" and its logotype are property of Gasteizko Margolariak.<br/><br/><br/><div style="background-color:#bbbbbb;padding:6px;border-radius:5px;border-style:solid;border-width:2px;border-color:#333333;text-align:center;">You can get the source code for this project at <a href="https://github.com/Seavenois/GM">Github</a></div>]]></string>
 	<string name="settings_privacy">Privacidad</string>
-	<string name="settings_privacy_content"><![CDATA[En Gasteizko Margolariak respetamos tu privacidad. No guardamos NINGÚN dato de nuestros usuarios, incluyendo información personal, localización, hábitos de compra, preferencias sexuales, color favorito, nombre de su primer amor… NINGUNO.<br/><br/>Para que nos creas, te explicamos lo que sí almacenamos:<br/><br/><ul><li>Un código de usuario, generado aleatoriamente, único para tu dispositivo. Esto es necesario para sincronizar la aplicación con nuestra base de datos y poder descargar los últimos posts, actividades... Pero esta hecho de tal manera que no podamos identificarte como persona a través de ese código.</li><li>Al publicar un comentario en una entrada o una foto, guardaremos temporalmente (120 minutos, lo justo para procesar información) tu dirección IP. Esto nos es súper útil para saber cuántas personas diferentes publican comentarios, y tampoco nos permite identificarte como persona.</li></ul>]]></string>
+	<string name="settings_privacy_content"><![CDATA[En Gasteizko Margolariak respetamos tu privacidad. No guardamos NINGÚN dato de nuestros usuarios, incluyendo información personal, localización, hábitos de compra, preferencias sexuales, color favorito, nombre de su primer amor… NINGUNO.<br/><br/>Para que nos creas, te explicamos lo que sí almacenamos:<br/><br/><ul><li>Un código de usuario, generado aleatoriamente, único para tu dispositivo. Esto es necesario para sincronizar la aplicación con nuestra base de datos y poder descargar los últimos posts, actividades Pero esta hecho de tal manera que no podamos identificarte como persona a través de ese código.</li><li>Al publicar un comentario en una entrada o una foto, guardaremos temporalmente (120 minutos, lo justo para procesar información) tu dirección IP. Esto nos es súper útil para saber cuántas personas diferentes publican comentarios, y tampoco nos permite identificarte como persona.</li></ul>]]></string>
 	<string name="settings_transparency">Transparencia</string>
 	<string name="settings_transparency_content"><![CDATA[¿Te preocupa saber dónde va tu dinero? En Gasteizko Margolariak queremos que estés tranquilo.<br/><br/>Muchas de nuestras actividades son gratuitas. Otras, como las fiestas de La Blanca, tienen una cuota de inscripción. Gasteizko Margolariak no es una empresa, es una Asociación Cultural, y como tal, no tenemos beneficios.<br/><br/>Queremos ser transparentes, y por eso, empezando en 2016, pondremos a tu disposición nuestras cuentas de manera online, para que sepas que cada euro se reinvierte en tí.]]></string>
 	<string name="lablanca_nofestivals">Las Fiestas de La Blanca. Esos días, del 4 al 9 de agosto, en los que Vitoria-Gasteiz se transforma. Se llena de color y alegría, y en cada esquina puedes encontrar actividades y espectáculos. Todos las estamos esperando.\n\nPor desgracia, aún hay que esperar más.\n\nNosotros ya estamos trabajando en ello. Tan pronto como vayamos sabiendo más, aquí iremos publicando nuestro programa y otra información sobre las fiestas.</string>
@@ -181,8 +181,7 @@
 
 
 	<!-- Pop-up menu entry strings -->
-	<string name="pop_menu_settings">Settings</string>
-	<string name="pop_menu_about">About us</string>
+	<string name="pop_menu_settings">Ajustes</string>
 	<string name="pop_menu_sponsors">Sponsors</string>
 
 	<!--Preferences screen-->
@@ -206,5 +205,6 @@
 	<string name="eur" translatable="false">€</string>
 	<string name="Kb" translatable="false"> Kb</string>
 	<string name="Mb" translatable="false"> Mb</string>
-	
+	<string name="pop_menu_about">Sobre nosotros</string>
+
 </resources>

+ 1 - 1
build.gradle

@@ -4,7 +4,7 @@ buildscript {
         jcenter()
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:2.1.2'
+        classpath 'com.android.tools.build:gradle:2.2.1'
     }
 }
 

+ 526 - 1
build/intermediates/dex-cache/cache.xml

@@ -1,2 +1,527 @@
 <?xml version="1.0" encoding="utf-8"?>
-<items version="2" />
+<items version="2" >
+
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.android.gms/play-services-base/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="af7582c6b327449c6d32518e6a10307860b7b5d6">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.android.gms-play-services-base-9.6.1_7ac195096f4e0ada520db3c53d8ae9864e1c8ba2.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.firebase/firebase-iid/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="1e14ebe1cda8491f152083e752ca5bf73cd37bff">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.firebase-firebase-iid-9.6.1_403ea5a61be49e21332f3462c234008ca60f6336.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.firebase/firebase-storage-common/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="159494eb362396d073d8c922b77e40c0da5fc5bc">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.firebase-firebase-storage-common-9.6.1_48af8689644cd6ecb17fc824c2419f055cba7102.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.firebase/firebase-config/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="cf2c4e6607c77b0e671dc5ca252c5ea0d9c1ce0d">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.firebase-firebase-config-9.6.1_db8ba77b89d7aa440ac0686170139e28f09de65e.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.android.gms/play-services-cast/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="a136366045678579300d161de97c2211c4c4f7a0">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.android.gms-play-services-cast-9.6.1_e6cebcfd61f8db0e1de857ed62e76f8cc4074fbf.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.android.gms/play-services-iid/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="0dfc12e4aacf514346ea866560c99807d032bc48">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.android.gms-play-services-iid-9.6.1_7014a27f57ed36da6bde443bc509f94d66ac5992.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.android.gms/play-services-safetynet/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="c828add497d2b7a3de7c047bf72050cd4b7521a1">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.android.gms-play-services-safetynet-9.6.1_4678d98ae5dccc9db0ffa35bc61cb0e5973b2165.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.android.gms/play-services-gass/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="ee61ace5f3e488fc610e1c67693c49186a441f32">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.android.gms-play-services-gass-9.6.1_896c4af60e1d386beaddc6f5875a8ade673aa21e.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.firebase/firebase-common/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="1ef72e72bc0d05602c366582afa9439f58bb31d6">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.firebase-firebase-common-9.6.1_1ee9d93b6a5bef16c77f067d004083613ebe5687.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.android.gms/play-services-basement/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="eccaaa9ffa6bb4bfbd2e870f01aaf69655d353be">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.android.gms-play-services-basement-9.6.1_3b563553288b9611099ebf3986f0aa6217a54dd2.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.android.gms/play-services-drive/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="f8db47be01cbe8cfa81e9cecce5ff1371ed1ad95">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.android.gms-play-services-drive-9.6.1_439b5527f332d0fde03f43ed22ef4715c0997ebf.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.android.gms/play-services-identity/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="518adc52081d41724932b41bc132e4d50a352b86">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.android.gms-play-services-identity-9.6.1_8dd6e8ed2658f860e3443c0a2cc4c83ba83d7f8a.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.android.gms/play-services-cast-framework/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="9c0cf3d4f9d7d076f301e110b92e18ae23da12c2">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.android.gms-play-services-cast-framework-9.6.1_e3b8b0d2f2f44a2811f6ff98a35fe11a83e590d2.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.android.support/mediarouter-v7/24.0.0/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="b327fbe2fad414bc38cf0ce8f7efe1a38f572837">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.android.support-mediarouter-v7-24.0.0_37111cf3f37cb25b45cafcbf20b7d59ebc35f8b4.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.android.gms/play-services-fitness/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="a76a6983a23788b3689524d4bed763955afbd306">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.android.gms-play-services-fitness-9.6.1_94a53ea85de76064035a935ef964dffc16dc8411.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.android.gms/play-services-awareness/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="90be14317e3f26d18438e8dbf8544500d1dfe07f">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.android.gms-play-services-awareness-9.6.1_13b3809e1cbc9236685716ab1101442909c4a61b.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.android.gms/play-services-plus/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="8ebae43cd06058e99e08c8b4d305b72f2b9927c6">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.android.gms-play-services-plus-9.6.1_74efde925ed72f28ae8a6c63e854c6f453803ba2.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.android.gms/play-services-nearby/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="f6aa7c76cbaa867e05dbad0afab1ef0e65c4e55f">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.android.gms-play-services-nearby-9.6.1_3ab37a921fabd80c6d6d8d25526c87e9cecacffb.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.android.gms/play-services-vision/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="0cae6511c5705869cacf199f8abb0ea84cd10157">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.android.gms-play-services-vision-9.6.1_7046c35ff7fab1822f646ca381c7c432487957ce.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.android.support/animated-vector-drawable/24.0.0/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="e70f608219ed6b974644c333249ab2bb7b71de98">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.android.support-animated-vector-drawable-24.0.0_0432bfa53eb43dda18ef4e2a2522d2908732ec51.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.android.gms/play-services-instantapps/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="babb25794a7599069d34705038744c5760a9a88c">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.android.gms-play-services-instantapps-9.6.1_502472f88bac34da85fdaac903a4792fd6880b0c.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.android.gms/play-services-ads-lite/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="6ff796a3173787afe2e712f3aec6f5b99796118e">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.android.gms-play-services-ads-lite-9.6.1_8ac9236c48c6caefc7f159eadfb1127b136cefaf.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.android.gms/play-services-clearcut/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="ee0ccb43f6ab276191b7f2d77c305fa9beb26db9">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.android.gms-play-services-clearcut-9.6.1_150290d780b3cbce95dff4c78f1ef8ad67452090.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.firebase/firebase-auth/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="2a18547ea91d258866d132c2a763185f15b01b25">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.firebase-firebase-auth-9.6.1_b65a93693af9823e5d71520a6b5252c45e96537c.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.android.gms/play-services-places/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="cdddf21e0a8205e9955661061d1735ed10e44a7b">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.android.gms-play-services-places-9.6.1_ae576b6d11930d9721dce2ad8c83a0665d9b2bfd.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.firebase/firebase-crash/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="4df40545c9b875855f3dd9d4928e5d07f07c20ba">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.firebase-firebase-crash-9.6.1_30cc233294b5b71e088083f80812d190a960a390.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.android.gms/play-services-ads/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="47bbb3ec4b31ba71e125241c090dabe70cbb4de7">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.android.gms-play-services-ads-9.6.1_cb8fcbdf4447e783b359533368a55cff32d7fcf4.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.android.gms/play-services-auth-base/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="465f40f840e583f8b7421a3aacf3fbf08c28f00a">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.android.gms-play-services-auth-base-9.6.1_7c13c2c00b4257fd63f3da661fa647661d12adf5.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.android.gms/play-services-auth/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="da3f763afd39da1c947d9442847c837e9182a09f">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.android.gms-play-services-auth-9.6.1_7b0956e5298c0704efcdce87ed69e78870b69577.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.android.gms/play-services-analytics/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="526f4b02f1771fa7607ff1b89fe2e9bd0344fee8">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.android.gms-play-services-analytics-9.6.1_7a7d4736db50f805b1dff41d472dae36c85fa415.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.android.gms/play-services-appinvite/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="0e0e0681c26a1f575d3e4c542182dd59bab75fd6">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.android.gms-play-services-appinvite-9.6.1_44fde3350ae98e4b6b4e01b9a98c6ebd08618756.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.android.support/appcompat-v7/24.0.0/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="0874286cc2fd88d54fdf130e9d5372730da6de97">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.android.support-appcompat-v7-24.0.0_fffe2f4e617ed3b8b602d243b14e5bfddbc035fe.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.android.gms/play-services-gcm/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="ad7706f8bb4a985656a8d2142f74021c8b0e03d8">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.android.gms-play-services-gcm-9.6.1_0cd90341675e204663d8aded708c25b798e726cd.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.android.support/mediarouter-v7/24.0.0/jars/libs/internal_impl-24.0.0.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="b6763651ec29b4c95e7bbe63fc393ffcc8c4c2c5">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/internal_impl-24.0.0_9e4eec54accb1025979d2d84a5828ba0a5fa0f7e.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.android.gms/play-services-tagmanager-api/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="f0e96478ca3147bc588aadb9ae467d85b42473cc">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.android.gms-play-services-tagmanager-api-9.6.1_3a3f426ee25c2910c6847e28a26e06c92b412979.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.android.support/palette-v7/24.0.0/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="734e80dffea7bd76c18ae256822a124c30d85850">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.android.support-palette-v7-24.0.0_1eb42a962599c03e9301e4593d9d3e0752764a5e.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.android.support/support-v4/24.0.0/jars/libs/internal_impl-24.0.0.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="fcaf463e10e69c1923ccf1edeba3b4196d239e00">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/internal_impl-24.0.0_0bfa3fe36cec8d9b2d7905ab662ceeb5a4a0e58a.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.android.gms/play-services-games/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="d45c048bb1c55cf963899190e507065aea8e5be9">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.android.gms-play-services-games-9.6.1_7187e617d56a281306570d28a8f3f1fc56d3bceb.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.android.gms/play-services-analytics-impl/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="a53ac0560ab915a02c50d2e5c7b003381e6fb5e3">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.android.gms-play-services-analytics-impl-9.6.1_0bf634eb6b12cfc69346189837582a52fcd9a023.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.firebase/firebase-auth-module/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="6ffe8b28ab61e5ee92ab9431a220379790c71ddf">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.firebase-firebase-auth-module-9.6.1_77dbaa8ee2121c4b96ecf141481f5174812c95b1.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.firebase/firebase-analytics/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="a1c351dc10b92d638da57f3a6513f87cfbdc9f83">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.firebase-firebase-analytics-9.6.1_23e650ce0aab172a687385979e89845d745beb83.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Programs/Android/android-sdk-linux/extras/android/m2repository/com/android/support/support-annotations/24.0.0/support-annotations-24.0.0.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="2943c74db6d4346ab8c6531adac91fa05c137c40">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/support-annotations-24.0.0_a307c3e70da68a9f44492c93047893d0b55d1b75.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.android.gms/play-services-appindexing/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="cc39b4bcc9e7062891db90d5a752fa9e4986f447">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.android.gms-play-services-appindexing-9.6.1_598e839e48d8dd58401117fe429593be893f77b9.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.firebase/firebase-database/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="b75ecdd04a37d31b4a0da2729fc5c558962a529b">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.firebase-firebase-database-9.6.1_d0ea3856c392787d15aac4247ee2e2ba824b6feb.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.firebase/firebase-storage/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="8c6ef5bd4072f8cda2b89d8356ea7d8acd0683aa">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.firebase-firebase-storage-9.6.1_130d84515be5e8380c6231d1accd13b45ca98716.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.firebase/firebase-database-connection/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="ac8365c87591065db07f7bac8551504504832cd4">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.firebase-firebase-database-connection-9.6.1_8dfb11044e39d6aeeee7f957383e98ef73f294b4.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.android.gms/play-services-tagmanager/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="1810ae0df0a1b9b539ce97df54e96f2bf8f5ff94">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.android.gms-play-services-tagmanager-9.6.1_4ebb81133a0d5b4b538b68858350108b02f5f790.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.android.gms/play-services-panorama/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="ff9be0a1e75a21c372735930d8987a55b5e6065f">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.android.gms-play-services-panorama-9.6.1_edbce62deb7f068efdf61b8b2fb432bfd6340029.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.android.support/support-v4/24.0.0/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="2f7e6e2dad1bccaf68a8820820adc6c0d5b96d37">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.android.support-support-v4-24.0.0_82200ff615466707c1068dc2de15d0e06b5f7532.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.firebase/firebase-messaging/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="80dd8bea08bc5fe10b10a3243b8279c856ccc690">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.firebase-firebase-messaging-9.6.1_ca9035fcae6e09c2bc3167ed7844256ed56f010c.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.android.gms/play-services-wearable/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="c4394d46152165cd3256fb711ac94f66a28fbae1">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.android.gms-play-services-wearable-9.6.1_032dcfc4a8f685fde6ebb96ace6ff7e6aefad27f.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.firebase/firebase-auth-common/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="77558e74bb2056ef38516296f13a27c4c4fc3e5c">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.firebase-firebase-auth-common-9.6.1_eeb25c700a88f601d9a935cdc3c0d9a2ea2720b4.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.android.gms/play-services-location/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="79f521967ad7f49b34748db89df02852c2b2f87b">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.android.gms-play-services-location-9.6.1_4dd398850bcc58da034d2a7115aef034f34715ee.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.android.support/support-vector-drawable/24.0.0/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="3bd97cfefd783e9de1dd1c30574a6adb44b4409b">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.android.support-support-vector-drawable-24.0.0_30a210962b76daf110f6c248d758ff76af36c91f.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.android.gms/play-services-tasks/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="4a36600281a648769411ef642c331851732bf29e">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.android.gms-play-services-tasks-9.6.1_e5da4cbad7106508e70f48ab1c58b1bd89f9d339.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.android.gms/play-services-maps/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="62ff84124b89177abc0c27f3e38c9732f2d41617">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.android.gms-play-services-maps-9.6.1_7d3d201dd83655f1e56d50051050b9fc7499d5e7.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.android.gms/play-services-wallet/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="dfcba20b85ef3669a8ced0a8760a6df281cbd7c7">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.android.gms-play-services-wallet-9.6.1_e14df77cbd735d7b7b5220855532ace3c5be2c47.jar" />
+    </item>
+    <item
+        is-multidex="false"
+        jar="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/exploded-aar/com.google.firebase/firebase-analytics-impl/9.6.1/jars/classes.jar"
+        jumboMode="false"
+        optimize="true"
+        revision="23.0.3"
+        sha1="0efd1b44f47c134fb245914a1d54a55adbfc2f8d">
+        <dex dex="/home/seavenois/Workspace/Programming/GM_APP/app/build/intermediates/pre-dexed/debug/com.google.firebase-firebase-analytics-impl-9.6.1_53934db2b49616a01755c1a794694d08424aa1a4.jar" />
+    </item>
+
+</items>

+ 4 - 0
gradle.properties

@@ -0,0 +1,4 @@
+org.gradle.daemon=true
+org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512M -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+org.gradle.parallel=true
+org.gradle.configureondemand=true

+ 2 - 2
gradle/wrapper/gradle-wrapper.properties

@@ -1,6 +1,6 @@
-#Mon Dec 28 10:00:20 PST 2015
+#Sun Oct 09 00:49:43 CEST 2016
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip