My problem:
For some requirements i need two different xml layouts for my activity:
One for Landscape mode.
And another one for Landscape-reverse mode (upside-down of Landscape).
Unfortunately Android doesn't allow creating a separate layout for landscape-reverse (like we can do for portrait and landscape with layout-land and layout-port).
AFAIK, the only way is to change the activity-xml from java code.
What i've tried:
1) Override onConfigurationChanged() method to detect orientation changes, but i can't figure out if it's Landscape or Landscape-reverse:
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
Log.d("TEST","Landscape");
}
}
( Whith android:configChanges="keyboardHidden|orientation|screenSize|layoutDirection" in my activity tag in manifest)
2) Use an OrientationEventListener with SENSOR_DELAY_NORMAL as suggested in this answer but the device orientation changes before entering my if blocks, so i get a delayed update of the view:
mOrientationEventListener = new OrientationEventListener(this, SensorManager.SENSOR_DELAY_NORMAL){
#Override
public void onOrientationChanged(int orientation) {
if (orientation==0){
Log.e("TEST", "orientation-Portrait = "+orientation);
} else if (orientation==90){
Log.e("TEST", "orientation-Landscape = "+orientation);
} else if(orientation==180){
Log.e("TEST", "orientation-Portrait-rev = "+orientation);
}else if (orientation==270){
Log.e("TEST", "orientation-Landscape-rev = "+orientation);
} else if (orientation==360){
Log.e("TEST", "orientation-Portrait= "+orientation);
}
}};
My question:
Is there a better solution to change activity-layout between "Landscape" and "Landscape-reverse" orientation?
Any suggestions are highly appreciated.
Are you trying as suggested here?. You may handle an event with a different types of configuration reverse and standart by using activity attribute sensorLandscape
EDITED: Try to use Display.getOrientation as described here http://android-developers.blogspot.in/2010/09/one-screen-turn-deserves-another.html
And do not forget to set configChanges flag on activity in manifest to handle changes manualy in onConfigurationChanges().
So it seems like only way to do this is to listen SensorManager as frequently as possible.
SensorManager sensorMan = (SensorManager)getSystemService(SENSOR_SERVICE);
Sensor sensor = sensorMan.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
sensorMan.registerListener(...)
in order to achive this goal, you need to implement a rotation listener,
you need also to know that android destroy objects and recreate them
to load your layouts, values... based on configuration qualifiers
STEP 01: create a Java interface [rotationCallbackFn]
public interface rotationCallbackFn {
void onRotationChanged(int lastRotation, int newRotation);
}
STEP 02: create a Java class [rotationListenerHelper]
import android.content.Context;
import android.hardware.SensorManager;
import android.view.OrientationEventListener;
import android.view.WindowManager;
public class rotationListenerHelper {
private int lastRotation;
private WindowManager windowManager;
private OrientationEventListener orientationEventListener;
private rotationCallbackFn callback;
public rotationListenerHelper() {
}
public void listen(Context context, rotationCallbackFn callback) {
// registering the listening only once.
stop();
context = context.getApplicationContext();
this.callback = callback;
this.windowManager = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
this.orientationEventListener = new OrientationEventListener(context, SensorManager.SENSOR_DELAY_NORMAL) {
#Override
public void onOrientationChanged(int orientation) {
WindowManager localWindowManager = windowManager;
rotationCallbackFn localCallback = rotationListenerHelper.this.callback;
if(windowManager != null && localCallback != null) {
int newRotation = localWindowManager.getDefaultDisplay().getRotation();
if (newRotation != lastRotation) {
localCallback.onRotationChanged(lastRotation, newRotation);
lastRotation = newRotation;
}
}
}
};
this.orientationEventListener.enable();
lastRotation = windowManager.getDefaultDisplay().getRotation();
}
public void stop() {
if(this.orientationEventListener != null) {
this.orientationEventListener.disable();
}
this.orientationEventListener = null;
this.windowManager = null;
this.callback = null;
}
}
STEP 03: add these statements to your mainActivity
// declaration
private rotationListenerHelper rotationListener = null;
private Context mContext;
//...
/* constructor ----------------------------------------------------------------*/
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = this;
final int curOrientation = getWindowManager().getDefaultDisplay().getRotation();
switch (curOrientation) {
case 0:
//. SCREEN_ORIENTATION_PORTRAIT
setContentView(R.layout.your_layout_port);
break;
//----------------------------------------
case 2:
//. SCREEN_ORIENTATION_REVERSE_PORTRAIT
setContentView(R.layout.your_layout_port_rev);
break;
//----------------------------------------
case 1:
//. SCREEN_ORIENTATION_LANDSCAPE
setContentView(R.layout.your_layout_land);
break;
//----------------------------------------
case 3:
//. SCREEN_ORIENTATION_REVERSE_LANDSCAPE
setContentView(R.layout.your_layout_land_rev);
break;
//----------------------------------------
} /*endSwitch*/
rotationListener = new rotationListenerHelper();
rotationListener.listen(mContext, rotationCB);
//...
}
private rotationCallbackFn rotationCB = new rotationCallbackFn() {
#Override
public void onRotationChanged(int lastRotation, int newRotation) {
Log.d(TAG, "onRotationChanged: last " + (lastRotation) +" new " + (newRotation));
/**
* no need to recreate activity if screen rotate from portrait to landscape
* android do the job in order to reload resources
*/
if (
(lastRotation == 0 && newRotation == 2) ||
(lastRotation == 2 && newRotation == 0) ||
(lastRotation == 1 && newRotation == 3) ||
(lastRotation == 3 && newRotation == 1)
)
((Activity) mContext).recreate();
}
};
/* destructor -----------------------------------------------------------------*/
#Override
protected void onDestroy() {
rotationListener.stop();
rotationListener = null;
Log.i(TAG, "onDestroy: activity destroyed");
super.onDestroy();
}
FINAL STEP : ENJOY
Related
I'm adding a small TextView at the bottom of my app when the app goes offline. So I have a BroadcastReceiver that monitors network connectivity changes and in the onReceive, I show the banner. Here is the banner class which adds the TextView on top of the existing view:
public static void show() {
if (!isShowing && !isAppBackgrounded()) {
MyApplication app = MyApplication.getInstance();
WindowManager windowManager = (WindowManager) app.getSystemService(Context.WINDOW_SERVICE);
Resources res = app.getResources();
TextView offlineTv = app.getOfflineTv();
if (offlineTv.getWindowToken() != null) {
return;
}
offlineTv.setText("Offline");
offlineTv.setTextColor(ContextCompat.getColor(app, R.color.yellow));
offlineTv.setGravity(Gravity.CENTER);
offlineTv.setBackgroundColor(ContextCompat.getColor(app, R.color.dark_grey));
offlineTv.setTextSize(TypedValue.COMPLEX_UNIT_SP, app.getResources().getInteger(R.integer.offline_banner_text_size));
WindowManager.LayoutParams params = createLayoutParams(WindowManager.LayoutParams.TYPE_TOAST, null);
windowManager.addView(offlineTv, params);
isShowing = true;
}
}
Here is the createLayoutParams method
private static WindowManager.LayoutParams createLayoutParams(int type, #Nullable IBinder windowToken) {
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
layoutParams.format = PixelFormat.TRANSLUCENT;
layoutParams.width = WindowManager.LayoutParams.MATCH_PARENT;
layoutParams.height = 25;
layoutParams.gravity = GravityCompat.getAbsoluteGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM, ViewCompat.LAYOUT_DIRECTION_LTR);
layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
layoutParams.type = type;
layoutParams.token = windowToken;
layoutParams.windowAnimations = android.R.style.Animation_Toast;
return layoutParams;
}
This code works fine on all the devices but 7.1.1 devices. In 7.1.1 device, the TextView shows for a while and then disappears. There is just a blank white space instead of the TextView on 7.1.1 devices. Any idea why is it happening?
EDIT: as asked in the comment, here is how I get the TextView: This is MyApplication class extending Application:
TextView offlineTv = null;
/** Get the TextView to show the offline message */
public TextView getOfflineTv() {
if (offlineTv == null) {
offlineTv = new TextView(this);
}
return offlineTv;
}
/** Clear the offline TextView once we are done showing it */
public void clearOfflineTv() {
if (offlineTv != null) {
offlineTv = null;
}
}
And this is my BroadcastReceiver, where I show / hide it:
public class DSConnectionChangeReceiver extends BroadcastReceiver {
/**
* Connection-changed callback
* #param context Context
* #param intent Intent
*/
#Override
public void onReceive(Context context, Intent intent) {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = cm.getActiveNetworkInfo();
boolean connected = false;
boolean isCellularData = false;
if (activeNetworkInfo != null) {
connected = activeNetworkInfo.isAvailable() && activeNetworkInfo.isConnected();
int type = activeNetworkInfo.getType();
isCellularData = (type == ConnectivityManager.TYPE_MOBILE) || (type == ConnectivityManager.TYPE_MOBILE_DUN);
}
if (connected) {
if (OfflineBanner.isShowing()) {
OfflineBanner.dismiss();
}
} else {
OfflineBanner.show();
}
}
}
The problem is caused by you adding the android.R.style.Animation_Toast windowAnimation. When the animation finishes on an actual Toast the whole toast would disappear. In this case your view is in the hierarchy so instead of disappearing, it becomes blank.
What you should do is leave layoutParams.windowAnimations off of the params and instead create and attach the view with the visibility set to View.GONE then animate the view onto the screen manually
Manually animating the view can be achieved with the following utility:
Animation animIn = AnimationUtils.makeInAnimation(context, true);
textView.setAnimation(animIn);
textView.setVisibility(View.VISIBLE);
textView.animate();
Snackbar Alternative:
public final class ConnectionBar {
private static boolean mIsConnected = true; //static to preserve state
private static ConnectionReceiver mReceiver; //static to detect leaks
private static SnackBar mSnack;
private ConnectionBar() { /* required */ )
public static void prepare(Context ctx) {
if (mReceiver != null) {
Log.e(TAG, "WARNING previous ConnectionBar was leaked");
}
mReceiver = new ConnectionReceiver();
ctx.registerBroadcastReceiver(mReceiver);
if (!mIsConnected) { //static so will remember from last screen
showBar(ctx);
}
}
private static void showBar(Context ctx) {
if (mSnack == null) {
mSnack = Snackbar.make(view, message, SnackBar.LENGTH_INDEFINITE);
mSnack.show();
}
}
public static void release(Context ctx) {
if (mReceiver != null) {
ctx.unregisterBroadcastReceiver(mReceiver);
mReceiver = null;
}
if (mSnack != null) {
mSnack.dismiss();
}
}
private static class ConnectionReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = cm.getActiveNetworkInfo();
boolean isCellularData = false; //migrate this how you want
if (activeNetworkInfo != null) {
ConnectionBar.mIsConnected = activeNetworkInfo.isAvailable() && activeNetworkInfo.isConnected();
int type = activeNetworkInfo.getType();
isCellularData = (type == ConnectivityManager.TYPE_MOBILE) || (type == ConnectivityManager.TYPE_MOBILE_DUN);
}
}
if (connected && ConnectionBar.mSnack != null) {
ConnectionBar.mSnack.dismiss(); //check this, might need to wrap in runOnUiThread
} else {
ConnectionBar.showBar(context);
}
}
}
Then in your activity:
public void onResume() {
ConnectionBar.prepare(this); //takes care of setting br too
}
public void onPause() {
ConnectionBar.release(this);
}
If you want View in WindowManager remains more than BroadcastReciever Lifecycle you need to do it inside a class extends of service. check out this tutorial
I think still there is problems with Lifecycle. How you use it and how the system handles it.
If you want to force system not to kill your service (and not to remove WindowManager) you have 3 options.
return OnStartCommand with the proper flag.
return Service.START_REDELIVER_INTENT;
add foreground notification
startForeground(123, NotificationFunction());
and If you have lots of processes to do add Accessibility Service. check out this
This is an intented behaviour since Android 7.1 to prevent apps from using a toast view to overlay other apps indefinitely. Whenever you use a TYPE_TOAST view, the system will impose a maximum of 3.5 seconds (i.e. that of a LONG toast) for the display of your view (and also change the view animation to the internal Toast style), after which your toast view will be hidden, EXCEPT where your app is the currently focused one.
To avoid crashing apps, your view still remains on the view hierarchy. In other words, you can still call removeView on it after it is hidden by the system, without causing an illegal state exception.
(Reference: See the commit message to the Android source:
https://github.com/aosp-mirror/platform_frameworks_base/commit/aa07653d2eea38a7a5bda5944c8a353586916ae9 )
To display a view over other apps on Android 7.1 or above you may need to request the SYSTEM_ALERT_WINDOW permission, prompt the user to get the Draw Over Apps permission, and use another view type, such as TYPE_SYSTEM_OVERLAY.
I am making an application where I have a camera inside of a viewPager. I am wondering what would best be suited to "pause" and "resume" the camera so it doesn't hog resources when it is pre-loaded. I have the feeling that stopPreview is better suited for this as it does not release the camera but keeps it however it doesn't display the camera which is the main reason it hogs resources.
Enter & exit application: startCamera() & releaseCamera()
Tab visible & not visible: startPreview() & stop Preview()
Would this be a good rule of thumb?
I had a similar situation. :
If I kept camera (in ViewPager) in on state, the swipe were clunky and OOM exceptions were frequent.
Two options came in my mind:
shift the entire instance in a different thread
OR
use stopPreview() and startPreview()
I went with the second one :
However, instead of doing this on Fragment lifecycle callbacks I gave a button on the fragment which toggled the preview. Reason being, if user is swiping very fast, you can still receive OOm exception since the preview calls will be queued, especially if there are very few fragments in the viewPager.
In essence Release camera onPause(), acquire camera in onResume() and give a groovy button in fragment which will toggle your Preview on the surface!
Hello Karl I had the same things need to implement in view pager. I have circular viewer in which one fragment has the camera fragment. I want to handle the camera preview in such a way so it should not consume the camera resource.
As you know android view pager default load two fragment in to the memory. We implemented the view pager change listener and call the fragment method to start and stop the preview. even also destroy the camera preview in on destroy method of fragment.
class ViewPagerChangeListener implements ViewPager.OnPageChangeListener {
int currentPosition = DEFAULT_FRAGMENT;
#Override
public void onPageScrollStateChanged(int state) {
TimberLogger.d(TAG, "onPageScrollStateChanged");
}
#Override
public void onPageScrolled(int index, float arg1, int arg2) {
TimberLogger.d(TAG, "onPageScrolled" + index);
}
#Override
public void onPageSelected(int position) {
mWatchPosition = position;
TimberLogger.d(TAG, "onPageSelected" + mWatchPosition);
int newPosition = 0;
if (position > 4) {
newPosition = position;
}
TimberLogger.d(TAG, "newPosition" + newPosition);
/**
* Listener knows the new position and can call the interface method
* on new Fragment with the help of PagerAdapter. We can here call
* onResumeFragment() for new fragment and onPauseFragment() on the
* current one.
*/
// new fragment onResume
loadedFragment(newPosition).onResumeFragment();
// current fragment onPuase called
loadedFragment(currentPosition).onPauseFragment();
currentPosition = newPosition;
TimberLogger.d(TAG, "currentPosition" + currentPosition);
}
}
See the two method onResumeFragment and onPuaseFragment this two are the custom function each view pager fragment implements. In view pager change event we call the pause of current fragment and onResume of the new fragment.
// new fragment onResume
loadedFragment(newPosition).onResumeFragment();
// current fragment onPuase called
loadedFragment(currentPosition).onPauseFragment();
You can write your camera start preview inside custom method onResumeFragment and stop preview in onPauseFragment and also make sure you should override the onDestory() method of your camera fragment for release the camera resources.
The best solution will be to startCamera() in onResume(), and release it in onPause(), so you can handle, that camera is not free in onResume().
In ViewPager you can startPreview(), when fragment with it is selected, and stopPreview() otherwise. Also u can startPreview() in onCreateView() and stopPreview() in onDestroyView() in fragment.
This takes care of most of the operations CameraPreview.java:
package com.example.fela;
import android.app.Activity;
import android.content.Context;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.hardware.Camera.ErrorCallback;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.Size;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.WindowManager;
import java.util.List;
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder holder;
private Camera camera;
private int cameraId;
private Activity activity;
private CameraPreviewActivityInterface activityInterface;
public CameraPreview(Activity activity, int cameraId) {
super(activity);
try {
activityInterface = (CameraPreviewActivityInterface) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement ExampleFragmentCallbackInterface ");
}
this.activity = activity;
this.cameraId = cameraId;
holder = getHolder();
holder.addCallback(this);
// deprecated setting, but required on Android versions prior to 3.0
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void surfaceCreated(SurfaceHolder holder) {}
/**
* custom camera tweaks and startPreview()
*/
public void refreshCamera() {
if (holder.getSurface() == null || camera == null) {
// preview surface does not exist, camera not opened created yet
return;
}
Log.i(null, "CameraPreview refreshCamera()");
// stop preview before making changes
try {
camera.stopPreview();
} catch (Exception e) {
// ignore: tried to stop a non-existent preview
}
int rotation = ((WindowManager) activity.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();
int degrees = 0;
// specifically for back facing camera
switch (rotation) {
case Surface.ROTATION_0:
degrees = 90;
break;
case Surface.ROTATION_90:
degrees = 0;
break;
case Surface.ROTATION_180:
degrees = 270;
break;
case Surface.ROTATION_270:
degrees = 180;
break;
}
camera.setDisplayOrientation(degrees);
setCamera(camera);
try {
camera.setPreviewDisplay(holder);
camera.startPreview();
} catch (Exception e) {
// this error is fixed in the camera Error Callback (Error 100)
Log.d(VIEW_LOG_TAG, "Error starting camera preview: " + e.getMessage());
}
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
Log.i(null, "CameraPreview surfaceChanged()");
// if your preview can change or rotate, take care of those events here.
// make sure to stop the preview before resizing or reformatting it.
// do not start the camera if the tab isn't visible
if(activityInterface.getCurrentPage() == 1)
startCamera();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {}
public Camera getCameraInstance() {
Camera camera = Camera.open();
// parameters for camera
Parameters params = camera.getParameters();
params.set("jpeg-quality", 100);
params.set("iso", "auto");
params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
params.setPictureFormat(PixelFormat.JPEG);
// set the image dimensions
List<Size> sizes = params.getSupportedPictureSizes();
int max = 0, width = 0, height = 0;
for(Size size : sizes) {
if(max < (size.width*size.height)) {
max = (size.width*size.height);
width = size.width;
height = size.height;
}
}
params.setPictureSize(width, height);
camera.setParameters(params);
// primarily used to fix Error 100
camera.setErrorCallback(new ErrorCallback() {
#Override
public void onError(int error, Camera camera) {
if(error == Camera.CAMERA_ERROR_SERVER_DIED) {
releaseCamera();
startCamera();
}
}
});
return camera;
}
/**
* intitialize a new camera
*/
protected void startCamera() {
if(getCamera() == null)
setCamera(getCameraInstance());
refreshCamera();
}
/**
* release camera so other applications can utilize the camera
*/
protected void releaseCamera() {
// if already null then the camera has already been released before
if (getCamera() != null) {
getCamera().release();
setCamera(null);
}
}
public Camera getCamera() {
return camera;
}
public void setCamera(Camera camera) {
this.camera = camera;
}
public void setCameraId(int cameraId) {
this.cameraId = cameraId;
}
/**
* get the current viewPager page
*/
public interface CameraPreviewActivityInterface {
public int getCurrentPage();
}
}
In my FragmentCamera.java file:
private CameraPreview cameraPreview;
// code...
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// code...
cameraPreview = new CameraPreview(getActivity(), cameraId);
previewLayout.addView(cameraPreview);
// code...
}
// code...
#Override
public void onPause() {
super.onPause();
cameraPreview.releaseCamera();
}
#Override
public void onResume() {
super.onResume();
cameraPreview.startCamera();
}
protected void fragmentVisible() {
onResume();
}
protected void fragmentNotVisible() {
onPause();
}
And the MainActivity.java file (implements CameraPreviewActivityInterface):
viewPager.setOnPageChangeListener(new OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
currentPage = position;
if (currentPage == 1) {
fragmentCamera.fragmentVisible();
} else {
fragmentCamera.fragmentNotVisible();
}
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
#Override
public int getCurrentPage() {
return currentPage;
}
I have a DrawerLayout which contains a FrameLayout and ListView in my app, I have to show the camera in the FrameLayout, I've done that fine (as following code) and the camera works correctly. The problem is when moving from portrait orientation to (right-Landscape orientation or left-landscape orientation), or vice versa, it take the mobile a long time to make changes, the problem does not appear when moving from right-Landscape orientation or left-landscape orientation or vice versa.
How could I make this operation as fast as I can?
public class ShowCamera extends SurfaceView implements SurfaceHolder.Callback{
//This ShowCamera class is a helpful class
private SurfaceHolder holdMe;
private Camera theCamera;
public ShowCamera(Context context,Camera camera) {
super(context);
theCamera = camera;
holdMe = getHolder();
holdMe.addCallback(this);
}
#Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
try {
theCamera.setPreviewDisplay(holder);
theCamera.startPreview();
synchronized (holder) {
}
} catch (Exception e) {}
}
#Override
public void surfaceDestroyed(SurfaceHolder arg0) {
holdMe.removeCallback(this);
theCamera.release();
}
}
Now the original class is:
public class MainActivity extends Activity {
private DrawerLayout mDrawerLayout;
private ListView mDrawerList;
String[] options = {"op1", "op2", "op3", "op4", "op5"}; //for the DrawerLayout
int[] icons = { 0,R.drawable.hospital,R.drawable.education,R.drawable.police,R.drawable.food}; //for the DrawerLayout
private Camera cameraObject;
private ShowCamera showCamera;
public static Camera getCamIfAvailable(){
Camera cam = null;
try { cam = Camera.open();}
catch (Exception e){}
return cam;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
cameraObject = getCamIfAvailable();
new Thread(new Runnable()
{
int rotation = getWindowManager().getDefaultDisplay().getRotation();
#Override
public void run()
{
switch(rotation){
case 0: // portrait
cameraObject.setDisplayOrientation(90);
break;
case 1: // left Landscape
cameraObject.setDisplayOrientation(0);
break;
case 3: //right Landscape
cameraObject.setDisplayOrientation(180);
break;
}
}
}).start();
showCamera = new ShowCamera(this, cameraObject);
FrameLayout preview = (FrameLayout) findViewById(R.id.content_frame);
preview.addView(showCamera);
//.
//.
//.
//.
//. Here, code for Drawerlayout no problrm
//.
//.
//.
}
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// Pass any configuration change to the drawer toggls
mDrawerToggle.onConfigurationChanged(newConfig);
// Checks the orientation of the screen
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
}
}
.
.
.
.
} // End of the class MainActivity
Also I have put in the manifest file the following as first answer mentioned here:
<activity android:name=".MyActivity"
android:configChanges="orientation|screenSize"
android:label="#string/app_name">
Any help will be appreciated.
Camera.open() is an heavy operation for the system. It is only triggered when a configuration change is happening, such as when you rotate your device from landscape to portrait and vice-versa.
When flipping the device (from portrait to reversed portrait, or from landscape to reversed landscape) this doesn't trigger a configuration change, only the screen rendering is flipped and therefore you don't call Camera.open() again.
So, you won't be able to make it faster (even though I recommend you to take a look at the systrace tool to see what's really happening when you rotate your screen).
But, I strongly encourage you to try calling the Camera.open() from a background Thread to avoid freezing the UI.
I have a DrawerLayout which contains a FrameLayout and ListView in my app, I have to show the camera in the FrameLayout, I've done that fine (as following code) and the camera works correctly. The problem is when moving from portrait orientation to (right-Landscape orientation or left-landscape orientation), or vice versa, it take the mobile a long time to make changes, the problem does not appear when moving from right-Landscape orientation or left-landscape orientation or vice versa.
How could I make this operation as fast as I can?
public class ShowCamera extends SurfaceView implements SurfaceHolder.Callback{
//This ShowCamera class is a helpful class
private SurfaceHolder holdMe;
private Camera theCamera;
public ShowCamera(Context context,Camera camera) {
super(context);
theCamera = camera;
holdMe = getHolder();
holdMe.addCallback(this);
}
#Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
try {
theCamera.setPreviewDisplay(holder);
theCamera.startPreview();
synchronized (holder) {
}
} catch (Exception e) {}
}
#Override
public void surfaceDestroyed(SurfaceHolder arg0) {
holdMe.removeCallback(this);
theCamera.release();
}
}
Now the original class is:
public class MainActivity extends Activity {
private DrawerLayout mDrawerLayout;
private ListView mDrawerList;
String[] options = {"op1", "op2", "op3", "op4", "op5"}; //for the DrawerLayout
int[] icons = { 0,R.drawable.hospital,R.drawable.education,R.drawable.police,R.drawable.food}; //for the DrawerLayout
private Camera cameraObject;
private ShowCamera showCamera;
public static Camera getCamIfAvailable(){
Camera cam = null;
try { cam = Camera.open();}
catch (Exception e){}
return cam;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
cameraObject = getCamIfAvailable();
new Thread(new Runnable()
{
int rotation = getWindowManager().getDefaultDisplay().getRotation();
#Override
public void run()
{
switch(rotation){
case 0: // portrait
cameraObject.setDisplayOrientation(90);
break;
case 1: // left Landscape
cameraObject.setDisplayOrientation(0);
break;
case 3: //right Landscape
cameraObject.setDisplayOrientation(180);
break;
}
}
}).start();
showCamera = new ShowCamera(this, cameraObject);
FrameLayout preview = (FrameLayout) findViewById(R.id.content_frame);
preview.addView(showCamera);
//.
//.
//.
//.
//. Here, code for Drawerlayout no problrm
//.
//.
//.
}
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// Pass any configuration change to the drawer toggls
mDrawerToggle.onConfigurationChanged(newConfig);
// Checks the orientation of the screen
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
}
}
.
.
.
.
} // End of the class MainActivity
Also I have put in the manifest file the following as first answer mentioned here:
<activity android:name=".MyActivity"
android:configChanges="orientation|screenSize"
android:label="#string/app_name">
Any help will be appreciated.
Camera.open() is an heavy operation for the system. It is only triggered when a configuration change is happening, such as when you rotate your device from landscape to portrait and vice-versa.
When flipping the device (from portrait to reversed portrait, or from landscape to reversed landscape) this doesn't trigger a configuration change, only the screen rendering is flipped and therefore you don't call Camera.open() again.
So, you won't be able to make it faster (even though I recommend you to take a look at the systrace tool to see what's really happening when you rotate your screen).
But, I strongly encourage you to try calling the Camera.open() from a background Thread to avoid freezing the UI.
I have 2 Activities, MainActivity and VideoPlayerActivity. In the MainActivity I have a socket that I can connect to it from my PC using telnet. Then I can write line and it will execute the commands I send to it. for example if I write start it will start VideoPlayerActivity and plays a movie.
Now I want to control the screen brightness. In the MainActivity when the VideoPlayerActivity is not started yet, I can easily write a command in telnet like brightness=0.1 and that will set the brightness of the MainActivity to 0.1:
if(msg.startsWith("brightness="))
{
String brightText = msg.substring(msg.indexOf('=') + 1, msg.length());
BrightnessValue = Float.parseFloat(brightText);
WindowManager.LayoutParams lp = getWindow().getAttributes();
lp.screenBrightness = BrightnessValue;
getWindow().setAttributes(lp);
if(_videoPlayerIntent != null && _videoPlayerActivity.isActive)
{
_videoPlayerActivity.setBrightnessLevel(BrightnessValue);
}
}
Now the problem is, when VideoActivity starts, it ignores the preset brightness and will use the system defined brightness. So I put this method in VideoActivity :
public void setBrightnessLevel(float value)
{
WindowManager.LayoutParams lp = getWindow().getAttributes();
lp.screenBrightness = value;
getWindow().setAttributes(lp);
}
but as soon as I write command to change brightness the whole app stops. Because of this section in the first code I put above in the question:
if(_videoPlayerIntent != null && _videoPlayerActivity.isActive)
{
Log.d("CALLING VIDEOACTIVITY", "SET BRIGHTNESS");
_videoPlayerActivity.setBrightnessLevel(BrightnessValue);
}
Can you tell me how can I handle this situation? I need to be able to change brightness of screen when the VideoActivity is running, and my socket is in MainActivity...
This is the method in VideoActivity....I tried to make it static then the problem is I can not access getWindow() if the method is static:
public void setBrightnessLevel(float value)
{
WindowManager.LayoutParams lp = getWindow().getAttributes();
lp.screenBrightness = value;
getWindow().setAttributes(lp);
}
You need a handle to that second Activity to set it's window brightness so i suggest you to make a model that tells the activity creation and destruction to the first Activity.
Here is the model for listening the activitys state:
public class ActivityStateListenerModel {
public interface OnActivityStateChangedListener {
void activityStarted(Activity activity);
void activityDestroyed();
}
private static ActivityStateListenerModel mInstance;
private OnActivityStateChangedListener mListener;
public static ActivityStateListenerModel getInstance() {
if(mInstance == null) {
mInstance = new ActivityStateListenerModel();
}
return mInstance;
}
public void setListener(OnActivityStateChangedListener listener) {
mListener = listener;
}
public void activityStarted(Activity activity) {
mListener.activityStarted(activity);
}
public void activityDestroyed() {
mListener.activityDestroyed();
}
}
And you need to implement OnActivityStateChangedListener interface in your MainActivity and set it to listen the changes:
public class MainActivity extends Activity implements
OnActivityStateChangedListener {
// in onCreate(...)
ActivityStateListenerModel.getInstance().setListener(this);
Then the callbacks, in those we set flag what we need to check to know is the activity still running:
private static boolean mOtherActivityStarted;
private static Activity mOtherActivity;
#Override
public void activityStarted(Activity activity) {
Log.d(TAG, "Second activity created");
mOtherActivityStarted = true;
mOtherActivity = activity;
}
#Override
public void activityDestroyed() {
Log.d(TAG, "Second activity destroyed");
mOtherActivityStarted = false;
mOtherActivity = null;
}
And when your socket reads data you just do this in your MainActivity:
if(mOtherActivity != null && mOtherActivityStarted) {
SecondActivity.setBrightnessLevel(brightnessValue, mOtherActivity);
}
Then you have that other Activity (VideoActivity), so there you need to notify the model that activity is created or destroyed:
// in onCreate(...)
// This gives the handle to MainActivity
ActivityStateListenerModel.getInstance().activityStarted(this);
// in onDestroy()
ActivityStateListenerModel.getInstance().activityDestroyed();
And the method that changes the brightess from VideoActivity (in this case this method can be anywhere because we pass the activity in the parameters):
static public void setBrightnessLevel(float value, Activity activity)
{
Window window = activity.getWindow();
if(window != null) {
Log.d(MainActivity.TAG, "Setting brightness to " + activity.getClass().getName());
WindowManager.LayoutParams lp = window.getAttributes();
lp.screenBrightness = value;
window.setAttributes(lp);
}
}
and also it's a good habit to name your variables starting with lower case letter...
(BrightnessValue => brightnessValue)