Loading src/com/android/documentsui/inspector/GpsCoordinatesTextClassifier.java 0 → 100644 +110 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.documentsui.inspector; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.net.Uri; import android.os.LocaleList; import android.view.textclassifier.TextClassification; import android.view.textclassifier.TextClassificationManager; import android.view.textclassifier.TextClassifier; import android.view.textclassifier.TextSelection; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Checks if a TextView has a latitude and longitude. If so shows the default maps app to open * those coordinates. * * Example - textView.setTextClassifier(new GpsCoordinatesTextClassifier(context, intent)); */ final class GpsCoordinatesTextClassifier implements TextClassifier { // Checks for latitude and longitude points, latitude ranges -90.0 to 90.0 and longitude // ranges -180.0 to 180.0 Valid entries can be of the format "0,0", "0, 0" and "0.0, 0.0" // in the mentioned range. private static final String geoPattern = "-?(90(\\.0*)?|[1-8]?[0-9](\\.[0-9]*)?), *-?(180(" + "\\.0*)?|([1][0-7][0-9]|[0-9]?[0-9])(\\.[0-9]*)?)"; private static final Pattern sGeoPattern = Pattern.compile(geoPattern); private final TextClassifier mSystemClassifier; private final PackageManager mPackageManager; public GpsCoordinatesTextClassifier(PackageManager pm, TextClassifier classifier) { assert pm != null; assert classifier != null; mPackageManager = pm; mSystemClassifier = classifier; } public static GpsCoordinatesTextClassifier create(Context context) { return new GpsCoordinatesTextClassifier( context.getPackageManager(), context.getSystemService(TextClassificationManager.class).getTextClassifier()); } // TODO: add support for local specific formatting @Override public TextClassification classifyText( CharSequence text, int start, int end, LocaleList localeList) { CharSequence sequence = text.subSequence(start, end); if (isGeoSequence(sequence)) { Intent intent = new Intent(Intent.ACTION_VIEW) .setData(Uri.parse(String.format("geo:0,0?q=%s", sequence))); final ResolveInfo info = mPackageManager.resolveActivity(intent, 0); if (info != null) { return new TextClassification.Builder() .setText(sequence.toString()) .setEntityType(TextClassifier.TYPE_ADDRESS, 1.0f) .setIcon(info.loadIcon(mPackageManager)) .setLabel(info.loadLabel(mPackageManager).toString()) .setIntent(intent) .build(); } } // return default if text was not latitude, longitude or we couldn't find an application // to handle the geo intent. return mSystemClassifier.classifyText(text, start, end, localeList); } @Override public TextSelection suggestSelection( CharSequence context, int start, int end, LocaleList localeList) { // Show map action menu popup when entire TextView is selected. final int[] boundaries = {0, context.length()}; if (boundaries != null) { return new TextSelection.Builder(boundaries[0], boundaries[1]) .setEntityType(TextClassifier.TYPE_ADDRESS, 1.0f) .build(); } return mSystemClassifier.suggestSelection(context, start, end, localeList); } private static boolean isGeoSequence(CharSequence text) { return sGeoPattern.matcher(text).matches(); } } src/com/android/documentsui/inspector/KeyValueRow.java +8 −2 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import android.text.Selection; import android.text.Spannable; import android.util.AttributeSet; import android.view.View; import android.view.textclassifier.TextClassifier; import android.widget.LinearLayout; import android.widget.TextView; Loading @@ -37,6 +38,7 @@ public class KeyValueRow extends LinearLayout { private final Resources mRes; private @Nullable ColorStateList mDefaultTextColor; private @Nullable TextClassifier mClassifier; public KeyValueRow(Context context) { this(context, null); Loading @@ -48,10 +50,13 @@ public class KeyValueRow extends LinearLayout { public KeyValueRow(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mRes = context.getResources(); } public void setTextClassifier(TextClassifier classifier) { mClassifier = classifier; } /** * Sets the raw value of the key. Only localized values * should be passed. Loading @@ -67,6 +72,7 @@ public class KeyValueRow extends LinearLayout { public void setValue(CharSequence value) { TextView text = ((TextView) findViewById(R.id.table_row_value)); text.setText(value); text.setTextClassifier(mClassifier); text.setOnLongClickListener((View view) -> { CharSequence textValue = text.getText(); Loading src/com/android/documentsui/inspector/TableView.java +6 −1 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.textclassifier.TextClassifier; import android.widget.LinearLayout; import android.widget.TextView; Loading @@ -40,7 +41,8 @@ public class TableView extends LinearLayout implements TableDisplay { private final Map<CharSequence, KeyValueRow> mRows = new HashMap<>(); private final Resources mRes; private Map<CharSequence, TextView> mTitles = new HashMap<>(); private final Map<CharSequence, TextView> mTitles = new HashMap<>(); private final TextClassifier mClassifier; public TableView(Context context) { this(context, null); Loading @@ -54,6 +56,7 @@ public class TableView extends LinearLayout implements TableDisplay { super(context, attrs, defStyleAttr); mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); mRes = context.getResources(); mClassifier = GpsCoordinatesTextClassifier.create(context); } void setTitle(@StringRes int title, boolean showDivider) { Loading @@ -79,6 +82,7 @@ public class TableView extends LinearLayout implements TableDisplay { protected KeyValueRow createKeyValueRow(ViewGroup parent) { KeyValueRow row = (KeyValueRow) mInflater.inflate(R.layout.table_key_value_row, null); parent.addView(row); row.setTextClassifier(mClassifier); return row; } Loading Loading @@ -106,6 +110,7 @@ public class TableView extends LinearLayout implements TableDisplay { } row.setValue(value); row.setTextClassifier(mClassifier); return row; } Loading tests/unit/com/android/documentsui/inspector/GpsCoordinatesTextClassifierTest.java 0 → 100644 +100 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.documentsui.inspector; import static junit.framework.Assert.assertEquals; import android.content.pm.PackageManager; import android.os.LocaleList; import android.support.test.InstrumentationRegistry; import android.content.Context; import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; import android.view.textclassifier.TextClassification; import android.view.textclassifier.TextClassificationManager; import android.view.textclassifier.TextClassifier; import com.android.documentsui.testing.TestPackageManager; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; /** * Test class for testing recognizing geo coordinates. */ @RunWith(AndroidJUnit4.class) @SmallTest public class GpsCoordinatesTextClassifierTest { private GpsCoordinatesTextClassifier mClassifier; @Before public void setUp() throws Exception { PackageManager pm = TestPackageManager.create(); Context context = InstrumentationRegistry.getTargetContext(); TextClassifier defaultClassifier = context.getSystemService(TextClassificationManager.class).getTextClassifier(); mClassifier = new GpsCoordinatesTextClassifier(pm, defaultClassifier); } @Test public void testBasicPatterns() throws Exception { assertClassifiedGeo("0,0", true); assertClassifiedGeo("0, 0", true); assertClassifiedGeo("0.0,0.0", true); assertClassifiedGeo("0.0, 0.0", true); assertClassifiedGeo("90,180", true); assertClassifiedGeo("90, 180", true); assertClassifiedGeo("90.0000,180.0000", true); assertClassifiedGeo("90.000, 180.000000000000000", true); assertClassifiedGeo("-77.5646564,133.656554654", true); assertClassifiedGeo("33, -179.324234242423", true); assertClassifiedGeo("44.4545454,70.0", true); assertClassifiedGeo("60.0, 60.0", true); assertClassifiedGeo("-33.33,-180", true); assertClassifiedGeo("-88.888888, -33.3333", true); assertClassifiedGeo("90.0, 180.000000", true); assertClassifiedGeo("-90.00000, -180.0", true); } @Test public void testInvalidPatterns() throws Exception { assertClassifiedGeo("0", false); assertClassifiedGeo("Geo Intent", false); assertClassifiedGeo("GeoIntent", false); assertClassifiedGeo("A.B, C.D", false); assertClassifiedGeo("5000, 5000", false); assertClassifiedGeo("500, 500", false); assertClassifiedGeo("90.165464, 180.1", false); assertClassifiedGeo("-90.1, -180.156754", false); } private void assertClassifiedGeo(CharSequence text, boolean expectClassified) { boolean wasClassified; TextClassification test = mClassifier.classifyText( text, 0, text.length(), new LocaleList()); try { wasClassified = "geo".equals(test.getIntent().getData().getScheme()); } catch (NullPointerException intentNotSet) { wasClassified = false; } assertEquals(wasClassified, expectClassified); } } Loading
src/com/android/documentsui/inspector/GpsCoordinatesTextClassifier.java 0 → 100644 +110 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.documentsui.inspector; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.net.Uri; import android.os.LocaleList; import android.view.textclassifier.TextClassification; import android.view.textclassifier.TextClassificationManager; import android.view.textclassifier.TextClassifier; import android.view.textclassifier.TextSelection; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Checks if a TextView has a latitude and longitude. If so shows the default maps app to open * those coordinates. * * Example - textView.setTextClassifier(new GpsCoordinatesTextClassifier(context, intent)); */ final class GpsCoordinatesTextClassifier implements TextClassifier { // Checks for latitude and longitude points, latitude ranges -90.0 to 90.0 and longitude // ranges -180.0 to 180.0 Valid entries can be of the format "0,0", "0, 0" and "0.0, 0.0" // in the mentioned range. private static final String geoPattern = "-?(90(\\.0*)?|[1-8]?[0-9](\\.[0-9]*)?), *-?(180(" + "\\.0*)?|([1][0-7][0-9]|[0-9]?[0-9])(\\.[0-9]*)?)"; private static final Pattern sGeoPattern = Pattern.compile(geoPattern); private final TextClassifier mSystemClassifier; private final PackageManager mPackageManager; public GpsCoordinatesTextClassifier(PackageManager pm, TextClassifier classifier) { assert pm != null; assert classifier != null; mPackageManager = pm; mSystemClassifier = classifier; } public static GpsCoordinatesTextClassifier create(Context context) { return new GpsCoordinatesTextClassifier( context.getPackageManager(), context.getSystemService(TextClassificationManager.class).getTextClassifier()); } // TODO: add support for local specific formatting @Override public TextClassification classifyText( CharSequence text, int start, int end, LocaleList localeList) { CharSequence sequence = text.subSequence(start, end); if (isGeoSequence(sequence)) { Intent intent = new Intent(Intent.ACTION_VIEW) .setData(Uri.parse(String.format("geo:0,0?q=%s", sequence))); final ResolveInfo info = mPackageManager.resolveActivity(intent, 0); if (info != null) { return new TextClassification.Builder() .setText(sequence.toString()) .setEntityType(TextClassifier.TYPE_ADDRESS, 1.0f) .setIcon(info.loadIcon(mPackageManager)) .setLabel(info.loadLabel(mPackageManager).toString()) .setIntent(intent) .build(); } } // return default if text was not latitude, longitude or we couldn't find an application // to handle the geo intent. return mSystemClassifier.classifyText(text, start, end, localeList); } @Override public TextSelection suggestSelection( CharSequence context, int start, int end, LocaleList localeList) { // Show map action menu popup when entire TextView is selected. final int[] boundaries = {0, context.length()}; if (boundaries != null) { return new TextSelection.Builder(boundaries[0], boundaries[1]) .setEntityType(TextClassifier.TYPE_ADDRESS, 1.0f) .build(); } return mSystemClassifier.suggestSelection(context, start, end, localeList); } private static boolean isGeoSequence(CharSequence text) { return sGeoPattern.matcher(text).matches(); } }
src/com/android/documentsui/inspector/KeyValueRow.java +8 −2 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import android.text.Selection; import android.text.Spannable; import android.util.AttributeSet; import android.view.View; import android.view.textclassifier.TextClassifier; import android.widget.LinearLayout; import android.widget.TextView; Loading @@ -37,6 +38,7 @@ public class KeyValueRow extends LinearLayout { private final Resources mRes; private @Nullable ColorStateList mDefaultTextColor; private @Nullable TextClassifier mClassifier; public KeyValueRow(Context context) { this(context, null); Loading @@ -48,10 +50,13 @@ public class KeyValueRow extends LinearLayout { public KeyValueRow(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mRes = context.getResources(); } public void setTextClassifier(TextClassifier classifier) { mClassifier = classifier; } /** * Sets the raw value of the key. Only localized values * should be passed. Loading @@ -67,6 +72,7 @@ public class KeyValueRow extends LinearLayout { public void setValue(CharSequence value) { TextView text = ((TextView) findViewById(R.id.table_row_value)); text.setText(value); text.setTextClassifier(mClassifier); text.setOnLongClickListener((View view) -> { CharSequence textValue = text.getText(); Loading
src/com/android/documentsui/inspector/TableView.java +6 −1 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.textclassifier.TextClassifier; import android.widget.LinearLayout; import android.widget.TextView; Loading @@ -40,7 +41,8 @@ public class TableView extends LinearLayout implements TableDisplay { private final Map<CharSequence, KeyValueRow> mRows = new HashMap<>(); private final Resources mRes; private Map<CharSequence, TextView> mTitles = new HashMap<>(); private final Map<CharSequence, TextView> mTitles = new HashMap<>(); private final TextClassifier mClassifier; public TableView(Context context) { this(context, null); Loading @@ -54,6 +56,7 @@ public class TableView extends LinearLayout implements TableDisplay { super(context, attrs, defStyleAttr); mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); mRes = context.getResources(); mClassifier = GpsCoordinatesTextClassifier.create(context); } void setTitle(@StringRes int title, boolean showDivider) { Loading @@ -79,6 +82,7 @@ public class TableView extends LinearLayout implements TableDisplay { protected KeyValueRow createKeyValueRow(ViewGroup parent) { KeyValueRow row = (KeyValueRow) mInflater.inflate(R.layout.table_key_value_row, null); parent.addView(row); row.setTextClassifier(mClassifier); return row; } Loading Loading @@ -106,6 +110,7 @@ public class TableView extends LinearLayout implements TableDisplay { } row.setValue(value); row.setTextClassifier(mClassifier); return row; } Loading
tests/unit/com/android/documentsui/inspector/GpsCoordinatesTextClassifierTest.java 0 → 100644 +100 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.documentsui.inspector; import static junit.framework.Assert.assertEquals; import android.content.pm.PackageManager; import android.os.LocaleList; import android.support.test.InstrumentationRegistry; import android.content.Context; import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; import android.view.textclassifier.TextClassification; import android.view.textclassifier.TextClassificationManager; import android.view.textclassifier.TextClassifier; import com.android.documentsui.testing.TestPackageManager; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; /** * Test class for testing recognizing geo coordinates. */ @RunWith(AndroidJUnit4.class) @SmallTest public class GpsCoordinatesTextClassifierTest { private GpsCoordinatesTextClassifier mClassifier; @Before public void setUp() throws Exception { PackageManager pm = TestPackageManager.create(); Context context = InstrumentationRegistry.getTargetContext(); TextClassifier defaultClassifier = context.getSystemService(TextClassificationManager.class).getTextClassifier(); mClassifier = new GpsCoordinatesTextClassifier(pm, defaultClassifier); } @Test public void testBasicPatterns() throws Exception { assertClassifiedGeo("0,0", true); assertClassifiedGeo("0, 0", true); assertClassifiedGeo("0.0,0.0", true); assertClassifiedGeo("0.0, 0.0", true); assertClassifiedGeo("90,180", true); assertClassifiedGeo("90, 180", true); assertClassifiedGeo("90.0000,180.0000", true); assertClassifiedGeo("90.000, 180.000000000000000", true); assertClassifiedGeo("-77.5646564,133.656554654", true); assertClassifiedGeo("33, -179.324234242423", true); assertClassifiedGeo("44.4545454,70.0", true); assertClassifiedGeo("60.0, 60.0", true); assertClassifiedGeo("-33.33,-180", true); assertClassifiedGeo("-88.888888, -33.3333", true); assertClassifiedGeo("90.0, 180.000000", true); assertClassifiedGeo("-90.00000, -180.0", true); } @Test public void testInvalidPatterns() throws Exception { assertClassifiedGeo("0", false); assertClassifiedGeo("Geo Intent", false); assertClassifiedGeo("GeoIntent", false); assertClassifiedGeo("A.B, C.D", false); assertClassifiedGeo("5000, 5000", false); assertClassifiedGeo("500, 500", false); assertClassifiedGeo("90.165464, 180.1", false); assertClassifiedGeo("-90.1, -180.156754", false); } private void assertClassifiedGeo(CharSequence text, boolean expectClassified) { boolean wasClassified; TextClassification test = mClassifier.classifyText( text, 0, text.length(), new LocaleList()); try { wasClassified = "geo".equals(test.getIntent().getData().getScheme()); } catch (NullPointerException intentNotSet) { wasClassified = false; } assertEquals(wasClassified, expectClassified); } }