From 399c5f0a7de3860deb45519fe3161b2f73a452c3 Mon Sep 17 00:00:00 2001 From: Pavol Zibrita Date: Fri, 27 Apr 2012 03:24:58 +0200 Subject: [PATCH] Merge remote-tracking branch 'poretsky/accessibility-proposals' Conflicts: OsmAnd/res/layout/tips_and_tricks.xml --- OsmAnd/assets/voice/zh-tts/ttsconfig.p | 166 +++++++++--------- OsmAnd/res/layout-land/menu.xml | 5 +- OsmAnd/res/layout-large-land/menu.xml | 5 +- OsmAnd/res/layout-large/menu.xml | 5 +- OsmAnd/res/layout/menu.xml | 5 +- OsmAnd/res/layout/tips_and_tricks.xml | 2 +- OsmAnd/res/values-ru/strings.xml | 2 +- OsmAnd/res/xml/settings_pref.xml | 12 +- .../osmand/access/AccessibilityDelegate.java | 2 + .../net/osmand/access/AccessibilityMode.java | 22 +++ .../net/osmand/access/AccessibleActivity.java | 19 +- .../osmand/access/AccessibleAlertBuilder.java | 51 ++++++ .../net/osmand/access/AccessibleContent.java | 70 ++++---- .../net/osmand/access/AccessibleLayout.java | 74 ++++---- .../net/osmand/access/AccessibleToast.java | 33 ++-- .../access/AccessibleTrackedActivity.java | 45 ----- .../net/osmand/access/ExplorableTextView.java | 12 +- OsmAnd/src/net/osmand/access/MapExplorer.java | 24 ++- .../src/net/osmand/access/NavigationInfo.java | 10 +- .../osmand/access/RelativeDirectionStyle.java | 4 +- OsmAnd/src/net/osmand/access/TextMessage.java | 18 +- .../net/osmand/plus/OsmandApplication.java | 12 ++ .../src/net/osmand/plus/OsmandSettings.java | 59 +++++-- .../plus/activities/MainMenuActivity.java | 9 +- .../osmand/plus/activities/MapActivity.java | 97 ++++++++-- .../plus/activities/MapActivityActions.java | 5 +- .../plus/activities/MapActivityLayers.java | 2 +- .../plus/activities/SettingsActivity.java | 28 ++- .../activities/TipsAndTricksActivity.java | 4 +- .../activities/search/SearchPOIActivity.java | 46 ++++- .../osmand/plus/views/ContextMenuLayer.java | 55 ++++-- .../osmand/plus/views/MapControlsLayer.java | 9 +- .../osmand/plus/views/OsmandMapTileView.java | 25 ++- .../net/osmand/plus/views/RouteInfoLayer.java | 8 +- 34 files changed, 610 insertions(+), 335 deletions(-) create mode 100644 OsmAnd/src/net/osmand/access/AccessibilityMode.java create mode 100644 OsmAnd/src/net/osmand/access/AccessibleAlertBuilder.java delete mode 100644 OsmAnd/src/net/osmand/access/AccessibleTrackedActivity.java diff --git a/OsmAnd/assets/voice/zh-tts/ttsconfig.p b/OsmAnd/assets/voice/zh-tts/ttsconfig.p index 04afb348a9..2003c27e0d 100644 --- a/OsmAnd/assets/voice/zh-tts/ttsconfig.p +++ b/OsmAnd/assets/voice/zh-tts/ttsconfig.p @@ -1,84 +1,84 @@ -:- op('==', xfy, 500). -version(101). -language(zh). - -% before each announcement (beep) -preamble - []. - - -%% TURNS -turn('left', ['左轉 ']). -turn('left_sh', ['向左急轉 ']). -turn('left_sl', ['稍向左轉 ']). -turn('right', ['右轉 ']). -turn('right_sh', ['向右急轉 ']). -turn('right_sl', ['稍向右轉 ']). - -prepare_turn(Turn, Dist) == ['請準備 ', D, ' 後 ', M] :- distance(Dist) == D, turn(Turn, M). -turn(Turn, Dist) == [D, ' 後 ',M] :- distance(Dist) == D, turn(Turn, M). -turn(Turn) == M :- turn(Turn, M). - -prepare_make_ut(Dist) == ['請準備', D, ' 後迴轉 '] :- distance(Dist) == D. -make_ut(Dist) == [D, ' 後請迴轉'] :- distance(Dist) == D. -make_ut == ['請迴轉 ']. -make_ut_wp == ['可能的話, 請迴轉 ']. - -prepare_roundabout(Dist) == ['請準備', D,' 後進入圓環 '] :- distance(Dist) == D. -roundabout(Dist, _Angle, Exit) == [D, ' 後進入圓環, 然後在 ', E, ' 出口離開'] :- distance(Dist) == D, nth(Exit, E). -roundabout(_Angle, Exit) == ['在 ', E, ' 出口離開'] :- nth(Exit, E). - -go_ahead == ['直直往前開 ']. -go_ahead(Dist) == ['沿著馬路往前 ', D]:- distance(Dist) == D. - -and_arrive_destination == ['然後可達終點 ']. - -then == ['然後 ']. -reached_destination == ['抵達終點 ']. -bear_right == ['靠右 ']. -bear_left == ['靠左 ']. - -route_new_calc(Dist) == ['路程有 ', D, ' 遠'] :- distance(Dist) == D. -route_recalc(Dist) == ['重新計算路程, 距離有 ', D] :- distance(Dist) == D. - -location_lost == ['接收不到 g p s 信號 ']. - - -%% -nth(1, '第一個 '). -nth(2, '第二個 '). -nth(3, '第三個 '). -nth(4, '第四個 '). -nth(5, '第五個 '). -nth(6, '第六個 '). -nth(7, '第七個 '). -nth(8, '第八個 '). -nth(9, '第九個 '). -nth(10, '第十個 '). -nth(11, '第十一個 '). -nth(12, '第十二個 '). -nth(13, '第十三個 '). -nth(14, '第十四個 '). -nth(15, '第十五個 '). -nth(16, '第十六個 '). -nth(17, '第十七個 '). - - -%%% distance measure -distance(Dist) == [ X, ' 公尺'] :- Dist < 100, D is round(Dist/10)*10, num_atom(D, X). -distance(Dist) == [ X, ' 公尺'] :- Dist < 1000, D is round(2*Dist/100)*50, num_atom(D, X). -distance(Dist) == ['約 1 公里 '] :- Dist < 1500. -distance(Dist) == ['約 ', X, ' 公里 '] :- Dist < 10000, D is round(Dist/1000), num_atom(D, X). -distance(Dist) == [ X, ' 公里 '] :- D is round(Dist/1000), num_atom(D, X). - - -%% resolve command main method -%% if you are familar with Prolog you can input specific to the whole mechanism, -%% by adding exception cases. -flatten(X, Y) :- flatten(X, [], Y), !. -flatten([], Acc, Acc). -flatten([X|Y], Acc, Res):- flatten(Y, Acc, R), flatten(X, R, Res). -flatten(X, Acc, [X|Acc]). - -resolve(X, Y) :- resolve_impl(X,Z), flatten(Z, Y). -resolve_impl([],[]). +:- op('==', xfy, 500). +version(101). +language(zh). + +% before each announcement (beep) +preamble - []. + + +%% TURNS +turn('left', ['左轉 ']). +turn('left_sh', ['向左急轉 ']). +turn('left_sl', ['稍向左轉 ']). +turn('right', ['右轉 ']). +turn('right_sh', ['向右急轉 ']). +turn('right_sl', ['稍向右轉 ']). + +prepare_turn(Turn, Dist) == ['請準備 ', D, ' 後 ', M] :- distance(Dist) == D, turn(Turn, M). +turn(Turn, Dist) == [D, ' 後 ',M] :- distance(Dist) == D, turn(Turn, M). +turn(Turn) == M :- turn(Turn, M). + +prepare_make_ut(Dist) == ['請準備', D, ' 後迴轉 '] :- distance(Dist) == D. +make_ut(Dist) == [D, ' 後請迴轉'] :- distance(Dist) == D. +make_ut == ['請迴轉 ']. +make_ut_wp == ['可能的話, 請迴轉 ']. + +prepare_roundabout(Dist) == ['請準備', D,' 後進入圓環 '] :- distance(Dist) == D. +roundabout(Dist, _Angle, Exit) == [D, ' 後進入圓環, 然後在 ', E, ' 出口離開'] :- distance(Dist) == D, nth(Exit, E). +roundabout(_Angle, Exit) == ['在 ', E, ' 出口離開'] :- nth(Exit, E). + +go_ahead == ['直直往前開 ']. +go_ahead(Dist) == ['沿著馬路往前 ', D]:- distance(Dist) == D. + +and_arrive_destination == ['然後可達終點 ']. + +then == ['然後 ']. +reached_destination == ['抵達終點 ']. +bear_right == ['靠右 ']. +bear_left == ['靠左 ']. + +route_new_calc(Dist) == ['路程有 ', D, ' 遠'] :- distance(Dist) == D. +route_recalc(Dist) == ['重新計算路程, 距離有 ', D] :- distance(Dist) == D. + +location_lost == ['接收不到 g p s 信號 ']. + + +%% +nth(1, '第一個 '). +nth(2, '第二個 '). +nth(3, '第三個 '). +nth(4, '第四個 '). +nth(5, '第五個 '). +nth(6, '第六個 '). +nth(7, '第七個 '). +nth(8, '第八個 '). +nth(9, '第九個 '). +nth(10, '第十個 '). +nth(11, '第十一個 '). +nth(12, '第十二個 '). +nth(13, '第十三個 '). +nth(14, '第十四個 '). +nth(15, '第十五個 '). +nth(16, '第十六個 '). +nth(17, '第十七個 '). + + +%%% distance measure +distance(Dist) == [ X, ' 公尺'] :- Dist < 100, D is round(Dist/10)*10, num_atom(D, X). +distance(Dist) == [ X, ' 公尺'] :- Dist < 1000, D is round(2*Dist/100)*50, num_atom(D, X). +distance(Dist) == ['約 1 公里 '] :- Dist < 1500. +distance(Dist) == ['約 ', X, ' 公里 '] :- Dist < 10000, D is round(Dist/1000), num_atom(D, X). +distance(Dist) == [ X, ' 公里 '] :- D is round(Dist/1000), num_atom(D, X). + + +%% resolve command main method +%% if you are familar with Prolog you can input specific to the whole mechanism, +%% by adding exception cases. +flatten(X, Y) :- flatten(X, [], Y), !. +flatten([], Acc, Acc). +flatten([X|Y], Acc, Res):- flatten(Y, Acc, R), flatten(X, R, Res). +flatten(X, Acc, [X|Acc]). + +resolve(X, Y) :- resolve_impl(X,Z), flatten(Z, Y). +resolve_impl([],[]). resolve_impl([X|Rest], List) :- resolve_impl(Rest, Tail), ((X == L) -> append(L, Tail, List); List = Tail). diff --git a/OsmAnd/res/layout-land/menu.xml b/OsmAnd/res/layout-land/menu.xml index 43bd3dda7b..eda7c41d68 100644 --- a/OsmAnd/res/layout-land/menu.xml +++ b/OsmAnd/res/layout-land/menu.xml @@ -1,6 +1,7 @@ - @@ -86,4 +87,4 @@ android:layout_width="fill_parent" android:layout_height="fill_parent" android:b - + diff --git a/OsmAnd/res/layout-large-land/menu.xml b/OsmAnd/res/layout-large-land/menu.xml index b7817c2167..1b1108c59f 100644 --- a/OsmAnd/res/layout-large-land/menu.xml +++ b/OsmAnd/res/layout-large-land/menu.xml @@ -1,6 +1,7 @@ - @@ -89,4 +90,4 @@ android:layout_width="fill_parent" android:layout_height="fill_parent" android:b - + diff --git a/OsmAnd/res/layout-large/menu.xml b/OsmAnd/res/layout-large/menu.xml index 78189d2abb..febb9105ea 100644 --- a/OsmAnd/res/layout-large/menu.xml +++ b/OsmAnd/res/layout-large/menu.xml @@ -1,6 +1,7 @@ - @@ -77,4 +78,4 @@ android:layout_width="fill_parent" android:layout_height="fill_parent" android:b - + diff --git a/OsmAnd/res/layout/menu.xml b/OsmAnd/res/layout/menu.xml index 25870cfaf7..040d179997 100644 --- a/OsmAnd/res/layout/menu.xml +++ b/OsmAnd/res/layout/menu.xml @@ -1,6 +1,7 @@ - @@ -77,4 +78,4 @@ android:layout_width="fill_parent" android:layout_height="fill_parent" android:b - + diff --git a/OsmAnd/res/layout/tips_and_tricks.xml b/OsmAnd/res/layout/tips_and_tricks.xml index 24bc1a1501..1a2824148d 100644 --- a/OsmAnd/res/layout/tips_and_tricks.xml +++ b/OsmAnd/res/layout/tips_and_tricks.xml @@ -2,7 +2,7 @@ - diff --git a/OsmAnd/res/values-ru/strings.xml b/OsmAnd/res/values-ru/strings.xml index 5e1103f203..114794d0dd 100644 --- a/OsmAnd/res/values-ru/strings.xml +++ b/OsmAnd/res/values-ru/strings.xml @@ -478,7 +478,7 @@ Источник позиционирования Включить фоновый сервис для обновления через долгий период времени Сервис навигации - Сервис маршрутизации OsmAnd включен, отключив позиционирование, он перестает вести по маршруту. + Сервис маршрутизации OsmAnd включен, если отключить позиционирование, он перестанет вести по маршруту. Изменить настройки навигации - Навигация Скрыть фильтр diff --git a/OsmAnd/res/xml/settings_pref.xml b/OsmAnd/res/xml/settings_pref.xml index 2441fa5e16..8f9f720d7c 100644 --- a/OsmAnd/res/xml/settings_pref.xml +++ b/OsmAnd/res/xml/settings_pref.xml @@ -131,10 +131,14 @@ - - - - + + + + + + + + diff --git a/OsmAnd/src/net/osmand/access/AccessibilityDelegate.java b/OsmAnd/src/net/osmand/access/AccessibilityDelegate.java index 90c6f0a3bf..4f4c446715 100644 --- a/OsmAnd/src/net/osmand/access/AccessibilityDelegate.java +++ b/OsmAnd/src/net/osmand/access/AccessibilityDelegate.java @@ -1,5 +1,7 @@ package net.osmand.access; +import net.osmand.access.AccessibleLayout; + import android.app.Activity; import android.app.Dialog; import android.content.Context; diff --git a/OsmAnd/src/net/osmand/access/AccessibilityMode.java b/OsmAnd/src/net/osmand/access/AccessibilityMode.java new file mode 100644 index 0000000000..a6d472b54e --- /dev/null +++ b/OsmAnd/src/net/osmand/access/AccessibilityMode.java @@ -0,0 +1,22 @@ +package net.osmand.access; + +import net.osmand.plus.R; +import android.content.Context; + +public enum AccessibilityMode { + + ON(R.string.accessibility_on), + OFF(R.string.accessibility_off), + DEFAULT(R.string.accessibility_default); + + private final int key; + + AccessibilityMode(int key) { + this.key = key; + } + + public String toHumanString(Context ctx) { + return ctx.getResources().getString(key); + } + +} diff --git a/OsmAnd/src/net/osmand/access/AccessibleActivity.java b/OsmAnd/src/net/osmand/access/AccessibleActivity.java index e7a6c7ed59..a39509f8da 100644 --- a/OsmAnd/src/net/osmand/access/AccessibleActivity.java +++ b/OsmAnd/src/net/osmand/access/AccessibleActivity.java @@ -1,8 +1,9 @@ package net.osmand.access; +import net.osmand.access.AccessibleContent; + import android.app.Activity; import android.view.MotionEvent; -import android.view.View; // Provide some additional accessibility means for activity view elements. // @@ -16,22 +17,6 @@ public class AccessibleActivity extends Activity implements AccessibleContent.Ca // to add element to it. public final AccessibleContent accessibleContent = new AccessibleContent(); - // Below are two helper methods to improve AlertDialog accessibility. - // - // Since usual message in an AlertDialog that is set by - // AlertDialog.Builder.setMessage() is spoken only once at the best case - // and there is no way to repeat it, use following two methods - // to wrap it into a View and set it by AlertDialog.Builder.setView(). - // Such message will be focusable and so it can be repeated by selecting. - - public View accessibleMessage(CharSequence msg) { - return TextMessage.makeView(this, msg); - } - - public View accessibleMessage(int msgid) { - return TextMessage.makeView(this, msgid); - } - @Override public boolean dispatchNativeTouchEvent(MotionEvent event) { return super.dispatchTouchEvent(event); diff --git a/OsmAnd/src/net/osmand/access/AccessibleAlertBuilder.java b/OsmAnd/src/net/osmand/access/AccessibleAlertBuilder.java new file mode 100644 index 0000000000..3c1c7e6d05 --- /dev/null +++ b/OsmAnd/src/net/osmand/access/AccessibleAlertBuilder.java @@ -0,0 +1,51 @@ +package net.osmand.access; + +import net.osmand.access.TextMessage; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.R; + +import android.app.AlertDialog.Builder; +import android.content.Context; + +// Since usual message in an AlertDialog that is set by +// AlertDialog.Builder.setMessage() is spoken only once +// at the best case and there is no way to explore or even repeat it, +// this class provides more accessible alternative +// that wraps the message into a dedicated view. +// Such message will be focusable and so it can be repeated +// by selecting. +// +// Note: when accessibility extensions option is not checked +// or system accessibility service is turned off this class +// acts just identical to it's direct parent. +// +public class AccessibleAlertBuilder extends Builder { + + // The method getContext() is only available + // starting from API level 11, so store it here. + private final Context context; + + // Conventional constructor. + public AccessibleAlertBuilder(Context context) { + super(context); + this.context = context; + } + + + // Provided setMessage() alternatives. + + @Override + public Builder setMessage(CharSequence msg) { + if (OsmandApplication.getSettings().ACCESSIBILITY_EXTENSIONS.get()) + return setView(TextMessage.makeView(context, msg, R.layout.alert)); + return super.setMessage(msg); + } + + @Override + public Builder setMessage(int msgid) { + if (OsmandApplication.getSettings().ACCESSIBILITY_EXTENSIONS.get()) + return setView(TextMessage.makeView(context, msgid, R.layout.alert)); + return super.setMessage(msgid); + } + +} diff --git a/OsmAnd/src/net/osmand/access/AccessibleContent.java b/OsmAnd/src/net/osmand/access/AccessibleContent.java index 1cc91089a8..ac665d3dea 100644 --- a/OsmAnd/src/net/osmand/access/AccessibleContent.java +++ b/OsmAnd/src/net/osmand/access/AccessibleContent.java @@ -2,6 +2,8 @@ package net.osmand.access; import java.util.ArrayList; +import net.osmand.plus.OsmandApplication; + import android.graphics.Rect; import android.os.SystemClock; import android.view.MotionEvent; @@ -19,7 +21,7 @@ import android.view.accessibility.AccessibilityEvent; // public class AccessibleContent extends ArrayList { - public interface Callback { + protected interface Callback { public boolean dispatchNativeTouchEvent(MotionEvent event); } @@ -36,39 +38,41 @@ public class AccessibleContent extends ArrayList { } public boolean dispatchTouchEvent(MotionEvent event, Callback callback) { - int action = event.getAction(); - View newTouch; - switch (action) { - case MotionEvent.ACTION_MOVE: - newTouch = findTouch(event); - if ((newTouch != null) && (newTouch != nowTouched)) { - float x = event.getX(); - float y = event.getY(); - float pressure = event.getPressure(); - float size = event.getSize(); - int metaState = event.getMetaState(); - float xPrecision = event.getXPrecision(); - float yPrecision = event.getYPrecision(); - int deviceId = event.getDeviceId(); - int edgeFlags = event.getEdgeFlags(); - event.setAction(MotionEvent.ACTION_CANCEL); - callback.dispatchNativeTouchEvent(event); - newTouch.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); - long now = SystemClock.uptimeMillis(); - event.recycle(); - event = MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, x, y, pressure, size, - metaState, xPrecision, yPrecision, deviceId, edgeFlags); + if (OsmandApplication.getSettings().ACCESSIBILITY_EXTENSIONS.get()) { + int action = event.getAction(); + View newTouch; + switch (action) { + case MotionEvent.ACTION_MOVE: + newTouch = findTouch(event); + if ((newTouch != null) && (newTouch != nowTouched)) { + float x = event.getX(); + float y = event.getY(); + float pressure = event.getPressure(); + float size = event.getSize(); + int metaState = event.getMetaState(); + float xPrecision = event.getXPrecision(); + float yPrecision = event.getYPrecision(); + int deviceId = event.getDeviceId(); + int edgeFlags = event.getEdgeFlags(); + event.setAction(MotionEvent.ACTION_CANCEL); + callback.dispatchNativeTouchEvent(event); + newTouch.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); + long now = SystemClock.uptimeMillis(); + event.recycle(); + event = MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, x, y, pressure, size, + metaState, xPrecision, yPrecision, deviceId, edgeFlags); + } + nowTouched = newTouch; + break; + case MotionEvent.ACTION_DOWN: + nowTouched = findTouch(event); + if (nowTouched != null) + nowTouched.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); + break; + default: + nowTouched = null; + break; } - nowTouched = newTouch; - break; - case MotionEvent.ACTION_DOWN: - nowTouched = findTouch(event); - if (nowTouched != null) - nowTouched.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); - break; - default: - nowTouched = null; - break; } return callback.dispatchNativeTouchEvent(event); } diff --git a/OsmAnd/src/net/osmand/access/AccessibleLayout.java b/OsmAnd/src/net/osmand/access/AccessibleLayout.java index e0a06bfc31..16545bb9be 100644 --- a/OsmAnd/src/net/osmand/access/AccessibleLayout.java +++ b/OsmAnd/src/net/osmand/access/AccessibleLayout.java @@ -1,5 +1,7 @@ package net.osmand.access; +import net.osmand.plus.OsmandApplication; + import android.content.Context; import android.graphics.Rect; import android.os.SystemClock; @@ -72,44 +74,46 @@ public class AccessibleLayout extends FrameLayout { @Override public boolean dispatchTouchEvent(MotionEvent event) { - int action = event.getAction(); - View newTouch; - switch (action) { - case MotionEvent.ACTION_MOVE: - newTouch = findTouch(event); - if ((newTouch != null) && (newTouch != nowTouched)) { - if (newTouch.isClickable()) { - float x = event.getX(); - float y = event.getY(); - float pressure = event.getPressure(); - float size = event.getSize(); - int metaState = event.getMetaState(); - float xPrecision = event.getXPrecision(); - float yPrecision = event.getYPrecision(); - int deviceId = event.getDeviceId(); - int edgeFlags = event.getEdgeFlags(); - event.setAction(MotionEvent.ACTION_CANCEL); - super.dispatchTouchEvent(event); - long now = SystemClock.uptimeMillis(); - event.recycle(); - event = MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, x, y, pressure, size, - metaState, xPrecision, yPrecision, deviceId, edgeFlags); + final boolean swallow = OsmandApplication.getSettings().ACCESSIBILITY_EXTENSIONS.get(); + if (swallow) { + int action = event.getAction(); + View newTouch; + switch (action) { + case MotionEvent.ACTION_MOVE: + newTouch = findTouch(event); + if ((newTouch != null) && (newTouch != nowTouched)) { + if (newTouch.isClickable()) { + float x = event.getX(); + float y = event.getY(); + float pressure = event.getPressure(); + float size = event.getSize(); + int metaState = event.getMetaState(); + float xPrecision = event.getXPrecision(); + float yPrecision = event.getYPrecision(); + int deviceId = event.getDeviceId(); + int edgeFlags = event.getEdgeFlags(); + event.setAction(MotionEvent.ACTION_CANCEL); + super.dispatchTouchEvent(event); + long now = SystemClock.uptimeMillis(); + event.recycle(); + event = MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, x, y, pressure, size, + metaState, xPrecision, yPrecision, deviceId, edgeFlags); + } + newTouch.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); } - newTouch.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); + nowTouched = newTouch; + break; + case MotionEvent.ACTION_DOWN: + nowTouched = findTouch(event); + if (nowTouched != null) + nowTouched.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); + break; + default: + nowTouched = null; + break; } - nowTouched = newTouch; - break; - case MotionEvent.ACTION_DOWN: - nowTouched = findTouch(event); - if (nowTouched != null) - nowTouched.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); - break; - default: - nowTouched = null; - break; } - super.dispatchTouchEvent(event); - return true; + return super.dispatchTouchEvent(event) || swallow; } } diff --git a/OsmAnd/src/net/osmand/access/AccessibleToast.java b/OsmAnd/src/net/osmand/access/AccessibleToast.java index ebb845c7b6..89331dadef 100644 --- a/OsmAnd/src/net/osmand/access/AccessibleToast.java +++ b/OsmAnd/src/net/osmand/access/AccessibleToast.java @@ -1,6 +1,9 @@ package net.osmand.access; +import net.osmand.access.TextMessage; +import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; + import android.content.Context; import android.view.accessibility.AccessibilityEvent; import android.widget.Toast; @@ -15,25 +18,23 @@ public class AccessibleToast extends Toast { } public static Toast makeText(Context context, int msg, int duration) { - if(true){ - // currently disabled (waiting for settings) - return Toast.makeText(context, msg, duration); - } - final Toast toast = new AccessibleToast(context); - toast.setView(TextMessage.makeView(context, msg, R.layout.notification)); - toast.setDuration(duration); - return toast; + if (OsmandApplication.getSettings().ACCESSIBILITY_EXTENSIONS.get()) { + final Toast toast = new AccessibleToast(context); + toast.setView(TextMessage.makeView(context, msg, R.layout.notification)); + toast.setDuration(duration); + return toast; + } + return Toast.makeText(context, msg, duration); } public static Toast makeText(Context context, CharSequence msg, int duration) { - if(true){ - // currently disabled (waiting for settings) - return Toast.makeText(context, msg, duration); - } - final Toast toast = new AccessibleToast(context); - toast.setView(TextMessage.makeView(context, msg, R.layout.notification)); - toast.setDuration(duration); - return toast; + if (OsmandApplication.getSettings().ACCESSIBILITY_EXTENSIONS.get()) { + final Toast toast = new AccessibleToast(context); + toast.setView(TextMessage.makeView(context, msg, R.layout.notification)); + toast.setDuration(duration); + return toast; + } + return Toast.makeText(context, msg, duration); } @Override diff --git a/OsmAnd/src/net/osmand/access/AccessibleTrackedActivity.java b/OsmAnd/src/net/osmand/access/AccessibleTrackedActivity.java deleted file mode 100644 index 4e671ebcb8..0000000000 --- a/OsmAnd/src/net/osmand/access/AccessibleTrackedActivity.java +++ /dev/null @@ -1,45 +0,0 @@ -package net.osmand.access; - -import android.app.Activity; -import android.view.MotionEvent; -import android.view.View; - -// Provide some additional accessibility means for activity view elements. -// -// To make use of these capabilities simply derive your activity from this class -// and then add view elements you wish to be accessible -// to the accessibleContent list. -// -public class AccessibleTrackedActivity extends Activity implements AccessibleContent.Callback { - - // List of accessible views. Use accessibleContent.add(element) - // to add element to it. - public final AccessibleContent accessibleContent = new AccessibleContent(); - - // Below are two helper methods to improve AlertDialog accessibility. - // - // Since usual message in an AlertDialog that is set by - // AlertDialog.Builder.setMessage() is spoken only once at the best case - // and there is no way to repeat it, use following two methods - // to wrap it into a View and set it by AlertDialog.Builder.setView(). - // Such message will be focusable and so it can be repeated by selecting. - - public View accessibleMessage(CharSequence msg) { - return TextMessage.makeView(this, msg); - } - - public View accessibleMessage(int msgid) { - return TextMessage.makeView(this, msgid); - } - - @Override - public boolean dispatchNativeTouchEvent(MotionEvent event) { - return super.dispatchTouchEvent(event); - } - - @Override - public boolean dispatchTouchEvent(MotionEvent event) { - return accessibleContent.dispatchTouchEvent(event, this); - } - -} diff --git a/OsmAnd/src/net/osmand/access/ExplorableTextView.java b/OsmAnd/src/net/osmand/access/ExplorableTextView.java index 644a3c168b..a705107d4a 100644 --- a/OsmAnd/src/net/osmand/access/ExplorableTextView.java +++ b/OsmAnd/src/net/osmand/access/ExplorableTextView.java @@ -1,5 +1,7 @@ package net.osmand.access; +import net.osmand.plus.OsmandApplication; + import android.content.Context; import android.text.Layout; import android.text.method.ArrowKeyMovementMethod; @@ -38,6 +40,8 @@ public class ExplorableTextView extends TextView { @Override public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + if (!OsmandApplication.getSettings().ACCESSIBILITY_EXTENSIONS.get()) + return super.dispatchPopulateAccessibilityEvent(event); cursorTrackingEnabled = false; boolean result = super.dispatchPopulateAccessibilityEvent(event); if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED) { @@ -57,13 +61,15 @@ public class ExplorableTextView extends TextView { @Override protected MovementMethod getDefaultMovementMethod() { - return ArrowKeyMovementMethod.getInstance(); + if (OsmandApplication.getSettings().ACCESSIBILITY_EXTENSIONS.get()) + return ArrowKeyMovementMethod.getInstance(); + return super.getDefaultMovementMethod(); } @Override protected void onTextChanged(CharSequence text, int start, int before, int after) { super.onTextChanged(text, start, before, after); - if (!isFocused()) { + if (OsmandApplication.getSettings().ACCESSIBILITY_EXTENSIONS.get() && !isFocused()) { selectionLength = Math.min(text.length(), AccessibilityEvent.MAX_TEXT_LENGTH); sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED); } @@ -72,7 +78,7 @@ public class ExplorableTextView extends TextView { @Override protected void onSelectionChanged(int start, int end) { super.onSelectionChanged(start, end); - if (cursorTrackingEnabled && isFocused()) { + if (OsmandApplication.getSettings().ACCESSIBILITY_EXTENSIONS.get() && cursorTrackingEnabled && isFocused()) { if (end >= getText().length()) { cursor = getText().length(); } else if (cursor != end) { diff --git a/OsmAnd/src/net/osmand/access/MapExplorer.java b/OsmAnd/src/net/osmand/access/MapExplorer.java index 88ba9e60ff..8989ab8383 100644 --- a/OsmAnd/src/net/osmand/access/MapExplorer.java +++ b/OsmAnd/src/net/osmand/access/MapExplorer.java @@ -75,16 +75,15 @@ public class MapExplorer implements OnGestureListener, IContextMenuProvider { contextProvider = this; } if (different(newSelections, selectedObjects)) { - // FIXME Map explorer -// ContextMenuLayer contextMenuLayer = mapView.getContextMenuLayer(); -// if (contextMenuLayer != null) { -// contextMenuLayer.setSelections(newSelections, contextProvider); -// if (!newSelections.isEmpty()) -// mapView.showMessage(mapView.getSettings().USE_SHORT_OBJECT_NAMES.get() ? -// contextMenuLayer.getSelectedObjectName() : -// contextMenuLayer.getSelectedObjectDescription()); -// } -// selectedObjects = newSelections; + ContextMenuLayer contextMenuLayer = mapView.getLayerByClass(ContextMenuLayer.class); + if (contextMenuLayer != null) { + contextMenuLayer.setSelections(newSelections, contextProvider); + if (!newSelections.isEmpty()) + mapView.showMessage(mapView.getSettings().USE_SHORT_OBJECT_NAMES.get() ? + contextMenuLayer.getSelectedObjectName() : + contextMenuLayer.getSelectedObjectDescription()); + } + selectedObjects = newSelections; } } @@ -96,9 +95,8 @@ public class MapExplorer implements OnGestureListener, IContextMenuProvider { if (mapView.getSettings().SCROLL_MAP_BY_GESTURES.get()) return fallback.onDown(e); ContextMenuLayer contextMenuLayer = mapView.getLayerByClass(ContextMenuLayer.class); - // FIXME -// if (contextMenuLayer != null) -// contextMenuLayer.setSelections(null, null); + if (contextMenuLayer != null) + contextMenuLayer.setSelections(null, null); selectedObjects = null; describePointedObjects(e); return false; diff --git a/OsmAnd/src/net/osmand/access/NavigationInfo.java b/OsmAnd/src/net/osmand/access/NavigationInfo.java index 088eccb3a9..1e76cf7917 100644 --- a/OsmAnd/src/net/osmand/access/NavigationInfo.java +++ b/OsmAnd/src/net/osmand/access/NavigationInfo.java @@ -4,6 +4,8 @@ import java.util.ArrayList; import java.util.List; import net.osmand.OsmAndFormatter; +import net.osmand.access.AccessibleToast; +import net.osmand.access.RelativeDirectionStyle; import net.osmand.osm.LatLon; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandSettings; @@ -100,7 +102,7 @@ public class NavigationInfo { private int directionTo(final Location point, float heading) { final float bearing = currentLocation.bearingTo(point) - heading; final int nSectors = (style == RelativeDirectionStyle.CLOCKWISE) ? 12 : direction.length; - int sector = (int)Math.round(Math.abs(bearing) * (float)nSectors / FULL_CIRCLE) % nSectors; + int sector = Math.round(Math.abs(bearing) * (float)nSectors / FULL_CIRCLE) % nSectors; if ((bearing < 0) && (sector != 0)) sector = nSectors - sector; return sector; @@ -139,7 +141,7 @@ public class NavigationInfo { public NavigationInfo(final Context context) { this.context = context; - settings = ((OsmandApplication)(context.getApplicationContext())).getSettings(); + settings = OsmandApplication.getSettings(); currentLocation = null; lastDirection = new RelativeDirection(); lastNotificationTime = SystemClock.uptimeMillis(); @@ -158,7 +160,7 @@ public class NavigationInfo { // The argument must be not null as well as the currentLocation private String absoluteDirectionString(float bearing) { - int direction = (int)Math.round(Math.abs(bearing) * (float)cardinal.length / FULL_CIRCLE) % cardinal.length; + int direction = Math.round(Math.abs(bearing) * (float)cardinal.length / FULL_CIRCLE) % cardinal.length; if ((bearing < 0) && (direction != 0)) direction = cardinal.length - direction; return getString(cardinal[direction]); @@ -234,7 +236,7 @@ public class NavigationInfo { public synchronized void setLocation(Location location) { currentLocation = location; - if (autoAnnounce) { + if (autoAnnounce && ((OsmandApplication)(context.getApplicationContext())).accessibilityEnabled()) { final LatLon point = settings.getPointToNavigate(); if (point != null) { if ((currentLocation != null) && currentLocation.hasBearing()) { diff --git a/OsmAnd/src/net/osmand/access/RelativeDirectionStyle.java b/OsmAnd/src/net/osmand/access/RelativeDirectionStyle.java index aa4fea52ff..2451e6b924 100644 --- a/OsmAnd/src/net/osmand/access/RelativeDirectionStyle.java +++ b/OsmAnd/src/net/osmand/access/RelativeDirectionStyle.java @@ -14,8 +14,8 @@ public enum RelativeDirectionStyle { this.key = key; } - public static String toHumanString(RelativeDirectionStyle style, Context ctx) { - return ctx.getResources().getString(style.key); + public String toHumanString(Context ctx) { + return ctx.getResources().getString(key); } } diff --git a/OsmAnd/src/net/osmand/access/TextMessage.java b/OsmAnd/src/net/osmand/access/TextMessage.java index f67777e975..30ddfa7163 100644 --- a/OsmAnd/src/net/osmand/access/TextMessage.java +++ b/OsmAnd/src/net/osmand/access/TextMessage.java @@ -1,6 +1,8 @@ package net.osmand.access; +import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; + import android.content.Context; import android.view.LayoutInflater; import android.view.View; @@ -9,14 +11,6 @@ import android.widget.TextView; // This class contains only static methods intended to improve // accessibility for AlertDialog and Toast messages. // -// Since usual message in an AlertDialog that is set by -// AlertDialog.Builder.setMessage() is spoken only once -// at the best case and there is no way to explore or even repeat it, -// use public methods of this class to wrap it into a View -// and set it by AlertDialog.Builder.setView(). -// Such message will be focusable and so it can be repeated -// by selecting. -// public class TextMessage { protected static View makeView(Context ctx, CharSequence msg, int resid) { @@ -31,12 +25,4 @@ public class TextMessage { return layout; } - public static View makeView(Context ctx, CharSequence msg) { - return makeView(ctx, msg, R.layout.alert); - } - - public static View makeView(Context ctx, int msgid) { - return makeView(ctx, msgid, R.layout.alert); - } - } diff --git a/OsmAnd/src/net/osmand/plus/OsmandApplication.java b/OsmAnd/src/net/osmand/plus/OsmandApplication.java index 83212425cf..c11afe9eb5 100644 --- a/OsmAnd/src/net/osmand/plus/OsmandApplication.java +++ b/OsmAnd/src/net/osmand/plus/OsmandApplication.java @@ -21,6 +21,7 @@ import net.osmand.plus.OsmandSettings; import net.osmand.plus.PoiFiltersHelper; import net.osmand.plus.ProgressDialogImplementation; import net.osmand.LogUtil; +import net.osmand.access.AccessibilityMode; import net.osmand.access.AccessibleToast; import net.osmand.plus.activities.DayNightHelper; import net.osmand.plus.activities.SavingTrackHelper; @@ -43,6 +44,7 @@ import android.content.Intent; import android.content.res.Configuration; import android.os.Handler; import android.text.format.DateFormat; +import android.view.accessibility.AccessibilityManager; import android.widget.Toast; import com.bidforfix.andorid.BidForFixHelper; @@ -488,4 +490,14 @@ public class OsmandApplication extends Application { } } + + public boolean accessibilityEnabled() { + final AccessibilityMode mode = getSettings().ACCESSIBILITY_MODE.get(); + if (mode == AccessibilityMode.ON) + return true; + else if (mode == AccessibilityMode.OFF) + return false; + return ((AccessibilityManager)getSystemService(ACCESSIBILITY_SERVICE)).isEnabled(); + } + } diff --git a/OsmAnd/src/net/osmand/plus/OsmandSettings.java b/OsmAnd/src/net/osmand/plus/OsmandSettings.java index de9882df38..98814af24e 100644 --- a/OsmAnd/src/net/osmand/plus/OsmandSettings.java +++ b/OsmAnd/src/net/osmand/plus/OsmandSettings.java @@ -8,8 +8,8 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import net.osmand.LogUtil; import net.osmand.Version; +import net.osmand.access.AccessibilityMode; import net.osmand.access.AccessibleToast; import net.osmand.access.RelativeDirectionStyle; import net.osmand.map.ITileSource; @@ -300,6 +300,32 @@ public class OsmandSettings { return prefs.edit().putBoolean(getId(), val).commit(); } } + + private class BooleanAccessibilityPreference extends BooleanPreference { + + private BooleanAccessibilityPreference(String id, boolean defaultValue, boolean global) { + super(id, defaultValue, global); + } + + private BooleanAccessibilityPreference(String id, boolean defaultValue, boolean global, boolean cache) { + super(id, defaultValue, global, cache); + } + + @Override + protected Boolean getValue(SharedPreferences prefs, Boolean defaultValue) { + return ctx.accessibilityEnabled() ? + super.getValue(prefs, defaultValue) : + defaultValue; + } + + @Override + protected boolean setValue(SharedPreferences prefs, Boolean val) { + return ctx.accessibilityEnabled() ? + super.setValue(prefs, val) : + false; + } + } + private class IntPreference extends CommonPreference { @@ -384,14 +410,13 @@ public class OsmandSettings { protected E getValue(SharedPreferences prefs, E defaultValue) { try { int i = prefs.getInt(getId(), -1); - if(i < 0 || i >= values.length){ - return defaultValue; + if(i >= 0 && i < values.length){ + return values[i]; } - return values[i]; - } catch (Exception e) { - android.util.Log.e(LogUtil.TAG, "Error getting value for: " + this.getId(), e); - return defaultValue; + } catch (ClassCastException ex) { + setValue(prefs, defaultValue); } + return defaultValue; } @Override @@ -414,32 +439,36 @@ public class OsmandSettings { // cache of metrics constants as they are used very often public final OsmandPreference METRIC_SYSTEM = new EnumIntPreference( "default_metric_system", MetricsConstants.KILOMETERS_AND_METERS, true, true, MetricsConstants.values()); - - public String getId() { - return "direction_style"; - }; - // this value string is synchronized with settings_pref.xml preference name // cache of metrics constants as they are used very often public final OsmandPreference DIRECTION_STYLE = new EnumIntPreference( "direction_style", RelativeDirectionStyle.SIDEWISE, true, true, RelativeDirectionStyle.values()); + // this value string is synchronized with settings_pref.xml preference name + // cache of metrics constants as they are used very often + public final OsmandPreference ACCESSIBILITY_MODE = new EnumIntPreference( + "accessibility_mode", AccessibilityMode.DEFAULT, true, true, AccessibilityMode.values()); + // this value string is synchronized with settings_pref.xml preference name public final OsmandPreference USE_TRACKBALL_FOR_MOVEMENTS = new BooleanPreference("use_trackball_for_movements", true, true); // this value string is synchronized with settings_pref.xml preference name public final OsmandPreference ZOOM_BY_TRACKBALL = - new BooleanPreference("zoom_by_trackball", true, true); + new BooleanAccessibilityPreference("zoom_by_trackball", true, true); // this value string is synchronized with settings_pref.xml preference name public final OsmandPreference SCROLL_MAP_BY_GESTURES = - new BooleanPreference("scroll_map_by_gestures", true, true); + new BooleanAccessibilityPreference("scroll_map_by_gestures", true, true); // this value string is synchronized with settings_pref.xml preference name public final OsmandPreference USE_SHORT_OBJECT_NAMES = - new BooleanPreference("use_short_object_names", false, true); + new BooleanAccessibilityPreference("use_short_object_names", false, true); + + // this value string is synchronized with settings_pref.xml preference name + public final OsmandPreference ACCESSIBILITY_EXTENSIONS = + new BooleanAccessibilityPreference("accessibility_extensions", false, true); // this value string is synchronized with settings_pref.xml preference name diff --git a/OsmAnd/src/net/osmand/plus/activities/MainMenuActivity.java b/OsmAnd/src/net/osmand/plus/activities/MainMenuActivity.java index 999f95ae7c..a0dd9c4a63 100644 --- a/OsmAnd/src/net/osmand/plus/activities/MainMenuActivity.java +++ b/OsmAnd/src/net/osmand/plus/activities/MainMenuActivity.java @@ -5,6 +5,7 @@ import java.text.MessageFormat; import java.util.Random; import net.osmand.Version; +import net.osmand.access.AccessibleAlertBuilder; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.ResourceManager; @@ -58,7 +59,7 @@ public class MainMenuActivity extends Activity { if (file.exists() && file.length() > 0) { if (size != file.length() && !firstTime) { String msg = MessageFormat.format(getString(R.string.previous_run_crashed), OsmandApplication.EXCEPTION_PATH); - Builder builder = new AlertDialog.Builder(MainMenuActivity.this); + Builder builder = new AccessibleAlertBuilder(MainMenuActivity.this); builder.setMessage(msg).setNeutralButton(getString(R.string.close), null); builder.setPositiveButton(R.string.send_report, new DialogInterface.OnClickListener() { @Override @@ -287,12 +288,12 @@ public class MainMenuActivity extends Activity { } if(netOsmandWasInstalled){ - Builder builder = new AlertDialog.Builder(this); + Builder builder = new AccessibleAlertBuilder(this); builder.setMessage(R.string.osmand_net_previously_installed); builder.setPositiveButton(R.string.default_buttons_ok, null); builder.show(); } else { - Builder builder = new AlertDialog.Builder(this); + Builder builder = new AccessibleAlertBuilder(this); builder.setMessage(R.string.first_time_msg); builder.setPositiveButton(R.string.first_time_download, new DialogInterface.OnClickListener() { @@ -313,7 +314,7 @@ public class MainMenuActivity extends Activity { boolean check = pref.getBoolean(VECTOR_INDEXES_CHECK, true); // do not show each time if (check && new Random().nextInt() % 5 == 1) { - Builder builder = new AlertDialog.Builder(this); + Builder builder = new AccessibleAlertBuilder(this); if(maps.isEmpty()){ builder.setMessage(R.string.vector_data_missing); } else if(!maps.basemapExists()){ diff --git a/OsmAnd/src/net/osmand/plus/activities/MapActivity.java b/OsmAnd/src/net/osmand/plus/activities/MapActivity.java index 946e447352..77b71b5750 100644 --- a/OsmAnd/src/net/osmand/plus/activities/MapActivity.java +++ b/OsmAnd/src/net/osmand/plus/activities/MapActivity.java @@ -10,7 +10,10 @@ import net.osmand.GPXUtilities.GPXFile; import net.osmand.GPXUtilities.WptPt; import net.osmand.LogUtil; import net.osmand.Version; +import net.osmand.access.AccessibleActivity; +import net.osmand.access.AccessibleAlertBuilder; import net.osmand.access.AccessibleToast; +import net.osmand.access.NavigationInfo; import net.osmand.data.MapTileDownloader.DownloadRequest; import net.osmand.data.MapTileDownloader.IMapDownloaderCallback; import net.osmand.map.IMapLocationListener; @@ -77,7 +80,7 @@ import android.view.animation.Animation; import android.view.animation.Transformation; import android.widget.Toast; -public class MapActivity extends Activity implements IMapLocationListener, SensorEventListener { +public class MapActivity extends AccessibleActivity implements IMapLocationListener, SensorEventListener { private static final String GPS_STATUS_ACTIVITY = "com.eclipsim.gpsstatus2.GPSStatus"; //$NON-NLS-1$ private static final String GPS_STATUS_COMPONENT = "com.eclipsim.gpsstatus2"; //$NON-NLS-1$ @@ -96,8 +99,8 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso private static final int LOST_LOCATION_MSG_ID = 10; private static final long LOST_LOCATION_CHECK_DELAY = 20000; -// private static final int LONG_KEYPRESS_MSG_ID = 28; -// private static final int LONG_KEYPRESS_DELAY = 500; + private static final int LONG_KEYPRESS_MSG_ID = 28; + private static final int LONG_KEYPRESS_DELAY = 500; private long lastTimeAutoZooming = 0; @@ -108,6 +111,7 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso final private MapActivityActions mapActions = new MapActivityActions(this); private EditingPOIActivity poiActions; final private MapActivityLayers mapLayers = new MapActivityLayers(this); + private NavigationInfo navigationInfo; private SavingTrackHelper savingTrackHelper; private LiveMonitoringHelper liveMonitoringHelper; @@ -150,6 +154,7 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); settings = getMyApplication().getSettings(); + navigationInfo = new NavigationInfo(this); requestWindowFeature(Window.FEATURE_NO_TITLE); // Full screen is not used here //getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); @@ -327,7 +332,7 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso if (pointToNavigate == null && gpxPath == null) { notRestoreRoutingMode(); } else { - Builder builder = new AlertDialog.Builder(MapActivity.this); + Builder builder = new AccessibleAlertBuilder(MapActivity.this); builder.setMessage(R.string.continue_follow_previous_route); builder.setPositiveButton(R.string.default_buttons_yes, new DialogInterface.OnClickListener() { @Override @@ -410,6 +415,8 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso public void changeZoom(int newZoom){ boolean changeLocation = settings.AUTO_ZOOM_MAP.get(); mapView.getAnimatedDraggingThread().startZooming(newZoom, changeLocation); + if (getMyApplication().accessibilityEnabled()) + AccessibleToast.makeText(this, getString(R.string.zoomIs) + " " + String.valueOf(newZoom), Toast.LENGTH_SHORT).show(); //$NON-NLS-1$ showAndHideMapPosition(); } @@ -515,6 +522,18 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso //that they could do some key combinations with it... // Victor : doing in that way doesn't close dialog properly! //return true; + } else if (getMyApplication().accessibilityEnabled() && (keyCode == KeyEvent.KEYCODE_DPAD_CENTER)) { + if (!uiHandler.hasMessages(LONG_KEYPRESS_MSG_ID)) { + Message msg = Message.obtain(uiHandler, new Runnable() { + @Override + public void run() { + emitNavigationHint(); + } + }); + msg.what = LONG_KEYPRESS_MSG_ID; + uiHandler.sendMessageDelayed(msg, LONG_KEYPRESS_DELAY); + } + return true; } else if (keyCode == KeyEvent.KEYCODE_SEARCH && event.getRepeatCount() == 0) { Intent newIntent = new Intent(MapActivity.this, SearchActivity.class); // causes wrong position caching: newIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); @@ -540,6 +559,51 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso return super.onKeyDown(keyCode, event); } + public String getNavigationHint(LatLon point) { + String hint = navigationInfo.getDirectionString(point, mapLayers.getLocationLayer().getHeading()); + if (hint == null) + hint = getString(R.string.no_info); + return hint; + } + + private void emitNavigationHint() { + final LatLon point = settings.getPointToNavigate(); + if (point != null) { + if (routingHelper.isRouteCalculated()) { + routingHelper.getVoiceRouter().announceCurrentDirection(); + } else { + AccessibleToast.makeText(this, getNavigationHint(point), Toast.LENGTH_LONG).show(); + } + } else { + AccessibleToast.makeText(this, R.string.mark_final_location_first, Toast.LENGTH_SHORT).show(); + } + } + + private void whereAmIDialog() { + final List items = new ArrayList(); + items.add(getString(R.string.show_location)); + items.add(getString(R.string.show_details)); + AlertDialog.Builder menu = new AlertDialog.Builder(this); + menu.setItems(items.toArray(new String[items.size()]), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int item) { + dialog.dismiss(); + switch (item) { + case 0: + backToLocationImpl(); + break; + case 1: + navigationInfo.show(settings.getPointToNavigate(), mapLayers.getLocationLayer().getHeading()); + break; + default: + break; + } + } + }); + menu.show(); + } + public void setMapLocation(double lat, double lon){ mapView.setLatLon(lat, lon); locationChanged(lat, lon, this); @@ -695,6 +759,7 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso registerUnregisterSensor(location); updateSpeedBearing(location); mapLayers.getLocationLayer().setLastKnownLocation(location); + navigationInfo.setLocation(location); if(routingHelper.isFollowingMode()){ if(location == null || !location.hasAccuracy() || location.getAccuracy() < ACCURACY_FOR_GPX_AND_ROUTING) { // Update routing position @@ -958,16 +1023,22 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso //onBackPressed(); //return true; } else if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) { - contextMenuPoint(mapView.getLatitude(), mapView.getLongitude()); + if (!getMyApplication().accessibilityEnabled()) { + contextMenuPoint(mapView.getLatitude(), mapView.getLongitude()); + } else if (uiHandler.hasMessages(LONG_KEYPRESS_MSG_ID)) { + uiHandler.removeMessages(LONG_KEYPRESS_MSG_ID); + contextMenuPoint(mapView.getLatitude(), mapView.getLongitude()); + } return true; - } else + } else if (settings.ZOOM_BY_TRACKBALL.get()) { // Parrot device has only dpad left and right if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) { changeZoom(mapView.getZoom() - 1); return true; - } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) { - changeZoom(mapView.getZoom() + 1); - return true; + } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) { + changeZoom(mapView.getZoom() + 1); + return true; + } } return super.onKeyUp(keyCode,event); } @@ -1103,7 +1174,11 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso startActivity(intentSettings); return true; } else if (itemId == R.id.map_where_am_i) { - backToLocationImpl(); + if (getMyApplication().accessibilityEnabled()) { + whereAmIDialog(); + } else { + backToLocationImpl(); + } return true; } else if (itemId == R.id.map_show_gps_status) { startGpsStatusIntent(); @@ -1181,7 +1256,7 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso if (resolved != null) { startActivity(intent); } else { - AlertDialog.Builder builder = new AlertDialog.Builder(this); + AlertDialog.Builder builder = new AccessibleAlertBuilder(this); builder.setMessage(getString(R.string.gps_status_app_not_found)); builder.setPositiveButton( getString(R.string.default_buttons_yes), diff --git a/OsmAnd/src/net/osmand/plus/activities/MapActivityActions.java b/OsmAnd/src/net/osmand/plus/activities/MapActivityActions.java index 484dbcb890..40f29db06d 100644 --- a/OsmAnd/src/net/osmand/plus/activities/MapActivityActions.java +++ b/OsmAnd/src/net/osmand/plus/activities/MapActivityActions.java @@ -13,6 +13,7 @@ import net.osmand.FavouritePoint; import net.osmand.GPXUtilities; import net.osmand.GPXUtilities.GPXFile; import net.osmand.LogUtil; +import net.osmand.access.AccessibleAlertBuilder; import net.osmand.access.AccessibleToast; import net.osmand.data.Amenity; import net.osmand.map.ITileSource; @@ -233,7 +234,7 @@ public class MapActivityActions implements DialogProvider { private Dialog createReloadTitleDialog(final Bundle args) { - Builder builder = new AlertDialog.Builder(mapActivity); + Builder builder = new AccessibleAlertBuilder(mapActivity); builder.setMessage(R.string.context_menu_item_update_map_confirm); builder.setNegativeButton(R.string.default_buttons_cancel, null); final OsmandMapTileView mapView = mapActivity.getMapView(); @@ -414,7 +415,7 @@ public class MapActivityActions implements DialogProvider { saveDirections(); } }; - Builder builder = new AlertDialog.Builder(mapActivity); + Builder builder = new AccessibleAlertBuilder(mapActivity); builder.setTitle(R.string.show_route); builder.setMessage(mapActivity.getRoutingHelper().getGeneralRouteInformation()); builder.setPositiveButton(R.string.default_buttons_save, saveDirections); diff --git a/OsmAnd/src/net/osmand/plus/activities/MapActivityLayers.java b/OsmAnd/src/net/osmand/plus/activities/MapActivityLayers.java index 47d7bf3b21..a763bb242b 100644 --- a/OsmAnd/src/net/osmand/plus/activities/MapActivityLayers.java +++ b/OsmAnd/src/net/osmand/plus/activities/MapActivityLayers.java @@ -155,7 +155,7 @@ public class MapActivityLayers { mapInfoLayer = new MapInfoLayer(activity, routeLayer); mapView.addLayer(mapInfoLayer, 9); // 10. route info layer - routeInfoLayer = new RouteInfoLayer(routingHelper, (LinearLayout) activity.findViewById(R.id.RouteLayout), contextMenuLayer); + routeInfoLayer = new RouteInfoLayer(routingHelper, activity, contextMenuLayer); mapView.addLayer(routeInfoLayer, 10); // 11. route info layer mapControlsLayer = new MapControlsLayer(activity); diff --git a/OsmAnd/src/net/osmand/plus/activities/SettingsActivity.java b/OsmAnd/src/net/osmand/plus/activities/SettingsActivity.java index 7e2a88eed9..02ec7a29de 100644 --- a/OsmAnd/src/net/osmand/plus/activities/SettingsActivity.java +++ b/OsmAnd/src/net/osmand/plus/activities/SettingsActivity.java @@ -12,6 +12,7 @@ import java.util.Set; import net.osmand.ResultMatcher; import net.osmand.Version; +import net.osmand.access.AccessibilityMode; import net.osmand.access.AccessibleToast; import net.osmand.access.RelativeDirectionStyle; import net.osmand.map.TileSourceManager; @@ -84,6 +85,9 @@ public class SettingsActivity extends PreferenceActivity implements OnPreference private ListPreference dayNightModePreference; private ListPreference routerServicePreference; + private ListPreference accessibilityModePreference; + private ListPreference directionStylePreference; + private CheckBoxPreference routeServiceEnabled; private BroadcastReceiver broadcastReceiver; @@ -184,6 +188,7 @@ public class SettingsActivity extends PreferenceActivity implements OnPreference registerBooleanPreference(osmandSettings.ZOOM_BY_TRACKBALL,screen); registerBooleanPreference(osmandSettings.SCROLL_MAP_BY_GESTURES,screen); registerBooleanPreference(osmandSettings.USE_SHORT_OBJECT_NAMES,screen); + registerBooleanPreference(osmandSettings.ACCESSIBILITY_EXTENSIONS,screen); registerBooleanPreference(osmandSettings.USE_HIGH_RES_MAPS,screen); registerBooleanPreference(osmandSettings.USE_ENGLISH_NAMES,screen); registerBooleanPreference(osmandSettings.AUTO_ZOOM_MAP,screen); @@ -283,9 +288,15 @@ public class SettingsActivity extends PreferenceActivity implements OnPreference } registerListPreference(osmandSettings.MAP_TEXT_SIZE, screen, entries, floatValues); + entries = new String[AccessibilityMode.values().length]; + for(int i=0; i items = new ArrayList(); + items.add(getString(R.string.show_poi_on_map)); + items.add(getString(R.string.navigate_to)); + if (((OsmandApplication)getApplication()).accessibilityEnabled()) + items.add(getString(R.string.show_details)); + AlertDialog.Builder builder = new AlertDialog.Builder(SearchPOIActivity.this); builder.setTitle(format); - builder.setItems(new String[]{getString(R.string.show_poi_on_map), getString(R.string.navigate_to)}, new DialogInterface.OnClickListener(){ + builder.setItems(items.toArray(new String[items.size()]), new DialogInterface.OnClickListener(){ @Override public void onClick(DialogInterface dialog, int which) { + if(which == 2){ + showPOIDetails(amenity, settings.usingEnglishNames()); + return; + } if(which == 0){ int z = settings.getLastKnownMapZoom(); String poiSimpleFormat = OsmAndFormatter.getPoiSimpleFormat(amenity, SearchPOIActivity.this, settings.usingEnglishNames()); @@ -762,7 +777,34 @@ public class SearchPOIActivity extends OsmandListActivity implements SensorEvent } } - + private void showPOIDetails(final Amenity amenity, boolean en) { + AlertDialog.Builder b = new AlertDialog.Builder(SearchPOIActivity.this); + b.setTitle(OsmAndFormatter.getPoiSimpleFormat(amenity, SearchPOIActivity.this, en)); + b.setPositiveButton(R.string.default_buttons_ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + dialog.cancel(); + } + }); + List attributes = new ArrayList(); + String direction = navigationInfo.getDirectionString(amenity.getLocation(), heading); + if (direction != null) + attributes.add(direction); + if (amenity.getPhone() != null) + attributes.add(getString(R.string.phone) + " " + amenity.getPhone()); + if (amenity.getOpeningHours() != null) + attributes.add(getString(R.string.opening_hours) + " " + amenity.getOpeningHours()); + attributes.add(getString(R.string.navigate_point_latitude) + " " + Double.toString(amenity.getLocation().getLatitude())); + attributes.add(getString(R.string.navigate_point_longitude) + " " + Double.toString(amenity.getLocation().getLongitude())); + b.setItems(attributes.toArray(new String[attributes.size()]), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + } + }); + b.show(); + } + // Working with location listeners private LocationListener networkListener = new LocationListener(){ @Override diff --git a/OsmAnd/src/net/osmand/plus/views/ContextMenuLayer.java b/OsmAnd/src/net/osmand/plus/views/ContextMenuLayer.java index f543a7d6d6..8da1ee433d 100644 --- a/OsmAnd/src/net/osmand/plus/views/ContextMenuLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/ContextMenuLayer.java @@ -150,8 +150,26 @@ public class ContextMenuLayer extends OsmandMapLayer { } + public void setSelections(List selections, IContextMenuProvider contextProvider) { + if (selections != null) { + selectedObjects = selections; + } else { + selectedObjects.clear(); + } + if (!selectedObjects.isEmpty()) { + selectedContextProvider = contextProvider; + latLon = contextProvider.getObjectLocation(selectedObjects.get(0)); + } + } + @Override public boolean onLongPressEvent(PointF point) { + if (!view.getSettings().SCROLL_MAP_BY_GESTURES.get()) { + if (!selectedObjects.isEmpty()) + view.showMessage(activity.getNavigationHint(latLon)); + return true; + } + if(pressedInTextView(point.x, point.y)){ setLocation(null, ""); //$NON-NLS-1$ view.refreshMap(); @@ -171,22 +189,15 @@ public class ContextMenuLayer extends OsmandMapLayer { } LatLon latLon = view.getLatLonFromScreenPoint(point.x, point.y); - StringBuilder description = new StringBuilder(); + String description = getSelectedObjectDescription(); if (!selectedObjects.isEmpty()) { - if (selectedObjects.size() > 1) { - description.append("1. "); - } - description.append(selectedContextProvider.getObjectDescription(selectedObjects.get(0))); - for (int i = 1; i < selectedObjects.size(); i++) { - description.append("\n" + (i + 1) + ". ").append(selectedContextProvider.getObjectDescription(selectedObjects.get(i))); - } LatLon l = selectedContextProvider.getObjectLocation(selectedObjects.get(0)); if (l != null) { latLon = l; } } - setLocation(latLon, description.toString()); + setLocation(latLon, description); view.refreshMap(); return true; } @@ -212,17 +223,37 @@ public class ContextMenuLayer extends OsmandMapLayer { public String getSelectedObjectName(){ if(!selectedObjects.isEmpty() && selectedContextProvider != null){ - return selectedContextProvider.getObjectName(selectedObjects); + StringBuilder name = new StringBuilder(); + if (selectedObjects.size() > 1) + name.append("1. "); + name.append(selectedContextProvider.getObjectName(selectedObjects.get(0))); + for (int i = 1; i < selectedObjects.size(); i++) + name.append("\n" + (i + 1) + ". ").append(selectedContextProvider.getObjectName(selectedObjects.get(i))); + return name.toString(); + } + return null; + } + + public String getSelectedObjectDescription(){ + if(!selectedObjects.isEmpty() && selectedContextProvider != null){ + StringBuilder description = new StringBuilder(); + if (selectedObjects.size() > 1) + description.append("1. "); + description.append(selectedContextProvider.getObjectDescription(selectedObjects.get(0))); + for (int i = 1; i < selectedObjects.size(); i++) + description.append("\n" + (i + 1) + ". ").append(selectedContextProvider.getObjectDescription(selectedObjects.get(i))); + return description.toString(); } return null; } @Override public boolean onSingleTap(PointF point) { - if (pressedInTextView(point.x, point.y)) { + boolean nativeMode = view.getSettings().SCROLL_MAP_BY_GESTURES.get(); + if ((!nativeMode) || pressedInTextView(point.x, point.y)) { if (!selectedObjects.isEmpty()) { showContextMenuForSelectedObjects(); - } else { + } else if (nativeMode) { activity.contextMenuPoint(latLon.getLatitude(), latLon.getLongitude()); } return true; diff --git a/OsmAnd/src/net/osmand/plus/views/MapControlsLayer.java b/OsmAnd/src/net/osmand/plus/views/MapControlsLayer.java index 079152fedc..8674b143c0 100644 --- a/OsmAnd/src/net/osmand/plus/views/MapControlsLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/MapControlsLayer.java @@ -228,7 +228,9 @@ public class MapControlsLayer extends OsmandMapLayer { private void initBackToMenuButton(final OsmandMapTileView view, FrameLayout parent) { android.widget.FrameLayout.LayoutParams params; - backToMenuButton = new Button(view.getContext()); + Context ctx = view.getContext(); + backToMenuButton = new Button(ctx); + backToMenuButton.setContentDescription(ctx.getString(R.string.backToMenu)); backToMenuButton.setBackgroundResource(R.drawable.map_btn_menu); params = new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, Gravity.BOTTOM | Gravity.LEFT); @@ -244,6 +246,8 @@ public class MapControlsLayer extends OsmandMapLayer { activity.backToMainMenu(); } }); + + activity.accessibleContent.add(backToMenuButton); } private void initRuler(OsmandMapTileView view, FrameLayout parent) { @@ -291,6 +295,9 @@ public class MapControlsLayer extends OsmandMapLayer { zoomOutButton.setContentDescription(ctx.getString(R.string.zoomOut)); parent.addView(zoomOutButton, params); + activity.accessibleContent.add(zoomInButton); + activity.accessibleContent.add(zoomOutButton); + zoomInButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { diff --git a/OsmAnd/src/net/osmand/plus/views/OsmandMapTileView.java b/OsmAnd/src/net/osmand/plus/views/OsmandMapTileView.java index f943d489f2..bc55147506 100644 --- a/OsmAnd/src/net/osmand/plus/views/OsmandMapTileView.java +++ b/OsmAnd/src/net/osmand/plus/views/OsmandMapTileView.java @@ -7,7 +7,9 @@ import java.util.List; import java.util.Map; import net.osmand.LogUtil; +import net.osmand.OsmAndFormatter; import net.osmand.access.AccessibleToast; +import net.osmand.access.MapExplorer; import net.osmand.data.MapTileDownloader.DownloadRequest; import net.osmand.data.MapTileDownloader.IMapDownloaderCallback; import net.osmand.map.IMapLocationListener; @@ -15,6 +17,7 @@ import net.osmand.osm.LatLon; import net.osmand.osm.MapUtils; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandSettings; +import net.osmand.plus.R; import net.osmand.plus.views.MultiTouchSupport.MultiTouchZoomListener; import net.osmand.plus.views.OsmandMapLayer.DrawSettings; @@ -36,6 +39,7 @@ import android.util.FloatMath; import android.view.GestureDetector; import android.view.GestureDetector.OnDoubleTapListener; import android.view.GestureDetector.OnGestureListener; +import android.view.KeyEvent; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceHolder.Callback; @@ -160,7 +164,7 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall getHolder().addCallback(this); animatedDraggingThread = new AnimateDraggingMapThread(this); - gestureDetector = new GestureDetector(getContext(), new MapTileViewOnGestureListener()); + gestureDetector = new GestureDetector(getContext(), new MapExplorer(this, new MapTileViewOnGestureListener())); multiTouchSupport = new MultiTouchSupport(getContext(), new MapTileViewMultiTouchZoomListener()); gestureDetector.setOnDoubleTapListener(new MapTileViewOnDoubleTapListener()); @@ -184,6 +188,11 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall public void surfaceDestroyed(SurfaceHolder holder) { } + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + return application.accessibilityEnabled() ? false : super.onKeyDown(keyCode, event); + } + public void addLayer(OsmandMapLayer layer, float zOrder) { int i = 0; for (i = 0; i < layers.size(); i++) { @@ -209,7 +218,7 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall @SuppressWarnings("unchecked") public T getLayerByClass(Class cl) { for(OsmandMapLayer lr : layers) { - if(cl.isInstance(cl)){ + if(cl.isInstance(lr)){ return (T) lr; } } @@ -765,7 +774,17 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall float dz = (float) (Math.log(relativeToStart) / Math.log(2) * 1.5); float calcZoom = initialMultiTouchZoom + dz; setZoom(Math.round(calcZoom)); - zoomPositionChanged(getZoom()); + final int newZoom = getZoom(); + zoomPositionChanged(newZoom); + if (application.accessibilityEnabled()) { + if (newZoom != initialMultiTouchZoom) { + showMessage(getContext().getString(R.string.zoomIs) + " " + String.valueOf(newZoom)); //$NON-NLS-1$ + } else { + final LatLon p1 = getLatLonFromScreenPoint(x1, y1); + final LatLon p2 = getLatLonFromScreenPoint(x2, y2); + showMessage(OsmAndFormatter.getFormattedDistance((float)MapUtils.getDistance(p1.getLatitude(), p1.getLongitude(), p2.getLatitude(), p2.getLongitude()), getContext())); + } + } } @Override diff --git a/OsmAnd/src/net/osmand/plus/views/RouteInfoLayer.java b/OsmAnd/src/net/osmand/plus/views/RouteInfoLayer.java index ce03dacb79..71a699cb2a 100644 --- a/OsmAnd/src/net/osmand/plus/views/RouteInfoLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/RouteInfoLayer.java @@ -3,6 +3,7 @@ package net.osmand.plus.views; import net.osmand.osm.LatLon; import net.osmand.plus.R; +import net.osmand.plus.activities.MapActivity; import net.osmand.plus.activities.ShowRouteInfoActivity; import net.osmand.plus.routing.RoutingHelper; import net.osmand.plus.routing.RoutingHelper.IRouteInformationListener; @@ -35,7 +36,8 @@ public class RouteInfoLayer extends OsmandMapLayer implements IRouteInformationL - public RouteInfoLayer(RoutingHelper routingHelper, LinearLayout layout, ContextMenuLayer contextMenu){ + public RouteInfoLayer(RoutingHelper routingHelper, MapActivity activity, ContextMenuLayer contextMenu){ + final LinearLayout layout = (LinearLayout) activity.findViewById(R.id.RouteLayout); this.routingHelper = routingHelper; this.contextMenu = contextMenu; prev = (Button) layout.findViewById(R.id.PreviousButton); @@ -44,6 +46,10 @@ public class RouteInfoLayer extends OsmandMapLayer implements IRouteInformationL routingHelper.addListener(this); attachListeners(); updateVisibility(); + + activity.accessibleContent.add(prev); + activity.accessibleContent.add(next); + activity.accessibleContent.add(info); } @Override