orientation lock in android programmatically - java

I have the following code.
Java
public void lockScreenOrientation() {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
}
public void unlockScreenOrientation() {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
}
I am calling these functions from javascript. Control is entering these methods. However the orientation is not locked.
I have tried following to lock the orientation
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
None of these seem to work. Any pointers would be helpful.

Activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
Locks the screen (activity) in whatever the orientation it was.
Requires API Level >= 18

I've created a few utility methods to help deal with orientation locking, feel free to use this class.
Example use:
In an Activity: OrientationUtils.lockOrientationPortrait(MyActivityName.this)
In a Fragment: OrientationUtils.lockOrientationLandscape(getActivity())
Code:
/** Static methods related to device orientation. */
public class OrientationUtils {
private OrientationUtils() {}
/** Locks the device window in landscape mode. */
public static void lockOrientationLandscape(Activity activity) {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
/** Locks the device window in portrait mode. */
public static void lockOrientationPortrait(Activity activity) {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
/** Allows user to freely use portrait or landscape mode. */
public static void unlockOrientation(Activity activity) {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}
}
Here's my full OrientationUtils class on GitHub that can be used in any Android app: https://github.com/danialgoodwin/android-simply-advanced-helper/blob/master/SimplyAdvancedHelperLibrary/src/net/simplyadvanced/utils/OrientationUtils.java

This is a class I wrote to handle locking and unlocking the screen orientation. I call toggleScreenOrientationLock(this, prefs, isChecked) from a toggle button's checkedChangedListener, and restoreScreenLock(this, prefs) from onCreate(). In both cases this is your activity and prefs is a SharedPrefences object, used for saving information about the state of the lock.
The complicated part pf the code is the getScreenOrientation() function, which I stole from and cleaned up from here. I'll try to explain logic behind how this works.
When we set the device's orientation with setRequestedOrienation(), we need to know if the device is in landscape or portrait mode, and we need to know if it's a reverse orientation (rotated 180 degrees).
Using getResources().getConfiguration().orientation will answer the question of which orientation we're in. If we could factor in the rotation of the device, we could tell whether it was rotated 180 or not. Unfortunately, depending on the device, ROTATE_0 might be portrait or landscape. Phones typically map ROTATE_0 to portrait, and tablets to landscape.
So the solution used here is to use the screen dimensions to determine if it is in landscape or portrait instead. If the screen is wider than it is tall, then we infer that the device is in a landscape orientation, and vice versa for portrait. Then we can factor in the rotation to figure out whether the orientation is reversed or not.
For example, if the screen is wider than it is tall, then we know we're in a landscape orientation. If the rotation is either 0 or 180 (in the code's logic, this is equal to !isRotatedOrthogonally), then we know that 0 is LANDSCAPE and 180 is REVERSE_LANDSCAPE.
It has been noted elsewhere that this won't work across all devices, since whether 90 or 270 is the reversed orientation is device specific. But this is still probably the best you're going to do; at worst, one orientation will rotate 180 degrees when you lock it, which is what would likely happen if tried locking the screen any other way.
public class ScreenLocker {
final private static String ROTATION_LOCKED_KEY = "LockedOrientationVal";
final private static String ROTATION_IS_LOCKED_KEY = "IsRotationLocked";
final private static String ROTATION_SAVED_KEY = "SavedOrientationVal";
public static int getScreenOrientation(Activity activity) {
final Display display = activity.getWindowManager().getDefaultDisplay();
final int rotation = display.getRotation();
Point size = new Point();
display.getSize(size);
final boolean isWiderThanTall = size.x > size.y;
final boolean isRotatedOrthogonally = (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270);
int orientation;
if (isRotatedOrthogonally) {
if (isWiderThanTall)
orientation = (rotation == Surface.ROTATION_90) ? ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE : ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
else
orientation = (rotation == Surface.ROTATION_90) ? ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT : ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; // normal and reversed switched intended
}
else {
if (isWiderThanTall)
orientation = (rotation == Surface.ROTATION_0) ? ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE : ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
else
orientation = (rotation == Surface.ROTATION_0) ? ActivityInfo.SCREEN_ORIENTATION_PORTRAIT : ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
}
return orientation;
}
public static void toggleScreenOrientationLock(Activity activity,SharedPreferences prefs, boolean lock) {
if(lock)
lockScreenOrientation(activity, prefs);
else
unlockScreenOrientation(activity, prefs);
}
// call this from your activity's onCreate() or onResume()
public static boolean restoreScreenLock(Activity activity, SharedPreferences prefs) {
final boolean isLocked = prefs.getBoolean(ROTATION_IS_LOCKED_KEY, false);
final int previousLockedOrientation = prefs.getInt(ROTATION_LOCKED_KEY, -999);
if(isLocked && previousLockedOrientation != -999) {
prefs.edit().putInt(ROTATION_SAVED_KEY, activity.getRequestedOrientation()).apply();
activity.setRequestedOrientation(previousLockedOrientation);
return true;
}
return false;
}
private static void lockScreenOrientation(Activity activity, SharedPreferences prefs) {
final int currentOrientation = activity.getRequestedOrientation();
final int lockOrientation = getScreenOrientation(activity);
// checking isCurrentlyLocked prevents the ROTATION_LOCKED_KEY and ROTATION_SAVED_KEY
// becoming identical, which results in the screen not being able to be unlocked.
final boolean isCurrentlyLocked = prefs.getBoolean(ROTATION_IS_LOCKED_KEY, false);
if(!isCurrentlyLocked) {
activity.setRequestedOrientation(lockOrientation);
prefs.edit()
.putInt(ROTATION_SAVED_KEY, currentOrientation)
.putInt(ROTATION_LOCKED_KEY, lockOrientation)
.putBoolean(ROTATION_IS_LOCKED_KEY, true)
.apply();
}
}
private static void unlockScreenOrientation(Activity activity, SharedPreferences prefs) {
final int savedOrientation = prefs.getInt(ROTATION_SAVED_KEY, activity.getRequestedOrientation());
activity.setRequestedOrientation(savedOrientation);
prefs.edit().putBoolean(ROTATION_IS_LOCKED_KEY, false).apply();
}
}

Here's another simple solution that works well for me.
private void orientationManager(boolean lock)
{
int currentOrientation = getResources().getConfiguration().orientation;
if(lock)
{
if (currentOrientation == Configuration.ORIENTATION_LANDSCAPE)
{
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
}
else
{
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
}
}
else
{
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}
}
I needed to lock orientationManager(true); the current screen orientation when dialogs were opened and unlock orientationManager(false); when the dialog was closed.

Related

Remove gestures from Android Q

I have a kids drawing app, but the gestures in Q keep quitting the app. I tried removing the system gesture but it does not seem to work.
In this case, I am trying to exclude the whole screen from system gesture:
List<Rect> exclusionRects = new ArrayList();
public void onLayout(boolean changedCanvas, int left, int top, int right, int bottom) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
exclusionRects.clear();
exclusionRects.add(new Rect(left, top, right, bottom));
setSystemGestureExclusionRects(exclusionRects);
}
}
As stated by Google:
First, to ensure reliable and consistent operation, there’s a 200dp vertical app exclusion limit for the Back gesture.
https://android-developers.googleblog.com/2019/08/final-beta-update-official-android-q.html
This means that the operating system will not allow you to override the back gesture fully.
This makes sense as it is a fairly fundamental part of the operating system and they probably don't want to allow apps that remove the gesture entirely, as it is bad for consistency across the platform
Try this.
Define this code in your Utils class.
static List<Rect> exclusionRects = new ArrayList<>();
public static void updateGestureExclusion(AppCompatActivity activity) {
if (Build.VERSION.SDK_INT < 29) return;
exclusionRects.clear();
Rect rect = new Rect(0, 0, SystemUtil.dpToPx(activity, 16), getScreenHeight(activity));
exclusionRects.add(rect);
activity.findViewById(android.R.id.content).setSystemGestureExclusionRects(exclusionRects);
}
public static int getScreenHeight(AppCompatActivity activity) {
DisplayMetrics displayMetrics = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int height = displayMetrics.heightPixels;
return height;
}
public static int dpToPx(Context context, int i) {
return (int) (((float) i) * context.getResources().getDisplayMetrics().density);
}
Check if your layout is set in that activity where you want to exclude the edge getures and then apply this code.
// 'content' is the root view of your layout xml.
ViewTreeObserver treeObserver = content.getViewTreeObserver();
treeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
content.getViewTreeObserver().removeOnGlobalLayoutListener(this);
SystemUtil.updateGestureExclusion(MainHomeActivity.this);
}
});
We are adding the view width to 16dp to trigger the code when user swipe right from left edge & height to screen height to do it fully left side.

Google Vision: setRequestPreviewSize no effect

I am working on an Android application with an embedded QR code scanner using the Google Vision API. The scanner functions, but the SurfaceView that acts as camera preview is stretched vertically. The degree of distortion is different for different emulated devices.
As I understand it, you would use mCameraSource.setRequestedPreviewSize(w,h) to set the correct size. w and h I have set as Resources.getSystem().getDisplayMetrics().widthPixels and Resources.getSystem().getDisplayMetrics().heightPixels, respectively. However, I have noticed that regardless of what numbers I parse as width and height, there are no changes in the way it displays.
However, resizing the SurfaceView on which it is displayed does have an effect on the distortion. For one particular emulated Android device I can statically set the right width and height. For different devices, however, with a slightly different pixel w:h ratio, the distortion can become quite large.
I have read various solutions on StackOverflow, but most use the CameraPreview instead of the CameraSource.Builder.
My code thus far is (part of ScannerActivity.java):
private SurfaceView svCamera;
private BarcodeDetector barcodeDetector;
private CameraSource cameraSource;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scanner);
initViews();
initListeners();
barcodeDetector = new BarcodeDetector.Builder(this)
.setBarcodeFormats(Barcode.QR_CODE)
.build();
cameraSource = new CameraSource.Builder(this, barcodeDetector)
.setRequestedPreviewSize(Resources.getSystem().getDisplayMetrics().widthPixels, Resources.getSystem().getDisplayMetrics().heightPixels)
.setAutoFocusEnabled(true)
.build();
svCamera.getHolder().addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
requestPermission();
try {
if (ActivityCompat.checkSelfPermission(ScannerActivity.this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
return;
}
cameraSource.start(svCamera.getHolder());
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
}
#Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
cameraSource.stop();
}
});
barcodeDetector.setProcessor(new Detector.Processor<Barcode>() {
#Override
public void release() {
scanned = true;
}
#Override
public void receiveDetections(Detector.Detections<Barcode> detections) {
...
}
});
} }
Can someone help me with setting preview size?
The way I fixed it with help of Alex Cohn's answer:
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int width = displayMetrics.widthPixels;
int height = displayMetrics.heightPixels;
...
cameraSource.setRequestedPreviewSize(1280, 720); // Hardcoded, for now.
And I set the size of the SurfaceView with:
svCamera.setLayoutParams(new RelativeLayout.LayoutParams(width, width/9*16));
If I remember I will update this to a non-hardcoded version.
Quite contrary. You cannot choose arbitrary resolution for setRequestedPreviewSize(). This CameraSource API wraps the ordinary Camera API, which exposes only some, "supported" pairs of width and height.
If you want to display the live view undistorted, you must setup your SurfaceView to match the aspect ratio of chosen camera preview. This means, it's OK to use surface of 320x240 pixel for camera 640x480. It's even OK to use surface of 1280x720 for camera 1920x1080.
But if you have surface of 800x480 for camera of 1280x720 (and if your devices supports such camera resolution), the picture will be slightly stretched.

ImageView.setImageResource is not displaying correct drawable

I have this app, it's basically a 'make fun' app I made with a person's face and the ability to either shoot it with a pistol, automatic rifle, or grenade (more weapons to come :-)).
I have a Weapon class, with the following code. (I'm pretty new to java so forgive me if I failed to follow proven ways, or do things as efficiently as I should)
public class Weapon {
// Parent
public static MainActivity ma;
// Constants
public static final int BULLET_WIDTH = 97;
public static final int BULLET_HEIGHT = 92;
public static final int EXPLOD_WIDTH = 299;
public static final int EXPLOD_HEIGHT = 237;
public static final int PISTOL = 0;
public static final int RIFLE = 1;
public static final int GRENADE = 2;
protected static int[] position = {0, 0};
protected static RelativeLayout rl = null;
protected static MediaPlayer mp;
// Protected stuff
// Object variables.
protected int type;
protected int drawableEffect;
protected int soundEffect;
// Functionals
protected ImageView effectImage;
public void equipWeapon() {
// Clear any existing listeners and assign a new one.
MainActivity.mainImage.setOnTouchListener(null);
MainActivity.mainImage.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// Which weapon are we using?
switch (type) {
case Weapon.PISTOL:
case Weapon.GRENADE:
if (event.getAction() == MotionEvent.ACTION_DOWN) {
// Fire the weapon.
fireWeapon(v, event);
}
case Weapon.RIFLE:
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE: // This doesn't work ???
// Fire the weapon.
fireWeapon(v, event);
}
}
// perfomClick needed.
return v.performClick();
}
});
}
public void fireWeapon(View v, MotionEvent event) {
// Reference the layout.
Weapon.rl = (RelativeLayout)Weapon.ma.findViewById(R.id.relativeLayout);
// Define properties.
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT,
RelativeLayout.LayoutParams.WRAP_CONTENT);
// Get the touch position.
Weapon.position[0] = (int)event.getX() - ((type == Weapon.PISTOL || type == Weapon.RIFLE) ? Weapon.BULLET_WIDTH:Weapon.EXPLOD_WIDTH);
Weapon.position[1] = (int)event.getY() - ((type == Weapon.PISTOL || type == Weapon.RIFLE) ? Weapon.BULLET_HEIGHT:Weapon.EXPLOD_HEIGHT);
// Set the position.
lp.setMargins(Weapon.position[0], Weapon.position[1], 0, 0);
effectImage.setLayoutParams(lp);
// Add the view to the layout.
Weapon.rl.addView(effectImage, lp);
// Play the sound.
Weapon.mp.seekTo(0);
Weapon.mp.start();
// Reload
reload();
}
public void reload() {
// Create the ImageView
this.effectImage = new ImageView(Weapon.ma);
this.effectImage.setImageResource(this.drawableEffect);
}
}
And I have a Pistol class which extends Weapon
public class Pistol extends Weapon {
public Pistol() {
// First save the type.
this.type = Weapon.PISTOL;
// Fetch the sound effect and image.
this.soundEffect = R.raw.gunshot;
this.drawableEffect = R.drawable.bullet_hole1;
// Create the media player and initialize the sound.
Weapon.mp = MediaPlayer.create(Weapon.ma, this.soundEffect);
// Create the ImageView
this.effectImage = null;
this.effectImage = new ImageView(Weapon.ma);
this.effectImage.setImageResource(this.drawableEffect);
}
}
As well as a Rifle class extending Weapon
public class Rifle extends Weapon {
public Rifle() {
// First save the type.
this.type = Weapon.RIFLE;
// Fetch the sound effect and image.
this.soundEffect = R.raw.gunshot;
this.drawableEffect = R.drawable.bullet_hole1;
// Create the media player and initialize the sound.
Weapon.mp = MediaPlayer.create(Weapon.ma, this.soundEffect);
// Create the ImageView
this.effectImage = null;
this.effectImage = new ImageView(Weapon.ma);
this.effectImage.setImageResource(this.drawableEffect);
}
}
finally, I have a Grenade class extending, yep you guessed it, Weapon as well.
public class Grenade extends Weapon {
public Grenade() {
// First save the type.
this.type = Weapon.GRENADE;
// Fetch the sound effect and image.
this.soundEffect = R.raw.grenade;
this.drawableEffect = R.drawable.boom;
// Create the media player and initialize the sound.
Weapon.mp = MediaPlayer.create(Weapon.ma, this.soundEffect);
// Create the ImageView
this.effectImage = new ImageView(Weapon.ma);
this.effectImage.setImageResource(this.drawableEffect);
}
}
I have buttons in the main view which I have registered onClick listeners to so you can switch weapons to your liking.. Here is one for example:
grenadeButton = (ImageButton)findViewById(R.id.grenadeButton);
grenadeButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if (MainActivity.this.weapon != Weapon.GRENADE) {
// Change the background color.
resetButtons();
v.setBackgroundResource(R.drawable.background_selected);
// Change weapons.
MainActivity.this.currentWeapon = equipWeapon(Weapon.GRENADE);
}
}
});
and the equip weapon method in MainActivity.
public Weapon equipWeapon(int type) {
Weapon weapon = null;
switch (type) {
case Weapon.PISTOL:
weapon = new Pistol();
break;
case Weapon.RIFLE:
weapon = new Rifle();
break;
case Weapon.GRENADE:
weapon = new Grenade();
break;
}
// Play a sound and save changes.
MainActivity.this.weapon = type;
MainActivity.mp.seekTo(0);
MainActivity.mp.start();
return weapon;
}
Now, I appreciate you taking the time to review all this code. I know that's a lot to digest for what I'm assuming is a simple issue. I'm also aware of the rules of this forum and I have attempted to search for this issue already, but I'm not sure if I was using the right queries, as I did not find anything related to this issue.
Here we go:
When you start the app, the pistol is automatically equipped so you can start shooting immediately. It shoots fine, I get a bullet noise, and bullet holes all over the picture of the person's face ^_^. The rifle works fine as well (but it uses the same noise and graphic as the pistol, so it could very well be failing as well. Refer below).
When I switch to the grenade, I here a lovely explosion sound, but the image displayed is still a bullet hole and not an explosion.. I have no earthly idea how to fix this and I am stumped..
When I create the grenade object I specifically assign the drawable resource to the instance variable...
this.drawableEffect = R.drawable.boom;
Yet it still displays R.drawable.bullet_hole1....
Please let me know if you need more information and ask any questions you need to..
Thank you for your time..
I'm so stupid.....
MainActivity.equipWeapon
wasn't calling
Weapon.equipWeapon....
So the weapon wasn't changing... Oy' vey...

Android Sliding Menu - user name and image in it. How can I load them once and forever

I have a sliding menu and an action bar in my Android app.
At the top of the sliding menu there is a user name and a user picture
If I set them once, they are lost when I close and open the menu again.
So every time its opened Im calling a user details downloader class and Im setting the name and the avatar again, which is very irritating.
How can I set them once and dont bother with this until the app is closed, no matter whether the sliding menu is opened or closed?
public class AsdActionBarAndSlidingMenu extends AsdActionBar implements IOnUserDetailsAndStatsReceivedListener{
private TextView tvSlidingMenuUserName;
private Typeface font2;
private UserDetailsAndStatsDownloader mUserDetailsDownloader;
private String userName;
private ImageView ivSlidingMenuUserAvatar;
private String avatarPath;
private Bitmap ivSlidingMenuUserBitmap;
private static final String APP_SHARED_PREFS = "asdasd_prefs";
SharedPreferences sharedPrefs;
public Editor editor;
protected int currentlyLoggedInUser;
protected String currentlyLoggedInUserString;
public AsdActionBarAndSlidingMenu(int titleRes) {
super(R.string.app_name);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setBehindContentView(R.layout.menu_frame);
sharedPrefs = getApplicationContext().getSharedPreferences(APP_SHARED_PREFS, Context.MODE_PRIVATE);
currentlyLoggedInUser = sharedPrefs.getInt("currentLoggedInUserId", 0);
currentlyLoggedInUserString = Integer.toString(currentlyLoggedInUser);
tvSlidingMenuUserName = (TextView) findViewById(R.id.tvSlidingMenuUserName);
tvSlidingMenuUserName.setTypeface(font2);
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.menu_frame, new AsdSlidingMenuListFragment()).commit();
getSlidingMenu().setTouchModeAbove(SlidingMenu.TOUCHMODE_FULLSCREEN);
setSlidingActionBarEnabled(true);
getSlidingMenu().setOnOpenedListener(new OnOpenedListener() {
#Override
public void onOpened() {
mUserDetailsDownloader = new UserDetailsAndStatsDownloader(currentlyLoggedInUserString, AsdActionBarAndSlidingMenu.this, AsdActionBarAndSlidingMenu.this);
mUserDetailsDownloader.downloadUserDetailsAndStats();
}
});
}
#Override
public void onUserDetailsAndStatsReceivedListener(UserDetailsAndStats userDetailsAndStats) {
userName = userDetailsAndStats.getUserName();
tvSlidingMenuUserName = (TextView) findViewById(R.id.tvSlidingMenuUserName);
tvSlidingMenuUserName.setText(userName);
avatarPath = userDetailsAndStats.getUserAvatar();
ivSlidingMenuUserBitmap = BitmapFactory.decodeFile(avatarPath);
ivSlidingMenuUserAvatar = (ImageView) findViewById(R.id.ivSlidingMenuUserAvatar);
ivSlidingMenuUserAvatar.setImageBitmap(ivSlidingMenuUserBitmap);
}
}
But, what gets unset is the BitMap, or the Views (ivSlidingMenuUserAvatar and tvSlidingMenuUserName)?
I dont know how you created UserDetailsAndStatsDownloader, but probably onUserDetailsAndStatsReceivedListener is called in a diferent thread. That could cause that when that thread is not running, and those views are unused, you can lose them. But im not sure.
Anyways, try to inflating the views in you onCreate, and also retrieving the data after that
public void onCreate(Bundle savedInstanceState) {
...
tvSlidingMenuUserName = (TextView) findViewById(R.id.tvSlidingMenuUserName);
ivSlidingMenuUserAvatar = (ImageView) findViewById(R.id.ivSlidingMenuUserAvatar);
mUserDetailsDownloader = new UserDetailsAndStatsDownloader(currentlyLoggedInUserString, AsdActionBarAndSlidingMenu.this, AsdActionBarAndSlidingMenu.this);
mUserDetailsDownloader.downloadUserDetailsAndStats();
}
and let the listener just like this
#Override
public void onUserDetailsAndStatsReceivedListener(UserDetailsAndStats userDetailsAndStats){
userName = userDetailsAndStats.getUserName();
tvSlidingMenuUserName.setText(userName);
avatarPath = userDetailsAndStats.getUserAvatar();
ivSlidingMenuUserBitmap = BitmapFactory.decodeFile(avatarPath);
ivSlidingMenuUserAvatar.setImageBitmap(ivSlidingMenuUserBitmap);
}
then, remove getSlidingMenu().setOnOpenedListener(...) and lets see what happend.
Besides, you should use any cache method for your downloads, so even if you need to download again file, if you have already done, no network operation is involved. For example you can do it like is shown in android-imagedownloader that is a really easy example.
/*
* Cache-related fields and methods.
*
* We use a hard and a soft cache. A soft reference cache is too aggressively cleared by the
* Garbage Collector.
*/
private static final int HARD_CACHE_CAPACITY = 10;
private static final int DELAY_BEFORE_PURGE = 10 * 1000; // in milliseconds
// Hard cache, with a fixed maximum capacity and a life duration
private final HashMap<String, Bitmap> sHardBitmapCache =
new LinkedHashMap<String, Bitmap>(HARD_CACHE_CAPACITY / 2, 0.75f, true) {
#Override
protected boolean removeEldestEntry(LinkedHashMap.Entry<String, Bitmap> eldest) {
if (size() > HARD_CACHE_CAPACITY) {
// Entries push-out of hard reference cache are transferred to soft reference cache
sSoftBitmapCache.put(eldest.getKey(), new SoftReference<Bitmap>(eldest.getValue()));
return true;
} else
return false;
}
};
// Soft cache for bitmaps kicked out of hard cache
private final static ConcurrentHashMap<String, SoftReference<Bitmap>> sSoftBitmapCache =
new ConcurrentHashMap<String, SoftReference<Bitmap>>(HARD_CACHE_CAPACITY / 2);
private final Handler purgeHandler = new Handler();
private final Runnable purger = new Runnable() {
public void run() {
clearCache();
}
};
/**
* Adds this bitmap to the cache.
* #param bitmap The newly downloaded bitmap.
*/
private void addBitmapToCache(String url, Bitmap bitmap) {
if (bitmap != null) {
synchronized (sHardBitmapCache) {
sHardBitmapCache.put(url, bitmap);
}
}
}
/**
* #param url The URL of the image that will be retrieved from the cache.
* #return The cached bitmap or null if it was not found.
*/
private Bitmap getBitmapFromCache(String url) {
// First try the hard reference cache
synchronized (sHardBitmapCache) {
final Bitmap bitmap = sHardBitmapCache.get(url);
if (bitmap != null) {
// Bitmap found in hard cache
// Move element to first position, so that it is removed last
sHardBitmapCache.remove(url);
sHardBitmapCache.put(url, bitmap);
return bitmap;
}
}
// Then try the soft reference cache
SoftReference<Bitmap> bitmapReference = sSoftBitmapCache.get(url);
if (bitmapReference != null) {
final Bitmap bitmap = bitmapReference.get();
if (bitmap != null) {
// Bitmap found in soft cache
return bitmap;
} else {
// Soft reference has been Garbage Collected
sSoftBitmapCache.remove(url);
}
}
return null;
}
/**
* Clears the image cache used internally to improve performance. Note that for memory
* efficiency reasons, the cache will automatically be cleared after a certain inactivity delay.
*/
public void clearCache() {
sHardBitmapCache.clear();
sSoftBitmapCache.clear();
}
If we have the code of the rest of the classes involved (just the ones you writed) the help could be much more accurate.
This line of code is responsible for loading the bitmap from the avatarPath, right?
ivSlidingMenuUserBitmap = BitmapFactory.decodeFile(avatarPath);
If you want to do it once and forever, you should only do this decoding once and store the value of it elsewhere in your code. You've already stored the value in a field, so you shouldn't need to keep decoding it from the file.
Adding a simple if (ivSlidingMenuUserBitmap != null) before that line should prevent that. Like so:
avatarPath = userDetailsAndStats.getUserAvatar();
if (ivSlidingMenuUserBitmap != null)
ivSlidingMenuUserBitmap = BitmapFactory.decodeFile(avatarPath);
ivSlidingMenuUserAvatar = (ImageView) findViewById(R.id.ivSlidingMenuUserAvatar);
ivSlidingMenuUserAvatar.setImageBitmap(ivSlidingMenuUserBitmap);

android get adMob banner height when appears

I am adding an adMob banner to my app successfully. When banner appears I need to get its height in order to resize all layout elements. I am using the event onReceivedAd, that is properly fired. However, alturaBanner is = 0. Then, how to get its height? thank you.
/** Called when an ad is received. */
#Override
public void onReceiveAd(Ad ad)
{
adView.setVisibility(View.VISIBLE);
int alturaBanner = adView.getHeight();
RelativeLayout.LayoutParams params1 = (android.widget.RelativeLayout.LayoutParams) browse2
.getLayoutParams();
params1.setMargins(0, alturaBanner, 0, 0);
Log.d(LOG_TAG, "onReceiveAd");
Toast.makeText(this, "onReceiveAd", Toast.LENGTH_SHORT).show();
}
You can get the height of any type of banner before it is even added to the layout.
int heightPixels = AdSize.SMART_BANNER.getHeightInPixels(this);
or
int heightPixels = AdSize.FULL_BANNER.getHeightInPixels(myContext);
or for DIP's
int heightDP = AdSize.BANNER.getHeight();
So for your need, you could do this:
/** Called when an ad is received. */
#Override
public void onReceiveAd(Ad ad)
{
adView.setVisibility(View.VISIBLE);
int alturaBanner = AdSize.BANNER.getHeight(); // This gets the adsize, even if the view is not inflated.
RelativeLayout.LayoutParams params1 = (android.widget.RelativeLayout.LayoutParams) browse2
.getLayoutParams();
params1.setMargins(0, alturaBanner, 0, 0);
Log.d(LOG_TAG, "onReceiveAd");
Toast.makeText(this, "onReceiveAd", Toast.LENGTH_SHORT).show();
}
Just change AdSize.BANNER to AdSize.SMART_BANNER or whatever banner type your using.
Add Sizes Get Height
getting the height of the view before it was prepared will always return you 0 .
use the next code in order to get its correct size , no matter which device/screen you have:
private static void runJustBeforeBeingDrawn(final View view, final Runnable runnable)
{
final ViewTreeObserver vto = view.getViewTreeObserver();
final OnPreDrawListener preDrawListener = new OnPreDrawListener()
{
#Override
public boolean onPreDraw()
{
runnable.run();
final ViewTreeObserver vto = view.getViewTreeObserver();
vto.removeOnPreDrawListener(this);
return true;
}
};
vto.addOnPreDrawListener(preDrawListener);
}
inside the given runnable , you can query the real size of the view.
alternatively , you can use addOnGlobalLayoutListener instead of addOnPreDrawListener if you wish.
another approach is to use onWindowFocusChanged (and check that hasFocus==true) , but that's not always the best way ( only use for simple views-creation, not for dynamic creations)
EDIT: Alternative to runJustBeforeBeingDrawn: https://stackoverflow.com/a/28136027/878126
I use the following method to get AdView's height:
adView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int height = adView.getHeight();
if (height > 0) {
// now the height is gotten, you can do things you want
}
}
});
onGlobalLayout() is triggered when the global layout state or the visibility of views within the view tree changes.
Actually, you don't need to wait for appearance of adview to get adMob banner height if you are using smart banner type for showing banner ads.
Use Smart banner as it automatically decides the height of ad based on device size. Use full width of screen to show the ad.
From android developers site:
Smart Banners are ad units that will render screen-wide banner ads on any screen size across different devices in either orientation. Smart Banners help deal with increasing screen fragmentation across different devices by "smartly" detecting the width of the phone in its current orientation, and making the ad view that size.
Three ad heights (in dp, density-independent pixel) are available:
32 - used when the screen height of a device is less than 400
50 - used when the screen height of a device is between 400 and 720
90 - used when the screen height of a device is greater than 720
Now, get the height of AdView and adjust the margin of the layout where you wish to place the banner ad. Once the ad is loaded (by overriding on onAdLoaded API), you know the height using below method:
public static int getAdViewHeightInDP(Activity activity) {
int adHeight = 0;
int screenHeightInDP = getScreenHeightInDP(activity);
if (screenHeightInDP < 400)
adHeight = 32;
else if (screenHeightInDP <= 720)
adHeight = 50;
else
adHeight = 90;
return adHeight;
}
public static int getScreenHeightInDP(Activity activity) {
DisplayMetrics displayMetrics = ((Context) activity).getResources().getDisplayMetrics();
float screenHeightInDP = displayMetrics.heightPixels / displayMetrics.density;
return Math.round(screenHeightInDP);
}
I had the same need to be able to display my own ads when AdMob fails to receive an ad or receives an empty ad (height=0).
I use the following code based on the fact that an AdView extends RelativeLayout:
mAdMobView = new AdView(pActivity, AdSize.SMART_BANNER, Constants.ADMOB_AD_UNIT_ID);
mAdMobView.addOnLayoutChangeListener(new OnLayoutChangeListener() {
#Override
public void onLayoutChange(final View pV, final int pLeft, final int pTop, final int pRight, final int pBottom, final int pOldLeft, final int pOldTop, final int pOldRight, final int pOldBottom) {
final float lAdHeight = mAdMobView.getHeight();
if (lAdHeight == 0) {
Debug.i(LOG_TAG, "mAdMobView.onLayoutChange(...) mAdMobView.height='" + lAdHeight + "'. AdMob returned an empty ad !");
// Show custom ads
} else {
Debug.d(LOG_TAG, "mAdMobView.onLayoutChange(...) mAdMobView.height='" + lAdHeight + "'");
// Make AdView visible
}
}
});
mAdMobView.setAdListener(new AdListener() {
#Override public void onReceiveAd(final Ad pAd) {
Debug.d(LOG_TAG, "onReceiveAd(...) AdMob ad received (mAdMobView.visibility='" + mAdMobView.getVisibility() + "').");
}
#Override public void onPresentScreen(final Ad pAd) {
Debug.d(LOG_TAG, "onPresentScreen(...)");
}
#Override public void onLeaveApplication(final Ad pAd) {
Debug.d(LOG_TAG, "onLeaveApplication(...)");
}
#Override public void onDismissScreen(final Ad pAd) {
Debug.d(LOG_TAG, "onDismissScreen(...)");
}
#Override
public void onFailedToReceiveAd(final Ad pAd, final ErrorCode pErrorCode) {
Debug.i(LOG_TAG, "onFailedToReceiveAd(...) AdMob ad error (" + pErrorCode + ").");
// Show custom ads
}
});
The code in 'onLayoutChange' is executed every time Admob receives a new ad.
EDIT: My answer is not proper since this method was added with the API 11... I changed it for the use of onPreDraw() as explained in the previous answer.

Categories

Resources