diff --git a/app/build.gradle b/app/build.gradle
index 119e145..767da27 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -5,7 +5,7 @@ android {
buildToolsVersion "25.0.3"
defaultConfig {
applicationId "de.trac.spherical"
- minSdkVersion 15
+ minSdkVersion 16
targetSdkVersion 25
versionCode 1
versionName "1.0"
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index cf3a456..c15743d 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -5,7 +5,7 @@
{
+
+ private MainActivity mainActivity;
+
+ public HandleImageTask(MainActivity mainActivity) {
+ this.mainActivity = mainActivity;
+ }
+
+ @Override
+ protected Void doInBackground(Intent... params) {
+ mainActivity.handleSentImageIntent(params[0]);
+ return null;
+ }
+}
diff --git a/app/src/main/java/de/trac/spherical/MainActivity.java b/app/src/main/java/de/trac/spherical/MainActivity.java
index bc7d659..a30c53e 100644
--- a/app/src/main/java/de/trac/spherical/MainActivity.java
+++ b/app/src/main/java/de/trac/spherical/MainActivity.java
@@ -1,11 +1,15 @@
package de.trac.spherical;
+import android.Manifest;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.graphics.BitmapFactory;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
-import android.support.design.widget.AppBarLayout;
import android.support.design.widget.FloatingActionButton;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
@@ -15,7 +19,10 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
import android.widget.Toast;
import java.io.FileNotFoundException;
@@ -34,11 +41,14 @@ public class MainActivity extends AppCompatActivity {
public static final String MIME_PHOTO_SPHERE = "application/vnd.google.panorama360+jpg";
public static final String MIME_IMAGE = "image/*";
+ private static final int PERMISSION_REQUEST_READ_EXTERNAL_STORAGE = 387;
+
private SphereSurfaceView surfaceView;
private Renderer renderer;
private FloatingActionButton fab;
private Toolbar toolbar;
- private AppBarLayout appBarLayout;
+
+ private Intent cachedIntent;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -49,10 +59,9 @@ public class MainActivity extends AppCompatActivity {
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayShowTitleEnabled(false);
- appBarLayout = (AppBarLayout) findViewById(R.id.lay_toolbar);
- AppBarLayout.LayoutParams lp = (AppBarLayout.LayoutParams) toolbar.getLayoutParams();
+ RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) toolbar.getLayoutParams();
lp.topMargin += getStatusBarHeight();
- appBarLayout.bringToFront();
+ toolbar.bringToFront();
fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
@@ -62,6 +71,11 @@ public class MainActivity extends AppCompatActivity {
}
});
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ Window w = getWindow();
+ w.setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
+ }
+
// Initialize renderer and setup surface view.
LinearLayout container = (LinearLayout) findViewById(R.id.container);
surfaceView = new SphereSurfaceView(this);
@@ -85,27 +99,59 @@ public class MainActivity extends AppCompatActivity {
}
});
- Intent intent = getIntent();
+ handleIntent(getIntent());
+ }
+
+ private void handleIntent(Intent intent) {
switch (intent.getAction()) {
//Image was sent into the app
case Intent.ACTION_SEND:
- handleSentImageIntent(intent);
+ checkPermissionAndHandleSentImage(intent);
break;
//App was launched via launcher icon
//TODO: Remove later together with launcher intent filter
default:
- Toast.makeText(this, R.string.prompt_share_image, Toast.LENGTH_LONG).show();
+ Toast.makeText(this, R.string.toast_prompt_share_image, Toast.LENGTH_LONG).show();
+ }
+ }
+
+ private void checkPermissionAndHandleSentImage(Intent intent) {
+ int status = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE);
+ if (status == PackageManager.PERMISSION_GRANTED) {
+ new HandleImageTask(this).doInBackground(intent);
+ }
+
+ // Cache intent and request permission
+ this.cachedIntent = intent;
+ ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
+ PERMISSION_REQUEST_READ_EXTERNAL_STORAGE);
+
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode,
+ String permissions[], int[] grantResults) {
+ switch (requestCode) {
+ case PERMISSION_REQUEST_READ_EXTERNAL_STORAGE: {
+ // If request is cancelled, the result arrays are empty.
+ if (grantResults.length > 0
+ && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ new HandleImageTask(this).doInBackground(cachedIntent);
+ } else {
+ Toast.makeText(this, R.string.toast_missing_permission, Toast.LENGTH_LONG).show();
+ }
+ }
}
}
private void displayUI(boolean display) {
if (display) {
fab.show();
- appBarLayout.setExpanded(true, true);
+ toolbar.setVisibility(View.VISIBLE);
} else {
fab.setVisibility(View.INVISIBLE);
- appBarLayout.setExpanded(false, true);
+ toolbar.setVisibility(View.GONE);
}
}
@@ -126,7 +172,7 @@ public class MainActivity extends AppCompatActivity {
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_force_sphere:
- Toast.makeText(this, R.string.not_yet_implemented, Toast.LENGTH_SHORT).show();
+ Toast.makeText(this, R.string.toast_not_yet_implemented, Toast.LENGTH_SHORT).show();
return true;
}
@@ -138,13 +184,13 @@ public class MainActivity extends AppCompatActivity {
* displayed, while images with MIME type image/* are being manually tested using {@link PhotoSphereParser}.
* @param intent incoming intent.
*/
- private void handleSentImageIntent(Intent intent) {
+ void handleSentImageIntent(Intent intent) {
String type = intent.getType();
if (type != null) {
Uri imageUri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
if (imageUri == null) {
- Toast.makeText(this, R.string.file_not_found, Toast.LENGTH_SHORT).show();
+ Toast.makeText(this, R.string.toast_file_not_found, Toast.LENGTH_SHORT).show();
return;
}
@@ -180,9 +226,11 @@ public class MainActivity extends AppCompatActivity {
}
} catch (FileNotFoundException e) {
- e.printStackTrace();
+ Log.e(TAG, "File not found.", e);
+ Toast.makeText(this, R.string.toast_file_not_found, Toast.LENGTH_SHORT).show();
} catch (IOException e) {
- e.printStackTrace();
+ Log.e(TAG, "IOException: ", e);
+ Toast.makeText(this, R.string.toast_io_error, Toast.LENGTH_SHORT).show();
}
}
@@ -205,10 +253,10 @@ public class MainActivity extends AppCompatActivity {
} catch (FileNotFoundException e) {
Log.e(TAG, "File not found.", e);
- Toast.makeText(this, R.string.file_not_found, Toast.LENGTH_SHORT).show();
+ Toast.makeText(this, R.string.toast_file_not_found, Toast.LENGTH_SHORT).show();
} catch (IOException e) {
Log.e(TAG, "IOException: ", e);
- Toast.makeText(this, R.string.ioerror, Toast.LENGTH_SHORT).show();
+ Toast.makeText(this, R.string.toast_io_error, Toast.LENGTH_SHORT).show();
}
}
@@ -223,6 +271,7 @@ public class MainActivity extends AppCompatActivity {
*/
private void displayFlatImage(InputStream inputStream) {
Log.d(TAG, "Display Flat Image!");
+ displayPhotoSphere(inputStream, new PhotoSphereMetadata());
}
public int getStatusBarHeight() {
diff --git a/app/src/main/java/de/trac/spherical/parser/PhotoSphereParser.java b/app/src/main/java/de/trac/spherical/parser/PhotoSphereParser.java
index 79c529a..4e0fa78 100644
--- a/app/src/main/java/de/trac/spherical/parser/PhotoSphereParser.java
+++ b/app/src/main/java/de/trac/spherical/parser/PhotoSphereParser.java
@@ -169,32 +169,45 @@ public class PhotoSphereParser {
private static Integer parseInteger(String key, String xmp, Integer defaultValue) {
String value = parseString(key, xmp);
- return value == null ? defaultValue : Integer.parseInt(value);
+ if (value == null) {
+ return defaultValue;
+ }
+ return Integer.parseInt(value);
+
}
private static Float parseFloat(String key, String xmp, Float defaultValue) {
String value = parseString(key, xmp);
if (value == null) {
return defaultValue;
- } else {
- return Float.parseFloat(value);
}
+ return Float.parseFloat(value);
+
}
private static Boolean parseBoolean(String key, String xmp, Boolean defaultValue) {
String value = parseString(key, xmp);
- return value == null ? defaultValue : Boolean.parseBoolean(value);
+ if (value == null) {
+ return defaultValue;
+ }
+ return Boolean.parseBoolean(value);
}
private static ProjectionType parseType(String key, String xmp, ProjectionType defaultValue) {
String value = parseString(key, xmp);
- return value == null ? defaultValue : ProjectionType.equirectangular;
+ if (value == null) {
+ return defaultValue;
+ }
+ return ProjectionType.equirectangular;
}
private static Date parseDate(String key, String xmp, Date defaultValue) {
String value = parseString(key, xmp);
try {
- return value == null ? defaultValue : dateFormat.parse(value);
+ if (value == null) {
+ return defaultValue;
+ }
+ return dateFormat.parse(value);
} catch (ParseException e) {
return defaultValue;
}
diff --git a/app/src/main/java/de/trac/spherical/rendering/Renderer.java b/app/src/main/java/de/trac/spherical/rendering/Renderer.java
index 7764237..23eaccb 100644
--- a/app/src/main/java/de/trac/spherical/rendering/Renderer.java
+++ b/app/src/main/java/de/trac/spherical/rendering/Renderer.java
@@ -10,8 +10,55 @@ import android.util.Log;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
-import static android.opengl.GLES20.*;
-import static android.opengl.GLUtils.getEGLErrorString;
+import static android.opengl.GLES20.GL_COLOR_BUFFER_BIT;
+import static android.opengl.GLES20.GL_COMPILE_STATUS;
+import static android.opengl.GLES20.GL_CULL_FACE;
+import static android.opengl.GLES20.GL_CW;
+import static android.opengl.GLES20.GL_DEPTH_BUFFER_BIT;
+import static android.opengl.GLES20.GL_DEPTH_TEST;
+import static android.opengl.GLES20.GL_FLOAT;
+import static android.opengl.GLES20.GL_FRAGMENT_SHADER;
+import static android.opengl.GLES20.GL_LINEAR;
+import static android.opengl.GLES20.GL_LINK_STATUS;
+import static android.opengl.GLES20.GL_NEAREST;
+import static android.opengl.GLES20.GL_TEXTURE0;
+import static android.opengl.GLES20.GL_TEXTURE_2D;
+import static android.opengl.GLES20.GL_TEXTURE_MAG_FILTER;
+import static android.opengl.GLES20.GL_TEXTURE_MIN_FILTER;
+import static android.opengl.GLES20.GL_TRIANGLES;
+import static android.opengl.GLES20.GL_TRUE;
+import static android.opengl.GLES20.GL_UNSIGNED_SHORT;
+import static android.opengl.GLES20.GL_VERTEX_SHADER;
+import static android.opengl.GLES20.glActiveTexture;
+import static android.opengl.GLES20.glAttachShader;
+import static android.opengl.GLES20.glBindTexture;
+import static android.opengl.GLES20.glClear;
+import static android.opengl.GLES20.glClearColor;
+import static android.opengl.GLES20.glCompileShader;
+import static android.opengl.GLES20.glCreateProgram;
+import static android.opengl.GLES20.glCreateShader;
+import static android.opengl.GLES20.glDeleteProgram;
+import static android.opengl.GLES20.glDeleteShader;
+import static android.opengl.GLES20.glDisableVertexAttribArray;
+import static android.opengl.GLES20.glDrawElements;
+import static android.opengl.GLES20.glEnable;
+import static android.opengl.GLES20.glEnableVertexAttribArray;
+import static android.opengl.GLES20.glFrontFace;
+import static android.opengl.GLES20.glGenTextures;
+import static android.opengl.GLES20.glGetAttribLocation;
+import static android.opengl.GLES20.glGetProgramInfoLog;
+import static android.opengl.GLES20.glGetProgramiv;
+import static android.opengl.GLES20.glGetShaderInfoLog;
+import static android.opengl.GLES20.glGetShaderiv;
+import static android.opengl.GLES20.glGetUniformLocation;
+import static android.opengl.GLES20.glLinkProgram;
+import static android.opengl.GLES20.glShaderSource;
+import static android.opengl.GLES20.glTexParameteri;
+import static android.opengl.GLES20.glUniform1i;
+import static android.opengl.GLES20.glUniformMatrix4fv;
+import static android.opengl.GLES20.glUseProgram;
+import static android.opengl.GLES20.glVertexAttribPointer;
+import static android.opengl.GLES20.glViewport;
public class Renderer implements GLSurfaceView.Renderer {
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index bacd066..ea61a5c 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -1,5 +1,5 @@
-
-
-
-
-
-
+ android:layout_alignParentTop="true"
+ app:layout_scrollFlags="scroll|enterAlways|snap"
+ app:theme="@style/AppTheme.ActionBar"
+ app:popupTheme="@style/ThemeOverlay.AppCompat" />
-
+
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
new file mode 100644
index 0000000..0755aec
--- /dev/null
+++ b/app/src/main/res/values-de/strings.xml
@@ -0,0 +1,9 @@
+
+
+ Erzwinge Kugelansicht
+ Datei nicht gefunden.
+ Ein Fehler ist aufgetreten: IO-Error.
+ Foto kann nicht angezeigt werden: Fehlende Berechtigung für externen Speicher.
+ Noch nicht implementiert!
+ Teile ein Bild mit der App!
+
\ No newline at end of file
diff --git a/app/src/main/res/values-v21/styles.xml b/app/src/main/res/values-v21/styles.xml
new file mode 100644
index 0000000..aaef456
--- /dev/null
+++ b/app/src/main/res/values-v21/styles.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index fefa961..3f60415 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,9 +1,9 @@
- Spherical
- Share an image with the app!
- File not found.
- An Error has occurred: IOError.
- WOW! SUCH 3D!!!
+ Spherical
+ Share an image with the app!
+ File not found.
+ An Error has occurred: IO-Error.
Show as Sphere
- Not yet implemented!
+ Not yet implemented!
+ Cannot display photo: Missing permissions to access external storage.