├── .classpath
├── .gitignore
├── .project
├── AndroidManifest.xml
├── README.md
├── assets
└── testtex.png
├── gen
└── uk
│ └── co
│ └── halfninja
│ └── wallpaper
│ └── parallax
│ └── R.java
├── lib
└── GLWallpaperService.jar
├── project.properties
├── raw
└── icon.svg
├── res
├── drawable
│ └── icon.png
├── layout
│ └── settings.xml
├── values
│ └── strings.xml
└── xml
│ └── wallpaper.xml
├── src
└── uk
│ └── co
│ └── halfninja
│ └── wallpaper
│ └── parallax
│ ├── ParallaxWallpaper.java
│ ├── ParallaxWallpaperRenderer.java
│ ├── ParallaxWallpaperSettings.java
│ └── gl
│ ├── BitmapUtils.java
│ ├── Capabilities.java
│ ├── Quad.java
│ ├── Texture.java
│ ├── TextureLoader.java
│ └── Utils.java
└── tests
├── .classpath
├── .project
├── AndroidManifest.xml
├── gen
└── uk
│ └── co
│ └── halfninja
│ └── wallpaper
│ └── parallax
│ └── test
│ └── R.java
├── proguard.cfg
├── project.properties
├── res
├── drawable-hdpi
│ └── ic_launcher.png
├── drawable-ldpi
│ └── ic_launcher.png
├── drawable-mdpi
│ └── ic_launcher.png
├── layout
│ └── main.xml
└── values
│ └── strings.xml
└── src
└── uk
└── co
└── halfninja
└── wallpaper
└── parallax
└── test
├── BitmapUtilsTest.java
└── ParallaxWallpaperTest.java
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | bin
2 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | ParallaxWallpaper
4 |
5 |
6 |
7 |
8 |
9 | com.android.ide.eclipse.adt.ResourceManagerBuilder
10 |
11 |
12 |
13 |
14 | com.android.ide.eclipse.adt.PreCompilerBuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.jdt.core.javabuilder
20 |
21 |
22 |
23 |
24 | com.android.ide.eclipse.adt.ApkBuilder
25 |
26 |
27 |
28 |
29 |
30 | com.android.ide.eclipse.adt.AndroidNature
31 | org.eclipse.jdt.core.javanature
32 |
33 |
34 |
--------------------------------------------------------------------------------
/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
12 |
13 |
14 |
18 |
19 |
20 |
21 |
22 |
23 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Inactive project
2 |
3 | Feel free to make use of this code in any way you like, but I haven't looked at it in years and it probably doesn't work very well and will not be able to provide support for you, especially over Twitter. Have fun!
4 |
5 | # Parallax live wallpapers
6 |
7 | This is a basic Android app that provides customisable multi-layer wallpapers.
8 | Android wallpapers already have a parallax effect with the icons and the wallpaper
9 | scrolling at different rates as you swipe left and right. The main difference here
10 | is that you can have multiple images all moving at different speeds.
11 | You provide a series of images of varying widths (usually on your SD card),
12 | select them, and they will be displayed together. All the images except the back one
13 | need to have some amount of transparency or else you won't be able to see what's behind
14 | them, so they will probably be 32-bit PNG files.
15 |
16 | ## Potential improvements
17 |
18 | * Save previously used layer sets for easy re-use
19 | * Support layers packed into a single file
20 | * An online gallery to choose some nice ready made sets
21 |
22 | Done:
23 |
24 | * OpenGL acceleration
25 | * A more sensible settings screen
26 | * A proper thumbnail icon
27 |
--------------------------------------------------------------------------------
/assets/testtex.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/halfninja/android-parallax-wallpaper/c7b76b36936a98288a9fb299c437d3633c127ee4/assets/testtex.png
--------------------------------------------------------------------------------
/gen/uk/co/halfninja/wallpaper/parallax/R.java:
--------------------------------------------------------------------------------
1 | /* AUTO-GENERATED FILE. DO NOT MODIFY.
2 | *
3 | * This class was automatically generated by the
4 | * aapt tool from the resource data it found. It
5 | * should not be modified by hand.
6 | */
7 |
8 | package uk.co.halfninja.wallpaper.parallax;
9 |
10 | public final class R {
11 | public static final class attr {
12 | }
13 | public static final class drawable {
14 | public static final int icon=0x7f020000;
15 | }
16 | public static final class id {
17 | public static final int description=0x7f060001;
18 | public static final int description2=0x7f060005;
19 | public static final int description3=0x7f060002;
20 | public static final int selectLayersButton=0x7f060004;
21 | public static final int selectedImage=0x7f060003;
22 | public static final int title=0x7f060000;
23 | }
24 | public static final class layout {
25 | public static final int settings=0x7f030000;
26 | }
27 | public static final class string {
28 | /** Application name
29 | */
30 | public static final int app_name=0x7f050000;
31 | public static final int no_image_picker_alert_message=0x7f050008;
32 | public static final int no_image_picker_alert_title=0x7f050007;
33 | public static final int settings_choose_button=0x7f050006;
34 | public static final int settings_description=0x7f050003;
35 | public static final int settings_description2=0x7f050004;
36 | public static final int settings_description3=0x7f050005;
37 | public static final int settings_title=0x7f050002;
38 | public static final int wallpaper_settings=0x7f050001;
39 | }
40 | public static final class xml {
41 | public static final int wallpaper=0x7f040000;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/lib/GLWallpaperService.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/halfninja/android-parallax-wallpaper/c7b76b36936a98288a9fb299c437d3633c127ee4/lib/GLWallpaperService.jar
--------------------------------------------------------------------------------
/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system use,
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 |
10 | # Project target.
11 | target=android-7
12 |
--------------------------------------------------------------------------------
/raw/icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
164 |
--------------------------------------------------------------------------------
/res/drawable/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/halfninja/android-parallax-wallpaper/c7b76b36936a98288a9fb299c437d3633c127ee4/res/drawable/icon.png
--------------------------------------------------------------------------------
/res/layout/settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
10 |
11 |
18 |
24 |
25 |
26 |
33 |
34 |
42 |
51 |
52 |
53 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Parallax Wallpapers
7 |
8 | Parallax Wallpaper Settings
9 |
10 | Pick your layers
11 | Your layer files should have the same name except for a number
12 | at the end of the name, starting with 1 for the topmost layer.
13 | Tip: The front layers should be wider to make them scroll faster.
14 |
15 | Example:\n
16 | mybackground1.png\n
17 | mybackground2.png\n
18 | mybackground3.png
19 |
20 |
21 | Pick one of the layer files, and I\'ll try to find the rest of them.
22 |
23 | Pick an image
24 | Embarassing…
25 |
26 | Your device doesn\'t seem to support any
27 | activity for picking an image, which is slightly odd
28 | as most should come with some sort of gallery.
29 | Try downloading File Manager from the Market.
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/res/xml/wallpaper.xml:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 |
21 |
22 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/src/uk/co/halfninja/wallpaper/parallax/ParallaxWallpaper.java:
--------------------------------------------------------------------------------
1 | package uk.co.halfninja.wallpaper.parallax;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.util.ArrayList;
6 | import java.util.List;
7 | import java.util.regex.Matcher;
8 | import java.util.regex.Pattern;
9 |
10 | import net.rbgrn.android.glwallpaperservice.GLWallpaperService;
11 | import android.content.SharedPreferences;
12 | import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
13 | import android.util.Log;
14 |
15 | /**
16 | * Draws a wallpaper much like the regular wallpapers which slide behind
17 | * the home screen with a parallax effect. The difference with this wallpaper
18 | * is that it supports multiple images with different widths, for a much more
19 | * intricate-looking parallax effect.
20 | *
21 | * The main thing is that it's configurable, so you can use any set of images.
22 | */
23 | public class ParallaxWallpaper extends GLWallpaperService {
24 |
25 | public static final String SHARED_PREFS_NAME="parallaxwallpaper_settings";
26 | public static final String TAG = "ParallaxWallpaper";
27 |
28 | private SharedPreferences mPrefs;
29 | private ParallaxWallpaperRenderer renderer;
30 |
31 | @Override
32 | public void onCreate() {
33 | super.onCreate();
34 | Log.d(TAG, "Wallpaper created");
35 | }
36 |
37 | @Override
38 | public void onDestroy() {
39 | super.onDestroy();
40 | }
41 |
42 | @Override
43 | public Engine onCreateEngine() {
44 | Log.d(TAG, "Wallpaper engine created");
45 | return new ParallaxEngine();
46 | }
47 | /*
48 | // static class Layer {
49 | // public Bitmap bitmap;
50 | // private float scale = 1.0f;
51 | // private Matrix matrix = new Matrix();
52 | // public Layer(Bitmap b) {
53 | // this.bitmap = b;
54 | // }
55 | // public void setScale(float factor) {
56 | // scale = factor;
57 | // }
58 | // public Matrix getMatrix(float x, float y) {
59 | // if (scale == 1) {
60 | // matrix.reset();
61 | // } else {
62 | // matrix.setScale(scale, scale);
63 | // }
64 | // matrix.postTranslate(x, y);
65 | // return matrix;
66 | // }
67 | // }
68 | */
69 | public static List findLayers(String path) {
70 | List files = new ArrayList();
71 | Pattern p = Pattern.compile("(.+)([1-9]\\d*)(\\..+)");
72 | Matcher matcher = p.matcher(path);
73 | File file = new File(path);
74 | if (file.exists()) {
75 | if (matcher.matches()) {
76 | String prefix = matcher.group(1);
77 | String suffix = matcher.group(3);
78 | for (int i=1; i<9; i++) {
79 | File f = new File(prefix + i + suffix);
80 | if (f.isFile()) {
81 | files.add(f.getAbsolutePath());
82 | } else {
83 | break;
84 | }
85 | }
86 | } else {
87 | Log.i(TAG, "Filename didn't end in a number - just using the one layer.");
88 | files.add(path);
89 | }
90 | }
91 | return files;
92 | }
93 |
94 | class ParallaxEngine extends GLEngine implements OnSharedPreferenceChangeListener {
95 |
96 | ParallaxEngine() {
97 | super();
98 | mPrefs = ParallaxWallpaper.this.getSharedPreferences(SHARED_PREFS_NAME, 0);
99 | renderer = new ParallaxWallpaperRenderer();
100 | renderer.setContext(ParallaxWallpaper.this);
101 | mPrefs.registerOnSharedPreferenceChangeListener(this);
102 | setRenderer(renderer);
103 | setRenderMode(RENDERMODE_WHEN_DIRTY);
104 | onSharedPreferenceChanged(mPrefs, null);
105 | requestRender();
106 | }
107 |
108 | public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
109 | String customPath = prefs.getString(ParallaxWallpaperSettings.CUSTOM_PATH_ACTUAL_KEY, null);
110 | Log.d(TAG, "customPath: " + customPath);
111 | try {
112 | if (customPath != null && renderer != null) {
113 | renderer.setLayerFiles(findLayers(customPath));
114 | renderer.reloadLayers();
115 | renderer.resizeLayers();
116 | }
117 | } catch (IOException e) {
118 | Log.e(TAG, "Error loading textures", e);
119 | }
120 | }
121 |
122 | public void onDestroy() {
123 | super.onDestroy();
124 | if (renderer != null) {
125 | renderer.release();
126 | }
127 | renderer = null;
128 | }
129 |
130 | @Override
131 | public void onOffsetsChanged(float xOffset, float yOffset,
132 | float xStep, float yStep, int xPixels, int yPixels) {
133 | if (renderer != null) {
134 | renderer.setOffset(xOffset);
135 | requestRender();
136 | }
137 | }
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/src/uk/co/halfninja/wallpaper/parallax/ParallaxWallpaperRenderer.java:
--------------------------------------------------------------------------------
1 | package uk.co.halfninja.wallpaper.parallax;
2 |
3 | import java.io.File;
4 | import java.io.FileInputStream;
5 | import java.io.IOException;
6 | import java.io.InputStream;
7 | import java.util.ArrayList;
8 | import java.util.List;
9 |
10 | import javax.microedition.khronos.egl.EGLConfig;
11 | import javax.microedition.khronos.opengles.GL10;
12 |
13 | import uk.co.halfninja.wallpaper.parallax.gl.Capabilities;
14 | import uk.co.halfninja.wallpaper.parallax.gl.Quad;
15 | import uk.co.halfninja.wallpaper.parallax.gl.Texture;
16 | import uk.co.halfninja.wallpaper.parallax.gl.TextureLoader;
17 | import uk.co.halfninja.wallpaper.parallax.gl.Utils;
18 |
19 | import android.content.Context;
20 | import android.graphics.Bitmap;
21 | import android.graphics.BitmapFactory;
22 | import android.util.Log;
23 | import android.widget.Toast;
24 |
25 | import net.rbgrn.android.glwallpaperservice.GLWallpaperService.Renderer;
26 | import static uk.co.halfninja.wallpaper.parallax.ParallaxWallpaper.TAG;
27 | import static javax.microedition.khronos.opengles.GL10.*;
28 |
29 | public final class ParallaxWallpaperRenderer implements Renderer {
30 |
31 | private float offset = 0.0f;
32 | private int height;
33 | private int width;
34 | private boolean visible;
35 | private Context context;
36 |
37 | private Capabilities capabilities = new Capabilities();
38 | private TextureLoader textureLoader = new TextureLoader(capabilities);
39 | // private String bitmapPath;
40 | // private Texture tex;
41 | private List layers = new ArrayList();
42 | private List layerFiles = new ArrayList();
43 |
44 | private GL10 gl;
45 |
46 | // public void setBitmapPath(String bitmapPath) {
47 | // this.bitmapPath = bitmapPath;
48 | // }
49 |
50 | public void setContext(Context context) {
51 | this.context = context;
52 | }
53 |
54 | @Override
55 | public void onDrawFrame(GL10 gl) {
56 | gl.glClearColor(offset, 0.4f, 0.2f, 1f);
57 | gl.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
58 | gl.glColor4f(1f, 1f, 1f, 1f);
59 | for (Quad quad : layers) {
60 | quad.setX(offset * (width-quad.getWidth()));
61 | quad.draw(gl);
62 | }
63 | }
64 |
65 | @Override
66 | public void onSurfaceChanged(GL10 gl, int w, int h) {
67 | width = w;
68 | height = h;
69 | Utils.pixelProjection(gl, w, h);
70 | gl.glEnable(GL_TEXTURE_2D);
71 | gl.glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
72 | gl.glEnable (GL_BLEND);
73 | gl.glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
74 |
75 | resizeLayers();
76 | }
77 |
78 | @Override
79 | public void onSurfaceCreated(GL10 gl, EGLConfig cfg) {
80 | this.gl = gl;
81 | capabilities.reload(gl);
82 |
83 | try {
84 | reloadLayers();
85 | } catch (IOException e) {
86 | Log.e(TAG, "Error loading textures", e);
87 | //Toast.makeText(context, "Error loading layers.", Toast.LENGTH_LONG).show();
88 | }
89 | }
90 |
91 | public void reloadLayers() throws IOException {
92 | if (gl != null) {
93 | layers.clear();
94 | textureLoader.clear(gl);
95 | for (String bitmapPath : layerFiles) {
96 | Quad quad = new Quad();
97 | Texture tex = textureLoader.loadTextureFromFile(gl, bitmapPath);
98 | Log.i(TAG, "Loaded texture " + tex.id);
99 | quad.setTexture(tex);
100 | layers.add(0, quad);
101 | }
102 | }
103 | }
104 |
105 | public void setOffset(float xOffset) {
106 | offset = xOffset;
107 | }
108 |
109 | public void setVisible(boolean visible) {
110 | this.visible = visible;
111 | }
112 |
113 | public void release() {
114 | }
115 |
116 | public void resizeLayers() {
117 | for (Quad quad : layers) {
118 | int bitmapHeight = quad.getTexture().getBitmapHeight();
119 | // Log.d(TAG, "Scaling quad with texture " + quad.getTexture());
120 | float ratio = (float)height/bitmapHeight;
121 | // Log.d(TAG, "Scale ratio " + ratio + " so that width is " + (quad.getTexture().getBitmapWidth() * ratio));
122 | quad.setHeight(height);
123 | quad.setWidth(quad.getTexture().getBitmapWidth() * ratio);
124 | }
125 | }
126 |
127 | public void setLayerFiles(List files) {
128 | layerFiles = files;
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/src/uk/co/halfninja/wallpaper/parallax/ParallaxWallpaperSettings.java:
--------------------------------------------------------------------------------
1 | package uk.co.halfninja.wallpaper.parallax;
2 |
3 | import android.app.Activity;
4 | import android.app.AlertDialog;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.content.SharedPreferences;
8 | import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
9 | import android.database.Cursor;
10 | import android.net.Uri;
11 | import android.os.Bundle;
12 | import android.provider.MediaStore;
13 | import android.text.Html;
14 | import android.util.Log;
15 | import android.view.View;
16 | import android.widget.Button;
17 | import android.widget.TextView;
18 | import static uk.co.halfninja.wallpaper.parallax.ParallaxWallpaper.TAG;
19 |
20 | public class ParallaxWallpaperSettings extends Activity implements OnSharedPreferenceChangeListener {
21 | public static final String CUSTOM_PATH_ACTUAL_KEY = "custom_path_actual";
22 | private static final int REQ_CODE_PICK_IMAGE = 100001;
23 |
24 | private final Context context = this; // for convenience in inner classes
25 |
26 | private SharedPreferences preferences;
27 | private Button pickButton;
28 | private TextView selectedImage;
29 |
30 | public String getLayerPath() {
31 | return preferences.getString(CUSTOM_PATH_ACTUAL_KEY, null);
32 | }
33 |
34 | public void setLayerPath(String path) {
35 | preferences.edit().putString(CUSTOM_PATH_ACTUAL_KEY, path).commit();
36 | }
37 |
38 | protected void onCreate(Bundle savedInstanceState) {
39 | super.onCreate(savedInstanceState);
40 | setContentView(R.layout.settings);
41 |
42 | preferences = getSharedPreferences(ParallaxWallpaper.SHARED_PREFS_NAME, 0);
43 | preferences.registerOnSharedPreferenceChangeListener(this);
44 |
45 | selectedImage = (TextView)findViewById(R.id.selectedImage);
46 | updateSelectedImageUi();
47 |
48 | pickButton = (Button)findViewById(R.id.selectLayersButton);
49 | pickButton.setOnClickListener(new View.OnClickListener() { public void onClick(View v) {
50 | Intent anyFile = new Intent(Intent.ACTION_GET_CONTENT);
51 | anyFile.setType("image/*");
52 | anyFile.addCategory(Intent.CATEGORY_OPENABLE);
53 | if (anyFile.resolveActivityInfo(getPackageManager(), 0) == null) {
54 | new AlertDialog.Builder(context)
55 | .setTitle(R.string.no_image_picker_alert_title)
56 | .setMessage(R.string.no_image_picker_alert_message)
57 | .create().show();
58 | } else {
59 | Intent chooser = Intent.createChooser(anyFile, "Pick the first layer");
60 | startActivityForResult(chooser, REQ_CODE_PICK_IMAGE);
61 | }
62 | }});
63 | }
64 |
65 | private void updateSelectedImageUi() {
66 | String value = getLayerPath();
67 | if (value != null) {
68 | selectedImage.setVisibility(View.VISIBLE);
69 | // TODO i18n
70 | selectedImage.setText(Html.fromHtml("Currently chosen:
" + value +""));
71 | }
72 | }
73 |
74 | private void processNewPath(String filePath) {
75 | Log.d(TAG, "new file path " + filePath);
76 | setLayerPath(filePath);
77 | }
78 |
79 | @Override
80 | public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
81 | Log.d(TAG, "onSharedPreferenceChanged("+key+")");
82 | if (key.equals(CUSTOM_PATH_ACTUAL_KEY)) {
83 | updateSelectedImageUi();
84 | }
85 | }
86 |
87 | @Override
88 | protected void onActivityResult(int requestCode, int resultCode, Intent returnedIntent) {
89 | super.onActivityResult(requestCode, resultCode, returnedIntent);
90 | switch(requestCode) {
91 | case REQ_CODE_PICK_IMAGE:
92 | if(resultCode == RESULT_OK){
93 | Uri selectedImage = returnedIntent.getData();
94 | Log.i(ParallaxWallpaper.TAG, "Picked image " + selectedImage);
95 | String[] filePathColumn = {MediaStore.Images.Media.DATA};
96 |
97 | Cursor cursor = null;
98 | try {
99 | cursor = getContentResolver().query(selectedImage, filePathColumn, null, null, null);
100 | cursor.moveToFirst();
101 | int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
102 | final String filePath = cursor.getString(columnIndex);
103 | processNewPath(filePath);
104 | } finally {
105 | if (cursor != null) cursor.close();
106 | }
107 |
108 | }
109 | break;
110 | }
111 | }
112 |
113 | }
114 |
--------------------------------------------------------------------------------
/src/uk/co/halfninja/wallpaper/parallax/gl/BitmapUtils.java:
--------------------------------------------------------------------------------
1 | package uk.co.halfninja.wallpaper.parallax.gl;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.Bitmap.Config;
5 | import android.graphics.Canvas;
6 | import android.util.Log;
7 |
8 | public class BitmapUtils {
9 |
10 | private static final String TAG = BitmapUtils.class.getName();
11 |
12 | public static Bitmap createPowerOfTwoBitmap(Bitmap bitmap) {
13 | int w = bitmap.getWidth();
14 | int h = bitmap.getHeight();
15 | int w2 = getNextPowerOfTwo(w);
16 | int h2 = getNextPowerOfTwo(h);
17 | if(w == w2 && h == h2) {
18 | // What luck, it's already compatible.
19 | return bitmap;
20 | } else {
21 | Bitmap biggerBitmap = Bitmap.createBitmap(w2, h2, Config.ARGB_8888);
22 | Canvas canvas = new Canvas(biggerBitmap);
23 | canvas.drawBitmap(bitmap, 0, 0, null);
24 | return biggerBitmap;
25 | }
26 | }
27 |
28 | /**
29 | * Returns the next power of two that is greater than or
30 | * equal to the given value. If the given number is already
31 | * a power of two, that number is returned.
32 | */
33 | public static int getNextPowerOfTwo(int value) {
34 | int result = 1;
35 | value--;
36 | while (value > 0) {
37 | result = result << 1;
38 | value = value >> 1;
39 | }
40 | return result;
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/src/uk/co/halfninja/wallpaper/parallax/gl/Capabilities.java:
--------------------------------------------------------------------------------
1 | package uk.co.halfninja.wallpaper.parallax.gl;
2 |
3 | import javax.microedition.khronos.opengles.GL10;
4 |
5 | import android.util.Log;
6 |
7 | /**
8 | * Calculates various capabilities of your OpenGL implementation and/or
9 | * your hardware.
10 | */
11 | public class Capabilities {
12 | private boolean nonPowerOfTwoTextures;
13 |
14 | public Capabilities() {}
15 |
16 | public Capabilities(GL10 gl) {
17 | reload(gl);
18 | }
19 |
20 | public void reload(GL10 gl) {
21 | String extensions = gl.glGetString(GL10.GL_EXTENSIONS);
22 |
23 | Log.d("GLCapabilities", extensions);
24 |
25 | nonPowerOfTwoTextures = extensions.contains("ARB_texture_non_power_of_two");
26 | }
27 |
28 | public boolean supportsNonPowerOfTwoTextures() {
29 | return nonPowerOfTwoTextures;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/uk/co/halfninja/wallpaper/parallax/gl/Quad.java:
--------------------------------------------------------------------------------
1 | package uk.co.halfninja.wallpaper.parallax.gl;
2 |
3 | import static javax.microedition.khronos.opengles.GL10.GL_FLOAT;
4 | import static javax.microedition.khronos.opengles.GL10.GL_MODELVIEW;
5 | import static javax.microedition.khronos.opengles.GL10.GL_TEXTURE_COORD_ARRAY;
6 | import static javax.microedition.khronos.opengles.GL10.GL_TRIANGLE_STRIP;
7 | import static javax.microedition.khronos.opengles.GL10.GL_VERTEX_ARRAY;
8 |
9 | import java.nio.ByteBuffer;
10 | import java.nio.ByteOrder;
11 | import java.nio.FloatBuffer;
12 | import java.nio.ShortBuffer;
13 |
14 | import javax.microedition.khronos.opengles.GL10;
15 |
16 | /**
17 | * Rectangle for OpenGL. Maintains static buffers, so
18 | * multiple quads will share a buffer.
19 | */
20 | public class Quad {
21 | private static final int SIZEOF_FLOAT = 4;
22 | private static final int SIZEOF_SHORT = 2;
23 | private static FloatBuffer vertexBuffer;
24 | private static ShortBuffer indexBuffer;
25 | private static FloatBuffer defaultTexBuffer; // buffer holding the texture coordinates
26 |
27 | static {
28 | float vertices[] = {
29 | 0f, 0f, // 0, Top Left
30 | 0f, 1f, // 1, Bottom Left
31 | 1f, 0f, // 3, Top Right
32 | 1f, 1f, // 2, Bottom Right
33 | };
34 | ByteBuffer byteBuffer = ByteBuffer.allocateDirect(vertices.length * SIZEOF_FLOAT);
35 | byteBuffer.order(ByteOrder.nativeOrder());
36 | vertexBuffer = byteBuffer.asFloatBuffer();
37 | vertexBuffer.put(vertices);
38 | vertexBuffer.position(0);
39 |
40 | defaultTexBuffer = allocateTextureBuffer(1f, 1f);
41 | }
42 |
43 | /**
44 | * Allocate a texture buffer with the given values replacing 1.
45 | * e.g. if x is 0.5, then the texture will be twice the normal width.
46 | */
47 | private static FloatBuffer allocateTextureBuffer(float u, float v) {
48 | float texturepoints[] = {
49 | 0f, 0f,
50 | 0f, v,
51 | u, 0,
52 | u, v,
53 | };
54 | ByteBuffer byteBuffer = ByteBuffer.allocateDirect(texturepoints.length * SIZEOF_FLOAT);
55 | byteBuffer.order(ByteOrder.nativeOrder());
56 | FloatBuffer texBuffer = byteBuffer.asFloatBuffer();
57 | texBuffer.put(texturepoints);
58 | texBuffer.position(0);
59 | return texBuffer;
60 | }
61 |
62 | private FloatBuffer textureBuffer = defaultTexBuffer;
63 | private Texture texture;
64 | private float x;
65 | private float y;
66 | private float width;
67 | private float height;
68 |
69 | public void draw(GL10 gl) {
70 | gl.glMatrixMode( GL_MODELVIEW );
71 | gl.glLoadIdentity();
72 | gl.glTranslatef(x, y, 0f);
73 | gl.glScalef(width,height,1f);
74 |
75 | gl.glEnableClientState(GL_VERTEX_ARRAY);
76 | gl.glEnable(GL10.GL_TEXTURE_2D);
77 | gl.glVertexPointer(2, GL_FLOAT, 0, vertexBuffer);
78 | if (texture != null) {
79 | gl.glEnableClientState(GL_TEXTURE_COORD_ARRAY);
80 | texture.bind(gl);
81 | gl.glTexCoordPointer(2, GL_FLOAT, 0, textureBuffer);
82 | }
83 | gl.glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
84 | //gl.glDrawElements(GL10.GL_TRIANGLE_STRIP, 4, GL10.GL_UNSIGNED_SHORT, indexBuffer);
85 | gl.glDisableClientState(GL_VERTEX_ARRAY);
86 | if (texture != null) {
87 | gl.glDisableClientState(GL_TEXTURE_COORD_ARRAY);
88 | }
89 | }
90 |
91 | public float getX() {
92 | return x;
93 | }
94 |
95 | public void setX(float x) {
96 | this.x = x;
97 | }
98 |
99 | public float getY() {
100 | return y;
101 | }
102 |
103 | public void setY(float y) {
104 | this.y = y;
105 | }
106 |
107 | public float getWidth() {
108 | return width;
109 | }
110 |
111 | public void setWidth(float width) {
112 | this.width = width;
113 | }
114 |
115 | public float getHeight() {
116 | return height;
117 | }
118 |
119 | public void setHeight(float height) {
120 | this.height = height;
121 | }
122 |
123 | public Texture getTexture() {
124 | return texture;
125 | }
126 |
127 | public void setTexture(Texture texture) {
128 | this.texture = texture;
129 | if (!texture.isMatchesDimensions()) {
130 | textureBuffer = allocateTextureBuffer(texture.getU(), texture.getV());
131 | } else {
132 | textureBuffer = defaultTexBuffer;
133 | }
134 | this.width = texture.getBitmapWidth();
135 | this.height = texture.getBitmapHeight();
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/src/uk/co/halfninja/wallpaper/parallax/gl/Texture.java:
--------------------------------------------------------------------------------
1 | package uk.co.halfninja.wallpaper.parallax.gl;
2 |
3 | import javax.microedition.khronos.opengles.GL10;
4 |
5 | public class Texture {
6 | public final int id;
7 | private int texWidth, texHeight, bitmapWidth, bitmapHeight;
8 | private boolean matchesDimensions;
9 | public Texture(int id) {
10 | this.id = id;
11 | }
12 | public void bind(GL10 gl) {
13 | gl.glBindTexture(GL10.GL_TEXTURE_2D, id);
14 | }
15 | public int getTexWidth() {
16 | return texWidth;
17 | }
18 | public void setTexWidth(int texWidth) {
19 | this.texWidth = texWidth;
20 | }
21 | public int getTexHeight() {
22 | return texHeight;
23 | }
24 | public void setTexHeight(int texHeight) {
25 | this.texHeight = texHeight;
26 | }
27 | public int getBitmapWidth() {
28 | return bitmapWidth;
29 | }
30 | public void setBitmapWidth(int bitmapWidth) {
31 | this.bitmapWidth = bitmapWidth;
32 | }
33 | public int getBitmapHeight() {
34 | return bitmapHeight;
35 | }
36 | public void setBitmapHeight(int bitmapHeight) {
37 | this.bitmapHeight = bitmapHeight;
38 | }
39 | public boolean isMatchesDimensions() {
40 | return bitmapHeight==texHeight && bitmapWidth==texWidth;
41 | }
42 | public float getU() {
43 | if (bitmapWidth==texWidth) return 1f;
44 | return ((float)bitmapWidth/texWidth);
45 | }
46 | public float getV() {
47 | if (bitmapHeight==texHeight) return 1f;
48 | return ((float)bitmapHeight/texHeight);
49 | }
50 | public String toString() {
51 | return String.format("Texture[id=%d,size=(%d,%d),bitmapsize=(%d,%d)",
52 | id, texWidth, texHeight, bitmapWidth, bitmapHeight
53 | );
54 |
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/uk/co/halfninja/wallpaper/parallax/gl/TextureLoader.java:
--------------------------------------------------------------------------------
1 | package uk.co.halfninja.wallpaper.parallax.gl;
2 |
3 | import java.io.IOException;
4 |
5 | import javax.microedition.khronos.opengles.GL10;
6 | import static javax.microedition.khronos.opengles.GL10.*;
7 |
8 | import android.graphics.Bitmap;
9 | import android.graphics.BitmapFactory;
10 | import android.opengl.GLUtils;
11 | import android.util.Log;
12 | import static uk.co.halfninja.wallpaper.parallax.ParallaxWallpaper.TAG;
13 |
14 | public class TextureLoader {
15 | public static final int MAX_TEXTURES = 16;
16 |
17 | private final Capabilities capabilities;
18 |
19 | public TextureLoader(Capabilities capabilities) {
20 | super();
21 | this.capabilities = capabilities;
22 | }
23 |
24 | private int[] textures = new int[MAX_TEXTURES];
25 | private int nextSlot = 0;
26 |
27 | public boolean slotsAvailable() {
28 | return nextSlot < MAX_TEXTURES-1;
29 | }
30 |
31 | public Texture loadTextureFromFile(GL10 gl, String filename) throws IOException {
32 | Bitmap originalBitmap, bitmap;
33 | originalBitmap = bitmap = BitmapFactory.decodeFile(filename);
34 | if (bitmap == null) {
35 | throw new IOException("Couldn't load bitmap " + filename);
36 | }
37 |
38 | Log.d(TAG, "Loaded bitmap: (" + bitmap.getWidth() +","+bitmap.getHeight()+")");
39 |
40 | if (!capabilities.supportsNonPowerOfTwoTextures()) {
41 | bitmap = BitmapUtils.createPowerOfTwoBitmap(bitmap);
42 | }
43 |
44 | Texture t = loadTexture(gl, bitmap);
45 | // in case we expanded the bitmap to fit the canvas,
46 | t.setBitmapWidth(originalBitmap.getWidth());
47 | t.setBitmapHeight(originalBitmap.getHeight());
48 | Log.d(TAG, "Loaded texture from file: " + t.toString());
49 | bitmap.recycle();
50 | originalBitmap.recycle();
51 | return t;
52 | }
53 |
54 | public Texture loadTexture(GL10 gl, Bitmap bitmap) {
55 | // generate one texture pointer
56 | gl.glGenTextures(1, textures, nextSlot);
57 | // ...and bind it to our array
58 | int id = textures[nextSlot];
59 | gl.glBindTexture(GL10.GL_TEXTURE_2D, id);
60 |
61 | // create nearest filtered texture
62 | gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
63 | gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
64 |
65 | // Use Android GLUtils to specify a two-dimensional texture image from our bitmap
66 | GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
67 |
68 | int error = gl.glGetError();
69 | if (error != 0) {
70 | Log.e(TAG, "Error loading GL texture. OpenGL code: " + error);
71 | }
72 |
73 | // Clean up
74 | nextSlot++;
75 | Texture t = new Texture(id);
76 | t.setTexWidth(bitmap.getWidth());
77 | t.setBitmapWidth(bitmap.getWidth());
78 | t.setTexHeight(bitmap.getHeight());
79 | t.setBitmapHeight(bitmap.getHeight());
80 | bitmap.recycle();
81 | return t;
82 | }
83 |
84 | void logError(GL10 gl) {
85 | int error = gl.glGetError();
86 | String msg;
87 | switch (error) {
88 | case GL_INVALID_VALUE: msg="invalid value"; break;
89 | default: msg = ""+error; break;
90 | }
91 | Log.d(TAG, "gl error "+msg);
92 | }
93 |
94 | public void clear(GL10 gl) {
95 | gl.glDeleteTextures(MAX_TEXTURES, textures, 0);
96 | nextSlot = 0;
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/uk/co/halfninja/wallpaper/parallax/gl/Utils.java:
--------------------------------------------------------------------------------
1 | package uk.co.halfninja.wallpaper.parallax.gl;
2 |
3 | import static javax.microedition.khronos.opengles.GL10.GL_PROJECTION;
4 |
5 | import javax.microedition.khronos.opengles.GL10;
6 |
7 | /**
8 | * Ah yes, the classic GL utility class.
9 | */
10 | public final class Utils {
11 |
12 | /**
13 | * Sets an orthographic projection with dimensions matching
14 | * the regular screen pixel coordinates. [0,0] is in the top left
15 | * and [w,h] is in the bottom right of the screen, rather than
16 | * the regular OpenGL convention of the origin being in the
17 | * bottom left.
18 | */
19 | public static void pixelProjection(GL10 gl, int w, int h) {
20 | gl.glMatrixMode(GL_PROJECTION);
21 | gl.glLoadIdentity();
22 | gl.glOrthof(0, w, h, 0, -100, 100);
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/tests/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/tests/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | ParallaxWallpaperTest
4 |
5 |
6 | ParallaxWallpaper
7 |
8 |
9 |
10 | com.android.ide.eclipse.adt.ResourceManagerBuilder
11 |
12 |
13 |
14 |
15 | com.android.ide.eclipse.adt.PreCompilerBuilder
16 |
17 |
18 |
19 |
20 | org.eclipse.jdt.core.javabuilder
21 |
22 |
23 |
24 |
25 | com.android.ide.eclipse.adt.ApkBuilder
26 |
27 |
28 |
29 |
30 |
31 | com.android.ide.eclipse.adt.AndroidNature
32 | org.eclipse.jdt.core.javanature
33 |
34 |
35 |
--------------------------------------------------------------------------------
/tests/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
12 |
13 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/tests/gen/uk/co/halfninja/wallpaper/parallax/test/R.java:
--------------------------------------------------------------------------------
1 | /* AUTO-GENERATED FILE. DO NOT MODIFY.
2 | *
3 | * This class was automatically generated by the
4 | * aapt tool from the resource data it found. It
5 | * should not be modified by hand.
6 | */
7 |
8 | package uk.co.halfninja.wallpaper.parallax.test;
9 |
10 | public final class R {
11 | public static final class attr {
12 | }
13 | public static final class drawable {
14 | public static final int ic_launcher=0x7f020000;
15 | }
16 | public static final class layout {
17 | public static final int main=0x7f030000;
18 | }
19 | public static final class string {
20 | public static final int app_name=0x7f040001;
21 | public static final int hello=0x7f040000;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/tests/proguard.cfg:
--------------------------------------------------------------------------------
1 | -optimizationpasses 5
2 | -dontusemixedcaseclassnames
3 | -dontskipnonpubliclibraryclasses
4 | -dontpreverify
5 | -verbose
6 | -optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
7 |
8 | -keep public class * extends android.app.Activity
9 | -keep public class * extends android.app.Application
10 | -keep public class * extends android.app.Service
11 | -keep public class * extends android.content.BroadcastReceiver
12 | -keep public class * extends android.content.ContentProvider
13 | -keep public class * extends android.app.backup.BackupAgentHelper
14 | -keep public class * extends android.preference.Preference
15 | -keep public class com.android.vending.licensing.ILicensingService
16 |
17 | -keepclasseswithmembernames class * {
18 | native ;
19 | }
20 |
21 | -keepclasseswithmembers class * {
22 | public (android.content.Context, android.util.AttributeSet);
23 | }
24 |
25 | -keepclasseswithmembers class * {
26 | public (android.content.Context, android.util.AttributeSet, int);
27 | }
28 |
29 | -keepclassmembers class * extends android.app.Activity {
30 | public void *(android.view.View);
31 | }
32 |
33 | -keepclassmembers enum * {
34 | public static **[] values();
35 | public static ** valueOf(java.lang.String);
36 | }
37 |
38 | -keep class * implements android.os.Parcelable {
39 | public static final android.os.Parcelable$Creator *;
40 | }
41 |
--------------------------------------------------------------------------------
/tests/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system use,
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 |
10 | # Project target.
11 | target=android-7
12 |
--------------------------------------------------------------------------------
/tests/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/halfninja/android-parallax-wallpaper/c7b76b36936a98288a9fb299c437d3633c127ee4/tests/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/tests/res/drawable-ldpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/halfninja/android-parallax-wallpaper/c7b76b36936a98288a9fb299c437d3633c127ee4/tests/res/drawable-ldpi/ic_launcher.png
--------------------------------------------------------------------------------
/tests/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/halfninja/android-parallax-wallpaper/c7b76b36936a98288a9fb299c437d3633c127ee4/tests/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/tests/res/layout/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
--------------------------------------------------------------------------------
/tests/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Hello World!
5 | ParallaxWallpaperTestTest
6 |
7 |
--------------------------------------------------------------------------------
/tests/src/uk/co/halfninja/wallpaper/parallax/test/BitmapUtilsTest.java:
--------------------------------------------------------------------------------
1 | package uk.co.halfninja.wallpaper.parallax.test;
2 |
3 | import uk.co.halfninja.wallpaper.parallax.gl.BitmapUtils;
4 | import android.test.AndroidTestCase;
5 |
6 | public class BitmapUtilsTest extends AndroidTestCase {
7 | public void testPowerOfTwo() {
8 | assertEquals(2, BitmapUtils.getNextPowerOfTwo(2));
9 | assertEquals(4, BitmapUtils.getNextPowerOfTwo(3));
10 | assertEquals(4, BitmapUtils.getNextPowerOfTwo(4));
11 | assertEquals(64, BitmapUtils.getNextPowerOfTwo(47));
12 | assertEquals(128, BitmapUtils.getNextPowerOfTwo(99));
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/tests/src/uk/co/halfninja/wallpaper/parallax/test/ParallaxWallpaperTest.java:
--------------------------------------------------------------------------------
1 | package uk.co.halfninja.wallpaper.parallax.test;
2 |
3 | import java.io.File;
4 | import java.util.Arrays;
5 | import java.util.List;
6 |
7 | import uk.co.halfninja.wallpaper.parallax.ParallaxWallpaper;
8 | import android.test.AndroidTestCase;
9 |
10 | public class ParallaxWallpaperTest extends AndroidTestCase {
11 | public void testFindLayers() throws Exception {
12 | File data = getContext().getFilesDir();
13 | String dataPath = data.getAbsolutePath();
14 | for (String name: new String[] {
15 | "mylayer0.png", // ignored
16 | "mylayer1.png",
17 | "mylayer2.png",
18 | "mylayer3.png",
19 | "something4.png", //ignored
20 | }) {
21 | new File(data, name).createNewFile();
22 | }
23 |
24 | List layers = ParallaxWallpaper.findLayers(new File(data, "mylayer2.png").getAbsolutePath());
25 | assertEquals(Arrays.asList(new String[]{
26 | dataPath+"/mylayer1.png",
27 | dataPath+"/mylayer2.png",
28 | dataPath+"/mylayer3.png"
29 | }), layers);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------