In a recent update of the android support library disabling the SwipeRefreshLayout also resets the layout whereas it didn't use to before.
void reset() {
mCircleView.clearAnimation();
mProgress.stop();
mCircleView.setVisibility(View.GONE);
setColorViewAlpha(MAX_ALPHA);
// Return the circle to its start position
if (mScale) {
setAnimationProgress(0 /* animation complete and view is hidden */);
} else {
setTargetOffsetTopAndBottom(mOriginalOffsetTop - mCurrentTargetOffsetTop,
true /* requires update */);
}
mCurrentTargetOffsetTop = mCircleView.getTop();
}
#Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
if (!enabled) {
reset();
}
}
I don't want reset being called when I disable the layout, but I can't find a straightforward way of doing it. I thought of extending the SwipeRefreshLayout class and overriding setEnabled but I can't access the grandparent class' setEnabled that way. Is there a way around this?
If you place your class to android.support.v4.widget, you can do something like this:
package android.support.v4.widget;
import android.content.Context;
import android.util.AttributeSet;
/**
*
*/
public class MySwipeRefreshLayout extends SwipeRefreshLayout {
private boolean settingEnabled = false;
public MySwipeRefreshLayout(Context context) {
super(context);
}
public MySwipeRefreshLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public void setEnabled(boolean enabled) {
try {
settingEnabled = true;
super.setEnabled(enabled);
} finally {
settingEnabled = false;
}
}
#Override
void reset() {
if (!settingEnabled) {
super.reset();
}
}
}
Related
I wrote a simple subclass of ImageView that I want to use to detect double clicks on GridView-items:
public class DoubleClickImageView extends ImageView {
public interface ClickListener {
void onSingleClick();
void onDoubleClick();
}
private ClickListener imageClickReceiver;
private GestureDetector gestureDetector;
#Override
public boolean onTouchEvent(MotionEvent event) {
gestureDetector.onTouchEvent(event);
// return super.onTouchEvent(event); does not work with gestureDetector
// return false; does not work with gestureDetector
return true; // works but breaks the rest of the application
}
public void setDoubleClickListener(ClickListener listener) {
imageClickReceiver = listener;
}
public DoubleClickImageView(Context cx, AttributeSet attrs) {
super(cx, attrs);
gestureDetector = new GestureDetector(cx, new InternalClickListener());
}
private class InternalClickListener extends GestureDetector.SimpleOnGestureListener {
#Override
public boolean onSingleTapConfirmed(MotionEvent event) {
if (imageClickReceiver != null) {
imageClickReceiver.onSingleClick();
}
return true;
}
#Override
public boolean onDoubleTap(MotionEvent e) {
if (imageClickReceiver != null) {
imageClickReceiver.onDoubleClick();
}
return true;
}
#Override
public boolean onDown(MotionEvent event) {
//return true for onDown is required according to docs but does not help
return true;
}
}
}
The GridView consists of Images that are displayed using this class.
The problem is that the double click detection works only when onTouchEvent returns true, otherwise the gestureDetector does not detect any click event.
However, when I return true in the onTouchEvent, it breaks the rest of my application, since I have a global onTouchListener to detect swipes over the whole GridView and a multiple choice select mode with long press.
How can I solve this problem so that all of these three features work together?
Update: I was able to trace down the problem with debug logs. If the initial onTouchEvent-call (MotionEvent.ACTION_DOWN) returns false,then the related follow-up-events are not delivered to the ImageView. Therefore the GestureDetector can not make any sense of it, since it needs all related MotionEvents of a given gesture.
I was finally able to find a workaround after reading the touch handling section of http://balpha.de/2013/07/android-development-what-i-wish-i-had-known-earlier/.
The workaround consists of manually passing MotionEvent's to the images of the GridView.
Therefore I extended the DoubleClickImageView class and my GridView's adapter class as follows:
/**
* A workaround to get all touch events of a gesture delivered to GridView-images,
* although the onTouchEvent-callback has to return false to prevent breaking the
* rest of the GridView-functionality. (these are swipe gestures and a contextual action bar in our case)
* See http://stackoverflow.com/questions/39107566/detect-double-click-on-imageview-works-only-when-ontouchevent-returns-true#
* http://stackoverflow.com/questions/39100565/gesturedetector-detect-doubleclick-on-gridview-items
* for more information
*/
public class DoubleClickImageView extends ImageView {
private TouchEventForwarder touchEventForwarder;
private ClickListener imageClickListener;
private GestureDetector gestureDetector;
/**
* This needs to be called in the parent GridView adapter's getView() method
**/
public void setListeners(ClickListener dest, TouchEventForwarder src) {
imageClickListener = dest;
touchEventForwarder = src;
}
/**
* The parent GridView adapter has to maintain an instance of this class
**/
public static class TouchEventForwarder {
private DoubleClickImageView currentlyClickedImage;
/**
* This needs to be called on each touch event received by the parent GridView
**/
public void forwardTouchEvent(MotionEvent event) {
if (currentlyClickedImage != null) {
currentlyClickedImage.onForwardedTouchEvent(event);
}
}
private void setNewReceiver(DoubleClickImageView doubleClickImageView) {
currentlyClickedImage = doubleClickImageView;
}
}
public interface ClickListener {
void onSingleClick();
void onDoubleClick();
}
private void onForwardedTouchEvent(MotionEvent event) {
/** Use only forwarded events for gesture detection
* to prevent the evaluation of duplicate events **/
gestureDetector.onTouchEvent(event);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
/** Use the system listener to register the view at the event forwarder**/
if (touchEventForwarder != null) {
touchEventForwarder.setNewReceiver(this);
}
return super.onTouchEvent(event); // always false
}
public DoubleClickImageView(Context cx, AttributeSet attrs) {
super(cx, attrs);
gestureDetector = new GestureDetector(cx, new InternalClickListener());
}
private class InternalClickListener extends GestureDetector.SimpleOnGestureListener {
#Override
public boolean onSingleTapConfirmed(MotionEvent event) {
if (imageClickListener != null) {
imageClickListener.onSingleClick();
}
return true;
}
#Override
public boolean onDoubleTap(MotionEvent event) {
if (imageClickListener != null) {
imageClickListener.onDoubleClick();
}
return true;
}
}
}
Yesterday i ask a simplified question of my problem, but think its too simplified.
What my programm should do, is to hear a keyword and when he hear it, he should listen to what i said. (like if you told to siri or google now, by saying siri or ok google).
I'm using pocketsphinx for the keyword and the google speechrecognizer for the longer parts. It works, but only for one time. The pocketsphinx is in the MainActivity and the google recognizer is in an extra class (Jarvis).
The programm starts with the pocketsphinx listener, when he hear the KEYPHRASE, he starts the google listener by calling jarvis.startListener() (by the next()-method) and there is the problem, when the googlelistener is done, i dont come back from the Jarvis-class to the MainActivity to call the next() method again.
(when the google recognizer is done, the last things he do is in the onResult() in Jarvis-class, but from there i cant call the next()-method from MainActivity-class)
MainActivity
package com.example.superuser.jarvis;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.speech.RecognitionListener;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import java.io.File;
import java.io.IOException;
import edu.cmu.pocketsphinx.Assets;
import edu.cmu.pocketsphinx.Hypothesis;
import edu.cmu.pocketsphinx.SpeechRecognizer;
import edu.cmu.pocketsphinx.SpeechRecognizerSetup;
import static android.widget.Toast.makeText;
import static edu.cmu.pocketsphinx.SpeechRecognizerSetup.defaultSetup;
public class MainActivity extends Activity implements edu.cmu.pocketsphinx.RecognitionListener {
private String LOG_TAG = "Jarvis_hears_anything";
private TextView tv;
private Jarvis jarvis;
private boolean wannahearjarvis = false;
/* Named searches allow to quickly reconfigure the decoder */
private static final String KWS_SEARCH = "wakeup";
/* Keyword we are looking for to activate menu */
private static final String KEYPHRASE = "jarvis";
private edu.cmu.pocketsphinx.SpeechRecognizer recognizer;
//private HashMap<String, Integer> captions;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Button button = (Button) findViewById(R.id.b1);
tv = (TextView) findViewById(R.id.tv1);
//captions = new HashMap<String, Integer>();
//captions.put(KWS_SEARCH, R.string.kws_caption);
jarvis = new Jarvis(getApplicationContext());
new AsyncTask<Void, Void, Exception>() {
#Override
protected Exception doInBackground(Void... params) {
try {
Assets assets = new Assets(MainActivity.this);
File assetDir = assets.syncAssets();
setupRecognizer(assetDir);
} catch (IOException e) {
return e;
}
return null;
}
#Override
protected void onPostExecute(Exception result) {
if (result != null) {
((TextView) findViewById(R.id.tv1))
.setText("Failed to init recognizer " + result);
} else {
//switchSearch(KWS_SEARCH);
recognizer.startListening(KWS_SEARCH);
}
}
}.execute();
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(getApplicationContext(), "geht", Toast.LENGTH_SHORT).show();
}
});
}
public void next(){
if (wannahearjarvis){
recognizer.startListening(KWS_SEARCH);
wannahearjarvis = false;
}
else{
jarvis.startListening();
wannahearjarvis = true;
}
}
#Override
public void onDestroy() {
super.onDestroy();
recognizer.cancel();
recognizer.shutdown();
}
/**
* In partial result we get quick updates about current hypothesis. In
* keyword spotting mode we can react here, in other modes we need to wait
* for final result in onResult.
*/
#Override
public void onPartialResult(Hypothesis hypothesis) {
if (hypothesis == null)
return;
String text = hypothesis.getHypstr();
if (text.equals(KEYPHRASE)){
tv.append("found");
recognizer.stop();
//switchSearch(KWS_SEARCH);
}
else {
//((TextView) findViewById(R.id.tv1)).append(text+"PR");
//Log.i(LOG_TAG, text+"PR");
}
}
/**
* This callback is called when we stop the recognizer.
*/
#Override
public void onResult(Hypothesis hypothesis) {
//((TextView) findViewById(R.id.tv1)).setText("");
((TextView) findViewById(R.id.tv1)).append("oR");
if (hypothesis != null) {
String text = hypothesis.getHypstr();
makeText(getApplicationContext(), text, Toast.LENGTH_SHORT).show();
}
next();
}
#Override
public void onBeginningOfSpeech() {
}
/**
* We stop recognizer here to get a final result
*/
#Override
public void onEndOfSpeech() {
if (!recognizer.getSearchName().equals(KWS_SEARCH)){
tv.append("fuck");
}
//switchSearch(KWS_SEARCH);
}
/*private void switchSearch(String searchName) {
recognizer.stop();
// If we are not spotting, start listening with timeout (10000 ms or 10 seconds).
if (searchName.equals(KWS_SEARCH))
recognizer.startListening(searchName);
else
recognizer.startListening(searchName, 10000);
//String caption = getResources().getString(captions.get(searchName));
//((TextView) findViewById(R.id.tv1)).setText(caption);
//((TextView) findViewById(R.id.tv1)).append(caption);
}*/
private void setupRecognizer(File assetsDir) throws IOException {
// The recognizer can be configured to perform multiple searches
// of different kind and switch between them
recognizer = defaultSetup()
.setAcousticModel(new File(assetsDir, "en-us-ptm"))
.setDictionary(new File(assetsDir, "cmudict-en-us.dict"))
// To disable logging of raw audio comment out this call (takes a lot of space on the device)
.setRawLogDir(assetsDir)
// Threshold to tune for keyphrase to balance between false alarms and misses
.setKeywordThreshold(1e-20f)
// Use context-independent phonetic search, context-dependent is too slow for mobile
.setBoolean("-allphone_ci", true)
.getRecognizer();
recognizer.addListener(this);
/** In your application you might not need to add all those searches.
* They are added here for demonstration. You can leave just one.
*/
// Create keyword-activation search.
recognizer.addKeyphraseSearch(KWS_SEARCH, KEYPHRASE);
}
#Override
public void onError(Exception error) {
((TextView) findViewById(R.id.tv1)).setText(error.getMessage());
}
#Override
public void onTimeout() {
//switchSearch(KWS_SEARCH);
}
}
Jarvis
package com.example.superuser.jarvis;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.os.Bundle;
import android.speech.RecognitionListener;
import android.speech.RecognizerIntent;
import android.speech.SpeechRecognizer;
import android.widget.Toast;
import java.util.ArrayList;
public class Jarvis implements RecognitionListener{
private AudioManager audiom;
private SpeechRecognizer speech;
private Intent recogIntent;
private Toast m;
private Context c;
private String text;
public Jarvis(Context context){
speech = SpeechRecognizer.createSpeechRecognizer(context);
speech.setRecognitionListener(this);
recogIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
recogIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_PREFERENCE, "de");
//recogIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, context.getPackageName());
m = new Toast(context);
c=context;
}
public void startListening(){
speech.startListening(recogIntent);
}
public void destroy(){
speech.stopListening();
speech.cancel();
speech.destroy();
}
#Override
public void onReadyForSpeech(Bundle params) {
}
#Override
public void onBeginningOfSpeech() {
}
#Override
public void onRmsChanged(float rmsdB) {
}
#Override
public void onBufferReceived(byte[] buffer) {
}
#Override
public void onEndOfSpeech() {
}
#Override
public void onError(int error) {
}
#Override
public void onResults(Bundle results) {
ArrayList<String> matches = results
.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
Toast.makeText(c, matches.get(0), Toast.LENGTH_LONG).show();
speech.cancel();
//tried
//MainActivity m = new MainActivity();
//m.next();
//but got a Nullpointer Exception
}
#Override
public void onPartialResults(Bundle partialResults) {
}
#Override
public void onEvent(int eventType, Bundle params) {
}
}
You can store reference to the main activity in Jarvis object in a field:
class Jarvis {
....
private MainActivity m;
....
public Jarvis(MainActivity m) {
this.m = m;
}
....
public void onResults(Bundle results) {
....
m.next();
}
You can also send intents to the main activity as described here. This might be overkill in your case though.
I wish to disable the native contextual menu that is shown when you select some text, the one with the select all, copy, share and search buttons. I do not however want to disable selections themselves. Ideally I would wish to extend the menu actually, but honestly, I am more than perfectly fine with just disabling it. With textfields and the like it tends to be relatively simple from the documentation I found, but I just can't figure out a way to make this work with XWalkView/CordovaWebView. Might be that I am just searching in entirely the wrong corner though.
I have a workaround.
For WebView there is a solution, but it doesn't work for XWalkView:
WebView selection menu workaround
My gradle includes compile 'org.xwalk:xwalk_core_library:14.43.343.17'
My solution, the main idea in the onAttachedToWindow method:
public class XWalkWebView extends XWalkView {
public XWalkWebView(Context context, AttributeSet attrs) {
super(context, attrs);
}
private ActionMode.Callback mOriginalCallback;
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
try {
View innerChild = ((ViewGroup) getChildAt(0)).getChildAt(0);
Field contentViewField = innerChild.getClass().getDeclaredField("mContentView");
contentViewField.setAccessible(true);
XWalkContentView xWalkContentView = (XWalkContentView) contentViewField.get(innerChild);
Field contentViewCoreField = xWalkContentView.getClass().getSuperclass().getDeclaredField("mContentViewCore");
contentViewCoreField.setAccessible(true);
ContentViewCore viewCore = (ContentViewCore) contentViewCoreField.get(xWalkContentView);
viewCore.setContainerView(this);
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
#Override
public ActionMode startActionMode(ActionMode.Callback callback) {
mOriginalCallback = callback;
ActionMode.Callback c = new // your callback...
return super.startActionMode(c);
}
}
I try Warabei's solution but it not work on 15.44.384.13. I improve to find ContentViewCore cross versions:
public class XWalkWebView extends XWalkView {
...
private Field getFields(Class clazz){
for(Field field:clazz.getDeclaredFields()){
if(ContentViewCore.class == field.getType()){
return field;
}
}
clazz = clazz.getSuperclass();
if(clazz!=null && clazz!=Object.class){
Field field = getFields(clazz);
if(field!=null)return field;
}
return null;
}
private void inject(View view){
Field field = getFields(view.getClass());
if(field!=null){
field.setAccessible(true);
try {
ContentViewCore viewCore = (ContentViewCore) field.get(view);
viewCore.setContainerView(this);
return;
}catch(Exception e){
}
}
if(view instanceof ViewGroup){
ViewGroup viewGroup = (ViewGroup)view;
int count = viewGroup.getChildCount();
for(int i = 0;i<count;i++){
inject(viewGroup.getChildAt(i));
}
}
}
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
inject(this);
}
...
To disable contextual selection menu:
#Override
public ActionMode startActionMode(ActionMode.Callback callback) {
return new ActionMode() {
#Override
public void setTitle(CharSequence charSequence) {
}
#Override
public void setTitle(int i) {
}
#Override
public void setSubtitle(CharSequence charSequence) {
}
#Override
public void setSubtitle(int i) {
}
#Override
public void setCustomView(View view) {
}
#Override
public void invalidate() {
}
#Override
public void finish() {
}
#Override
public Menu getMenu() {
return null;
}
#Override
public CharSequence getTitle() {
return null;
}
#Override
public CharSequence getSubtitle() {
return null;
}
#Override
public View getCustomView() {
return null;
}
#Override
public MenuInflater getMenuInflater() {
return null;
}
};
}
It is an old post but I haven't been able to find another solution.
A simple workaround to disable context options in crosswalk view..
Go to your crosswalk project into res/menu/select_action_menu.xml
Delete or comment on the item you don't want to show
Save, build and run
This CSS should prevent context menus in both Android and IOS, as given in the cordova template
* {
-webkit-tap-highlight-color: rgba(0,0,0,0); /* make transparent link selection, adjust last value opacity 0 to 1.0 */
}
body {
-webkit-touch-callout: none; /* prevent callout to copy image, etc when tap to hold */
-webkit-text-size-adjust: none; /* prevent webkit from resizing text to fit */
-webkit-user-select: none; /* prevent copy paste, to allow, change 'none' to 'text' */
}
I have this code:
public class CopyOfLinearLayoutEntry extends LinearLayout implements Checkable {
private CheckedTextView _checkbox;
private Context c;
public CopyOfLinearLayoutEntry(Context context) {
super(context);
this.c = context;
setWillNotDraw(false);
}
public CopyOfLinearLayoutEntry(Context context, AttributeSet attrs) {
super(context, attrs);
this.c = context;
setWillNotDraw(false);
}
#Override
protected void onDraw(Canvas canvas) {
Paint strokePaint = new Paint();
strokePaint.setARGB(200, 255, 230, 230);
strokePaint.setStyle(Paint.Style.STROKE);
strokePaint.setStrokeWidth(12);
Rect r = canvas.getClipBounds();
Rect outline = new Rect(1, 1, r.right - 1, r.bottom - 1);
canvas.drawLine(r.left, r.top, r.right, r.top, strokePaint);
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
// find checked text view
int childCount = getChildCount();
for (int i = 0; i < childCount; ++i) {
View v = getChildAt(i);
if (v instanceof CheckedTextView) {
_checkbox = (CheckedTextView) v;
}
}
}
#Override
public boolean isChecked() {
return _checkbox != null ? _checkbox.isChecked() : false;
}
#Override
public void setChecked(boolean checked) {
if (_checkbox != null) {
_checkbox.setChecked(checked);
}
}
#Override
public void toggle() {
if (_checkbox != null) {
_checkbox.toggle();
}
}
}
Now I also need a version for RelativeLayout, so I would duplicate the class file and replace "extends LinearLayout" with "extends RelativeLayout". I think that would be bad, because I do not want any duplicate code.
How would I go about achieving my goal, seeing that Java does not allow multiple inheritance?
I read something about the composition design pattern, but I am not sure how to implement that.
Maybe someone could give me a starting point as to how to most elegantly solve this problem?
You don't need to extend both to avoid duplicate code. You can do something like this:
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckedTextView;
public class GenericLayout extends ViewGroup{
private CheckedTextView _checkbox;
public GenericLayout(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
#Override
protected void onDraw(Canvas canvas) {
Paint strokePaint = new Paint();
strokePaint.setARGB(200, 255, 230, 230);
strokePaint.setStyle(Paint.Style.STROKE);
strokePaint.setStrokeWidth(12);
Rect r = canvas.getClipBounds();
Rect outline = new Rect(1, 1, r.right - 1, r.bottom - 1);
canvas.drawLine(r.left, r.top, r.right, r.top, strokePaint);
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
// find checked text view
int childCount = getChildCount();
for (int i = 0; i < childCount; ++i) {
View v = getChildAt(i);
if (v instanceof CheckedTextView) {
_checkbox = (CheckedTextView) v;
}
}
}
public boolean isChecked() {
return _checkbox != null ? _checkbox.isChecked() : false;
}
public void setChecked(boolean checked) {
if (_checkbox != null) {
_checkbox.setChecked(checked);
}
}
public void toggle() {
if (_checkbox != null) {
_checkbox.toggle();
}
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// TODO Auto-generated method stub
}
}
public class Linear extends LinearLayout {
GenericLayout generic;
public Linear(Context context) {
super(context);
// TODO Auto-generated constructor stub
generic = new GenericLayout(context);
}
#Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
generic.onDraw(canvas);
}
...
}
public class Relative extends RelativeLayout{
GenericLayout generic;
public Relative(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
#Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
generic.onDraw(canvas);
}
...
}
Of what I have learned and been using, there are two ways:
You can do what you are trying to avoid (duplicate the class file and replace "extends LinearLayout" with "extends RelativeLayout")
You can create 2 interfaces and 1 class: One interface that extends LinearLayout, another one for extending RelativeLayout and the class implementing the methods and variables of the extending interfaces.
I hope that helps a little
You have to rethink your approach.
Seems like you are are using layout to control VIEW logic. Unfortunately your question does not have too much information about what you are trying to achieve.
You have few possibilities:
implement LAYOUT proxy / delegate with the custom logic (bad approach IMO)
make a dedicated HANDLER class to control your VIEW objects... these will be independent on the LAYOUT
make your VIEW object and use VIEW object instead of LAYOUT (probably the way to go)
I do wallpapers to andoid and I want that the user could choose options. Menu with options is showed but it have problem. When i click any options and go back to wallpapers screen, they dont update with new options. Why?
My code WallpaperService:
public MyWallpaperEngine(WallpaperService ws)
{
context = ws;
prefs = LiveWallpaperService.this.getSharedPreferences(SHARED_PREFS_NAME, 0);
OnSharedPreferenceChangeListener listener
= new SharedPreferences.OnSharedPreferenceChangeListener() {
public void onSharedPreferenceChanged(SharedPreferences prefs, String key)
{
if(key != null){
if(key.equals("BACKREPEAT")){
if(BACKREPEAT)
BACKREPEAT = false;
else
BACKREPEAT = true;
}
}
}
};
prefs.registerOnSharedPreferenceChangeListener(listener);
handler.post(drawRunner);
}
upd:
I have made, as was written in an example, but the result hasn't changed..
LiveWallpaperService code:
package com.samples;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.service.wallpaper.WallpaperService;
import android.util.Log;
import android.view.SurfaceHolder;
public class LiveWallpaperService extends WallpaperService
{
public static final String SHARED_PREFS_NAME = "leoSettings01";
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onDestroy() {
super.onDestroy();
}
#Override
public Engine onCreateEngine() {
return new MyWallpaperEngine();
}
private class MyWallpaperEngine extends Engine implements
SharedPreferences.OnSharedPreferenceChangeListener {
private final Handler handler = new Handler();
int draw_x = 0;
int draw_y = 0;
//...
boolean BACKREPEAT = false;
private final Runnable drawRunner = new Runnable() {
#Override
public void run()
{
draw();
}
};
private boolean visible = true;
private SharedPreferences prefs;
MyWallpaperEngine()
{
prefs = LiveWallpaperService.this.getSharedPreferences(SHARED_PREFS_NAME, 0);
prefs.registerOnSharedPreferenceChangeListener(this);
onSharedPreferenceChanged(prefs, null);
handler.post(drawRunner);
}
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
if(key != null)
{
Log.v("key:", key); //no message!
if(key.equals("BACKREPEAT")){
if(BACKREPEAT)
BACKREPEAT = false;
else
BACKREPEAT = true;
}
}
}
#Override
public void onVisibilityChanged(boolean visible) {
this.visible = visible;
if (visible) {
handler.post(drawRunner);
} else {
handler.removeCallbacks(drawRunner);
}
}
#Override
public void onSurfaceDestroyed(SurfaceHolder holder) {
super.onSurfaceDestroyed(holder);
this.visible = false;
handler.removeCallbacks(drawRunner);
}
private void draw()
{
SurfaceHolder holder = getSurfaceHolder();
Canvas canvas = null;
try
{
canvas = holder.lockCanvas();
//...draw draw draw
}
finally
{
if (canvas != null)
holder.unlockCanvasAndPost(canvas);
}
handler.removeCallbacks(drawRunner);
if (visible)
{
handler.postDelayed(drawRunner, DELAY);
}
}
}
}
prefs.xml:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:title="General">
<CheckBoxPreference
android:title="Animation repeat"
android:key="BACKREPEAT"
android:defaultValue="false"
/>
</PreferenceCategory>
</PreferenceScreen>
Your wallpaper will never restart after coming back from settings. The only method invoked will be your Engine's onVisibilityChanged. If your correctly implements SharedPreferences.OnSharedPreferenceChangeListener on your Engine, then onSharedPreferenceChanged should be called too.
Please check if you implemented OnSharedPreferenceChangeListener exactly this way:
http://developer.android.com/resources/samples/CubeLiveWallpaper/src/com/example/android/livecubes/cube2/CubeWallpaper2.html
If you did and it is still not working, please post your entire WallpaperService and settings preference activity code here.