How to handle screen rotation/orientation in Xamarin.Forms/XLabs? - android

I'm trying to determine when a screen is rotated (in Android) using the XLabs method detailed here How to handle screen rotation/orientation in Xamarin Forms? and I'm having trouble with it.
I override the OnConfigurationChanged method in MainActivity
public override void OnConfigurationChanged (Android.Content.Res.Configuration newConfig)
{
base.OnConfigurationChanged (newConfig);
var xapp = Resolver.Resolve<IXFormsApp> ();
if (xapp == null)
return;
switch (newConfig.Orientation) {
case Android.Content.Res.Orientation.Landscape:
xapp.Orientation = XLabs.Enums.Orientation.Landscape;
break;
case Android.Content.Res.Orientation.Portrait:
//xapp.Orientation = XLabs.Enums.Orientation.Portrait;
break;
default:
break;
}
}
I'm having trouble with the Orientation variable in IXFormsApp i.e. xapp.Orientation. The XLabs documentation lists this as 'protected set', as does the compiler:
MainActivity.cs(109,5,109,21): error CS0200: Property or indexer 'XLabs.Platform.Mvvm.IXFormsApp.Orientation' cannot be assigned to -- it is read only
and it doesn't get set automagically (when I check where it is used, it is always set to 'None'), so I was wondering how to make use of it, and indeed, how to use the XLabs/IXFormsApp to determine rotation?
On a related note, I was also trying to set the Rotation handler (not sure why, but I thought I'd give it a go) with unusual results.
xapp.Rotation += (sender, args) =>
{
switch (args.Value)
{
case XLabs.Enums.Orientation.Landscape:
//xapp.Orientation = XLabs.Enums.Orientation.Landscape;
...
break;
case XLabs.Enums.Orientation.Portrait:
...
break;
default:
break;
}
};
If I try in the Android code I get the following error:
MainActivity.cs(60,4,60,22): error CS0019: Operator '+=' cannot be applied to operands of type 'System.EventHandler<XLabs.EventArgs<XLabs.Enums.Orientation>>' and 'lambda expression'
however, if I set it in the Forms code (where the results are used), it is fine (altho the handler never seems to actually be called). Does anyone know whay this would be the case?

There are two different solutions I have used in the past.
The first is by making a PageBase class which all my pages inherit from, and PageBase inherits from a regular Page.
My PageBase has two abstract methods (so the children of it have to fill it in), which are UpdateLandscape and UpdatePortait. Children will fill these methods in for how to layout the page depending on whether it is being laid out in landscape or portrait mode.
Pages have a method OnSizeAllocated, as Daniel said. I made PageBase override it and make it call UpdateLandscape and UpdatePortait accordingly.
If, as you said, you are only looking to check when it has rotated, the above works just fine, as OnSizeAllocated gets called for a page whenever you rotate your phone.
If you are checking for landscape vs portait because you want your code to be able to check at any time, then the second solution below works too.
The second way I solved it is to use dependency services to fill in an IDeviceInfo interface, and write all dynamic things by checking if DeviceInfo.IsPortait() is true or false (and this way I also let DeviceInfo have a Width and Height, so I can request the screen dimensions at any point).
On Android, I filled in my Android code as so:
[assembly: Dependency (typeof(Namespace.DeviceInfoProvider))]
namespace Namespace
{
public class DeviceInfoProvider : IDeviceInfoProvider
{
public bool IsPortait () { return DeviceInfoManager.Width < DeviceInfoManager.Height; }
public int GetWidth () { return DeviceInfoManager.Width; }
public int GetHeight () { return DeviceInfoManager.Height; }
}
public static class DeviceInfoManager
{
public static MainActivity MainActivity { get; set; }
public static int Width { get { return MainActivity.GetWidth (); } }
public static int Height { get { return MainActivity.GetHeight (); } }
}
}
Then in MainActivity I gave it these methods:
public int GetWidth() {
return (int)(Resources.DisplayMetrics.WidthPixels / Resources.DisplayMetrics.Density);
}
public int GetHeight() {
return (int)(Resources.DisplayMetrics.HeightPixels / Resources.DisplayMetrics.Density);
}
And on the iOS side, I filled it in as so:
[assembly: Dependency (typeof(Namespace.DeviceInfoProvider))]
namespace Namespace {
public class DeviceInfoProvider : IDeviceInfoProvider {
public bool IsPortait() { return UIScreen.MainScreen.Bounds.Width < UIScreen.MainScreen.Bounds.Height; }
public int GetWidth() { return (int)UIScreen.MainScreen.Bounds.Width; }
public int GetHeight() { return (int)UIScreen.MainScreen.Bounds.Height; }
}
}
Personally, I am more of a fan of writing it the second way and making it check "if we are in portait mode, here are the differences". That way those things that are not different between portait and landscape only have to be written once, only the differences are written twice.

You can use OnSizeAllocated method override to detect orientation change;
double previousWidth;
double previousHeight;
protected override void OnSizeAllocated(double width, double height)
{
base.OnSizeAllocated(width, height);
if (previousWidth != width || previousHeight != height)
{
previousWidth = width;
previousHeight = height;
if (width > height)
{
// landscape mode
}
else
{
// portrait mode
}
}
}

Related

Unity GameObject "flash" after SetActive on Android Project

I have a project that use Android to display Unity Player. So I export Untiy project as Android module which implemented by Android Application.
I create buttons in Android Activity which contains UnityPlayer, And when I click button, it send a message to Unity Player to invoke C# function, just like this:
findViewById(R.id.btnChange).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mUnityPlayer.UnitySendMessage("ScriptHolder", "ChangeSkin", "");
}
});
And the function named "ChangeSkin" is just to change some GameObjects' active. Just like this:
void ChangeSkin()
{
int prefab;
if (_currentPrefab == PREFAB_DEFAULT)
{
prefab = PREFAB_PRINCESS;
}
else
{
prefab = PREFAB_DEFAULT;
}
ShowSkin(prefab);
}
private void ShowSkin(int prefab)
{
_currentPrefab = prefab;
foreach (var item in _defaultDressList)
{
item.SetActive(prefab == PREFAB_DEFAULT);
}
foreach (var item in _princessDressList)
{
item.SetActive(prefab == PREFAB_PRINCESS);
}
}
And something weird happening: when I click button to change the person's cloth in Unity, the GameObjects which called SetActive(true) show at the position above the right position for a frame and become normal, it looks like they flash. Here is the gif of the project demo:
It looks like the position offset is equal to the height of status bar. If I create a button on Unity Scene and call "ChangeSkin" function, everything will be OK.
I tried all I can to fix this but not succeed. So I hope you will help me, thx.
You should check your Unity layout. If you have a layout group that has content size fitter component, and the child objects also have it (content size fitter), this could cause these glitches.
I fixed this problem by using a flag (or trigger). Just like this:
// set value true to trigger ChangeSkin() in Update(),
// instead of invoke ChangeSkin() directly
private bool _changingSkinTrigger = false;
void ChangeSkin()
{
if (_currentPrefab == PREFAB_DEFAULT)
{
_currentPrefab = PREFAB_PRINCESS;
}
else
{
_currentPrefab = PREFAB_DEFAULT;
}
_changingSkinTrigger = true;
}
void Update()
{
if (_changingSkinTrigger)
{
_changingSkinTrigger = false;
ShowSkin();
}
}
private void ShowSkin()
{
foreach (var item in _defaultDressList)
{
item.SetActive(_currentPrefab == PREFAB_DEFAULT);
}
foreach (var item in _princessDressList)
{
item.SetActive(_currentPrefab == PREFAB_PRINCESS);
}
}
Thank #AndriyMarshalek for enlightening me.

Handle Talkback in a Xamarin app using a virtual DPAD

I have a Xamarin app that was not meant to handle the talkback functionality of android, because for it to work well it had to be build in a specific way.
My app is a little order, and I simply can't make a do-over of the whole thing.
So, what is happening?
My Xamarin app is made with non-native libs, that are not supported by the Talkback, so, when the user turns on the Talkback functionality the app effectively stops receiving the DPAD events since they are handled by the systems Accessibility Service.
That service, gets the events, and tries to handle them within my app, but, since my components are non-native, the system does not recognize them and the DPAD is wasted, hence, the illusion that the DPADs are not working.
So, what do you have to do if you just want to handle the DPADs (and nothing else) yourself with Talkback on?
The answer to this post will contain the code that describes the following behavior:
1. The talkback wont be able to 'talk' about your components
2. The DPAD events will be handled by an Accessibility Delegate
3. A virtual DPAD will handle the navigation
4. The green rectangle used for focus will be disabled, since you wont need it anyway
5. The app will look exactly the same with Talkback on and off
This post was made for educational purposes, since I had a hard time coming up with the solution, and hope the next guy finds it helpfull.
The first step is to create a class that inherits the AccessibilityDelegateCompat in order to create our own Accessibility Service.
class MyAccessibilityHelper : AccessibilityDelegateCompat
{
const string Tag = "MyAccessibilityHelper";
const int ROOT_NODE = -1;
const int INVALID_NODE = -1000;
const string NODE_CLASS_NAME = "My_Node";
public const int NODE_UP = 1;
public const int NODE_LEFT = 2;
public const int NODE_CENTER = 3;
public const int NODE_RIGHT = 4;
public const int NODE_DOWN = 5;
private class MyAccessibilityProvider : AccessibilityNodeProviderCompat
{
private readonly MyAccessibilityHelper mHelper;
public MyAccessibilityProvider(MyAccessibilityHelper helper)
{
mHelper = helper;
}
public override bool PerformAction(int virtualViewId, int action, Bundle arguments)
{
return mHelper.PerformNodeAction(virtualViewId, action, arguments);
}
public override AccessibilityNodeInfoCompat CreateAccessibilityNodeInfo(int virtualViewId)
{
var node = mHelper.CreateNode(virtualViewId);
return AccessibilityNodeInfoCompat.Obtain(node);
}
}
private readonly View mView;
private readonly MyAccessibilityProvider mProvider;
private Dictionary<int, Rect> mRects = new Dictionary<int, Rect>();
private int mAccessibilityFocusIndex = INVALID_NODE;
public MyAccessibilityHelper(View view)
{
mView = view;
mProvider = new MyAccessibilityProvider(this);
}
public override AccessibilityNodeProviderCompat GetAccessibilityNodeProvider(View host)
{
return mProvider;
}
public override void SendAccessibilityEvent(View host, int eventType)
{
Android.Util.Log.Debug(Tag, "SendAccessibilityEvent: host={0} eventType={1}", host, eventType);
base.SendAccessibilityEvent(host, eventType);
}
public void AddRect(int id, Rect rect)
{
mRects.Add(id, rect);
}
public AccessibilityNodeInfoCompat CreateNode(int virtualViewId)
{
var node = AccessibilityNodeInfoCompat.Obtain(mView);
if (virtualViewId == ROOT_NODE)
{
node.ContentDescription = "Root node";
ViewCompat.OnInitializeAccessibilityNodeInfo(mView, node);
foreach (var r in mRects)
{
node.AddChild(mView, r.Key);
}
}
else
{
node.ContentDescription = "";
node.ClassName = NODE_CLASS_NAME;
node.Enabled = true;
node.Focusable = true;
var r = mRects[virtualViewId];
node.SetBoundsInParent(r);
int[] offset = new int[2];
mView.GetLocationOnScreen(offset);
node.SetBoundsInScreen(new Rect(offset[0] + r.Left, offset[1] + r.Top, offset[0] + r.Right, offset[1] + r.Bottom));
node.PackageName = mView.Context.PackageName;
node.SetSource(mView, virtualViewId);
node.SetParent(mView);
node.VisibleToUser = true;
if (virtualViewId == mAccessibilityFocusIndex)
{
node.AccessibilityFocused = true;
node.AddAction(AccessibilityNodeInfoCompat.ActionClearAccessibilityFocus);
}
else
{
node.AccessibilityFocused = false;
node.AddAction(AccessibilityNodeInfoCompat.FocusAccessibility);
}
}
return node;
}
private AccessibilityEvent CreateEvent(int virtualViewId, EventTypes eventType)
{
var e = AccessibilityEvent.Obtain(eventType);
if (virtualViewId == ROOT_NODE)
{
ViewCompat.OnInitializeAccessibilityEvent(mView, e);
}
else
{
var record = AccessibilityEventCompat.AsRecord(e);
record.Enabled = true;
record.SetSource(mView, virtualViewId);
record.ClassName = NODE_CLASS_NAME;
e.PackageName = mView.Context.PackageName;
}
return e;
}
public bool SendEventForVirtualView(int virtualViewId, EventTypes eventType)
{
if (mView.Parent == null)
return false;
var e = CreateEvent(virtualViewId, eventType);
return ViewParentCompat.RequestSendAccessibilityEvent(mView.Parent, mView, e);
}
public bool PerformNodeAction(int virtualViewId, int action, Bundle arguments)
{
if (virtualViewId == ROOT_NODE)
{
return ViewCompat.PerformAccessibilityAction(mView, action, arguments);
}
else
{
switch (action)
{
case AccessibilityNodeInfoCompat.ActionAccessibilityFocus:
if (virtualViewId != mAccessibilityFocusIndex)
{
if (mAccessibilityFocusIndex != INVALID_NODE)
{
SendEventForVirtualView(mAccessibilityFocusIndex, EventTypes.ViewAccessibilityFocusCleared);
}
mAccessibilityFocusIndex = virtualViewId;
mView.Invalidate();
SendEventForVirtualView(virtualViewId, EventTypes.ViewAccessibilityFocused);
// virtual key event
switch (virtualViewId)
{
case NODE_UP:
HandleDpadEvent(Keycode.DpadUp);
break;
case NODE_LEFT:
HandleDpadEvent(Keycode.DpadLeft);
break;
case NODE_RIGHT:
HandleDpadEvent(Keycode.DpadRight);
break;
case NODE_DOWN:
HandleDpadEvent(Keycode.DpadDown);
break;
}
// refocus center
SendEventForVirtualView(NODE_CENTER, EventTypes.ViewAccessibilityFocused);
return true;
}
break;
case AccessibilityNodeInfoCompat.ActionClearAccessibilityFocus:
mView.RequestFocus();
if (virtualViewId == mAccessibilityFocusIndex)
{
mAccessibilityFocusIndex = INVALID_NODE;
mView.Invalidate();
SendEventForVirtualView(virtualViewId, EventTypes.ViewAccessibilityFocusCleared);
return true;
}
break;
}
}
return false;
}
private void HandleDpadEvent(Keycode keycode)
{
//Here you know what DPAD was pressed
//You can create your own key event and send it to your app
//This code depends on your own application, and I wont be providing the code
//Note, it is important to handle both, the KeyDOWN and the KeyUP event for it to work
}
}
Since the code is a bit large, I'll just explain the crutal parts.
Once the talkback is active, the dictionary (from our view bellow) will be used to create a virtual tree node of our virtual DPAD. With that in mind, the function PerformNodeAction will be the most important one.
It handles the actions once a virtual node was focused by the Accessibility system, based on the provided id of the virtual element, there are two parts, the first one is the ROOT_NODE, which is the view iteslf that contains our virtual dpad, which for the most part can be ignored, but the seond part is where the handling is done.
The second part is where the actions ActionAccessibilityFocus and ActionClearAccessibilityFocus are handled. The two of witch are both important, but the first one is where we can finally handle our virtual dpad.
What is done here is that with the provided virtual ID from the dictionary, we know which DPAD was selected (virtualViewId). Based on the selected DPAD, we can perform the action we want in the HandleDpadEvent function. What is important to notice, is that after we handle the selecteds DPAD event, we will refocus our CENTER node, in order to be ready to handle the next button press. This is very important, since, you dont want to find yourself in a situation where you go DOWN, and then UP, just for the virtual dpad to focus the CENTER pad.
So, I'll epeat myself, the refocusing of the CENTER pad after the previous' DPAD event was handled needs to be done in order for us to know EXACTLY where we will be after the next DPAD button was pressed!
There is one function that I wont post here, since the code for it is very specific for my app, the function is HandleDpadEvent, there you must create a keydown and a keyup event and send it to your main activity where the function onKeyDown/Up will be triggered. Once you do that, the delegate is done.
And once the Delegate is done, we have to make our view like this:
/**
* SimplestCustomView
*/
public class AccessibilityHelperView : View
{
private MyAccessibilityHelper mHelper;
Dictionary<int, Rect> virtualIdRectMap = new Dictionary<int, Rect>();
public AccessibilityHelperView(Context context) :
base(context)
{
Init();
}
public AccessibilityHelperView(Context context, IAttributeSet attrs) :
base(context, attrs)
{
Init();
}
public AccessibilityHelperView(Context context, IAttributeSet attrs, int defStyle) :
base(context, attrs, defStyle)
{
Init();
}
public void Init()
{
this.SetFocusable(ViewFocusability.Focusable);
this.Focusable = true;
this.FocusedByDefault = true;
setRectangle();
mHelper = new MyAccessibilityHelper(this);
ViewCompat.SetAccessibilityDelegate(this, mHelper);
foreach (var r in virtualIdRectMap)
{
mHelper.AddRect(r.Key, r.Value);
}
}
private void setRectangle()
{
virtualIdRectMap.Add(MRAccessibilityHelper.NODE_CENTER, new Rect(1, 1, 2, 2));
virtualIdRectMap.Add(MRAccessibilityHelper.NODE_LEFT, new Rect(0, 1, 1, 2));
virtualIdRectMap.Add(MRAccessibilityHelper.NODE_UP, new Rect(1, 0, 2, 1));
virtualIdRectMap.Add(MRAccessibilityHelper.NODE_RIGHT, new Rect(2, 1, 3, 2));
virtualIdRectMap.Add(MRAccessibilityHelper.NODE_DOWN, new Rect(1, 2, 2, 3));
}
protected override void OnDraw(Canvas canvas)
{
base.OnDraw(canvas);
}
}
That view looks like this:
What is to notice?
The size of the node pads is in pixels, and they will be found on the top left corner of your app.
They are set to that single pixel size, because the Talkback functionality would otherwise select the first node pad that was added to the dictionary with a green rectangle (thats standard behavior for talkback)
All the rectangles in the view are added to a dictionary that will be used in our own Accessibility Delegate, to mention here is that the CENTER pad was added first, and therefor will be in focus once the talkback is activated by default
The Init function
The Init function is crutial for this, there we will create our view, and set some talkback parameters nessessary for our virtual dpad to be recognized by the systems own Accessibility Service.
Also, there will our Accessibility Delegate be initialized and our dictionary with all the created DPADs.
Ok, so far, we made a Delegate and a View, I placed them both in the same file, so they can see each other. But it is not a must.
So what now? We must add the AccessibilityHelperView to our app, in the MainActivity.cs file
AccessibilityHelperView mAccessibilityHelperView;
In the OnCreate function, you can add the following code to initiate the view:
mAccessibilityHelperView = new AccessibilityHelperView(this);
In the OnResume function, you can check if the talkback is on or off, based on the result, you can add or remove the mAccessibilityHelperView from your mBackgroundLayout(AddView, and RemoveView).
The OnResume function should look like this:
if (TalkbackEnabled && !_isVirtualDPadShown)
{
mBackgroundLayout.AddView(mAccessibilityHelperView);
_isVirtualDPadShown = true;
}
else if (!TalkbackEnabled && _isVirtualDPadShown)
{
mBackgroundLayout.RemoveView(mAccessibilityHelperView);
_isVirtualDPadShown = false;
}
The TalkbackEnabled variable is a local one that checks if the Talkback service is on or off, like this:
public bool TalkbackEnabled
{
get
{
AccessibilityManager am = MyApp.Instance.GetSystemService(Context.AccessibilityService) as AccessibilityManager;
if (am == null) return false;
String TALKBACK_SETTING_ACTIVITY_NAME = "com.android.talkback.TalkBackPreferencesActivity";
var serviceList = am.GetEnabledAccessibilityServiceList(FeedbackFlags.AllMask);
foreach (AccessibilityServiceInfo serviceInfo in serviceList)
{
String name = serviceInfo.SettingsActivityName;
if (name.Equals(TALKBACK_SETTING_ACTIVITY_NAME))
{
Log.Debug(LogArea, "Talkback is active");
return true;
}
}
Log.Debug(LogArea, "Talkback is inactive");
return false;
}
}
That should be all you need to make it work.
Hope I could help you out.

Accessibility function implementation problems in Android

I'm developing application that views books. There is a screen (Activity) which shows a book. It has custom view, something similar to ViewSwitcher and every page is a bitmap that is rendered by a custom View.
Now I should implement accessibility function - book should be read by the phone (audio).
I've read Accessibility section here https://developer.android.com/guide/topics/ui/accessibility/index.html but it is not clear enough.
I use SupportLibrary for accessibility management and now I have this code in ViewGroup (which manages book pages). Code 1:
private class EditionPagesViewSwitcherAccessibilityDelegate extends AccessibilityDelegateCompat {
private int mPageCount;
private double[] mPageRange;
#Override
public void onInitializeAccessibilityEvent(final View host, final AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(host, event);
event.setClassName(EditionPagesViewSwitcher.class.getName());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
event.setScrollable(canScroll());
}
if (event.getEventType() == AccessibilityEventCompat.TYPE_VIEW_SCROLLED && updatePageValues()) {
event.setItemCount(mPageCount);
// we use +1 because of user friendly numbers (from 1 not 0)
event.setFromIndex((int) (mPageRange[0] + 1));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
event.setToIndex((int) (mPageRange[1] + 1));
}
}
}
#Override
public void onInitializeAccessibilityNodeInfo(final View host, final AccessibilityNodeInfoCompat info) {
super.onInitializeAccessibilityNodeInfo(host, info);
info.setClassName(EditionPagesViewSwitcher.class.getName());
info.setScrollable(canScroll());
info.setLongClickable(true);
if (canScrollForward()) {
info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
}
if (canScrollBackward()) {
info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
}
}
#Override
public boolean performAccessibilityAction(final View host, final int action, final Bundle args) {
if (super.performAccessibilityAction(host, action, args)) {
return true;
}
switch (action) {
case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD: {
if (canScrollForward()) {
showNext();
return true;
}
}
return false;
case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD: {
if (canScrollBackward()) {
showPrevious();
return true;
}
}
return false;
}
return false;
}
Here is code from page view Code 2:
#Override
public void onInitializeAccessibilityEvent(final View host, final AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(host, event);
event.setClassName(EditionPageView.class.getName());
if (hasText()) {
event.getText().add(getPageRangeText());
final String trimText = mSurfaceUpdateData.getPageText().trim();
if (trimText.length() > MAX_TEXT_LENGTH) {
event.getText().add(trimText.substring(0, MAX_TEXT_LENGTH));
// event.getText().add(trimText.substring(MAX_TEXT_LENGTH, trimText.length()));
}
else {
event.getText().add(trimText);
}
}
}
#Override
public void onInitializeAccessibilityNodeInfo(final View host, final AccessibilityNodeInfoCompat info) {
super.onInitializeAccessibilityNodeInfo(host, info);
info.setClassName(EditionPageView.class.getName());
}
Because page text data loads asynchronous first time accessibility don't have any text while executes onInitializeAccessibilityEvent code. And then when data have been loaded I fire AccessibilityEvent.TYPE_VIEW_SELECTED and AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED events. Then onInitializeAccessibilityEvent executes again and phone "read" book text.
So my questions:
Is my Accessibility implementation right? May be it is design wrong? Because I didn't find any good tutorial about this feature.
Why I need to use SDK versions checks in Support implementations in Code 1? Why support implementation doesn't handle it correctly?
Is firing TYPE_VIEW_SELECTED and TYPE_VIEW_TEXT_CHANGED really needed? Or may be some other code should be implemented?
The main question. In Code 2 there is commented code line. This code statement substring text to be less then MAX_TEXT_LENGTH (it's 3800) because if text is bigger nothing is played. Nothing. Is it accessibility restriction? Any other text that is less then this value is played well.
Does anyone know where I can find any good tutorial? (yes I saw samples).
Does anyone have any custom realizations to look through?
UPDATED
Well. Here is some answers:
As I can see TYPE_VIEW_SELECTED and TYPE_VIEW_TEXT_CHANGED events are not needed if you don't want this text to be read as soon as you get it.
On Nexus 7 all large text is played well (text up to 8000 symbols), so this issue doesn't reproduce on it, but on Samsung Galaxy Tab 10.1 (Android 4.0.4) and Genymotion emulator of Tab 10.1 with Android 4.3 does. And this is strange...
4.. According to the documentation of String.substring()
The first argument you pass is the start index in the original string, the second argument is the end index in the original string.
Example:
String text = "Hello";
partOfText = text.substring(2,text.length() - 1);
partOfText equals to "llo" (the first char is index 0)
So by putting your constant MAX_TEXT_LENGTH as a first argument, it would start at index 3800 to take out the substring.
http://developer.android.com/reference/java/lang/String.html#substring(int)
You are right MAX_TEXT_LENGTH is 3800.
About your doubt,
this code:
event.getText().add(trimText.substring(MAX_TEXT_LENGTH, trimText.length()));
}
you are trying to substring "trimText" from MAX_TEXT_LENGTH to trimText.length() !
Supposing that trimText = "STACK", trimText.length() = 5, then trimText.substring(3800,5) is going to be ?
At first, this doesn't have sense, using correctly would be like this:
trimText.substring(0,2) = "ST";

Flash Builder 4.6 Mobile Orientation

I'm working with FlashBuilder 4.6 to develop Android app.
In the application there is a requirement of setting some values when device orientation changes. i.e. setting the values when the screen/device orientation changes from Landscape to Portrait and vice-verse.
Although Initially my application has LandScape orientation. And this requirement is on specific view.
I want every time when the screen/device orientation changes the values must be set there.
I am not getting the desired result.
Please guide me and tell me if I am at wrong in code or how can i achieve this.
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" title="{data}"
xmlns:mx="library://ns.adobe.com/flex/mx"
viewActivate="view1_viewActivateHandler(event)"
creationComplete="view1_creationCompleteHandler(event)">
<fx:Script>
<![CDATA[
protected function view1_viewActivateHandler(event:ViewNavigatorEvent):void
{
if(Accelerometer.isSupported)
{
accl = new Accelerometer();
accl.addEventListener(AccelerometerEvent.UPDATE,update); //accl.addEventListener(AccelerometerEvent.UPDATE,adjustImage);
}
}
private function update(event:AccelerometerEvent):void
{
this.stage.autoOrients = true;
//in the below line I am attaching StageOrienationEvent that will adjust the
//values.
stage.addEventListener(StageOrientationEvent.ORIENTATION_CHANGE,adjust);
}
private function adjust(event:StageOrientationEvent):void
{
if(event.afterOrientation == StageOrientation.ROTATED_LEFT)
{
testVal.text ="After OR is LEFT";
}
else if(event.afterOrientation == StageOrientation.ROTATED_RIGHT)
{
testVal.text ="After OR is RIGHT";
}
else if(StageAspectRatio.LANDSCAPE)
{
testVal.text ="StageAspectRatio is Landscape";
}
else if(StageAspectRatio.PORTRAIT)
{
testVal.text="StageAspectRatio is Portrait";
}
}
</fx:Script>
Thanks.
Finally found the solution after spending two days on this.
in update function add OrientationChanging Event in stage.addEventListner.
private function update(event:AccelerometerEvent):void
{
this.stage.autoOrients = true;
stage.addEventListener(StageOrientationEvent.ORIENTATION_CHANGING,adjust);
}
private function adjust(event:StageOrientationEvent):void
{
switch(event.afterOrientation)
{
case StageOrientation.DEFAULT:
//Do Something here. The Portrait Position.
break;
case StageOrienatation.ROTATED_LEFT:
//Do Something here when you rotate your phone portrait to left side.
break;
case StageOrienatation.ROTATED_RIGHT:
//Do something here when you rotate your phone portrait to right side.
break;
}
}
Many thanks to all who viewed this question and try put their efforts for solution. :)
i am not a experimented coder! but : private function update(event:AccelerometerEvent):void should be : private function accl(event:AccelerometerEvent):void ?? or something like this in below code if(stage[not event].afterOrientation == ??? its just a opinion!

How to toggle auto brightness on and off? (not a repeat)

I'm simply trying to toggle auto brightness on and off.
I started with this code (inside the onCreate method)
final ToggleButton autoBrightToggle = (ToggleButton) findViewById(R.id.brightToggle);
// display auto brightness state
final ToggleButton autoBrightToggle = (ToggleButton) findViewById(R.id.autoToggle);
autoOnOrOff.setText(String.valueOf(getAutoBrightnessMode()));
autoBrightToggle.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
if (autoBrightToggle.isChecked()) {
setAutoBright(true);
} else {
setAutoBright(false);
}
}
}); // end anonymous OnClickListener function
// toggle the brightness mode
private void setAutoBright(boolean mode) {
if (mode) {
Settings.System.putInt(cr, SCREEN_BRIGHTNESS_MODE, SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
autoOnOrOff.setText(String.valueOf(getAutoBrightnessMode()));
} else {
Settings.System.putInt(cr, SCREEN_BRIGHTNESS_MODE, SCREEN_BRIGHTNESS_MODE_MANUAL);
autoOnOrOff.setText(String.valueOf(getAutoBrightnessMode()));
}
}
Which doesn't seem to work. The setAutoBrightnessMode() method is also called again in onResume() but with the same non-results.
Anyway, I'm sorry if someone feels this question is redundant but the other posts did not get me where I need to go!
(FWIW - I'm testing this on my old Droid X and my Galaxy Nexus, not the Emulator)
EDITED - UPDATE ON THIS:
I'm 99% sure now that I am not seeing any changes to the Auto-Brightness mode reflected in the Settings panel and desktop widgets - even though I may actually be changing it's value.
part of the problem is that I don't know how exactly to determine if Auto-Brightness is on or not!
For instance, does the screen quickly and visibly change? I've been expecting immediate visible changes in brightness according to environment - but perhaps the changes are subtle? and over a longer period? or perhaps it takes 30 seconds or more of environment change before brightness changes?
Can someone suggest how I can track this? I've tried querying the Settings.System.SCREEN_BRIGHTNESS_MODE constant - hooking this method up to a textfield:
private int getAutoBrightnessMode() {
try {
int brightnessMode = Settings.System.getInt(cr, SCREEN_BRIGHTNESS_MODE);
} catch (Settings.SettingNotFoundException e) {
e.printStackTrace();
int brightnessMode = -10000;
}
return brightnessMode;
}
But it always reads 0, even after an onResume(). :-((
I know this is a simple procedure, but I'm trying to learn this stuff on my own, and have had almost no formal CS training... So all I can say is I'm very frustrated by this and feel like I've worked myself into a corner and at this point I'm so annoyed I can't think straight anymore.
So help would be great.
I use following approach in my application. Tested on HTC Desire HD and pair of noname chinese tablets.
Add to manifest permission:
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
And use below code to toggle auto brightness. There is one trick in the code: we need to "refresh" brightness of app manually, because it doesn't changes automatically. May be it is the problem in your case.
void setAutoBrightness(boolean value) {
if (value) {
Settings.System.putInt(getContentResolver(), SCREEN_BRIGHTNESS_MODE, SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
} else {
Settings.System.putInt(getContentResolver(), SCREEN_BRIGHTNESS_MODE, SCREEN_BRIGHTNESS_MODE_MANUAL);
}
// After brightness change we need to "refresh" current app brightness
if (isChecked) {
refreshBrightness(-1);
} else {
refreshBrightness(getBrightnessLevel());
}
}
private void refreshBrightness(float brightness) {
WindowManager.LayoutParams lp = getWindow().getAttributes();
if (brightness < 0) {
lp.screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE;
} else {
lp.screenBrightness = brightness;
}
getWindow().setAttributes(lp);
}
int getBrightnessLevel() {
try {
int value = Settings.System.getInt(getContentResolver(), SCREEN_BRIGHTNESS);
// convert brightness level to range 0..1
value = value / 255;
return value;
} catch (SettingNotFoundException e) {
return 0;
}
}

Categories

Resources