Hi, at last I was able to create my own live wallpaper " MATRIX RAIN ".
Download :
click here
The matrix rain effect which I am using is the same which I created in canvas.
ref :-
http://www.androidlearner.com/2016/09/create-custom-view-in-android-matrix.html
I this post I will describe how I put together the live wallpaper.
To create live wallpaper you need to set the following things,
1. A class which extends
WallpaperService and further implements a nested class which extends
Engine class.This Class is responsible for calling the draw code.
2. A activity which displays list of settings for the wallpaper.(optional). Here I discovered android
Preference API which easily enable to create setting for applications
3. XML definition for wallpaper in
xml folder.
4. AndroidManifest.xml entry for the live-wallpaper and the preference activity.
Getting Started :-
1. For creating the Live wallpaper you must class which extends class
WallpaperService find more info about class at
https://developer.android.com/reference/android/service/wallpaper/WallpaperService.html
This class must override method
onCreateEngine() method which return
Engine class object
Further this class must have a nested class which extends Engine class.
To create the falling animation I need to draw the surface continuously.Earlier in the when
using canvas view invalidate() method was called to update the view.However in case of
Engine class there is no such method.
To draw the effect first you need to get the canvas from the getSurfaceHolder();
After that a thread is runned at specific interval to update the draw surface.
Below is the skeleton class from the live wallpaper.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
|
package matrixlw.app.skd.wa;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.service.wallpaper.WallpaperService;
import android.util.Log;
import android.view.SurfaceHolder;
import com.enrico.colorpicker.colorDialog;
import java.util.Random;
/**
* Created by sapan on 1/10/2017.
*/
public class matrixWall extends WallpaperService {
private boolean mVisible; // visible flag
Canvas canvas; // canvas reference
int Drawspeed=10; // thread call delay time
Context mcontext; //reference to the current context
@Override
public Engine onCreateEngine() {
//set the Preference default value
mcontext = this; //set the current context
//return the Engine Class
return new LiveWall(); // this calls contain the wallpaper code
}
/*
* this class extends the engine for the live wallpaper
* THis class implements all the draw calls required to draw the wallpaper
* This call is to neseted inside the wallpaper service class to function properly
* don't know why though :(
*/
public class LiveWall extends Engine
{
final Handler mHandler = new Handler(); // this is to handle the thread
//the tread responsibe for drawing this thread get calls every time
// drawspeed vars set the execution speed
private final Runnable mDrawFrame = new Runnable() {
public void run() {
// This method get called each time to drwaw thw frame
// Engine class does not provide any invlidate methods
// as used in canvas
// set your draw call here
drawFrame();
}
};
//Called when the surface is created
@Override
public void onSurfaceCreated(SurfaceHolder holder) {
super.onSurfaceCreated(holder);
//call the draw method
// this is where you must call your draw code
drawFrame();
}
// remove thread
@Override
public void onDestroy() {
super.onDestroy();
mHandler.removeCallbacks(mDrawFrame);
}
//called when varaible changed
@Override
public void onVisibilityChanged(boolean visible) {
mVisible = visible;
if (visible) {
//call the drawFunction
drawFrame();
} else {
//this is necessay to remove the call back
mHandler.removeCallbacks(mDrawFrame);
}
}
//called when surface destroyed
@Override
public void onSurfaceDestroyed(SurfaceHolder holder) {
super.onSurfaceDestroyed(holder);
mVisible = false;
//this is necessay to remove the call back
mHandler.removeCallbacks(mDrawFrame);
}
// my function which contain the code to draw
//this function contain the the main draw call
/// this function need to call every time the code is executed
// the thread call this functioin with some delay "drawspeed"
public void drawFrame()
{
//getting the surface holder
final SurfaceHolder holder = getSurfaceHolder();
canvas = null; // canvas
try {
canvas = holder.lockCanvas(); //get the canvas
if (canvas != null) {
// draw something
// my draw code
}
} finally {
if (canvas != null)
holder.unlockCanvasAndPost(canvas);
}
// Reschedule the next redraw
// this is the replacement for the invilidate funtion
// every time call the drawFrame to draw the matrix
mHandler.removeCallbacks(mDrawFrame);
if (mVisible) {
// set the execution delay
mHandler.postDelayed(mDrawFrame, Drawspeed);
}
}
@Override
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
super.onSurfaceChanged(holder, format, width, height);
// update when surface changed
}
}
}
|
2. Creating xml definition.
As the for the wallpaper service a xml file must be created in xml folder like mywallpaper.xml
1
2
3
4
5
6
|
<?xml version="1.0" encoding="UTF-8"?>
<wallpaper
xmlns:android="http://schemas.android.com/apk/res/android"
android:thumbnail="@mipmap/ic_launcher"
android:settingsActivity="matrixlw.app.skd.wa.SettingsActivity"/>
|
here android:settingsActivity="matrixlw.app.skd.wa.SettingsActivity"/>
points to the settings activity which get called when setting button is clicked
at the live wallpaper preview screen
3. Setting up the android AndroidManifest.xml to get the live wallpaper running this file must be set properly.
a. Set the features
1
| <uses-feature android:name="android.software.live_wallpaper" />
|
b. Add the service for the wallpaper and the resource xml file.
1
2
3
4
5
6
7
8
9
10
11
12
13
| <service
android:name=".matrixWall"
android:enabled="true"
android:label="MATRIX RAIN"
android:permission="android.permission.BIND_WALLPAPER">
<intent-filter>
<action android:name="android.service.wallpaper.WallpaperService" />
</intent-filter>
<meta-data
android:name="android.service.wallpaper"
android:resource="@xml/mywallpaper"></meta-data>
</service>
|
c. Add the settings activity
1
2
3
4
5
6
7
8
9
10
11
| <activity
android:name=".SettingsActivity"
android:label="@string/title_activity_settings"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
|
Here android:exported="true" must be set else this activity will not open when clicking setting button
3. Creating the Setting activity
The Settings activity Contains the UI for customize the property of the live wallpaper.
The preference activity is created using android preference API this provides a easy way to created the preference API
For more info ref :
https://developer.android.com/guide/topics/ui/settings.html
Color Picker is created using library by enricocid
Ref :-
https://github.com/enricocid/Color-picker-library
I am skipping about the setting activity. The files and layout for the setting activity is
SettingsActivity.java and
xml/preferences.xml4. Putting together the Live wallpaper :
The matrix rain effect is going to be my live wallpaper which I have explained in my previous article.
ref-
below is the draw code from that project
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
// ======== MATRIX LIVE WALLPAPER VARS
int background_color= Color.parseColor("#FF000000");
int text_color=Color.parseColor("#FF8BFF4A");
int width = 1000000; //default initial width
int height = 100; //default initial height
int fontSize = 15; //font size of the text which will fall
int columnSize = width/fontSize; //column size ; no of digit required to fill the screen
int parentWidth;
String text = "MATRIXRAIN"; // Text which need to be drawn
char[] textChar = text.toCharArray(); // split the character of the text
int textLength = textChar.length; //length of the length text
Random rand = new Random(); //random generater
int[] textPosition; // contain the position which will help to draw the text
//======================
//old matrix effect code
void drawText()
{
//Set up the paint
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(text_color);
paint.setTextSize(15);
//loop and paint
for(int i =0 ;i<textPosition.length;i++)
{
// draw the text at the random position
canvas.drawText(""+textChar[rand.nextInt(textLength)+0],i*fontSize,textPosition[i]*fontSize,paint);
// check if text has reached bottom or not
if(textPosition[i]*fontSize > height && Math.random() > 0.975)
textPosition[i] = 0; // change text position to zero when 0 when text is at the bottom
textPosition[i]++; //increment the position array
}
}
//old martix effect code
public void canvasDraw()
{
Log.d("canvas ","drawing");
//set the paint for the canvas
Paint paint = new Paint();
paint.setColor(background_color);
paint.setAlpha(5);
paint.setStyle(Paint.Style.FILL);
canvas.drawRect(0, 0, width, height, paint);//draw rect to clear the canvas
drawText(); // draw the canvas
}
|
Now the above code is need to be inserted into the skeleton wallpaper Service code.In the skeleton class DrawFrame() method is used to call the draw statements.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
|
package matrixlw.app.skd.wa;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.service.wallpaper.WallpaperService;
import android.util.Log;
import android.view.SurfaceHolder;
import com.enrico.colorpicker.colorDialog;
import java.util.Random;
/**
* Created by sapan on 1/10/2017.
*/
public class matrixWall extends WallpaperService {
private boolean mVisible; // visible flag
Canvas canvas; // canvas reference
int Drawspeed=10; // thread call delay time
Context mcontext; //reference to the current context
// ======== MATRIX LIVE WALLPAPER VARS
int background_color= Color.parseColor("#FF000000");
int text_color=Color.parseColor("#FF8BFF4A");
int width = 1000000; //default initial width
int height = 100; //default initial height
int fontSize = 15; //font size of the text which will fall
int columnSize = width/fontSize; //column size ; no of digit required to fill the screen
int parentWidth;
String text = "MATRIXRAIN"; // Text which need to be drawn
char[] textChar = text.toCharArray(); // split the character of the text
int textLength = textChar.length; //length of the length text
Random rand = new Random(); //random generater
int[] textPosition; // contain the position which will help to draw the text
//======================
@Override
public Engine onCreateEngine() {
//set the Preference default value
mcontext = this; //set the current context
//Initalise and read the preference
PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
text = sharedPref.getString("matrix_scroll_text", "MATRIX");
Drawspeed = Integer.parseInt(sharedPref.getString("matrix_falling_speed","10"));
fontSize = Integer.parseInt(sharedPref.getString("matrix_font_size","15"));
background_color = colorDialog.getPickerColor(getBaseContext(), 1);
text_color =colorDialog.getPickerColor(getBaseContext(), 2);
//Some loggers Commnet or remove if you want
Log.d("back_color",""+background_color);
Log.d("text_color",""+text_color);
textChar = text.toCharArray(); // split the character of the text
textLength = textChar.length;
columnSize = width/fontSize;
//return the Engine Class
return new LiveWall(); // this calls contain the wallpaper code
}
/*
* this class extends the engine for the live wallpaper
* THis class implements all the draw calls required to draw the wallpaper
* This call is to neseted inside the wallpaper service class to function properly
* don't know why though :(
*/
public class LiveWall extends Engine
{
final Handler mHandler = new Handler(); // this is to handle the thread
//the tread responsibe for drawing this thread get calls every time
// drawspeed vars set the execution speed
private final Runnable mDrawFrame = new Runnable() {
public void run() {
//Matrix code to the color when changed
// callback can also be used but I havent
background_color = colorDialog.getPickerColor(getBaseContext(), 1);
text_color =colorDialog.getPickerColor(getBaseContext(), 2);
// ^^^^^^^^
// This method get called each time to drwaw thw frame
// Engine class does not provide any invlidate methods
// as used in canvas
// set your draw call here
drawFrame();
}
};
//Called when the surface is created
@Override
public void onSurfaceCreated(SurfaceHolder holder) {
super.onSurfaceCreated(holder);
//update the matrix variables
width = getDesiredMinimumWidth();
height = getDesiredMinimumHeight();
columnSize = width/fontSize;
//initalise the textposiotn to zero
textPosition = new int[columnSize+1]; //add one more drop
for(int x = 0; x < columnSize; x++) {
textPosition[x] = 1;
}
//^^^^^^^^^^^^^^^
//call the draw method
// this is where you must call your draw code
drawFrame();
}
// remove thread
@Override
public void onDestroy() {
super.onDestroy();
mHandler.removeCallbacks(mDrawFrame);
}
//called when varaible changed
@Override
public void onVisibilityChanged(boolean visible) {
mVisible = visible;
if (visible) {
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(mcontext);
text = sharedPref.getString("matrix_scroll_text", "MATRIX");
Drawspeed = Integer.parseInt(sharedPref.getString("matrix_falling_speed","10"));
fontSize = Integer.parseInt(sharedPref.getString("matrix_font_size","15"));
background_color = colorDialog.getPickerColor(getBaseContext(), 1);
text_color =colorDialog.getPickerColor(getBaseContext(), 2);
textChar = text.toCharArray(); // split the character of the text
textLength = textChar.length;
columnSize = width/fontSize;
drawFrame();
} else {
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(mcontext);
text = sharedPref.getString("matrix_scroll_text", "MATRIX");
Drawspeed = Integer.parseInt(sharedPref.getString("matrix_falling_speed","10"));
fontSize = Integer.parseInt(sharedPref.getString("matrix_font_size","15"));
background_color = colorDialog.getPickerColor(getBaseContext(), 1);
text_color =colorDialog.getPickerColor(getBaseContext(), 2);
textChar = text.toCharArray(); // split the character of the text
textLength = textChar.length;
columnSize = width/fontSize;
//this is necessay to remove the call back
mHandler.removeCallbacks(mDrawFrame);
}
}
//called when surface destroyed
@Override
public void onSurfaceDestroyed(SurfaceHolder holder) {
super.onSurfaceDestroyed(holder);
mVisible = false;
//this is necessay to remove the call back
mHandler.removeCallbacks(mDrawFrame);
}
//this function contain the the main draw call
/// this function need to call every time the code is executed
// the thread call this functioin with some delay "drawspeed"
public void drawFrame()
{
//getting the surface holder
final SurfaceHolder holder = getSurfaceHolder();
canvas = null; // canvas
try {
canvas = holder.lockCanvas(); //get the canvas
if (canvas != null) {
// draw something
// canvas matrix draw code
canvasDraw();
//^^^^
}
} finally {
if (canvas != null)
holder.unlockCanvasAndPost(canvas);
}
// Reschedule the next redraw
// this is the replacement for the invilidate funtion
// every time call the drawFrame to draw the matrix
mHandler.removeCallbacks(mDrawFrame);
if (mVisible) {
// set the execution delay
mHandler.postDelayed(mDrawFrame, Drawspeed);
}
}
@Override
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
super.onSurfaceChanged(holder, format, width, height);
// some matrix variable
// though not needed
Paint paint = new Paint();
paint.setColor(background_color);
paint.setAlpha(255); //set the alpha
paint.setStyle(Paint.Style.FILL);
canvas.drawRect(0, 0, width, height, paint);
}
}
//old matrix effect code
void drawText()
{
//Set up the paint
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(text_color);
paint.setTextSize(15);
//loop and paint
for(int i =0 ;i<textPosition.length;i++)
{
// draw the text at the random position
canvas.drawText(""+textChar[rand.nextInt(textLength)+0],i*fontSize,textPosition[i]*fontSize,paint);
// check if text has reached bottom or not
if(textPosition[i]*fontSize > height && Math.random() > 0.975)
textPosition[i] = 0; // change text position to zero when 0 when text is at the bottom
textPosition[i]++; //increment the position array
}
}
//old martix effect code
public void canvasDraw()
{
Log.d("canvas ","drawing");
//set the paint for the canvas
Paint paint = new Paint();
paint.setColor(background_color);
paint.setAlpha(5);
paint.setStyle(Paint.Style.FILL);
canvas.drawRect(0, 0, width, height, paint);//draw rect to clear the canvas
drawText(); // draw the canvas
}
}
|
Here the below methods are used to set up the surface
1
2
3
4
|
public void onSurfaceCreated(SurfaceHolder holder)
public void onVisibilityChanged(boolean visible)
public void onSurfaceDestroyed(SurfaceHolder holder)
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height)
|
Nice tutorial, thanks for it. Very helpful
ReplyDelete