Repository: lemberg/mappwidget Branch: master Commit: a5e233469da9 Files: 139 Total size: 453.0 KB Directory structure: gitextract_albf_jio/ ├── .gitignore ├── LICENSE ├── README.md ├── bin/ │ ├── mAppWidget-1.4.2-javadoc.jar │ └── mAppWidget-1.4.2.jar ├── mAppWidget/ │ ├── build.gradle │ ├── demo1app/ │ │ ├── .gitignore │ │ ├── build.gradle │ │ ├── proguard-rules.pro │ │ └── src/ │ │ └── main/ │ │ ├── AndroidManifest.xml │ │ ├── assets/ │ │ │ ├── grid/ │ │ │ │ └── grid.xml │ │ │ ├── grid2/ │ │ │ │ └── grid.xml │ │ │ └── map/ │ │ │ └── map.xml │ │ ├── java/ │ │ │ └── com/ │ │ │ └── ls/ │ │ │ └── demo/ │ │ │ └── demo1/ │ │ │ ├── BrowseMapActivity.java │ │ │ ├── CaptionMapObject.java │ │ │ ├── Category.java │ │ │ ├── ExBrowseMapActivity.java │ │ │ ├── HomeActivity.java │ │ │ ├── Location.java │ │ │ ├── Model.java │ │ │ ├── Sample1Activity.java │ │ │ ├── Sample2Activity.java │ │ │ ├── model/ │ │ │ │ ├── MapObjectContainer.java │ │ │ │ └── MapObjectModel.java │ │ │ └── popup/ │ │ │ ├── MapPopupBase.java │ │ │ └── TextPopup.java │ │ └── res/ │ │ ├── layout/ │ │ │ ├── home.xml │ │ │ └── main.xml │ │ ├── menu/ │ │ │ ├── a1menu.xml │ │ │ ├── map_menu.xml │ │ │ └── menu.xml │ │ └── values/ │ │ ├── strings.xml │ │ ├── styles.xml │ │ └── theme.xml │ ├── demo2app/ │ │ ├── .gitignore │ │ ├── build.gradle │ │ ├── proguard-rules.pro │ │ └── src/ │ │ ├── androidTest/ │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── ls/ │ │ │ └── demo/ │ │ │ └── demo2/ │ │ │ └── ApplicationTest.java │ │ └── main/ │ │ ├── AndroidManifest.xml │ │ ├── assets/ │ │ │ └── map/ │ │ │ └── map.xml │ │ ├── java/ │ │ │ └── com/ │ │ │ └── ls/ │ │ │ └── demo/ │ │ │ └── demo2/ │ │ │ ├── BrowseMapActivity.java │ │ │ ├── model/ │ │ │ │ ├── MapObjectContainer.java │ │ │ │ └── MapObjectModel.java │ │ │ └── popup/ │ │ │ ├── MapPopupBase.java │ │ │ └── TextPopup.java │ │ └── res/ │ │ ├── layout/ │ │ │ ├── activity_main.xml │ │ │ └── main.xml │ │ ├── menu/ │ │ │ ├── menu.xml │ │ │ └── menu_main.xml │ │ ├── values/ │ │ │ ├── dimens.xml │ │ │ ├── strings.xml │ │ │ ├── styles.xml │ │ │ └── theme.xml │ │ └── values-w820dp/ │ │ └── dimens.xml │ ├── gradle/ │ │ └── wrapper/ │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradle.properties │ ├── gradlew │ ├── gradlew.bat │ ├── mappwidgetlib/ │ │ ├── .gitignore │ │ ├── build.gradle │ │ ├── proguard-rules.pro │ │ └── src/ │ │ ├── androidTest/ │ │ │ ├── assets/ │ │ │ │ └── map/ │ │ │ │ └── map.xml │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── ls/ │ │ │ └── widgets/ │ │ │ └── map/ │ │ │ ├── AllTests.java │ │ │ ├── CellTest.java │ │ │ ├── GridTest.java │ │ │ ├── MapLayerTest.java │ │ │ ├── MapObjectTest.java │ │ │ ├── MapWidgetTest.java │ │ │ ├── OfflineMapConfigTest.java │ │ │ └── OfflineMapUtilTest.java │ │ └── main/ │ │ ├── AndroidManifest.xml │ │ └── java/ │ │ └── com/ │ │ └── ls/ │ │ └── widgets/ │ │ └── map/ │ │ ├── MapWidget.java │ │ ├── commands/ │ │ │ ├── GetTileTask.java │ │ │ ├── MapCommand.java │ │ │ └── MapCommandDelegate.java │ │ ├── config/ │ │ │ ├── GPSConfig.java │ │ │ ├── MapConfigParser.java │ │ │ ├── MapGraphicsConfig.java │ │ │ ├── OfflineMap.java │ │ │ └── OfflineMapConfig.java │ │ ├── events/ │ │ │ ├── MapScrolledEvent.java │ │ │ ├── MapTouchedEvent.java │ │ │ └── ObjectTouchEvent.java │ │ ├── interfaces/ │ │ │ ├── Layer.java │ │ │ ├── MapEventsListener.java │ │ │ ├── MapLocationListener.java │ │ │ ├── OnGridReadyListener.java │ │ │ ├── OnLocationChangedListener.java │ │ │ ├── OnMapDoubleTapListener.java │ │ │ ├── OnMapLongClickListener.java │ │ │ ├── OnMapScrollListener.java │ │ │ ├── OnMapTilesFinishedLoadingListener.java │ │ │ ├── OnMapTouchListener.java │ │ │ └── TileManagerDelegate.java │ │ ├── location/ │ │ │ └── PositionMarker.java │ │ ├── model/ │ │ │ ├── Cell.java │ │ │ ├── Grid.java │ │ │ ├── MapLayer.java │ │ │ ├── MapObject.java │ │ │ └── MapTouchable.java │ │ ├── providers/ │ │ │ ├── AssetTileProvider.java │ │ │ ├── ExternalStorageTileProvider.java │ │ │ ├── GPSLocationProvider.java │ │ │ └── TileProvider.java │ │ └── utils/ │ │ ├── GeoUtils.java │ │ ├── Graphics.java │ │ ├── LogUtils.java │ │ ├── MapCalibrationData.java │ │ ├── MathUtils.java │ │ ├── OfflineMapUtil.java │ │ ├── PivotFactory.java │ │ ├── Resources.java │ │ ├── Size.java │ │ └── TransformUtils.java │ └── settings.gradle └── slicingtool/ ├── .classpath ├── .project ├── .settings/ │ └── org.eclipse.jdt.core.prefs ├── META-INF/ │ └── MANIFEST.MF ├── build.properties ├── contexts.xml ├── libs/ │ └── org.eclipse.core.resources.jar ├── plugin.xml └── src/ └── com/ └── ls/ └── mappwidget/ └── slicingtool/ ├── Activator.java ├── cutter/ │ ├── Constants.java │ ├── Cutter.java │ ├── ImageXML.java │ ├── OnCompliteListener.java │ └── OnProgressUpdateListener.java ├── utils/ │ ├── EclipseUtils.java │ ├── FileUtils.java │ └── XMLUtils.java ├── views/ │ ├── DirectoryChooser.java │ ├── FileChooser.java │ ├── GPSChooser.java │ ├── MainView.java │ └── RadioGroup.java └── vo/ └── PointVO.java ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .gradle .idea local.properties *.iml .DS_Store build ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. ================================================ FILE: README.md ================================================ ![mAppWidget](images/app_anim.gif) Map services are a major growth area for mobile applications. Services like Google maps have brought map technology to the masses in recent years, but it has been hard to integrate them into applications as Google maps and others require a constant Internet connection and use proprietary resources. This means that if the app has to work offline, use original graphics or other features, a custom map has to be built. At Lemberg we’ve developed many Android apps with custom maps, and to streamline this process we came up with the mAppWidget code library. ![ill](images/ill-3_1.jpg) Designed to simplify custom map building, mAppWidget is a powerful tool that significantly cuts time and reduces the cost of the developing offline maps. Now any Android developer can benefit from our experience. Using apps developed with mAppWidget is possible without data connection, unlike most of the other solutions. mAppWidget also uses a tiling technique for graphics that generates a larger map byre-using smaller images (tiles). This approach saves RAM and increases real-time performance, which is especially important on mobile devices. Zooming into images is not limited by the resolution of the input map image, as beyond the maximum zoom level of the image, the library uses digital zoom. ## Features * Create map from any image * Tile engine rendering * GPS support * Zoom in/out * Pinch to zoom gesture * Zoom on double tap * Digital zoom * Pan * Inertial scroll * Smooth tiles appearing * Support of more than one map per application * Ability to move the map object after it was added to the map * Add/remove layers * Add/remove objects * Click handlers/info bubbles * Works offline * External storage support ## Requirements * Android API 7 or higher * Android OS 2.1 or higher * Latest JDK, Android Studio 1.1 is recommended ## Demo app mAppWidget demo is available for download on Google Play - [https://play.google.com/store/apps/details?id=com.ls.mappwidgetdemo](https://play.google.com/store/apps/details?id=com.ls.mappwidgetdemo) ## How to use Here you can find information on how to use mAppWidget library and FAQ's - [http://lemberg.github.io/mappwidget/user_guide.html](http://lemberg.github.io/mappwidget/user_guide.html) ## Proudly brought to you mAppWidget code library has been developed and open sourced by Lemberg Solutions Android development team. If you are interested in integration or customization of the mAppWidget or looking for any other mobile app development solution please feel free to reach us - [http://lemberg.co.uk](http://lemberg.co.uk). ================================================ FILE: mAppWidget/build.gradle ================================================ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:1.5.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } allprojects { repositories { jcenter() } } ================================================ FILE: mAppWidget/demo1app/.gitignore ================================================ /build ================================================ FILE: mAppWidget/demo1app/build.gradle ================================================ apply plugin: 'com.android.application' android { compileSdkVersion 23 buildToolsVersion "23.0.2" defaultConfig { applicationId "com.ls.demo.demo1" minSdkVersion 7 targetSdkVersion 23 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:appcompat-v7:23.1.1' compile project(':mappwidgetlib') } ================================================ FILE: mAppWidget/demo1app/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # By default, the flags in this file are appended to flags specified # in /Users/dmytrobaryskyy/Library/Android/sdk/tools/proguard/proguard-android.txt # You can edit the include path and order by changing the proguardFiles # directive in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # Add any project specific keep options here: # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} ================================================ FILE: mAppWidget/demo1app/src/main/AndroidManifest.xml ================================================ ================================================ FILE: mAppWidget/demo1app/src/main/assets/grid/grid.xml ================================================ ================================================ FILE: mAppWidget/demo1app/src/main/assets/grid2/grid.xml ================================================ ================================================ FILE: mAppWidget/demo1app/src/main/assets/map/map.xml ================================================ ================================================ FILE: mAppWidget/demo1app/src/main/java/com/ls/demo/demo1/BrowseMapActivity.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.demo.demo1; import android.app.Activity; import android.app.AlertDialog; import android.content.Intent; import android.content.res.AssetManager; import android.graphics.Color; import android.graphics.drawable.Drawable; import android.location.Location; import android.os.Bundle; import android.util.Log; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.Window; import android.view.WindowManager; import android.widget.LinearLayout; import com.ls.widgets.map.MapWidget; import com.ls.widgets.map.config.OfflineMap; import com.ls.widgets.map.events.MapTouchedEvent; import com.ls.widgets.map.interfaces.Layer; import com.ls.widgets.map.interfaces.MapEventsListener; import com.ls.widgets.map.interfaces.OnLocationChangedListener; import com.ls.widgets.map.interfaces.OnMapTouchListener; import com.ls.widgets.map.model.MapLayer; import java.io.IOException; import java.io.InputStream; public class BrowseMapActivity extends Activity { /** Called when the activity is first created. */ private MapWidget mapWidget; // private static long PIN_ID = 0xdb70bca16186d187L; public static final long LAYER_ATTRACTIONS = 1000; public static final long LAYER_KIDS = 2000; public static final long LAYER_SPORT_AND_LEASURE = 3000; public static final long PIN_LAYER = 4000; private Model model; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.main); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); this.model = new Model(); LinearLayout layout = (LinearLayout) findViewById(R.id.mainLayout); mapWidget = new MapWidget(this, OfflineMap.MAP_ROOT); // mapWidget.setMemoryEconomyMode(true); mapWidget.setBackgroundColor(Color.GREEN); MapLayer layer = mapWidget.createLayer(LAYER_ATTRACTIONS); initLayer(layer, Model.CAT_MAIN_ATTRACTIONS); layer = mapWidget.createLayer(LAYER_KIDS); initLayer(layer, Model.CAT_KIDS); layer = mapWidget.createLayer(LAYER_SPORT_AND_LEASURE); initLayer(layer, Model.CAT_SPORT_AND_LEISURE); mapWidget.getConfig().setMapCenteringEnabled(false); mapWidget.createLayer(PIN_LAYER); // layer.addTouchable(id, drawable, offsetX, offsetY) // mapWidget.addLayer(2); mapWidget.setAnimationEnabled(true); mapWidget.setOnMapTouchListener(new OnMapTouchListener() { @Override public void onTouch(MapWidget v, MapTouchedEvent event){ AlertDialog.Builder builder = new AlertDialog.Builder(v.getContext()); builder.setMessage("OnTouch, X: " + event.getScreenX() + " Y: " + event.getScreenY() + " MAPX: " + event.getMapX() + " MAPY: " + event.getMapY() + " Touched Count: " + event.getTouchedObjectIds().size()); builder.create().show(); } }); mapWidget.addMapEventsListener(new MapEventsListener() { public void onPreZoomOut() { Log.i("BrowseMapActivity", "On Map will zoom out"); } public void onPreZoomIn() { Log.i("BrowseMapActivity", "On Map will zoom in"); } public void onPostZoomOut() { Log.i("BrowseMapActivity", "On Map did zoom out"); } public void onPostZoomIn() { Log.i("BrowseMapActivity", "On Map did zoom in"); } }); mapWidget.setOnLocationChangedListener(new OnLocationChangedListener() { @Override public void onLocationChanged(MapWidget v, Location location) { v.scrollMapTo(location); } }); mapWidget.setMinZoomLevel(1); // mapWidget.setScale(2.0f); layout.addView(mapWidget); // Runnable runnable = new Runnable() { // @Override // public void run() { // generateRandomMarkers(); // BitmapDrawable image = (BitmapDrawable) getResources().getDrawable(R.drawable.maps_blue_dot); // if (image != null) { // Layer layer = mapWidget.getLayerById(PIN_LAYER); // MapObject object = new MapObject(PIN_ID, image, new Point(500, 250), PivotFactory.createPivotPoint(image, PivotPosition.PIVOT_CENTER), true); // layer.addMapObject(object); // // Pin pin = new Pin(PIN_ID, image, new android.graphics.Point(image.getBitmap().getWidth()/2, image.getBitmap().getHeight()/2)); // // pin.moveTo(500, 250); // // mapWidget.getPinLayer().addPin(pin); // // mapWidget.setPinLayerVisible(true); // } // } // }; // // Thread thread = new Thread(runnable); // thread.setPriority(Thread.MIN_PRIORITY); // thread.start(); } // private void generateRandomMarkers() { // try { // final int GREEN_POINT_COUNT = 100; // final int BLUE_POINT_COUNT = GREEN_POINT_COUNT; // final int MAP_WIDTH = 1500; // final int MAP_HEIGHT = 900; // // InputStream is = getAssets().open("other/trail_difficulty_green_circle.png"); // BitmapDrawable drawable = new BitmapDrawable(is); // is.close(); // Random random = new Random(System.currentTimeMillis()); // // for (int i = 0; i < GREEN_POINT_COUNT; ++i) { // int randW = random.nextInt(MAP_WIDTH); // int randH = random.nextInt(MAP_HEIGHT); // BitmapDrawable dr = new BitmapDrawable(drawable.getBitmap()); // // Layer layer = mapWidget.getLayer(0); // layer.addMapObject(new MapObject(new Integer(random.nextInt(1000)), dr, randW, randH)); // } // // // is = getAssets().open("other/trail_difficulty_blue_rect.png"); // drawable = new BitmapDrawable(is); // is.close(); // for (int i = 0; i < BLUE_POINT_COUNT; ++i) { // int randW = random.nextInt(MAP_WIDTH); // int randH = random.nextInt(MAP_HEIGHT); // // BitmapDrawable dr = new BitmapDrawable(drawable.getBitmap()); // // Layer layer = mapWidget.getLayer(1); // layer.addMapObject(new MapObject(new Integer(random.nextInt(1000)), dr, randW, randH, true, false)); // } // // } catch (IOException e) { // Log.e("TileManagerWorkerThread", "Exception: " + e); // e.printStackTrace(); // } // } private void initLayer(Layer theLayer, String theCategoryId) { // List listLocations = this.model.getLocations(); // // for (Location location : listLocations) // { // try // { // String categoryId = location.getCategoryId(); // // if (categoryId.equals(theCategoryId)) // { // Point point = location.getPoint(); // MapObject object = new MapObject(location.getId(), // getIcon(categoryId), // point, // true, // Touchable // false); // scalable // // theLayer.addMapObject(object); // } // // } catch (IOException e) // { // e.printStackTrace(); // } // } theLayer.setVisible(true); // if (settings.isCategorySelected(CategoryIds.LEISURE)) // { // layerLeisure.enable(); // } } public Drawable getIcon(String theCatId) throws IOException { String path = "media/icons/"; if (Model.CAT_MAIN_ATTRACTIONS.equalsIgnoreCase(theCatId)) { path += "map_icon_leisure.png"; } else if (Model.CAT_KIDS.equalsIgnoreCase(theCatId)) { path += "map_icon_meals.png"; } else if (Model.CAT_SPORT_AND_LEISURE.equalsIgnoreCase(theCatId)) { path += "map_icon_others_3.png"; } AssetManager manager = getAssets(); InputStream input = manager.open(path); return Drawable.createFromStream(input, null); } public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.map_menu, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle item selection int i = item.getItemId(); if (i == R.id.zoom_in) { try { mapWidget.zoomIn(); } catch (Exception e) { Log.e("BrowseMapActivity", "Exception while zoom in. " + e); } return true; } else if (i == R.id.zoom_out) { try { mapWidget.zoomOut(); } catch (Exception e) { Log.e("BrowseMapActivity", "Exception while zoom out. " + e); } return true; } else if (i == R.id.double_size) { mapWidget.setScale(2.0f); return true; } else if (i == R.id.original_size) { mapWidget.setScale(1.0f); return true; } else if (i == R.id.half_size) { mapWidget.setScale(0.5f); return true; } else if (i == R.id.open_map) { Intent intent = new Intent(this, BrowseMapActivity.class); startActivity(intent); return true; } else { return super.onOptionsItemSelected(item); } } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { Layer layer = null; switch (keyCode) { case KeyEvent.KEYCODE_1: layer = mapWidget.getLayer(0); break; case KeyEvent.KEYCODE_2: layer = mapWidget.getLayer(1); break; } if (layer != null) { layer.setVisible(!layer.isVisible()); return true; } else return super.onKeyDown(keyCode, event); } @Override protected void onStop() { super.onStop(); } @Override protected void onPause() { super.onPause(); } @Override protected void onDestroy() { // mapWidget.destroy(); super.onDestroy(); } }; ================================================ FILE: mAppWidget/demo1app/src/main/java/com/ls/demo/demo1/CaptionMapObject.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.demo.demo1; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.Align; import android.graphics.Point; import android.graphics.Typeface; import android.graphics.drawable.Drawable; import com.ls.widgets.map.model.MapObject; public class CaptionMapObject extends MapObject { private String caption; private Paint paint; public CaptionMapObject(Object id, Drawable drawable, Point position, Point pivotPoint, boolean isTouchable, boolean isScalable) { super(id, drawable, position, pivotPoint, isTouchable, isScalable); paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setColor(Color.WHITE); paint.setTextAlign(Align.CENTER); paint.setTextSize(14); paint.setTypeface(Typeface.SANS_SERIF); paint.setFakeBoldText(true); paint.setShadowLayer(1, 0, 0, Color.BLACK); } public void setCaption(String caption) { this.caption = caption; } @Override public void draw(Canvas canvas) { super.draw(canvas); if (caption != null) { canvas.drawText(caption, posScaled.x, posScaled.y - 10, paint); } } } ================================================ FILE: mAppWidget/demo1app/src/main/java/com/ls/demo/demo1/Category.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.demo.demo1; public class Category { private String id; private String name; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } ================================================ FILE: mAppWidget/demo1app/src/main/java/com/ls/demo/demo1/ExBrowseMapActivity.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.demo.demo1; import android.app.Activity; import android.app.AlertDialog; import android.content.Intent; import android.graphics.Color; import android.graphics.Point; import android.graphics.drawable.BitmapDrawable; import android.location.Location; import android.os.Bundle; import android.os.Environment; import android.util.Log; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.Window; import android.view.WindowManager; import android.widget.LinearLayout; import com.ls.widgets.map.MapWidget; import com.ls.widgets.map.config.MapGraphicsConfig; import com.ls.widgets.map.config.OfflineMapConfig; import com.ls.widgets.map.events.MapScrolledEvent; import com.ls.widgets.map.events.MapTouchedEvent; import com.ls.widgets.map.interfaces.Layer; import com.ls.widgets.map.interfaces.MapEventsListener; import com.ls.widgets.map.interfaces.OnLocationChangedListener; import com.ls.widgets.map.interfaces.OnMapScrollListener; import com.ls.widgets.map.interfaces.OnMapTouchListener; import com.ls.widgets.map.model.MapObject; import com.ls.widgets.map.utils.PivotFactory; import com.ls.widgets.map.utils.PivotFactory.PivotPosition; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.Random; public class ExBrowseMapActivity extends Activity { /** Called when the activity is first created. */ private MapWidget mapWidget; private Thread animationThread; private boolean stopThreads; public static final long LAYER_A = 1000; public static final long LAYER_B = 2000; public static final long LAYER_C = 3000; public static final long PIN_LAYER = 4000; private int amplitude = 400; private float currX = 0; private float currY = 0; private boolean followPointer; public static final long[] LAYERS = new long[]{}; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); stopThreads = false; followPointer = true; File dir = Environment.getExternalStorageDirectory(); Log.d("ExBrowseActivity", "External Storage Directory: " + Environment.getDataDirectory().getAbsolutePath() + "/maps/grid2"); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.main); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); Log.d("ExBrowseActivity", "Window format is: " + getWindow().getAttributes().format); LinearLayout layout = (LinearLayout) findViewById(R.id.mainLayout); layout.setBackgroundColor(0xFFFFFFFF); mapWidget = new MapWidget(savedInstanceState, this, new File("/sdcard/maps/grid2"),12); mapWidget.createLayer(0); mapWidget.createLayer(1); mapWidget.setAnimationEnabled(true); mapWidget.setZoomButtonsVisible(true); MapGraphicsConfig gconfig = mapWidget.getMapGraphicsConfig(); if (gconfig != null) { gconfig.setAccuracyAreaBorderColor(Color.RED); gconfig.setAccuracyAreaColor(0x4400FF00); } mapWidget.setOnMapTouchListener(new OnMapTouchListener() { @Override public void onTouch(MapWidget v, MapTouchedEvent event) { AlertDialog.Builder builder = new AlertDialog.Builder(v.getContext()); builder.setMessage("OnTouch, X: " + event.getScreenX() + " Y: " + event.getScreenY() + " MAPX: " + event.getMapX() + " MAPY: " + event.getMapY() + " Touched Count: " + event.getTouchedObjectIds().size()); builder.create().show(); } }); mapWidget.setOnMapScrolledListener(new OnMapScrollListener() { @Override public void onScrolledEvent(MapWidget v, MapScrolledEvent event) { if (event.isByUser()) { followPointer = false; } } }); mapWidget.setMinZoomLevel(1); mapWidget.setMaxZoomLevel(12); // mapWidget.setZoomButtonsVisible(true); // mapWidget.setScale(2.0f); layout.addView(mapWidget); mapWidget.addMapEventsListener(new MapEventsListener() { public void onPreZoomOut() { Log.i("BrowseMapActivity", "On Map will zoom out"); } public void onPreZoomIn() { Log.i("BrowseMapActivity", "On Map will zoom in"); } public void onPostZoomOut() { Log.i("BrowseMapActivity", "On Map did zoom out"); } public void onPostZoomIn() { Log.i("BrowseMapActivity", "On Map did zoom in"); } }); mapWidget.setOnLocationChangedListener(new OnLocationChangedListener() { @Override public void onLocationChanged(MapWidget v, Location location) { if (followPointer) { v.scrollMapTo(location); } } }); Runnable runnable = new Runnable() { @Override public void run() { generateRandomMarkers(); // BitmapDrawable image = (BitmapDrawable) getResources().getDrawable(R.drawable.maps_blue_dot); // if (image != null) { // Layer layer = mapWidget.getLayerById(PIN_LAYER); // MapObject object = new MapObject(PIN_ID, image, new Point(500, 250), PivotFactory.createPivotPoint(image, PivotPosition.PIVOT_CENTER), false, true); // layer.addMapObject(object); // layer.setVisible(true); // } } }; Thread thread = new Thread(runnable); thread.setPriority(Thread.MIN_PRIORITY); thread.run(); OfflineMapConfig config = mapWidget.getConfig(); if (config != null) { config.setFlingEnabled(true); config.setMapCenteringEnabled(false); } //layout.removeView(mapWidget); //mapWidget.setShowMyPosition(true); mapWidget.centerMap(); } private void generateRandomMarkers() { try { final int GREEN_POINT_COUNT = 100; final int BLUE_POINT_COUNT = GREEN_POINT_COUNT; final int MAP_WIDTH = 1500; final int MAP_HEIGHT = 900; InputStream is = getAssets().open("other/trail_difficulty_green_circle.png"); BitmapDrawable drawable = new BitmapDrawable(is); is.close(); Random random = new Random(System.currentTimeMillis()); Layer layer = mapWidget.getLayer(0); for (int i = 0; i < GREEN_POINT_COUNT; ++i) { int randW = random.nextInt(MAP_WIDTH); int randH = random.nextInt(MAP_HEIGHT); BitmapDrawable dr = new BitmapDrawable(drawable.getBitmap()); MapObject object = new MapObject(Integer.valueOf(random.nextInt(1000)), dr, randW, randH, false); layer.addMapObject(object); //layer.addTouchable(new Integer(random.nextInt(1000)), dr, randW, randH); } is = getAssets().open("other/trail_difficulty_blue_rect.png"); drawable = new BitmapDrawable(is); is.close(); layer = mapWidget.getLayer(1); for (int i = 0; i < BLUE_POINT_COUNT; ++i) { int randW = random.nextInt(MAP_WIDTH); int randH = random.nextInt(MAP_HEIGHT); BitmapDrawable dr = new BitmapDrawable(drawable.getBitmap()); MapObject object = new MapObject(Integer.valueOf((random.nextInt(1000))), dr, new Point(randW, randH), PivotFactory.createPivotPoint(dr, PivotPosition.PIVOT_CENTER), true, false); layer.addMapObject(object); // layer.addTouchable(new Integer(random.nextInt(1000)), dr, new Point(randW, randH), PivotFactory.createPivotPoint(dr, PivotPosition.PIVOT_CENTER), false); // mapWidget.addDrawable(dr, randW, randH); } } catch (IOException e) { Log.e("TileManagerWorkerThread", "Exception: " + e); e.printStackTrace(); } } public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.map_menu, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle item selection int i = item.getItemId(); if (i == R.id.zoom_in) { try { mapWidget.zoomIn(); mapWidget.scrollMapTo(1500, 700); } catch (Exception e) { Log.e("BrowseMapActivity", "Exception while zoom in. " + e); } return true; } else if (i == R.id.zoom_out) { try { mapWidget.zoomOut(); } catch (Exception e) { Log.e("BrowseMapActivity", "Exception while zoom out. " + e); } return true; } else if (i == R.id.double_size) { mapWidget.setScale(2.0f); return true; } else if (i == R.id.original_size) { mapWidget.setScale(1.0f); return true; } else if (i == R.id.half_size) { mapWidget.setScale(0.5f); return true; } else if (i == R.id.open_map) { Intent intent = new Intent(this, ExBrowseMapActivity.class); startActivity(intent); return true; } else if (i == R.id.my_location) { //mapWidget.scrollToCurrentLocation(); //followPointer = true; mapWidget.centerMap(); return true; } else { return super.onOptionsItemSelected(item); } } private void animatePin() { // if (animationThread == null || (animationThread != null && !animationThread.isAlive())) { // animationThread = new Thread(new Runnable() { // @Override // public void run() { // // MapObject pin = mapWidget.getLayerById(PIN_LAYER).getMapObject(PIN_ID); // // if (pin == null) { // Log.e("PIN", "Pin not foind"); // } // // while (!stopThreads) { // currX += 0.01; // currY += 0.01; // // if (pin != null) { // pin.moveTo((int)(700 + Math.sin(currX) * amplitude), (int)(350 + Math.cos(currY)*250)); // } // // try { // Thread.sleep(50); // } catch (InterruptedException e) { // e.printStackTrace(); // return; // } // } // // } // }); // // animationThread.start(); // } } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { Layer layer = null; switch (keyCode) { case KeyEvent.KEYCODE_1: layer = mapWidget.getLayer(0); break; case KeyEvent.KEYCODE_2: layer = mapWidget.getLayer(1); break; } if (layer != null) { layer.setVisible(!layer.isVisible()); return true; } else return super.onKeyDown(keyCode, event); } @Override protected void onStop() { stopThreads = true; super.onStop(); } @Override protected void onPause() { super.onPause(); } @Override protected void onDestroy() { //mapWidget.destroy(); super.onDestroy(); } @Override protected void onSaveInstanceState(Bundle outState) { mapWidget.saveState(outState); super.onSaveInstanceState(outState); } }; ================================================ FILE: mAppWidget/demo1app/src/main/java/com/ls/demo/demo1/HomeActivity.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.demo.demo1; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.Button; public class HomeActivity extends Activity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.home); initWidgets(); } private void initWidgets() { Button button1 = (Button) findViewById(R.id.button_sample_1); button1.setOnClickListener(new View.OnClickListener() { public void onClick(View arg0) { doOpenSample1(); } }); Button button2 = (Button) findViewById(R.id.button_sample_2); button2.setOnClickListener(new View.OnClickListener() { public void onClick(View arg0) { doOpenSample2(); } }); Button button3 = (Button) findViewById(R.id.button_sample_3); button3.setOnClickListener(new View.OnClickListener() { public void onClick(View arg0) { doOpenExBrowseActivity(); } }); } private void doOpenExBrowseActivity() { Intent intent = new Intent(this, ExBrowseMapActivity.class); startActivity(intent); } private void doOpenSample1() { Intent intent = new Intent(this,Sample1Activity.class); startActivity(intent); } private void doOpenSample2() { Intent intent = new Intent(this,Sample2Activity.class); startActivity(intent); } } ================================================ FILE: mAppWidget/demo1app/src/main/java/com/ls/demo/demo1/Location.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.demo.demo1; import android.graphics.Point; public class Location { private String id; private String title; private String categoryId; private Point point; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getCategoryId() { return categoryId; } public void setCategoryId(String categoryId) { this.categoryId = categoryId; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public Point getPoint() { return point; } public void setPoint(Point point) { this.point = point; } } ================================================ FILE: mAppWidget/demo1app/src/main/java/com/ls/demo/demo1/Model.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.demo.demo1; import java.util.ArrayList; import java.util.List; import android.graphics.Point; public class Model { public static final String CAT_MAIN_ATTRACTIONS = "category.main.attractions"; public static final String CAT_KIDS = "category.kids"; public static final String CAT_SPORT_AND_LEISURE = "category.sport.and.leisure"; private List locations; private List categories; public Model() { createCategories(); createLocations(); } public List getCategories() { return categories; } public List getLocations() { return locations; } private void createCategories() { this.categories = new ArrayList(); Category category = new Category(); category.setId(CAT_MAIN_ATTRACTIONS); category.setName("Main Attractions"); this.categories.add(category); category = new Category(); category.setId(CAT_KIDS); category.setName("Kids In the Park"); this.categories.add(category); category = new Category(); category.setId(CAT_SPORT_AND_LEISURE); category.setName("Sport and Leisure"); this.categories.add(category); } private void createLocations() { this.locations = new ArrayList(); Point point = new Point(20, 100); Location location = new Location(); location.setId("id.thehub"); location.setTitle("The Hub"); location.setCategoryId(CAT_MAIN_ATTRACTIONS); location.setPoint(point); this.locations.add(location); point = new Point(20, 100); location = new Location(); location.setId("id.queen.mary.garden"); location.setTitle("Queen Mary's Garden"); location.setCategoryId(CAT_MAIN_ATTRACTIONS); location.setPoint(point); this.locations.add(location); point = new Point(20, 100); location = new Location(); location.setId("id.wildlifegarden"); location.setTitle("Wildlife Garden"); location.setCategoryId(CAT_KIDS); location.setPoint(point); this.locations.add(location); point = new Point(20, 100); location = new Location(); location.setId("id.boatinglake"); location.setTitle("Boating Lake"); location.setCategoryId(CAT_KIDS); location.setPoint(point); this.locations.add(location); point = new Point(20, 100); location = new Location(); location.setId("id.openairtheatre"); location.setTitle("Open Air Theatre"); location.setCategoryId(CAT_SPORT_AND_LEISURE); location.setPoint(point); this.locations.add(location); point = new Point(20, 100); location = new Location(); location.setId("id.tenniscenter"); location.setTitle("Tennis Center"); location.setCategoryId(CAT_SPORT_AND_LEISURE); location.setPoint(point); this.locations.add(location); } } ================================================ FILE: mAppWidget/demo1app/src/main/java/com/ls/demo/demo1/Sample1Activity.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.demo.demo1; import android.app.Activity; import android.graphics.Color; import android.graphics.Point; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.view.View.OnLongClickListener; import android.view.View.OnTouchListener; import android.widget.LinearLayout; import android.widget.Toast; import com.ls.widgets.map.MapWidget; import com.ls.widgets.map.config.OfflineMap; import com.ls.widgets.map.events.MapTouchedEvent; import com.ls.widgets.map.interfaces.MapEventsListener; import com.ls.widgets.map.interfaces.OnMapDoubleTapListener; import com.ls.widgets.map.model.MapLayer; import com.ls.widgets.map.model.MapObject; import com.ls.widgets.map.utils.PivotFactory; import com.ls.widgets.map.utils.PivotFactory.PivotPosition; public class Sample1Activity extends Activity { public static final int MAP_ID = 1; private static final long LAYER_ID = 5; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); final int initialZoomLevel = 10; final MapWidget mapWidget = new MapWidget(savedInstanceState, this, OfflineMap.MAP_ROOT, initialZoomLevel); mapWidget.setId(MAP_ID); LinearLayout layout = (LinearLayout) findViewById(R.id.mainLayout); layout.addView(mapWidget); mapWidget.getConfig().setFlingEnabled(true); mapWidget.getConfig().setPinchZoomEnabled(true); mapWidget.setMaxZoomLevel(13); mapWidget.setUseSoftwareZoom(true); mapWidget.setZoomButtonsVisible(true); mapWidget.setBackgroundColor(Color.GREEN); mapWidget.setOnLongClickListener(new OnLongClickListener() { @Override public boolean onLongClick(View v) { Toast.makeText(Sample1Activity.this, "Long press works!", Toast.LENGTH_SHORT).show(); return true; } }); mapWidget.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View arg0, MotionEvent arg1) { return false; } }); mapWidget.setOnDoubleTapListener(new OnMapDoubleTapListener() { @Override public boolean onDoubleTap(MapWidget v, MapTouchedEvent event) { Log.d("Sample1Activity", "On double tap"); Toast.makeText(Sample1Activity.this, "Double tap overridden", Toast.LENGTH_SHORT).show(); return true; } }); MapLayer layer = mapWidget.createLayer(LAYER_ID); // getting icon from assets Drawable icon = getResources().getDrawable(R.drawable.map_icon_attractions); // define coordinates of icon on map int x = 240; int y = 362; // set ID for the object final long OBJ_ID = 25; // adding object to layer layer.addMapObject(new MapObject(OBJ_ID, icon, new Point(x, y), PivotFactory.createPivotPoint(icon, PivotPosition.PIVOT_CENTER), false, false)); mapWidget.addMapEventsListener(new MapEventsListener() { @Override public void onPreZoomOut() { // TODO Auto-generated method stub } @Override public void onPreZoomIn() { // TODO Auto-generated method stub } @Override public void onPostZoomOut() { runOnUiThread(new Runnable() { @Override public void run() { setTitle("Zoom level: " + mapWidget.getZoomLevel()); } }); } @Override public void onPostZoomIn() { runOnUiThread(new Runnable() { @Override public void run() { setTitle("Zoom level: " + mapWidget.getZoomLevel()); } }); } }); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); MapWidget map = (MapWidget) findViewById(MAP_ID); map.saveState(outState); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onRestoreInstanceState(savedInstanceState); } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = new MenuInflater(this); inflater.inflate(R.menu.a1menu, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { MapWidget map = (MapWidget) findViewById(MAP_ID); int i = item.getItemId(); if (i == R.id.scrollTo) { map.scrollMapTo(new Point(240, 320)); } else if (i == R.id.jumpTo) { map.jumpTo(new Point(240, 320)); } return super.onOptionsItemSelected(item); } } ================================================ FILE: mAppWidget/demo1app/src/main/java/com/ls/demo/demo1/Sample2Activity.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.demo.demo1; import android.app.Activity; import android.graphics.Point; import android.graphics.drawable.Drawable; import android.location.Location; import android.os.Bundle; import android.util.Log; import android.view.View; import android.view.View.OnLongClickListener; import android.view.WindowManager; import android.widget.LinearLayout; import android.widget.Toast; import com.ls.widgets.map.MapWidget; import com.ls.widgets.map.events.MapTouchedEvent; import com.ls.widgets.map.interfaces.Layer; import com.ls.widgets.map.interfaces.MapEventsListener; import com.ls.widgets.map.interfaces.OnLocationChangedListener; import com.ls.widgets.map.interfaces.OnMapDoubleTapListener; import com.ls.widgets.map.interfaces.OnMapLongClickListener; import com.ls.widgets.map.interfaces.OnMapTilesFinishedLoadingListener; import com.ls.widgets.map.model.MapLayer; import com.ls.widgets.map.model.MapObject; import com.ls.widgets.map.utils.GeoUtils; import com.ls.widgets.map.utils.PivotFactory; import com.ls.widgets.map.utils.PivotFactory.PivotPosition; public class Sample2Activity extends Activity { public static final int MAP_ID = 1; private int currId = 35; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); final MapWidget mapWidget = new MapWidget(savedInstanceState, this, "map", 10); mapWidget.setSaveEnabled(true); mapWidget.getConfig().setMinZoomLevelLimit(10); mapWidget.getConfig().setZoomBtnsVisible(false); mapWidget.setId(MAP_ID); // create map layer with specified ID final long LAYER_ID = 5; MapLayer layer = mapWidget.createLayer(LAYER_ID); // getting icon from assets Drawable icon = getResources().getDrawable(R.drawable.map_icon_attractions); // define coordinates of icon on map int x = 200; int y = 300; // set ID for the object final long OBJ_ID = 25; // adding object to layer MapObject obj = new MapObject(OBJ_ID, icon, new Point(x, y), PivotFactory.createPivotPoint(icon, PivotPosition.PIVOT_CENTER), true, false); // obj.setCaption("5434 KNNB"); //MapObject(OBJ_ID, icon, new Point(x, y), PivotFactory.createPivotPoint(icon, PivotPosition.PIVOT_CENTER), true, false) layer.addMapObject(obj); LinearLayout layout = (LinearLayout) findViewById(R.id.mainLayout); layout.setBackgroundColor(0xFFFFFFFF); layout.addView(mapWidget); mapWidget.getMapGraphicsConfig().setArrowPointerDrawableId(R.drawable.maps_blue_arrow); mapWidget.getMapGraphicsConfig().setDotPointerDrawableId(R.drawable.maps_blue_dot); mapWidget.setOnMapTilesFinishLoadingListener(new OnMapTilesFinishedLoadingListener() { @Override public void onMapTilesFinishedLoading() { mapWidget.zoomIn(); mapWidget.scrollMapTo(1000, 800); mapWidget.setOnMapTilesFinishLoadingListener(null); } }); // mapWidget.setShowMyPosition(true); mapWidget.addMapEventsListener(new MapEventsListener() { @Override public void onPreZoomOut() { // TODO Auto-generated method stub } @Override public void onPreZoomIn() { // TODO Auto-generated method stub } @Override public void onPostZoomOut() { // TODO Auto-generated method stub } @Override public void onPostZoomIn() { // TODO Auto-generated method stub } }); mapWidget.setOnLocationChangedListener(new OnLocationChangedListener() { @Override public void onLocationChanged(MapWidget v, Location location) { v.scrollMapTo(location); } }); mapWidget.setOnDoubleTapListener(new OnMapDoubleTapListener() { @Override public boolean onDoubleTap(MapWidget v, MapTouchedEvent event) { Log.d("Sample1Activity", "On double tap"); Location location = addObjetWhereTouched(mapWidget, event, R.drawable.map_icon_attractions); Toast.makeText(Sample2Activity.this, "New object coords: Lat: " + location.getLatitude() + " Lon:" + location.getLongitude(), Toast.LENGTH_SHORT).show(); return true; } }); mapWidget.setOnLongClickListener(new OnLongClickListener() { @Override public boolean onLongClick(View arg0) { return false; } }); mapWidget.setOnMapLongClickListener(new OnMapLongClickListener() { @Override public boolean onLongClick(MapWidget v, MapTouchedEvent e) { if (e.getTouchedObjectIds().size() == 0) { addObjetWhereTouched(v, e, R.drawable.map_icon_leasure); } else { Toast.makeText(Sample2Activity.this, "Layer Id: " + e.getTouchedObjectIds().get(0).getLayerId(), Toast.LENGTH_SHORT).show(); } return false; } }); } private Location addObjetWhereTouched(final MapWidget mapWidget, MapTouchedEvent event, int iconId) { // getting icon from assets Drawable icon = getResources().getDrawable(iconId); MapObject obj = new MapObject(currId++, icon, new Point(0, 0), PivotFactory.createPivotPoint(icon, PivotPosition.PIVOT_CENTER), true, false); Layer layer = mapWidget.getLayerById(5); layer.addMapObject(obj); Location location = new Location("custom"); GeoUtils.translate(mapWidget, event.getMapX(), event.getMapY(), location); obj.moveTo(location); return location; } @Override protected void onSaveInstanceState(Bundle outState) { MapWidget map = (MapWidget) findViewById(MAP_ID); map.saveState(outState); super.onSaveInstanceState(outState); } } ================================================ FILE: mAppWidget/demo1app/src/main/java/com/ls/demo/demo1/model/MapObjectContainer.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.demo.demo1.model; import java.util.ArrayList; public class MapObjectContainer { private ArrayList container; public MapObjectContainer() { container = new ArrayList(); } public void addObject(MapObjectModel object) { container.add(object); } public void removeObject(MapObjectModel object) { container.remove(object); } public MapObjectModel getObject(int index) { return container.get(index); } public MapObjectModel getObjectById(int id) { for (MapObjectModel model:container) { if (model.getId() == id) { return model; } } return null; } public int size() { return container.size(); } } ================================================ FILE: mAppWidget/demo1app/src/main/java/com/ls/demo/demo1/model/MapObjectModel.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.demo.demo1.model; import android.location.Location; public class MapObjectModel { private int x; private int y; private int id; private String caption; private Location location; public MapObjectModel(int id, Location location, String caption) { this.location = location; this.caption = caption; this.id = id; } public MapObjectModel(int id, int x, int y, String caption) { this.id = id; this.x = x; this.y = y; this.caption = caption; } public int getId() { return id; } public int getX() { return x; } public int getY() { return y; } public Location getLocation() { return location; } public String getCaption() { return caption; } } ================================================ FILE: mAppWidget/demo1app/src/main/java/com/ls/demo/demo1/popup/MapPopupBase.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.demo.demo1.popup; import android.content.Context; import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.widget.LinearLayout; public class MapPopupBase { protected ViewGroup parentView; protected LinearLayout container; protected float dipScaleFactor; protected int lastX; protected int lastY; protected int screenHeight; protected int screenWidth; public MapPopupBase(Context context, ViewGroup parentView) { screenHeight = context.getResources().getDisplayMetrics().heightPixels; screenWidth = context.getResources().getDisplayMetrics().widthPixels; this.parentView = parentView; dipScaleFactor = context.getResources().getDisplayMetrics().density; container = new LinearLayout(context); container.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); lastX = lastY = -1; } public void show(ViewGroup view, int theX, int theY) { hide(); container.measure(view.getWidth(), view.getHeight()); int x = theX - (getWidth() / 2); int y = theY - getHeight(); container.setPadding(x, y, 0, 0); lastX = x; lastY = y; parentView.addView(container); container.setVisibility(View.VISIBLE); } public int getHeight() { return container.getMeasuredHeight(); } public int getWidth() { return container.getMeasuredWidth(); } public boolean isVisible() { return container.getVisibility() == View.VISIBLE; } public void hide() { container.setPadding(0, 0, 0, 0); container.setVisibility(View.INVISIBLE); parentView.removeView(container); } public void setOnClickListener(View.OnTouchListener listener) { if(container != null){ container.setOnTouchListener(listener); } } } ================================================ FILE: mAppWidget/demo1app/src/main/java/com/ls/demo/demo1/popup/TextPopup.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.demo.demo1.popup; import com.ls.demo.demo1.R; import android.content.Context; import android.graphics.Color; import android.graphics.drawable.BitmapDrawable; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; public class TextPopup extends MapPopupBase { public final static int ZERO = 0; public final static int PADDING_BOTTOM = 11; public final static int PADDING_TOP = 5; public final static int PADDING_LEFT = 15; public final static int PADDING_RIGHT = 10; public final static float DEF_TEXT_SIZE = 16; public final static int IMAGE_SIZE = 30; public final static int MAX_EMS = 14; private TextView text; public TextPopup(Context context, ViewGroup parentView) { super(context, parentView); text = new TextView(context); text.setPadding((int)(PADDING_LEFT * dipScaleFactor), (int)(PADDING_TOP * dipScaleFactor), (int)(PADDING_RIGHT * dipScaleFactor), (int)(PADDING_BOTTOM * dipScaleFactor)); text.setBackgroundResource(R.drawable.map_description_background); text.setTextSize(DEF_TEXT_SIZE); text.setGravity(Gravity.LEFT|Gravity.CENTER_VERTICAL); text.setMaxEms(MAX_EMS); text.setTextColor(Color.WHITE); container.addView(text); text.setFocusable(true); text.setClickable(true); } public void moveBy(int dx, int dy) { if (lastX != -1 && lastY != -1){ int paddingBottom = 0; int paddingRight = 0; if(container.getPaddingTop() > (screenHeight - (text.getHeight() + 3))){ paddingBottom = (container.getPaddingBottom() - dy); } if(container.getPaddingLeft() > (screenWidth - (text.getWidth() + 3))){ paddingRight = container.getPaddingRight() - dx; } container.setPadding(container.getPaddingLeft() + dx, container.getPaddingTop() + dy, paddingRight, paddingBottom); } } public void setText(String theText) { text.setPadding((int)(PADDING_LEFT * dipScaleFactor), (int)(PADDING_TOP * dipScaleFactor), (int)(PADDING_RIGHT * dipScaleFactor), (int)(PADDING_BOTTOM * dipScaleFactor)); text.setText(theText + " "); } public void setIcon(BitmapDrawable theDrawable) { if (theDrawable != null) { theDrawable.setBounds(0,0, (int) (theDrawable.getBitmap().getWidth()), (int)(theDrawable.getBitmap().getHeight())); } text.setCompoundDrawables(null, null, theDrawable, null); } public void removeIcon() { text.setCompoundDrawables(null, null, null, null); } public void setOnClickListener(View.OnTouchListener listener) { if(text != null){ text.setOnTouchListener(listener); } } } ================================================ FILE: mAppWidget/demo1app/src/main/res/layout/home.xml ================================================ ================================================ FILE: mAppWidget/demo1app/src/main/res/layout/main.xml ================================================ ================================================ FILE: mAppWidget/demo1app/src/main/res/menu/a1menu.xml ================================================ ================================================ FILE: mAppWidget/demo1app/src/main/res/menu/map_menu.xml ================================================ ================================================ FILE: mAppWidget/demo1app/src/main/res/menu/menu.xml ================================================ ================================================ FILE: mAppWidget/demo1app/src/main/res/values/strings.xml ================================================ mAppWidget ================================================ FILE: mAppWidget/demo1app/src/main/res/values/styles.xml ================================================ ================================================ FILE: mAppWidget/demo1app/src/main/res/values/theme.xml ================================================ ================================================ FILE: mAppWidget/demo2app/.gitignore ================================================ /build ================================================ FILE: mAppWidget/demo2app/build.gradle ================================================ apply plugin: 'com.android.application' android { compileSdkVersion 23 buildToolsVersion "23.0.2" defaultConfig { applicationId "com.ls.demo.demo2" minSdkVersion 7 targetSdkVersion 23 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:appcompat-v7:23.1.1' compile project(':mappwidgetlib') } ================================================ FILE: mAppWidget/demo2app/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # By default, the flags in this file are appended to flags specified # in /Users/dmytrobaryskyy/Library/Android/sdk/tools/proguard/proguard-android.txt # You can edit the include path and order by changing the proguardFiles # directive in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # Add any project specific keep options here: # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} ================================================ FILE: mAppWidget/demo2app/src/androidTest/java/com/ls/demo/demo2/ApplicationTest.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.demo.demo2; import android.app.Application; import android.test.ApplicationTestCase; /** * Testing Fundamentals */ public class ApplicationTest extends ApplicationTestCase { public ApplicationTest() { super(Application.class); } } ================================================ FILE: mAppWidget/demo2app/src/main/AndroidManifest.xml ================================================ ================================================ FILE: mAppWidget/demo2app/src/main/assets/map/map.xml ================================================  ================================================ FILE: mAppWidget/demo2app/src/main/java/com/ls/demo/demo2/BrowseMapActivity.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.demo.demo2; import java.util.ArrayList; import android.app.Activity; import android.graphics.Color; import android.graphics.Point; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.location.Location; import android.os.Bundle; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.view.View.OnTouchListener; import android.widget.RelativeLayout; import com.ls.demo.demo2.model.MapObjectContainer; import com.ls.demo.demo2.model.MapObjectModel; import com.ls.demo.demo2.popup.TextPopup; import com.ls.widgets.map.MapWidget; import com.ls.widgets.map.config.GPSConfig; import com.ls.widgets.map.config.MapGraphicsConfig; import com.ls.widgets.map.config.OfflineMapConfig; import com.ls.widgets.map.events.MapScrolledEvent; import com.ls.widgets.map.events.MapTouchedEvent; import com.ls.widgets.map.events.ObjectTouchEvent; import com.ls.widgets.map.interfaces.Layer; import com.ls.widgets.map.interfaces.MapEventsListener; import com.ls.widgets.map.interfaces.OnLocationChangedListener; import com.ls.widgets.map.interfaces.OnMapScrollListener; import com.ls.widgets.map.interfaces.OnMapTouchListener; import com.ls.widgets.map.location.PositionMarker; import com.ls.widgets.map.model.MapObject; import com.ls.widgets.map.utils.PivotFactory; import com.ls.widgets.map.utils.PivotFactory.PivotPosition; public class BrowseMapActivity extends Activity implements MapEventsListener, OnMapTouchListener { private static final String TAG = "BrowseMapActivity"; private static final Integer LAYER1_ID = 0; private static final Integer LAYER2_ID = 1; private static final int MAP_ID = 23; private int nextObjectId; private int pinHeight; private MapObjectContainer model; private MapWidget map; private TextPopup mapObjectInfoPopup; private Location points[]; private int currentPoint; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); nextObjectId = 0; model = new MapObjectContainer(); initTestLocationPoints(); initMap(savedInstanceState); initModel(); initMapObjects(); initMapListeners(); // Will show the position of the user on a map. // Do not forget to enable ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION permission int the manifest. // Uncomment this if you are at Filitheyo island :) // map.setShowMyPosition(true); map.centerMap(); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); map.saveState(outState); } private void initTestLocationPoints() { points = new Location[5]; for (int i=0; i touchedObjs = event.getTouchedObjectIds(); if (touchedObjs.size() > 0) { int xInMapCoords = event.getMapX(); int yInMapCoords = event.getMapY(); int xInScreenCoords = event.getScreenX(); int yInScreenCoords = event.getScreenY(); ObjectTouchEvent objectTouchEvent = event.getTouchedObjectIds().get(0); // Due to a bug this is not actually the layer id, but index of the layer in layers array. // Will be fixed in the next release. long layerId = objectTouchEvent.getLayerId(); Integer objectId = (Integer)objectTouchEvent.getObjectId(); // User has touched one or more map object // We will take the first one to show in the toast message. String message = "You touched the object with id: " + objectId + " on layer: " + layerId + " mapX: " + xInMapCoords + " mapY: " + yInMapCoords + " screenX: " + xInScreenCoords + " screenY: " + yInScreenCoords; Log.d(TAG, message); MapObjectModel objectModel = model.getObjectById(objectId.intValue()); if (objectModel != null) { // This is a case when we want to show popup info exactly above the pin image float density = getResources().getDisplayMetrics().density; int imgHeight = (int) (pinHeight / density / 2); // Calculating position of popup on the screen int x = xToScreenCoords(objectModel.getX()); int y = yToScreenCoords(objectModel.getY()) - imgHeight; // Show it showLocationsPopup(x, y, objectModel.getCaption()); } else { // This is a case when we want to show popup where the user has touched. showLocationsPopup(xInScreenCoords, yInScreenCoords, "Shows where user touched"); } // Hint: If user touched more than one object you can show the dialog in which ask // the user to select concrete object } else { if (mapObjectInfoPopup != null) { mapObjectInfoPopup.hide(); } } } private void showLocationsPopup(int x, int y, String text) { RelativeLayout mapLayout = (RelativeLayout) findViewById(R.id.rootLayout); if (mapObjectInfoPopup != null) { mapObjectInfoPopup.hide(); } ((TextPopup) mapObjectInfoPopup).setIcon((BitmapDrawable) getResources().getDrawable(R.drawable.map_popup_arrow)); ((TextPopup) mapObjectInfoPopup).setText(text); mapObjectInfoPopup.setOnClickListener(new OnTouchListener() { public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_UP) { if (mapObjectInfoPopup != null) { mapObjectInfoPopup.hide(); } } return false; } }); ((TextPopup) mapObjectInfoPopup).show(mapLayout, x, y); } /*** * Transforms coordinate in map coordinate system to screen coordinate system * @param mapCoord - X in map coordinate in pixels. * @return X coordinate in screen coordinates. You can use this value to display any object on the screen. */ private int xToScreenCoords(int mapCoord) { return (int)(mapCoord * map.getScale() - map.getScrollX()); } private int yToScreenCoords(int mapCoord) { return (int)(mapCoord * map.getScale() - map.getScrollY()); } } ================================================ FILE: mAppWidget/demo2app/src/main/java/com/ls/demo/demo2/model/MapObjectContainer.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.demo.demo2.model; import java.util.ArrayList; public class MapObjectContainer { private ArrayList container; public MapObjectContainer() { container = new ArrayList(); } public void addObject(MapObjectModel object) { container.add(object); } public void removeObject(MapObjectModel object) { container.remove(object); } public MapObjectModel getObject(int index) { return container.get(index); } public MapObjectModel getObjectById(int id) { for (MapObjectModel model:container) { if (model.getId() == id) { return model; } } return null; } public int size() { return container.size(); } } ================================================ FILE: mAppWidget/demo2app/src/main/java/com/ls/demo/demo2/model/MapObjectModel.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.demo.demo2.model; import android.location.Location; public class MapObjectModel { private int x; private int y; private int id; private String caption; private Location location; public MapObjectModel(int id, Location location, String caption) { this.location = location; this.caption = caption; this.id = id; } public MapObjectModel(int id, int x, int y, String caption) { this.id = id; this.x = x; this.y = y; this.caption = caption; } public int getId() { return id; } public int getX() { return x; } public int getY() { return y; } public Location getLocation() { return location; } public String getCaption() { return caption; } } ================================================ FILE: mAppWidget/demo2app/src/main/java/com/ls/demo/demo2/popup/MapPopupBase.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.demo.demo2.popup; import android.content.Context; import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.widget.LinearLayout; public class MapPopupBase { protected ViewGroup parentView; protected LinearLayout container; protected float dipScaleFactor; protected int lastX; protected int lastY; protected int screenHeight; protected int screenWidth; public MapPopupBase(Context context, ViewGroup parentView) { screenHeight = context.getResources().getDisplayMetrics().heightPixels; screenWidth = context.getResources().getDisplayMetrics().widthPixels; this.parentView = parentView; dipScaleFactor = context.getResources().getDisplayMetrics().density; container = new LinearLayout(context); container.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); lastX = lastY = -1; } public void show(ViewGroup view, int theX, int theY) { hide(); container.measure(view.getWidth(), view.getHeight()); int x = theX - (getWidth() / 2); int y = theY - getHeight(); container.setPadding(x, y, 0, 0); lastX = x; lastY = y; parentView.addView(container); container.setVisibility(View.VISIBLE); } public int getHeight() { return container.getMeasuredHeight(); } public int getWidth() { return container.getMeasuredWidth(); } public boolean isVisible() { return container.getVisibility() == View.VISIBLE; } public void hide() { container.setPadding(0, 0, 0, 0); container.setVisibility(View.INVISIBLE); parentView.removeView(container); } public void setOnClickListener(View.OnTouchListener listener) { if(container != null){ container.setOnTouchListener(listener); } } } ================================================ FILE: mAppWidget/demo2app/src/main/java/com/ls/demo/demo2/popup/TextPopup.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.demo.demo2.popup; import com.ls.demo.demo2.R; import android.content.Context; import android.graphics.Color; import android.graphics.drawable.BitmapDrawable; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; public class TextPopup extends MapPopupBase { public final static int ZERO = 0; public final static int PADDING_BOTTOM = 11; public final static int PADDING_TOP = 5; public final static int PADDING_LEFT = 15; public final static int PADDING_RIGHT = 10; public final static float DEF_TEXT_SIZE = 16; public final static int IMAGE_SIZE = 30; public final static int MAX_EMS = 14; private TextView text; public TextPopup(Context context, ViewGroup parentView) { super(context, parentView); text = new TextView(context); text.setPadding((int)(PADDING_LEFT * dipScaleFactor), (int)(PADDING_TOP * dipScaleFactor), (int)(PADDING_RIGHT * dipScaleFactor), (int)(PADDING_BOTTOM * dipScaleFactor)); text.setBackgroundResource(R.drawable.map_description_background); text.setTextSize(DEF_TEXT_SIZE); text.setGravity(Gravity.LEFT|Gravity.CENTER_VERTICAL); text.setMaxEms(MAX_EMS); text.setTextColor(Color.WHITE); container.addView(text); text.setFocusable(true); text.setClickable(true); } public void moveBy(int dx, int dy) { if (lastX != -1 && lastY != -1){ int paddingBottom = 0; int paddingRight = 0; if(container.getPaddingTop() > (screenHeight - (text.getHeight() + 3))){ paddingBottom = (container.getPaddingBottom() - dy); } if(container.getPaddingLeft() > (screenWidth - (text.getWidth() + 3))){ paddingRight = container.getPaddingRight() - dx; } container.setPadding(container.getPaddingLeft() + dx, container.getPaddingTop() + dy, paddingRight, paddingBottom); } } public void setText(String theText) { text.setPadding((int)(PADDING_LEFT * dipScaleFactor), (int)(PADDING_TOP * dipScaleFactor), (int)(PADDING_RIGHT * dipScaleFactor), (int)(PADDING_BOTTOM * dipScaleFactor)); text.setText(theText + " "); } public void setIcon(BitmapDrawable theDrawable) { if (theDrawable != null) { theDrawable.setBounds(0,0, (int) (theDrawable.getBitmap().getWidth()), (int)(theDrawable.getBitmap().getHeight())); } text.setCompoundDrawables(null, null, theDrawable, null); } public void removeIcon() { text.setCompoundDrawables(null, null, null, null); } public void setOnClickListener(View.OnTouchListener listener) { if(text != null){ text.setOnTouchListener(listener); } } } ================================================ FILE: mAppWidget/demo2app/src/main/res/layout/activity_main.xml ================================================ ================================================ FILE: mAppWidget/demo2app/src/main/res/layout/main.xml ================================================ ================================================ FILE: mAppWidget/demo2app/src/main/res/menu/menu.xml ================================================ ================================================ FILE: mAppWidget/demo2app/src/main/res/menu/menu_main.xml ================================================ ================================================ FILE: mAppWidget/demo2app/src/main/res/values/dimens.xml ================================================ 16dp 16dp ================================================ FILE: mAppWidget/demo2app/src/main/res/values/strings.xml ================================================ Demo2 Hello world! Settings ================================================ FILE: mAppWidget/demo2app/src/main/res/values/styles.xml ================================================ ================================================ FILE: mAppWidget/demo2app/src/main/res/values/theme.xml ================================================ ================================================ FILE: mAppWidget/demo2app/src/main/res/values-w820dp/dimens.xml ================================================ 64dp ================================================ FILE: mAppWidget/gradle/wrapper/gradle-wrapper.properties ================================================ #Wed Apr 10 15:27:10 PDT 2013 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip ================================================ FILE: mAppWidget/gradle.properties ================================================ # Project-wide Gradle settings. # IDE (e.g. Android Studio) users: # Gradle settings configured through the IDE *will override* # any settings specified in this file. # For more details on how to configure your build environment visit # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. # Default value: -Xmx10248m -XX:MaxPermSize=256m # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true ================================================ FILE: mAppWidget/gradlew ================================================ #!/usr/bin/env bash ############################################################################## ## ## Gradle start up script for UN*X ## ############################################################################## # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS="" APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" warn ( ) { echo "$*" } die ( ) { echo echo "$*" echo exit 1 } # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false case "`uname`" in CYGWIN* ) cygwin=true ;; Darwin* ) darwin=true ;; MINGW* ) msys=true ;; esac # For Cygwin, ensure paths are in UNIX format before anything is touched. if $cygwin ; then [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` fi # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" # Need this for relative symlinks. while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`"/$link" fi done SAVED="`pwd`" cd "`dirname \"$PRG\"`/" >&- APP_HOME="`pwd -P`" cd "$SAVED" >&- CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD="java" which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi # Increase the maximum file descriptors if we can. if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD="$MAX_FD_LIMIT" fi ulimit -n $MAX_FD if [ $? -ne 0 ] ; then warn "Could not set maximum file descriptor limit: $MAX_FD" fi else warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" fi fi # For Darwin, add options to specify how the application appears in the dock if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi # For Cygwin, switch paths to Windows format before running java if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` SEP="" for dir in $ROOTDIRSRAW ; do ROOTDIRS="$ROOTDIRS$SEP$dir" SEP="|" done OURCYGPATTERN="(^($ROOTDIRS))" # Add a user-defined pattern to the cygpath arguments if [ "$GRADLE_CYGPATTERN" != "" ] ; then OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" fi # Now convert the arguments - kludge to limit ourselves to /bin/sh i=0 for arg in "$@" ; do CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` else eval `echo args$i`="\"$arg\"" fi i=$((i+1)) done case $i in (0) set -- ;; (1) set -- "$args0" ;; (2) set -- "$args0" "$args1" ;; (3) set -- "$args0" "$args1" "$args2" ;; (4) set -- "$args0" "$args1" "$args2" "$args3" ;; (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules function splitJvmOpts() { JVM_OPTS=("$@") } eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" ================================================ FILE: mAppWidget/gradlew.bat ================================================ @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS= set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if "%ERRORLEVEL%" == "0" goto init echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto init echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :init @rem Get command-line arguments, handling Windowz variants if not "%OS%" == "Windows_NT" goto win9xME_args if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. set CMD_LINE_ARGS= set _SKIP=2 :win9xME_args_slurp if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* goto execute :4NT_args @rem Get arguments from the 4NT Shell from JP Software set CMD_LINE_ARGS=%$ :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% :end @rem End local scope for the variables with windows NT shell if "%ERRORLEVEL%"=="0" goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 exit /b 1 :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: mAppWidget/mappwidgetlib/.gitignore ================================================ /build ================================================ FILE: mAppWidget/mappwidgetlib/build.gradle ================================================ apply plugin: 'com.android.library' android { compileSdkVersion 23 buildToolsVersion "23.0.2" defaultConfig { minSdkVersion 7 targetSdkVersion 23 versionCode 1 versionName "1.4.2" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } task androidJavadocs(type: Javadoc) { source = android.sourceSets.main.java.srcDirs classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) } task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) { classifier = 'javadoc' from androidJavadocs.destinationDir } task cleanJar(type: Delete) { delete 'build/outputs/mAppWidget.jar' } task makeJar(type: Copy) { from('build/intermediates/bundles/release/') into('build/outputs/') include('classes.jar') rename ('classes.jar', 'mAppWidget.jar') } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:support-v4:23.1.1' } ================================================ FILE: mAppWidget/mappwidgetlib/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # By default, the flags in this file are appended to flags specified # in /Users/dmytrobaryskyy/Library/Android/sdk/tools/proguard/proguard-android.txt # You can edit the include path and order by changing the proguardFiles # directive in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # Add any project specific keep options here: # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} ================================================ FILE: mAppWidget/mappwidgetlib/src/androidTest/assets/map/map.xml ================================================ ================================================ FILE: mAppWidget/mappwidgetlib/src/androidTest/java/com/ls/widgets/map/AllTests.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map; import junit.framework.Test; import junit.framework.TestSuite; public class AllTests extends TestSuite { public static Test suite() { TestSuite suite = new TestSuite(AllTests.class.getName()); //$JUnit-BEGIN$ suite.addTestSuite(OfflineMapUtilTest.class); suite.addTestSuite(GridTest.class); suite.addTestSuite(CellTest.class); suite.addTestSuite(OfflineMapConfigTest.class); suite.addTestSuite(MapLayerTest.class); suite.addTestSuite(MapWidgetTest.class); //$JUnit-END$ return suite; } } ================================================ FILE: mAppWidget/mappwidgetlib/src/androidTest/java/com/ls/widgets/map/CellTest.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map; import com.ls.widgets.map.config.OfflineMapConfig; import com.ls.widgets.map.model.Cell; import com.ls.widgets.map.model.Grid; import com.ls.widgets.map.providers.AssetTileProvider; import android.graphics.Bitmap; import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.test.AndroidTestCase; import android.widget.LinearLayout; public class CellTest extends AndroidTestCase { private static final int MAP_ORIGINAL_WIDTH = 1520; private static final int MAP_ORIGINAL_HEIGHT = 920; private static final int MAX_ZOOM_LEVEL = 11; private OfflineMapConfig config; private AssetTileProvider tileManager; private Drawable drawable; protected void setUp() throws Exception { super.setUp(); config = new OfflineMapConfig("map", MAP_ORIGINAL_WIDTH, MAP_ORIGINAL_HEIGHT, 256, 1, "png"); tileManager = new AssetTileProvider(getContext(), config); drawable = new BitmapDrawable(Bitmap.createBitmap(config.getTileSize(), config.getTileSize(), Bitmap.Config.RGB_565)); } protected void tearDown() throws Exception { super.tearDown(); } public void testRecalculateDrawableRect() { Grid grid = new Grid(new LinearLayout(getContext()), config, tileManager, MAX_ZOOM_LEVEL); CellImpl cell = new CellImpl(grid, tileManager, 6, 0, 0, config.getTileSize(), 1.0f); cell.setImage(drawable); cell.recalculateDrawableRect(0, 0); Rect imageRect = cell.getDrawableRect(); Rect shouldBe = new Rect(0,0,256,256); assertEquals(shouldBe, imageRect); cell.setScale(2.0f); imageRect = cell.getDrawableRect(); shouldBe.set(0,0, 512, 512); assertEquals(shouldBe, imageRect); cell = new CellImpl(grid, tileManager, 11, 1, 1, config.getTileSize(), 1.0f); drawable = new BitmapDrawable(Bitmap.createBitmap(config.getTileSize(), config.getTileSize(), Bitmap.Config.RGB_565)); cell.setImage(drawable); cell.setScale(1.0f); cell.recalculateDrawableRect(0, 0); shouldBe.set(256, 256, 512, 512); imageRect = cell.getDrawableRect(); assertEquals(shouldBe, imageRect); } private void assertEquals(Rect r1, Rect r2) { assertEquals(r1.top, r2.top); assertEquals(r1.left, r2.left); assertEquals(r1.bottom, r2.bottom); assertEquals(r1.right, r2.right); } private static class CellImpl extends Cell { public CellImpl(Grid parent, AssetTileProvider tileManager, int zoomLevel, int col, int row, int tileSize, float scale) { super(parent, tileManager, zoomLevel, col, row, tileSize, scale); } @Override protected void recalculateDrawableRect(float dx, float dy) { // TODO Auto-generated method stub super.recalculateDrawableRect(dx, dy); } public void setImage(Drawable drawable) { this.image = drawable; } public Rect getDrawableRect() { return image.getBounds(); } } } ================================================ FILE: mAppWidget/mappwidgetlib/src/androidTest/java/com/ls/widgets/map/GridTest.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map; import android.content.Context; import android.graphics.BitmapFactory; import android.graphics.drawable.BitmapDrawable; import android.test.AndroidTestCase; import android.view.View; import android.widget.LinearLayout; import com.ls.widgets.map.config.OfflineMapConfig; import com.ls.widgets.map.interfaces.TileManagerDelegate; import com.ls.widgets.map.model.Grid; import com.ls.widgets.map.providers.AssetTileProvider; public class GridTest extends AndroidTestCase { OfflineMapConfig config; AssetTileProvider tileManager; private static final int MAP_ORIGINAL_WIDTH = 1520; private static final int MAP_ORIGINAL_HEIGHT = 920; private static final int MAX_ZOOM_LEVEL = 11; private int colCount[] = {1,1,1,1,1,1,1,1,1,2,3,6}; // col count on each zoom level private int rowCount[] = {1,1,1,1,1,1,1,1,1,1,2,4}; // row count on each zoom level private int widthOnZoomLevel[] = {1,2,3,6,12,24,48,95,190,380,760,1520}; // width of the grid on each zoom level in pixels private int heightOnZoomLevel[] = {1,1,2,4,8,15,29,58,115,230,460,920}; // height of the grid on each zoom level in pixels // scale on each zoom level private double scaleOnZoomLevel[] = {0.00048828125f, 0.0009765625f, 0.001953125f, 0.00390625f, 0.0078125f, 0.015625f, 0.03125f, 0.0625f, 0.125f, 0.25f, 0.5f, 1.0f}; private BitmapDrawable drawable; protected void setUp() throws Exception { super.setUp(); config = new OfflineMapConfig("map", MAP_ORIGINAL_WIDTH, MAP_ORIGINAL_HEIGHT, 256, 1, "png"); drawable = new BitmapDrawable(getContext().getResources(), BitmapFactory.decodeResource(getContext().getResources(), com.ls.widgets.map.test.R.drawable.maps_blue_dot)); tileManager = new TestTileManager(getContext(), config); } protected void tearDown() throws Exception { super.tearDown(); } public void testGetHeight() { for (int i=0; i< heightOnZoomLevel.length; ++i) { GridToTest grid = new GridToTest(new LinearLayout(getContext()), config, tileManager, i); assertEquals(heightOnZoomLevel[i], grid.getHeight()); } } public void testGetOriginalHeight() { GridToTest grid = new GridToTest(new LinearLayout(getContext()), config, tileManager, MAX_ZOOM_LEVEL); assertEquals(MAP_ORIGINAL_HEIGHT, grid.getOriginalHeight()); } public void testGetOriginalWidth() { for (int i=0; i<=MAX_ZOOM_LEVEL; ++i) { GridToTest grid = new GridToTest(new LinearLayout(getContext()), config, tileManager, MAX_ZOOM_LEVEL); assertEquals(MAP_ORIGINAL_WIDTH, grid.getOriginalWidth()); } } public void testGetWidth() { for (int i=0; i<=MAX_ZOOM_LEVEL; ++i) { GridToTest grid = new GridToTest(new LinearLayout(getContext()), config, tileManager, i); assertEquals(widthOnZoomLevel[i], grid.getWidth()); } } public void testGetMaxZoomLevel() { for (int i=0; i<= MAX_ZOOM_LEVEL; ++i) { GridToTest grid = new GridToTest(new LinearLayout(getContext()), config, tileManager, i); assertEquals(MAX_ZOOM_LEVEL, grid.getMaxZoomLevel()); } } public void testGetMinZoomLevel() { for (int i=0; i<= MAX_ZOOM_LEVEL; ++i) { GridToTest grid = new GridToTest(new LinearLayout(getContext()), config, tileManager, i); assertEquals(0, grid.getMinZoomLevel()); } } public void testGetColCount() { for (int i=0; i touchedIds = ((MapLayer)layer).getTouched(touchedX, touchedY); // // if (touchable) { // assertEquals(1, touchedIds.size()); // } else { // assertEquals(0, touchedIds.size()); // } // // if (touchable) { // Object id = touchedIds.get(0); // assertEquals(counter, id); // } // // counter += 1; // } // } // } private void getTouchedTest2(boolean touchable) { int counter = 0; int width = drawable.getIntrinsicWidth(); int height = drawable.getIntrinsicHeight(); getTouchedGenerateMapObjects(touchable, width, height); counter = 0; for (int i=0; i < 480/width; ++i) { for (int j=0; j < 800/height; ++j) { int touchedX = (int)((float)i*(float)width+(float)width/2.0f); int touchedY = (int)((float)j*(float)height+(float)height/2.0f); ArrayList touchedIds = ((MapLayer)layer).getTouched(new Rect(touchedX - 2, touchedY - 2, touchedX + 2, touchedY + 2)); if (touchable) { assertEquals(1, touchedIds.size()); } else { assertEquals(0, touchedIds.size()); } if (touchable) { Object id = touchedIds.get(0); assertEquals(counter, id); } counter += 1; } } ArrayList touchedIds = ((MapLayer)layer).getTouched(new Rect(0, 0, width * 2, height * 2)); if (touchable) { assertEquals(4, touchedIds.size()); } else { assertEquals(0, touchedIds.size()); } } private void getTouchedGenerateMapObjects(boolean touchable, int width, int height) { int counter = 0; for (int i=0; i < 480/width; ++i) { for (int j=0; j < 800/height; ++j) { MapObject object = new MapObject(counter, drawable, i*width, j*height, touchable); layer.addMapObject(object); counter += 1; } } } private static class FakeCanvas extends Canvas { private boolean drawPerformed; public FakeCanvas() { drawPerformed = false; } @Override public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) { super.drawBitmap(bitmap, left, top, paint); drawPerformed = true; } @Override public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) { super.drawBitmap(bitmap, matrix, paint); drawPerformed = true; } @Override public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) { super.drawBitmap(bitmap, src, dst, paint); drawPerformed = true; } @Override public void drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint) { super.drawBitmap(bitmap, src, dst, paint); drawPerformed = true; } public void setDrawPerformed(boolean performed) { drawPerformed = performed; } } } ================================================ FILE: mAppWidget/mappwidgetlib/src/androidTest/java/com/ls/widgets/map/MapObjectTest.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map; import com.ls.widgets.map.MapWidget; import com.ls.widgets.map.interfaces.Layer; import com.ls.widgets.map.model.MapLayer; import com.ls.widgets.map.model.MapObject; import com.ls.widgets.map.utils.Size; import android.R; import android.graphics.BitmapFactory; import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.test.AndroidTestCase; public class MapObjectTest extends AndroidTestCase { private MapWidget map; private MapLayer layer; private Drawable drawable1; private Drawable drawable2; private FakeMapObject scalableObject; private FakeMapObject nonScalableObject; private Size originalDrawableSize; protected void setUp() throws Exception { super.setUp(); // map = new MapWidget(getContext(), "map", 11); drawable1 = new BitmapDrawable(BitmapFactory.decodeResource(getContext().getResources(), R.drawable.presence_online)); drawable2 = new BitmapDrawable(BitmapFactory.decodeResource(getContext().getResources(), R.drawable.presence_online)); originalDrawableSize = new Size(drawable1.getIntrinsicWidth(), drawable1.getIntrinsicHeight()); // layer = map.createLayer(1); scalableObject = new FakeMapObject("1", drawable1, 10, 10, 0, 0, true, true); nonScalableObject = new FakeMapObject("2", drawable2, 100, 100, 0, 0, true, false); } protected void tearDown() throws Exception { // map.removeAllLayers(); super.tearDown(); } public void testMapObjectObjectDrawableIntIntIntIntBooleanBoolean() { fail("Not yet implemented"); } public void testGetDrawable() { fail("Not yet implemented"); } public void testSetDrawable() { fail("Not yet implemented"); } public void testDraw() { fail("Not yet implemented"); } public void testGetId() { fail("Not yet implemented"); } public void testIsTouchedIntInt() { fail("Not yet implemented"); } public void testIsTouchedRect() { assertEquals(true, nonScalableObject.isTouchable()); Rect touchRect = new Rect(100,100,101,101); assertEquals(true, nonScalableObject.isTouched(touchRect)); touchRect.set(100, 99, 101, 100); assertEquals(false, nonScalableObject.isTouched(touchRect)); Rect drawableRect = nonScalableObject.getDrawable().getBounds(); Rect touchableRect = nonScalableObject.getTouchArea(); assertEquals(drawableRect.width(), touchableRect.width()); nonScalableObject.setNewScale(2.0f); assertEquals(drawableRect.width(), touchableRect.width() / 2); } public void testGetXScaled() { fail("Not yet implemented"); } public void testGetYScaled() { fail("Not yet implemented"); } public void testGetX() { fail("Not yet implemented"); } public void testGetY() { fail("Not yet implemented"); } public void testIsTouchable() { fail("Not yet implemented"); } public void testGetBounds() { fail("Not yet implemented"); } public void testMoveTo() { fail("Not yet implemented"); } public void testSetScale() { fail("Not yet implemented"); } public void testSetParent() { fail("Not yet implemented"); } private class FakeMapObject extends MapObject { public FakeMapObject(Object id, Drawable drawable, int x, int y, int pivotX, int pivotY, boolean isTouchable, boolean isScalable) { super(id, drawable, x, y, pivotX, pivotY, isTouchable, isScalable); } public void setNewScale(float scale) { this.scale = scale; recalculateBounds(); } public Rect getTouchArea() { return this.touchRect; } } } ================================================ FILE: mAppWidget/mappwidgetlib/src/androidTest/java/com/ls/widgets/map/MapWidgetTest.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map; import java.util.ArrayList; import android.content.Context; import android.graphics.BitmapFactory; import android.graphics.Point; import android.graphics.drawable.BitmapDrawable; import android.test.AndroidTestCase; import android.util.Log; import android.view.animation.Animation.AnimationListener; import com.ls.widgets.map.config.OfflineMapConfig; import com.ls.widgets.map.interfaces.Layer; import com.ls.widgets.map.interfaces.MapEventsListener; import com.ls.widgets.map.interfaces.TileManagerDelegate; import com.ls.widgets.map.model.MapObject; import com.ls.widgets.map.providers.AssetTileProvider; public class MapWidgetTest extends AndroidTestCase { private TestMapWidget map; private BitmapDrawable drawable; @Override protected void setUp() throws Exception { map = new TestMapWidget(getContext(), "map", 11); map.setScale(1.0f); drawable = new BitmapDrawable(getContext().getResources(), BitmapFactory.decodeResource(getContext().getResources(), com.ls.widgets.map.test.R.drawable.maps_blue_dot)); super.setUp(); } @Override protected void tearDown() throws Exception { map.removeAllLayers(); map.startProcessing(); super.tearDown(); } public void testCreateLayer() { int layerIds[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; Layer layers[] = new Layer[layerIds.length]; for (int i = 0; i < layerIds.length; ++i) { layers[i] = map.createLayer(layerIds[i]); } for (int i = 0; i < layerIds.length; ++i) { Layer layer = map.getLayerById(Integer.valueOf(i)); assertNotNull(layer); assertSame(layers[i], layer); } boolean ok = false; try { map.createLayer(0); } catch (IllegalArgumentException e) { ok = true; } assertTrue(ok); } public void testRemoveLayer() { for (int i = -100; i <= 100; ++i) { map.createLayer(i); } for (int i = -100; i <= 100; ++i) { Layer layer = map.getLayerById(i); assertNotNull(layer); map.removeLayer(i); layer = map.getLayerById(i); assertNull(layer); } try { map.removeLayer(0); } catch (Exception e) { fail(); } } public void testGetLayer() { int layerIds[] = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19}; Layer layers[] = new Layer[layerIds.length]; for (int i = 0; i < layerIds.length; ++i) { layers[i] = map.createLayer(layerIds[i]); } for (int i = 0; i < layerIds.length; ++i) { Layer layer = map.getLayer(i); assertSame(layers[i], layer); } boolean ok = false; try { map.getLayer(layerIds.length); } catch (IndexOutOfBoundsException e) { ok = true; } assertTrue(ok); } public void testGetLayerById() { int layerIds[] = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19}; Layer layers[] = new Layer[layerIds.length]; for (int i = 0; i < layerIds.length; ++i) { layers[i] = map.createLayer(layerIds[i]); } for (int i = 0; i < layerIds.length; ++i) { Layer layer = map.getLayerById(layerIds[i]); assertSame(layers[i], layer); } assertNull(map.getLayerById(0)); } public void testGetLayerCount() { for (int j = 0; j < 100; ++j) { for (int i = 0; i < j; ++i) { map.createLayer(i); } assertEquals(map.getLayerCount(), j); map.removeAllLayers(); } } public void testGetMapHeight() { int height = map.getConfig().getImageHeight(); for (int i = 1; i <= 11; ++i) { assertEquals("i:" + i, height, map.getMapHeight()); map.zoomOut(); height = (int) Math.ceil(height / 2.0f); } } public void testGetMapWidth() { int width = map.getConfig().getImageWidth(); for (int i = 1; i <= 11; ++i) { assertEquals("i:" + i, width, map.getMapWidth()); map.zoomOut(); width = (int) Math.ceil(width / 2.0f); } } public void testGetOriginalMapWidth() { int width = map.getConfig().getImageWidth(); for (int i = 1; i <= 11; ++i) { assertEquals(width, map.getOriginalMapWidth()); map.zoomOut(); } } public void testGetOriginalMapHeight() { int height = map.getConfig().getImageHeight(); for (int i = 1; i <= 11; ++i) { assertEquals(height, map.getOriginalMapHeight()); map.zoomOut(); } } public void testGetConfig() { OfflineMapConfig config = map.getConfig(); assertNotNull(config); config.setMaxZoomLevelLimit(1); config = map.getConfig(); assertTrue(config.getMaxZoomLevelLimit() == 1); } public void testGetScale() { float scalesUp[] = {1.0f, 2.0f, 4.0f, 8.0f, 16f, 32f}; float scalesDown[] = {1.0f, 0.5f, 0.25f, 0.125f}; map.setAnimationEnabled(false); for (int i = 0; i < scalesUp.length; ++i) { assertEquals("i=" + i, scalesUp[i], map.getScale()); map.zoomIn(); } for (int i = 0; i < scalesUp.length; ++i) { map.zoomOut(); } for (int i = 0; i < scalesDown.length; ++i) { assertEquals(scalesDown[i], map.getScale()); map.zoomOut(); } for (int i = 0; i < 20; i++) { double testScale = 0.1; final double step = 0.1; while (testScale < 3.0f) { float prevScale = map.getScale(); int prevWidth = map.getMapWidth(); Log.d("MapWidgetTest", "ZL: " + map.getZoomLevel() + ", scale: " + map.getScale() + ", width: " + map.getMapWidth() + ", testScale: " + testScale); map.setScale((float) testScale); assertEquals("ZL: " + map.getZoomLevel() + ", prevScale: " + prevScale + ", CurScale:" + (float) testScale + ", prevWidth: " + prevWidth, (float) (prevScale * testScale), map.getScale()); map.setScale(1.0f); testScale += step; } map.zoomIn(); } } public void testGetZoomLevel() { map.setAnimationEnabled(false); // Test lower bound for (int i = 11; i >= -11; --i) { int zl = map.getZoomLevel(); if (i >= 0) { assertEquals(i, zl); } map.zoomOut(); } assertEquals(0, map.getZoomLevel()); // Test regular zoom with software zoom is enabled for (int i = 0; i <= 22; ++i) { int zl = map.getZoomLevel(); assertEquals(i, zl); map.zoomIn(); } // Test upper bound when software zoom is disabled map = new TestMapWidget(getContext(), "map", 1); map.setScale(1.0f); map.setAnimationEnabled(false); OfflineMapConfig conf = map.getConfig(); conf.setSoftwareZoomEnabled(false); for (int i = 1; i <= 22; ++i) { int zl = map.getZoomLevel(); if (i <= 11) { assertEquals(i, zl); } else { assertEquals(11, zl); } map.zoomIn(); } } public void testRemoveAllLayers() { for (int i = -50; i < 50; ++i) { map.createLayer(i); } assertEquals(100, map.getLayerCount()); map.removeAllLayers(); for (int i = -50; i < 50; ++i) { assertNull("i:" + i, map.getLayerById(i)); } } public void testClearLayers() { int count = 50; MapObject[] objects = generateMapObjects(count); ArrayList layers = new ArrayList(10); for (int i = -5; i < 5; ++i) { Layer layer = map.createLayer(i); for (MapObject object : objects) { layer.addMapObject(object); } layers.add(layer); assertEquals(count, layer.getMapObjectCount()); } map.clearLayers(); for (Layer layer : layers) { assertEquals(0, layer.getMapObjectCount()); } } public void testSetMinZoomLevel() { for (int j = 10; j >= 0; --j) { map = new TestMapWidget(getContext(), "map", 11); map.setAnimationEnabled(false); map.setMinZoomLevel(j); for (int i = j; i >= 0; --i) { map.zoomOut(); int zl = map.getZoomLevel(); if (zl < j) { fail("ZL: " + zl + ", j: " + j); } } } } public void testSetMaxZoomLevel() { for (int j = 2; j <= 11; ++j) { map = new TestMapWidget(getContext(), "map", 1); map.setAnimationEnabled(false); map.setMaxZoomLevel(j); for (int i = 2; i <= 11; ++i) { map.zoomIn(); int zl = map.getZoomLevel(); if (zl > j) { fail("ZL: " + zl + ", j: " + j); } } } } public void testSetScale() { map = new TestMapWidget(getContext(), "map", 1); map.setAnimationEnabled(false); float scalesUp[] = {1.0f, 2.0f, 4.0f, 8.0f, 16f, 32f}; float scalesDown[] = {1.0f, 0.5f, 0.25f, 0.125f}; for (int j = 0; j < 15; ++j) { for (int i = 0; i < scalesDown.length; ++i) { float oldScale = map.getScale(); map.setScale(scalesDown[i]); float newScale = map.getScale(); assertEquals("zl:" + map.getZoomLevel() + ", i:" + i + ", os:" + oldScale + ", ns:" + newScale, scalesDown[i], newScale / oldScale); map.setScale(1.0f); } for (int i = 0; i < scalesUp.length; ++i) { float oldScale = map.getScale(); map.setScale(scalesUp[i]); float newScale = map.getScale(); assertEquals("zl:" + map.getZoomLevel(), scalesUp[i], newScale / oldScale); map.setScale(1.0f); } map.zoomIn(); } } public void testUseSoftwareZoom() { map.setAnimationEnabled(false); map.setUseSoftwareZoom(false); for (int i = 0; i < 10; ++i) { map.zoomIn(); } assertEquals(11, map.getZoomLevel()); map.setUseSoftwareZoom(true); for (int i = 0; i < 10; ++i) { map.zoomIn(); } assertEquals(11 + 10, map.getZoomLevel()); } public void testZoomIn() { map = new TestMapWidget(getContext(), "map", 1); map.setAnimationEnabled(false); TestMapEventsListener listener = new TestMapEventsListener(); for (int i = 2; i <= 13; ++i) { map.addMapEventsListener(listener); float oldScale = map.getScale(); map.zoomIn(); float newScale = map.getScale(); assertTrue("I:" + i + ", zoomIn: " + listener.counters[TestMapEventsListener.PRE_ZOOM_IN] + " " + listener.counters[TestMapEventsListener.POST_ZOOM_IN], listener.zoomInValid()); assertEquals("i:" + i, 2.0f, newScale / oldScale); map.removeMapEventsListener(listener); listener.clearCounters(); } } public void testZoomOut() { map.setAnimationEnabled(false); TestMapEventsListener listener = new TestMapEventsListener(); for (int i = 2; i <= 11; ++i) { map.addMapEventsListener(listener); float oldScale = map.getScale(); map.zoomOut(); float newScale = map.getScale(); assertTrue("I:" + i + ", zoomOut: " + listener.counters[TestMapEventsListener.PRE_ZOOM_OUT] + " " + listener.counters[TestMapEventsListener.POST_ZOOM_OUT], listener.zoomOutValid()); assertEquals(2.0f, oldScale / newScale); map.removeMapEventsListener(listener); listener.clearCounters(); } } // FIXME: Temporarily disabled // public void testAnimatedZoomIn() { // map = new TestMapWidget(getContext(), "map", 1); // map.setAnimationEnabled(true); // final Object event = new Object(); // // TestMapEventsListener listener = new TestMapEventsListener() { // @Override // public void onPostZoomIn() { // super.onPostZoomIn(); // // synchronized (event) { // event.notify(); // } // } // }; // // // for (int i = 2; i <= 11; ++i) { // map.addMapEventsListener(listener); // float oldScale = map.getScale(); // map.zoomIn(); // // synchronized (event) { // try { // event.wait(3000); // } catch (InterruptedException e) { // fail(e.toString()); // e.printStackTrace(); // } // } // // float newScale = map.getScale(); // map.removeMapEventsListener(listener); // assertTrue("I:" + i + ", zoomIn: " + listener.counters[TestMapEventsListener.PRE_ZOOM_IN] + // " " + listener.counters[TestMapEventsListener.POST_ZOOM_IN], // listener.zoomInValid()); // assertEquals(2.0f, newScale / oldScale); // listener.clearCounters(); // } // } public void testAnimatedZoomOut() { map.setAnimationEnabled(true); TestMapEventsListener listener = new TestMapEventsListener(); for (int i = 2; i <= 11; ++i) { map.addMapEventsListener(listener); float oldScale = map.getScale(); map.zoomOut(); float newScale = map.getScale(); map.removeMapEventsListener(listener); assertTrue("I:" + i + ", zoomOut: " + listener.counters[TestMapEventsListener.PRE_ZOOM_OUT] + " " + listener.counters[TestMapEventsListener.POST_ZOOM_OUT], listener.zoomOutValid()); assertEquals(2.0f, oldScale / newScale); listener.clearCounters(); } } private MapObject[] generateMapObjects(int count) { MapObject[] result = new MapObject[count]; for (int i = 0; i < count; ++i) { MapObject object = new MapObject(i, drawable, new Point(0, 0)); result[i] = object; } return result; } private class TestMapEventsListener implements MapEventsListener { public static final int PRE_ZOOM_IN = 0; public static final int PRE_ZOOM_OUT = 1; public static final int POST_ZOOM_IN = 2; public static final int POST_ZOOM_OUT = 3; private int counters[]; public TestMapEventsListener() { counters = new int[]{0, 0, 0, 0}; } @Override public void onPreZoomIn() { counters[PRE_ZOOM_IN] += 1; } @Override public void onPostZoomIn() { counters[POST_ZOOM_IN] += 1; } @Override public void onPreZoomOut() { counters[PRE_ZOOM_OUT] += 1; } @Override public void onPostZoomOut() { counters[POST_ZOOM_OUT] += 1; } public void clearCounters() { counters = new int[]{0, 0, 0, 0}; } public boolean zoomInValid() { return counters[PRE_ZOOM_IN] == 1 && counters[POST_ZOOM_IN] == 1; } public boolean zoomOutValid() { return counters[PRE_ZOOM_OUT] == 1 && counters[POST_ZOOM_OUT] == 1; } } private class TestMapWidget extends MapWidget { public TestMapWidget(Context context, String rootMapFolder) { super(context, rootMapFolder); this.tileProvider = new TestTileManager(context, this.getConfig()); this.setTileProvider(tileProvider); } public TestMapWidget(Context context, String rootMapFolder, int initialZl) { super(context, rootMapFolder, initialZl); this.tileProvider = new TestTileManager(context, this.getConfig()); this.setTileProvider(tileProvider); } @Override protected void animateZoomIn(final AnimationListener listener, float pivotX, float pivotY) { (new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } post(new Runnable() { public void run() { onAnimationEnd(); } }); } })).start(); } @Override protected void animateZoomOut(final AnimationListener listener) { (new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } post(new Runnable() { public void run() { onAnimationEnd(); } }); //onAnimationEnd(); //listener.onAnimationEnd(null); } })).start(); } public void startProcessing() { super.startProcessingRequests(); } } private class TestTileManager extends AssetTileProvider { public TestTileManager(Context context, OfflineMapConfig config) { super(context, config); } @Override public void requestTile(int zoomLevel, int col, int row, TileManagerDelegate delegate) { delegate.onTileReady(zoomLevel, col, row, drawable); } } } ================================================ FILE: mAppWidget/mappwidgetlib/src/androidTest/java/com/ls/widgets/map/OfflineMapConfigTest.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map; import android.test.AndroidTestCase; import com.ls.widgets.map.config.OfflineMapConfig; public class OfflineMapConfigTest extends AndroidTestCase { private static final int TEST_IMAGE_WIDTH = 1024; private static final int TEST_IMAGE_HEIGHT = 768; private static final int TEST_TILE_SIZE = 256; private static final int TEST_OVERLAP = 1; private static final String TEST_IMAGE_FORMAT = "png"; private static final String MAP_ROOT_PATH = "map"; private OfflineMapConfig config; protected void setUp() throws Exception { config = new OfflineMapConfig(MAP_ROOT_PATH, TEST_IMAGE_WIDTH, TEST_IMAGE_HEIGHT, TEST_TILE_SIZE, TEST_OVERLAP, TEST_IMAGE_FORMAT); super.setUp(); } protected void tearDown() throws Exception { super.tearDown(); } public void testDefaults() { assertEquals(0, config.getMaxZoomLevelLimit()); assertEquals(0, config.getMinZoomLevelLimit()); assertEquals(5, config.getTouchAreaSize()); assertEquals(64, config.getTrackballScrollStepX()); assertEquals(64, config.getTrackballScrollStepY()); assertEquals(false, config.isPinchZoomEnabled()); assertEquals(false, config.isFlingEnabled()); assertEquals(true, config.isSoftwareZoomEnabled()); assertEquals(true, config.isZoomBtnsVisible()); } public void testConstructor() { assertEquals(TEST_IMAGE_WIDTH, config.getImageWidth()); assertEquals(TEST_IMAGE_HEIGHT, config.getImageHeight()); assertEquals(TEST_TILE_SIZE, config.getTileSize()); assertEquals(TEST_OVERLAP, config.getOverlap()); assertEquals(TEST_IMAGE_FORMAT, config.getImageFormat()); } public void testCopyConstructor() { // If this exception is thrown, please, update this test in order to test new members or // throw the tests for removed fields out. assertEquals(19, config.getClass().getDeclaredFields().length); config.setFlingEnabled(true); config.setMaxZoomLevelLimit(15); config.setMinZoomLevelLimit(10); config.setPinchZoomEnabled(true); config.setMapCenteringEnabled(true); config.setSoftwareZoomEnabled(false); config.setTouchAreaSize(10); config.setTrackballScrollStepX(25); config.setTrackballScrollStepY(30); config.setZoomBtnsVisible(false); OfflineMapConfig testConfig = new OfflineMapConfig(config); assertEquals(MAP_ROOT_PATH, testConfig.getMapRootPath()); assertEquals(true, testConfig.isFlingEnabled()); assertEquals(15, testConfig.getMaxZoomLevelLimit()); assertEquals(10, testConfig.getMinZoomLevelLimit()); assertEquals(true, testConfig.isPinchZoomEnabled()); assertEquals(true, testConfig.isMapCenteringEnabled()); assertEquals(false, testConfig.isSoftwareZoomEnabled()); assertEquals(10, testConfig.getTouchAreaSize()); assertEquals(25, testConfig.getTrackballScrollStepX()); assertEquals(30, testConfig.getTrackballScrollStepY()); assertEquals(false, testConfig.isZoomBtnsVisible()); assertNotSame(config, testConfig); } public void testFlingEnabledOption() { config.setFlingEnabled(true); assertEquals(true, config.isFlingEnabled()); config.setFlingEnabled(false); assertEquals(false, config.isFlingEnabled()); } public void testMaxZoomLevelLimitOption() { for (int i=-1000; i<0; ++i) { boolean result = false; try { config.setMaxZoomLevelLimit(i); } catch (IllegalArgumentException e) { result = true; } assertTrue("Exception was not thrown for i=" + i, result); } for (int i=0; i<1000; ++i) { config.setMaxZoomLevelLimit(i); assertEquals(i, config.getMaxZoomLevelLimit()); } } public void testMinZoomLevelLimitOption() { for (int i=-1000; i<0; ++i) { boolean result = false; try { config.setMinZoomLevelLimit(i); } catch (IllegalArgumentException e) { result = true; } assertTrue("Exception was not thrown for i=" + i, result); } for (int i=0; i<1000; ++i) { config.setMinZoomLevelLimit(i); assertEquals(i, config.getMinZoomLevelLimit()); } } public void testPinchZoomEnabledOption() { config.setPinchZoomEnabled(true); assertEquals(true, config.isPinchZoomEnabled()); config.setPinchZoomEnabled(false); assertEquals(false, config.isPinchZoomEnabled()); } public void testSoftwareZoomEnabledOption() { config.setSoftwareZoomEnabled(true); assertEquals(true, config.isSoftwareZoomEnabled()); config.setSoftwareZoomEnabled(false); assertEquals(false, config.isSoftwareZoomEnabled()); } public void testTouchAreaSizeOption() { for (int i=-1000; i<=0; ++i) { boolean result = false; try { config.setTouchAreaSize(i); } catch (IllegalArgumentException e) { result = true; } assertTrue("Exception was not thrown for i=" + i, result); } for (int i=1; i<1000; ++i) { config.setTouchAreaSize(i); assertEquals(i, config.getTouchAreaSize()); } } public void testTrackballScrollStepXOption() { for (int i=-1000; i< 0; ++i) { boolean result = false; try { config.setTrackballScrollStepX(i); } catch (IllegalArgumentException e) { result = true; } assertTrue("Exception was not thrown for i=" + i, result); } for (int i=0; i<1000; ++i) { config.setTrackballScrollStepX(i); assertEquals(i, config.getTrackballScrollStepX()); } } public void testTrackballScrollStepYOption() { for (int i=-1000; i< 0; ++i) { boolean result = false; try { config.setTrackballScrollStepY(i); } catch (IllegalArgumentException e) { result = true; } assertTrue("Exception was not thrown for i=" + i, result); } for (int i=0; i<1000; ++i) { config.setTrackballScrollStepX(i); assertEquals(i, config.getTrackballScrollStepX()); } } public void testZoomBtnsVisibleOption() { config.setZoomBtnsVisible(true); assertEquals(true, config.isZoomBtnsVisible()); config.setZoomBtnsVisible(false); assertEquals(false, config.isZoomBtnsVisible()); } } ================================================ FILE: mAppWidget/mappwidgetlib/src/androidTest/java/com/ls/widgets/map/OfflineMapUtilTest.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map; import android.test.AndroidTestCase; import com.ls.widgets.map.utils.OfflineMapUtil; public class OfflineMapUtilTest extends AndroidTestCase { private float imageSizesAtZoomLevel[] = {1,2,3,6,12,24,47,94,188,375,750,1500,3000,6000}; // Zoom Level 0 1 2 3 4 5 6 7 8 9 10 11 12 13 private int zoomLevelStart[] = {1, 2, 3, 5, 9, 17, 33, 65, 129, 257, 513, 1025, 2049, 4097}; private int zoomLevelEnd[] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192}; public void testGetScaledImageSize() { for (int i=0; i ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/MapWidget.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import org.xml.sax.SAXException; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.location.Location; import android.os.Bundle; import android.os.Looper; import android.util.Log; import android.view.GestureDetector; import android.view.GestureDetector.SimpleOnGestureListener; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup.LayoutParams; import android.view.ViewTreeObserver; import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.view.animation.Animation; import android.view.animation.Animation.AnimationListener; import android.view.animation.DecelerateInterpolator; import android.view.animation.ScaleAnimation; import android.view.animation.TranslateAnimation; import android.widget.Scroller; import android.widget.ZoomButtonsController; import android.widget.ZoomButtonsController.OnZoomListener; import com.ls.widgets.map.config.GPSConfig; import com.ls.widgets.map.config.MapConfigParser; import com.ls.widgets.map.config.MapGraphicsConfig; import com.ls.widgets.map.config.OfflineMapConfig; import com.ls.widgets.map.events.MapScrolledEvent; import com.ls.widgets.map.events.MapTouchedEvent; import com.ls.widgets.map.events.ObjectTouchEvent; import com.ls.widgets.map.interfaces.Layer; import com.ls.widgets.map.interfaces.MapEventsListener; import com.ls.widgets.map.interfaces.MapLocationListener; import com.ls.widgets.map.interfaces.OnGridReadyListener; import com.ls.widgets.map.interfaces.OnLocationChangedListener; import com.ls.widgets.map.interfaces.OnMapDoubleTapListener; import com.ls.widgets.map.interfaces.OnMapLongClickListener; import com.ls.widgets.map.interfaces.OnMapScrollListener; import com.ls.widgets.map.interfaces.OnMapTilesFinishedLoadingListener; import com.ls.widgets.map.interfaces.OnMapTouchListener; import com.ls.widgets.map.location.PositionMarker; import com.ls.widgets.map.model.Grid; import com.ls.widgets.map.model.MapLayer; import com.ls.widgets.map.providers.AssetTileProvider; import com.ls.widgets.map.providers.ExternalStorageTileProvider; import com.ls.widgets.map.providers.GPSLocationProvider; import com.ls.widgets.map.providers.TileProvider; import com.ls.widgets.map.utils.Graphics; import com.ls.widgets.map.utils.MathUtils; import com.ls.widgets.map.utils.OfflineMapUtil; import com.ls.widgets.map.utils.PivotFactory; import com.ls.widgets.map.utils.PivotFactory.PivotPosition; import com.ls.widgets.map.utils.Resources; import com.ls.widgets.map.utils.TransformUtils; public class MapWidget extends View implements MapLocationListener { private static final String MSG_MAP_DATA_IS_CORRUPTED_OR_MISSING = "Map data is corrupted or missing."; private final static String TAG = "MAP WIDGET"; private final static long POS_PIN_ID = 1; private enum Mode { NONE, ZOOMED, ZOOM }; private OfflineMapConfig config; private ZoomButtonsController zoomBtnsController; private Grid grid; private Grid prevGrid; private Paint paint; private float scale; private double pinchZoomScale; private boolean doNotZoom; private boolean isAnimationEnabled; private boolean byUser; // Represents layers in the map private MapLayer topmostLayer; private ArrayList layers; private Map layersMap; // Provider that handles loading of map tiles. protected TileProvider tileProvider; protected GPSLocationProvider locationProvider; // Listeners private OnMapTouchListener mapTouchListener; private OnMapTilesFinishedLoadingListener mapTilesReadyListener; private OnMapScrollListener mapScrollListener; private ArrayList mapEventsListeners; private OnLocationChangedListener locationChangeListener; private OnLongClickListener longClickListener; private OnMapLongClickListener mapLongClicklistener; private OnMapDoubleTapListener onDoubleTapListener; private OnTouchListener onTouchListener; private Mode mode; // Smooth scrolling private GestureDetector gestureDetector; private Scroller scroller; // debug private boolean debugEnabled = false; private RectF lastTouchedRect; private boolean isZooming; private boolean isDestroying; private boolean userTouching; private double pinchStartDistance; private int mapPivotX; private int mapPivotY; private static Bitmap logo; private Rect drawingRect; private Runnable restoreScrollPosRunnable; private Runnable performAfterZoom; private Runnable performAfterTranslate; private boolean requestCenterMap; /** * Creates instance of map widget. * * @param context * - context * @param rootMapFolder * - folder that contains map resources inside your assets. * @param initialZoomLevel * - initial zoom level. */ public MapWidget(Context context, String rootMapFolder, int initialZoomLevel) { this(null, context, rootMapFolder, initialZoomLevel); } /** * Creates instance of map widget. * * @param context * - context * @param rootMapFolder * - instance of File that points to the map resources which are * located on the external storage. * @param initialZoomLevel * - initial zoom level */ public MapWidget(Context context, File rootMapFolder, int initialZoomLevel) { this(null, context, rootMapFolder, initialZoomLevel); } /** * Creates instance of map widget. Zoom level will be set to 10. * * @param context * - Context * @param rootMapFolder * - folder that contains map resources inside your assets. */ public MapWidget(Context context, String rootMapFolder) { this(null, context, rootMapFolder, 10); } /** * Creates instance of map widget. Zoom level will be set to 10. * * @param context * - Context * @param rootMapFolder * - instance of File that points to the map resources which are * located on the external storage. */ public MapWidget(Context context, File rootMapFolder) { this(null, context, rootMapFolder, 10); } /** * Creates instance of map widget. * * @param bundle * - bundle that were used to save map widget's state. * @param context * - Context * @param rootMapFolder * - instance of File that points to the map resources which are * located on the external storage. * @param initialZoomLevel * - zoom level that will be set in case if bundle doesn't * contain previously saved state. */ public MapWidget(Bundle bundle, Context context, File rootMapFolder, int initialZoomLevel) { super(context); initCommonStuff(context); String configPath = OfflineMapUtil.getConfigFilePath(rootMapFolder .getAbsolutePath()); try { MapConfigParser configParser = new MapConfigParser( rootMapFolder.getAbsolutePath()); config = configParser.parse(context, new File(configPath)); if (config != null) { tileProvider = new ExternalStorageTileProvider(config); int maxZoomLevel = OfflineMapUtil.getMaxZoomLevel( config.getImageWidth(), config.getImageHeight()); int zoomLevel = initialZoomLevel; float scale = 1.0f; if (bundle != null) { if (bundle.containsKey("com.ls.zoomLevel")) zoomLevel = bundle.getInt("com.ls.zoomLevel"); if (bundle.containsKey("com.ls.scale")) scale = bundle.getFloat("com.ls.scale"); } if (zoomLevel > maxZoomLevel) { grid = new Grid(this, config, tileProvider, maxZoomLevel); if (scale == 1.0f) { scale = (float) Math.pow(2, zoomLevel - maxZoomLevel); } } else { grid = new Grid(this, config, tileProvider, zoomLevel); } this.scale = scale; grid.setInternalScale(scale); initPositionPin(); restoreMapPosition(bundle); } } catch (SAXException e) { Log.e(TAG, "Exception: " + e); e.printStackTrace(); } catch (IOException e) { Log.e(TAG, "Exception: " + e); e.printStackTrace(); } } /** * Creates instance of map widget. * * @param bundle * - bundle that were used to save map widget's state. * @param context * - Context * @param rootMapFolder * - folder that contains map resources inside your assets. * @param initialZoomLevel * - zoom level that will be set in case if bundle doesn't * contain previously saved state. */ public MapWidget(Bundle bundle, Context context, String rootMapFolder, int initialZoomLevel) { super(context); initCommonStuff(context); String configPath = OfflineMapUtil.getConfigFilePath(rootMapFolder); try { MapConfigParser configParser = new MapConfigParser(rootMapFolder); config = configParser.parse(context, configPath); tileProvider = new AssetTileProvider(getContext(), config); int maxZoomLevel = OfflineMapUtil.getMaxZoomLevel( config.getImageWidth(), config.getImageHeight()); int zoomLevel = initialZoomLevel; float scale = 1.0f; if (bundle != null) { if (bundle.containsKey("com.ls.zoomLevel")) zoomLevel = bundle.getInt("com.ls.zoomLevel"); if (bundle.containsKey("com.ls.scale")) scale = bundle.getFloat("com.ls.scale"); } if (zoomLevel > maxZoomLevel) { grid = new Grid(this, config, tileProvider, maxZoomLevel); if (scale == 1.0f) { scale = (float) Math.pow(2, zoomLevel - maxZoomLevel); } } else { grid = new Grid(this, config, tileProvider, zoomLevel); } this.scale = scale; grid.setInternalScale(scale); initPositionPin(); restoreMapPosition(bundle); } catch (SAXException e) { Log.e(TAG, "Exception: " + e); e.printStackTrace(); } catch (IOException e) { Log.e(TAG, "Exception: " + e); e.printStackTrace(); } } private void restoreMapPosition(Bundle bundle) { if (bundle != null && bundle.containsKey("com.ls.curPosOnMapX")) { final int mapX = (int) bundle.getFloat("com.ls.curPosOnMapX"); final int mapY = (int) bundle.getFloat("com.ls.curPosOnMapY"); Log.d("MapWidget", "Restored pos: [" + mapX + "," + mapY + "]"); restoreScrollPosRunnable = new Runnable() { public void run() { jumpTo(new Point(mapX, mapY)); }; }; } else { doCorrectPosition(false, false); } } private void initCommonStuff(Context context) { scale = 1.0f; mode = Mode.NONE; drawingRect = new Rect(); isAnimationEnabled = true; requestCenterMap = false; userTouching = false; this.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); setBackgroundDrawable(null); this.setClickable(true); this.setEnabled(true); this.setFocusable(true); initializeZoomBtnsController(); gestureDetector = new GestureDetector(context, new MyGestureDetector()); decelerateInterpolator = new DecelerateInterpolator(1.5f); scroller = new Scroller(context, decelerateInterpolator); topmostLayer = new MapLayer(1, this); topmostLayer.setVisible(false); layers = new ArrayList(); layersMap = new HashMap(); mapEventsListeners = new ArrayList(); paint = new Paint(); paint.setColor(Color.RED); paint.setStyle(Style.STROKE); paint.setStrokeWidth(1); if (Resources.LOGO != null) { logo = BitmapFactory.decodeByteArray(Resources.LOGO, 0, Resources.LOGO.length); } locationProvider = null; performAfterTranslate = null; } private void initPositionPin() { BitmapDrawable arrow = new BitmapDrawable(getResources(), BitmapFactory.decodeByteArray(Graphics.BLUE_ARROW, 0, Graphics.BLUE_ARROW.length)); BitmapDrawable dot = new BitmapDrawable(getResources(), BitmapFactory.decodeByteArray( Graphics.BLUE_DOT, 0, Graphics.BLUE_DOT.length)); PositionMarker pin = new PositionMarker(this, POS_PIN_ID, dot, arrow); topmostLayer.addMapObject(pin); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); if (w == 0 && h == 0) return; if (restoreScrollPosRunnable != null) { restoreScrollPosRunnable.run(); restoreScrollPosRunnable = null; } } protected void setTileProvider(TileProvider tileManager) { if (grid != null) { grid.setTileProvider(tileManager); } } /** * Adds listener for map events. * * @param listener * - instance of MapEventListener */ public void addMapEventsListener(MapEventsListener listener) { if (mapEventsListeners == null) { mapEventsListeners = new ArrayList(); } mapEventsListeners.add(listener); } /** * Creates new map layer with a given id. * * @param theLayerId * - id of the new layer * @return returns instance of the MapLayer or null if error occured. * @throws IllegalArgumentException * when layer with the given id exists already. */ public MapLayer createLayer(long theLayerId) { if (this.layersMap.containsKey(theLayerId)) { throw new IllegalArgumentException( "Attempt to create layer with duplicated ID"); } try { MapLayer layer = new MapLayer(theLayerId, this); layers.add(layer); layersMap.put(theLayerId, layer); return layer; } catch (Exception e) { Log.e("MapWidget", "Exception: " + e); return null; } } /** * Removes layer with the given id from the map. * * @param theLayerId * the id of previously created layer. */ public void removeLayer(long theLayerId) { Layer layer = layersMap.remove(theLayerId); layers.remove(layer); } /** * Removes all layers from the map. */ public void removeAllLayers() { layers.clear(); layersMap.clear(); } /** * Centers the map horizontally */ public void centerMapHorizontally() { if (grid.getWidth() > getWidth()) { int dx = (getWidth() - grid.getWidth()) / 2; scrollBy(-dx, 0); } } /** * Returns map layer by index. * * @param index * the index of the layer * @return instance of Layer * @throws ArrayIndexOutOfBoundsException * when index is out of bounds. */ public Layer getLayer(int index) { return layers.get(index); } /** * Returns map layer by layer id. * * @param id * - layer id * @return instance of Layer */ public Layer getLayerById(long id) { return layersMap.get(id); } /** * Returns total layer count * * @return layer count */ public int getLayerCount() { return layers.size(); } /** * Returns height of the map taking current scale into account. * * @return height of the map in pixels. */ public int getMapHeight() { if (grid != null) { return grid.getHeight(); } return 0; } /** * Returns width of the map taking current scale into account. * * @return width of the map in pixels. */ public int getMapWidth() { if (grid != null) { return grid.getWidth(); } return 0; } /** * Returns the height of the map on the max zoom level. * * @return original map height in pixels. */ public int getOriginalMapHeight() { if (grid != null) { return grid.getOriginalHeight(); } return 0; } /** * Returns the width of the map on the max zoom level. * * @return original map width in pixels. */ public int getOriginalMapWidth() { if (grid != null) { return grid.getOriginalWidth(); } return 0; } /** * Returns the copy of the map widget's configuration * * @return instance of OfflineMapConfig class. */ public OfflineMapConfig getConfig() { return config; } /** * Returns the current scale of the map. * * @return float that represents the scale of the map. 1.0 = max zoom level. * 0.5 - map is scaled down to half of it's size. */ public float getScale() { if (grid != null) { return (float) grid.getScale(); } return 0; } /** * Returns current zoom level of the map. * * @return current zoom level of the map. Should be greater than 0. */ public int getZoomLevel() { if (grid == null) { return 0; } double scale = grid.getScale(); int zoomLevel = grid.getZoomLevel(); if (scale <= 1.0f) { return zoomLevel; } else { return OfflineMapUtil.getMaxZoomLevel(grid.getWidth(), grid.getHeight()); } } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (config == null) { return false; } int keyCode2 = event.getKeyCode(); switch (keyCode2) { case KeyEvent.KEYCODE_DPAD_LEFT: scrollBy(-config.getTrackballScrollStepX(), 0); return true; case KeyEvent.KEYCODE_DPAD_RIGHT: scrollBy(config.getTrackballScrollStepX(), 0); return true; case KeyEvent.KEYCODE_DPAD_UP: scrollBy(0, -config.getTrackballScrollStepY()); return true; case KeyEvent.KEYCODE_DPAD_DOWN: scrollBy(0, config.getTrackballScrollStepY()); return true; case KeyEvent.KEYCODE_I: zoomIn(); return true; case KeyEvent.KEYCODE_O: zoomOut(); return true; } return false; } @Override public boolean onTouchEvent(MotionEvent event) { if (config == null) { return false; } if (isDestroying) { Log.w("MapWidget", "Map is destroying... OnTouch skipped"); return false; } if (this.onTouchListener != null) { // this.onTouchListener.onTouch(this, event); } boolean result = false; super.onTouchEvent(event); gestureDetector.onTouchEvent(event); int action = event.getAction(); int actionCode = action & MotionEvent.ACTION_MASK; if (actionCode == MotionEvent.ACTION_DOWN) { byUser = true; userTouching = true; try { if (config.isZoomBtnsVisible()) { zoomBtnsController.setVisible(true); } } catch (Exception e) { Log.w("MapWidget", "Exception e: " + e); } tileProvider.pauseProcessingCommands(); result = true; } else if (actionCode == MotionEvent.ACTION_POINTER_DOWN) { if (config.isPinchZoomEnabled()) { mode = Mode.ZOOM; doNotZoom = false; float x1 = event.getX(0); float y1 = event.getY(0); float x2 = event.getX(1); float y2 = event.getY(1); pinchStartDistance = MathUtils.distance(x1, y1, x2, y2); PointF mapPivot = MathUtils.middle(x1, y1, x2, y2); mapPivotX = (int) (mapPivot.x); mapPivotY = (int) (mapPivot.y); result = true; } } else if (actionCode == MotionEvent.ACTION_MOVE) { if (zoomBtnsController != null && config.isZoomBtnsVisible() && !zoomBtnsController.isVisible()) zoomBtnsController.setVisible(true); if (mode == Mode.ZOOM) { float x1 = event.getX(0); float y1 = event.getY(0); float x2 = event.getX(1); float y2 = event.getY(1); double pinchDistance = MathUtils.distance(x1, y1, x2, y2); if (pinchStartDistance != 0) { pinchZoomScale = pinchDistance / pinchStartDistance; if (pinchZoomScale >= 1.025) { mode = Mode.ZOOMED; zoomIn(mapPivotX, mapPivotY); } else if (pinchZoomScale <= 0.975) { mode = Mode.ZOOMED; zoomOut(); } } } result = true; } else if (actionCode == MotionEvent.ACTION_POINTER_UP) { mode = Mode.NONE; result = true; } else if (actionCode == MotionEvent.ACTION_UP) { byUser = false; userTouching = false; if (!isZooming) { doCorrectPosition(); } result = true; } return result; } /** * Removes map event listener from the map. * * @param listener * instance of the MapEventsListener */ public void removeMapEventsListener(MapEventsListener listener) { if (mapEventsListeners != null) { mapEventsListeners.remove(listener); } } /** * Removes all map event listeners. */ public void removeAllMapEventsListeners() { if (mapEventsListeners != null) { mapEventsListeners.clear(); } mapEventsListeners = new ArrayList(); } /** * Removes all graphical object from all layers */ public void clearLayers() { if (Looper.myLooper() == null) { throw new IllegalThreadStateException( "Should be called from UI thread"); } for (int i = layers.size() - 1; i >= 0; --i) { layers.get(i).clearAll(); } layers.clear(); } /** * Sets touch listener to the map. * * @param mapTouchListener * instance of OnMapTouchListener. */ public void setOnMapTouchListener(OnMapTouchListener mapTouchListener) { this.mapTouchListener = mapTouchListener; } /** * Sets the listener to handle long click on the map. * @param onMapLongClickListener - instance of {@link OnMapLongClickListener}. Can be null. */ public void setOnMapLongClickListener(OnMapLongClickListener onMapLongClickListener) { this.mapLongClicklistener = onMapLongClickListener; } /** * Sets the listener that will be called when all visible tiles has been * loaded and displayed. * * @param listener * instance of OnMapTilesFinishedLoadingListener or null. */ public void setOnMapTilesFinishLoadingListener( OnMapTilesFinishedLoadingListener listener) { this.mapTilesReadyListener = listener; if (grid != null) { grid.setOnReadyListener(new OnGridReadyListener() { @Override public void onReady() { if (mapTilesReadyListener != null) { mapTilesReadyListener.onMapTilesFinishedLoading(); } } }); } } /** * Sets scroll listener to the map. * * @param mapScrollListener * instance of OnMapScrollListener. */ public void setOnMapScrolledListener(OnMapScrollListener mapScrollListener) { this.mapScrollListener = mapScrollListener; } /** * Sets listener for retrieving the location * * @param listener * - instance of OnLocationChangedListener. May be null. */ public void setOnLocationChangedListener(OnLocationChangedListener listener) { this.locationChangeListener = listener; } /** * Sets the listener to handle long click event. * * @param listener * instance of OnLongClickListener. Can be null. * @see android.view.View.OnLongClickListener */ @Override public void setOnLongClickListener(OnLongClickListener listener) { this.longClickListener = listener; } /** * Sets the listener to handle double tap event. * @param listener instance of OnMapDoubleTapListener. Can be null. * @see com.ls.widgets.map.interfaces.OnMapDoubleTapListener */ public void setOnDoubleTapListener(OnMapDoubleTapListener listener) { this.onDoubleTapListener = listener; } /** * Sets the listener to handle touch events * @param listener instance of OnTouchListener * @see android.view.View.OnTouchListener */ @Override public void setOnTouchListener(OnTouchListener listener) { super.setOnTouchListener(listener); } /** * Enables or disables the standard zoom controls. * * @param enabled * true in order to make zoom controls visible, otherwise false. */ public void setZoomButtonsVisible(boolean enabled) { if (config != null) { config.setZoomBtnsVisible(enabled); if (enabled) { if (zoomBtnsController == null) { initializeZoomBtnsController(); } } else { if (zoomBtnsController != null) { zoomBtnsController.setVisible(false); zoomBtnsController.setOnZoomListener(null); zoomBtnsController = null; } } } else { Log.w(TAG, "Ignored. Map is not initialized properly."); } } /** * Sets the min zoom level the user can zoom out to. * * @param minZoomLevel * int from 0 to count of zoom levels. */ public void setMinZoomLevel(int minZoomLevel) { if (config == null) { Log.w(TAG, "setMinZoomLevel skipped. MapWidget is not initialized properly"); return; } int maxAvailableZoomLevel = grid.getMaxZoomLevel(); int minAvailableZoomLevel = grid.getMinZoomLevel(); if (minZoomLevel < minAvailableZoomLevel) { Log.w(TAG, "There is no " + minZoomLevel + " zoom level. Will use " + minAvailableZoomLevel + " as min zoom level."); config.setMinZoomLevelLimit(minZoomLevel); } else if (minZoomLevel > maxAvailableZoomLevel) { Log.w(TAG, "Min zoom level should be less than max zoom level. Min zoom level: " + minAvailableZoomLevel + " Max zoom level: " + maxAvailableZoomLevel + ", " + " You are setting: " + config.getMaxZoomLevelLimit() + " as min zoom level."); Log.w(TAG, "Will use max zoom level as min zoom level."); config.setMinZoomLevelLimit(maxAvailableZoomLevel); } else { config.setMinZoomLevelLimit(minZoomLevel); } updateZoomButtons(); } /** * The max zoom level the user can zoom in to. * * @param maxZoomLevel * int from 0 to max zoom levels. */ public void setMaxZoomLevel(int maxZoomLevel) { if (config == null) { Log.w(TAG, "setMaxZoomLevel skipped. MapWidget was not initialized properly"); return; } if (grid == null) { throw new IllegalStateException(); } int maxAvailableZoomLevel = grid.getMaxZoomLevel(); int minAvailableZoomLevel = grid.getMinZoomLevel(); if (!config.isSoftwareZoomEnabled() && maxZoomLevel > maxAvailableZoomLevel) { Log.w(TAG, "There is no " + maxZoomLevel + " zoom level. Will use " + maxAvailableZoomLevel + " as max zoom level."); config.setMaxZoomLevelLimit(maxAvailableZoomLevel); } else if (maxZoomLevel < minAvailableZoomLevel) { Log.w(TAG, "Max zoom level should be greater than min zoom level. Min zoom level: " + minAvailableZoomLevel + " Max zoom level: " + maxAvailableZoomLevel + ", " + " you are setting: " + maxZoomLevel + " as max zoom level."); Log.w(TAG, "Will use min zoom level as max zoom level."); config.setMaxZoomLevelLimit(minAvailableZoomLevel); } else { config.setMaxZoomLevelLimit(maxZoomLevel); } updateZoomButtons(); } /** * Set's the scale to the map. Map will be scaled by resizing the existing * tiles. Zoom level will be preserved. * * @param scale * scale value. 2.0 means that you want to make map two times * bigger. */ public void setScale(float scale) { if (Looper.myLooper() == null) { throw new IllegalThreadStateException( "Should be called from UI thread"); } if (grid == null) { return; } grid.setSoftScale(scale); setScaleToOtherDrawables((float) getScale()); invalidate(); } /** * Enables the map to keep zooming in when no more zoom levels left. * Software scale will be used. * * @param useSoftwareZoom * true if you want to enable software zoom, false otherwise. */ public void setUseSoftwareZoom(boolean useSoftwareZoom) { if (config != null) { config.setSoftwareZoomEnabled(useSoftwareZoom); } } /** * Enables/disables the zoom in/zoom out animations * * @param isEnabled * true if you want to enable the animations, false otherwise. */ public void setAnimationEnabled(boolean isEnabled) { this.isAnimationEnabled = isEnabled; } /** * Sets the size of the touch area when user touches the map. * * @param pixels * radius of the touch area in pixels */ public void setTouchAreaSize(int pixels) { if (config != null) { config.setTouchAreaSize(pixels); } } /** * Zooms map in by one zoom level. */ public void zoomIn() { final int pivotX = (getWidth() / 2); final int pivotY = (getHeight() / 2); zoomIn(pivotX, pivotY); } /** * Shows current position of the user on the map. You can configure the view * of the pointer. See MapWidget.getMapGraphicsConfig() for details. You can * configure the GPS receiver by setting configuration parameters before * setShowMyPosition is called. See MapWidget.getGPSConfig() for details. *

* * In order to calibrate the map you should add the calibration data to the * map.xml. Calibration data consists of two points - top left and bottom * right. X and Y is a coordinate of the point in your original map image in * pixels. lat and lon is latitude and longitude of the same point in real * world.
* *

	 * For example, your map.xml may look like this:
* {@code * * * * * * * * } *
* * @param show * - set true in order to show the position marker on the map, * false - in order to hide it. * @throws java.lang.IllegalStateException * () in case if map is not calibrated. */ public void setShowMyPosition(boolean show) { GPSConfig config = getConfig().getGpsConfig(); if (!config.isMapCalibrated()) { throw new IllegalStateException( "Map is not calibrated in order to use gps positioning"); } if (show) { MapGraphicsConfig graphics = getConfig().getGraphicsConfig(); PositionMarker marker = (PositionMarker) topmostLayer .getMapObject(POS_PIN_ID); if (graphics.getDotPointerDrawableId() != -1) { Drawable dot = getResources().getDrawable( graphics.getDotPointerDrawableId()); marker.setDotPointer(dot, PivotFactory.createPivotPoint(dot, PivotPosition.PIVOT_CENTER)); } if (graphics.getArrowPointerDrawableId() != -1) { Drawable arrow = getResources().getDrawable( graphics.getArrowPointerDrawableId()); marker.setArrowPointer(arrow, PivotFactory.createPivotPoint( arrow, PivotPosition.PIVOT_CENTER)); } marker.setColor(graphics.getAccuracyAreaColor(), graphics.getAccuracyAreaBorderColor()); if (locationProvider == null) { locationProvider = new GPSLocationProvider(this.getContext()); locationProvider.setMinRefreshTime(config.getMinTime()); locationProvider.setMinRefreshDistance(config.getMinDistance()); locationProvider.setMapLocationListener(this); } locationProvider.start(config.getPassiveMode()); } else { if (locationProvider != null) { locationProvider.stop(); } } } /** * @return instance of MapGraphicsConfig or null, if map was not configured * properly */ public MapGraphicsConfig getMapGraphicsConfig() { if (config != null) { return config.getGraphicsConfig(); } return null; } /** * Returns GPSConfig object that will allow you to configure the GPS * receiver. * * @return instance of GPSConfig, or null if map configuration file doesn't * contain GPS calibration data or file was not found. */ public GPSConfig getGpsConfig() { if (config != null) return config.getGpsConfig(); return null; } public void zoomIn(final int pivotX, final int pivotY) { if (doNotZoom) { Log.d(TAG, "Zoom is in progress. Skipped..."); return; } if (config == null) { Log.w(TAG, "Zoom in skipped. Map was not initialized properly"); return; } if (!config.isSoftwareZoomEnabled() && getZoomLevel() == grid.getMaxZoomLevel()) { return; } else if (config.isSoftwareZoomEnabled() && config.getMaxZoomLevelLimit() != 0 && getZoomLevel() >= config.getMaxZoomLevelLimit()) { return; } if (Looper.myLooper() == null) { throw new IllegalThreadStateException( "Should be called from UI thread"); } notifyAboutPreZoomIn(mapEventsListeners); isZooming = true; doNotZoom = true; if (!isAnimationEnabled) { doZoom(getZoomLevel() + 1, pivotX, pivotY); isZooming = false; doCorrectPosition(); return; } performAfterZoom = new Runnable() { @Override public void run() { doZoom(getZoomLevel() + 1, pivotX, pivotY); isZooming = false; doCorrectPosition(true); } }; animateZoomIn(null, pivotX, pivotY); } /** * Zooms map out by one zoom level. */ public void zoomOut() { if (doNotZoom) { Log.d(TAG, "Zoom is in progress. Skipped..."); return; } if (config == null) { Log.w(TAG, "Zoom in skipped. Map was not initialized properly"); return; } int currZoomLevel = getZoomLevel(); if (currZoomLevel == 0 || currZoomLevel <= config.getMinZoomLevelLimit()) { return; } doNotZoom = true; if (grid == null) { Log.w(TAG, "zoomOut() grid is null"); doNotZoom = false; return; } if (Looper.myLooper() == null) { throw new IllegalThreadStateException( "Should be called from UI thread"); } int pivotX = getWidth() / 2; int pivotY = getHeight() / 2; notifyAboutPreZoomOut(mapEventsListeners); doZoom(currZoomLevel - 1, pivotX, pivotY); if (!isAnimationEnabled) { doNotZoom = false; isZooming = false; doCorrectPosition(); return; } isZooming = true; performAfterZoom = new Runnable() { public void run() { isZooming = false; doCorrectPosition(true); } }; animateZoomOut(null); } @Override protected void onAnimationEnd() { super.onAnimationEnd(); Animation animation = getAnimation(); if (animation == null) { Log.w(TAG, "Unknown animation has been finished."); } if (animation instanceof ScaleAnimation && performAfterZoom != null) { performAfterZoom.run(); performAfterZoom = null; } if (animation instanceof TranslateAnimation && performAfterTranslate != null) { performAfterTranslate.run(); performAfterTranslate = null; } } @Override protected void onDraw(Canvas canvas) { this.getDrawingRect(drawingRect); if (config != null) { if (prevGrid != null) { prevGrid.draw(canvas, paint, drawingRect); } if (grid != null) { grid.draw(canvas, paint, drawingRect); } drawLayers(canvas, drawingRect); if (logo != null) { canvas.drawBitmap(logo, getWidth() + getScrollX() - logo.getWidth() - 10, getHeight() + getScrollY() - logo.getHeight() - 10, null); } } else { scrollTo(0, 0); drawMissingDataErrorMessage(canvas); } } private void drawMissingDataErrorMessage(Canvas canvas) { paint.setTextSize(24); paint.setStyle(Style.FILL); paint.setAntiAlias(true); paint.setSubpixelText(true); paint.setColor(Color.BLACK); canvas.drawPaint(paint); paint.setColor(Color.WHITE); Rect rect = new Rect(); paint.getTextBounds(MSG_MAP_DATA_IS_CORRUPTED_OR_MISSING, 0, MSG_MAP_DATA_IS_CORRUPTED_OR_MISSING.length(), rect); Rect rect2 = canvas.getClipBounds(); canvas.drawText(MSG_MAP_DATA_IS_CORRUPTED_OR_MISSING, // (getWidth() - rect.width()) / 2, getHeight() / 2, paint); (rect2.width() - rect.width()) / 2, rect2.height() / 2, paint); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); if (requestCenterMap) { requestCenterMap = false; final ViewTreeObserver observer = this.getViewTreeObserver(); if (observer.isAlive()) { observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { doCorrectPosition(true, false); jumpTo(getOriginalMapWidth() / 2, getOriginalMapHeight() / 2); observer.removeGlobalOnLayoutListener(this); } }); } } else { doCorrectPosition(false, false); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } static final MapScrolledEvent scrolledEvent = new MapScrolledEvent(0, 0); private DecelerateInterpolator decelerateInterpolator; @Override protected void onScrollChanged(int horOrigin, int verOrigin, int oldl, int oldt) { super.onScrollChanged(horOrigin, verOrigin, oldl, oldt); if (mapScrollListener != null) { scrolledEvent.setData(oldl - horOrigin, oldt - verOrigin, byUser); mapScrollListener.onScrolledEvent(this, scrolledEvent); } } protected void animateZoomIn(AnimationListener listener, float pivotX, float pivotY) { Animation zoomInAnimation = getZoomInAnimation(pivotX, pivotY); if (listener != null) { zoomInAnimation.setAnimationListener(listener); } this.startAnimation(zoomInAnimation); } protected void animateZoomOut(AnimationListener listener) { Animation zoomOutAnimation = null; if (zoomOutAnimation == null) { zoomOutAnimation = getZoomOutAnimation(); } if (listener != null) { zoomOutAnimation.setAnimationListener(listener); } this.startAnimation(zoomOutAnimation); } private void doCorrectPosition() { doCorrectPosition(false); } private void doCorrectPosition(boolean force) { doCorrectPosition(force, isAnimationEnabled); } private void doCorrectPosition(boolean force, boolean animate) { if (Looper.myLooper() == null) throw new IllegalThreadStateException( "Should be called from UI thread"); if (grid == null) { return; } if (isZooming) return; if (!config.isMapCenteringEnabled() && !force) { onPositionCorrected(); return; } int viewWidth = getWidth(); int viewHeight = getHeight(); float fromX = 0.0f; float toX = 0.0f; float fromY = 0.0f; float toY = 0.0f; boolean positionCorrected = false; int gridWidth = getMapWidth(); int gridHeight = getMapHeight(); if (gridWidth > viewWidth) { int dx2 = gridWidth - getScrollX(); if (getScrollX() < 0) { fromX = getScrollX() * -1; scrollTo(0, getScrollY()); positionCorrected = true; } else if (dx2 < viewWidth) { int gap = viewWidth - dx2; fromX = -gap; scrollTo((int) getScrollX() - gap, getScrollY()); positionCorrected = true; } } else { toX = (viewWidth - gridWidth) / 2; fromX = ((-getScrollX()) - toX); scrollTo(-(int) toX, getScrollY()); positionCorrected = true; toX = 0; } if (gridHeight > viewHeight) { int dy2 = gridHeight - getScrollY(); if (getScrollY() < 0) { fromY = -getScrollY(); scrollTo(getScrollX(), 0); positionCorrected = true; } else if (dy2 < viewHeight) { int gap = viewHeight - dy2; fromY = -gap; scrollTo((int) getScrollX(), (int) (getScrollY() - gap)); positionCorrected = true; } } else { toY = (viewHeight - gridHeight) / 2.0f; fromY = ((-getScrollY()) - toY); scrollTo(getScrollX(), -(int) toY); positionCorrected = true; toY = 0; } if (positionCorrected || force) { if (animate) { TranslateAnimation moveAnimation = new TranslateAnimation( fromX, toX, fromY, toY); performAfterTranslate = new Runnable() { public void run() { onPositionCorrected(); }; }; moveAnimation.setDuration(500); moveAnimation.setInterpolator(decelerateInterpolator); moveAnimation.setFillAfter(true); this.startAnimation(moveAnimation); } else { onPositionCorrected(); } } else { onPositionCorrected(); } } private void onPositionCorrected() { grid.freeResources(); if (!userTouching) { tileProvider.startProcessingCommands(); } } private void doZoom(int zoomLevel, int pivotX, int pivotY) { if (grid == null) { doNotZoom = false; return; } int maxZoomLevel = OfflineMapUtil.getMaxZoomLevel( config.getImageWidth(), config.getImageHeight()); int currZoomLevel = getZoomLevel(); prevGrid = grid; prevGrid.setLoadTiles(false); final int gWidth = grid.getWidth(); final int gHeight = grid.getHeight(); float newScale = (float) Math.pow(2, zoomLevel - getZoomLevel()); // Resolving offsets that we need to move the map in order pivot point // become in the center of the screen. final Rect currRect = new Rect(-getScrollX(), -getScrollY(), gWidth - getScrollX(), gHeight - getScrollY()); final Rect transformed = TransformUtils.scaleRect(currRect, newScale, pivotX, pivotY); boolean zoomIn = zoomLevel > currZoomLevel; if ((zoomIn && currZoomLevel < maxZoomLevel) || (!zoomIn && currZoomLevel > 0) && scale == 1.0f) { grid = new Grid(this, config, tileProvider, zoomLevel); grid.setOnReadyListener(new OnGridReadyListener() { @Override public void onReady() { grid.setOnReadyListener(null); prevGrid = null; if (mapTilesReadyListener != null) { mapTilesReadyListener.onMapTilesFinishedLoading(); } } }); prevGrid.setInternalScale(newScale); } else { scale *= newScale; if (prevGrid != null) { prevGrid = null; } grid.setOnReadyListener(null); grid.setLoadTiles(false); grid.setInternalScale(scale); grid.setLoadTiles(true); } updateZoomButtons(); float scale_temp = getScale(); setScaleToOtherDrawables(scale_temp); scrollTo(-transformed.left, -transformed.top); doNotZoom = false; if (zoomIn) { notifyAboutPostZoomIn(mapEventsListeners); } else { notifyAboutPostZoomOut(mapEventsListeners); } } private Animation getZoomInAnimation(float pivotX, float pivotY) { float fromX = 1.0f; float fromY = 1.0f; float toX = 2.0f; float toY = 2.0f; Animation zoomInAnimation = new ScaleAnimation(fromX, toX, fromY, toY, pivotX, pivotY); zoomInAnimation.setDuration(500); zoomInAnimation.setInterpolator(decelerateInterpolator); zoomInAnimation.setFillAfter(true); return zoomInAnimation; } private Animation getZoomOutAnimation() { float fromX = 2.0f; float fromY = 2.0f; float toX = 1.0f; float toY = 1.0f; float pivotX = getWidth() / 2.0f; float pivotY = getHeight() / 2.0f; Animation zoomOutAnimation = new ScaleAnimation(fromX, toX, fromY, toY, pivotX, pivotY); zoomOutAnimation.setDuration(500); zoomOutAnimation.setInterpolator(decelerateInterpolator); zoomOutAnimation.setFillAfter(true); return zoomOutAnimation; } static final Rect touchRect = new Rect(); private ArrayList getTouchedElementIds(final int normX, final int normY) { ArrayList result = new ArrayList(); float d = 5.0f; if (config != null) { d = (float) config.getTouchAreaSize() / 2.0f; } touchRect.set((int) (normX - d), (int) (normY - d), (int) (normX + d), (int) (normY + d)); for (int i = layers.size() - 1; i >= 0; --i) { MapLayer layer = layers.get(i); if (layer.isVisible()) { ArrayList tempResult = layer.getTouched(touchRect); for (Object id : tempResult) { ObjectTouchEvent touchEvent = new ObjectTouchEvent(id, layer.getId()); result.add(touchEvent); } } } return result; } private void initializeZoomBtnsController() { zoomBtnsController = new ZoomButtonsController(this); zoomBtnsController.setOnZoomListener(new OnZoomListener() { @Override public void onVisibilityChanged(boolean arg0) { // Left unimplemented } @Override public void onZoom(boolean zoomIn) { if (zoomIn) { try { zoomIn(); } catch (Exception e) { doNotZoom = false; Log.e("MapWidget", "Exception while zoom in. " + e); } } else { try { zoomOut(); } catch (Exception e) { doNotZoom = false; Log.e("MapWidget", "Exception while zoom out. " + e); } } } }); } private void drawLayers(Canvas canvas, Rect drawingRect) { int size = layers.size(); for (int i = 0; i < size; ++i) { MapLayer layer = layers.get(i); layer.draw(canvas, drawingRect); } topmostLayer.draw(canvas, drawingRect); } private void setScaleToOtherDrawables(float scale) { int size = layers.size(); topmostLayer.setScale(scale); for (int i = 0; i < size; ++i) { MapLayer layer = layers.get(i); layer.setScale(scale); } } private float translateXToMapCoordinate(float x) { float scale = getScale(); if (scale != 0) { return (x + (float) getScrollX()) / scale; } return 0; } private float translateYToMapCoordinate(float y) { float scale = getScale(); if (scale != 0) { return (y + (float) getScrollY()) / scale; } else { return 0; } } private void updateZoomButtons() { if (config == null || zoomBtnsController == null || config.isZoomBtnsVisible() == false) return; int currZoomLevel = getZoomLevel(); int minZoomLevel = Math.max(config.getMinZoomLevelLimit(), grid.getMinZoomLevel()); int maxZoomLevel = grid.getMaxZoomLevel(); int maxZoomLevelLimit = config.getMaxZoomLevelLimit(); if (maxZoomLevelLimit != 0 && config.isSoftwareZoomEnabled()) { maxZoomLevel = maxZoomLevelLimit; } else if (maxZoomLevelLimit != 0 && !config.isSoftwareZoomEnabled()) { maxZoomLevel = Math.min(maxZoomLevelLimit, maxZoomLevel); } if (currZoomLevel == maxZoomLevel) { // At the max zoom level // zoomBtnsController.setZoomInEnabled(false); zoomBtnsController.setZoomOutEnabled(true); if (!config.isSoftwareZoomEnabled() || maxZoomLevelLimit != 0) { zoomBtnsController.setZoomInEnabled(false); } } else if (currZoomLevel == minZoomLevel) { // At the min zoom level zoomBtnsController.setZoomInEnabled(true); zoomBtnsController.setZoomOutEnabled(scale > 1); } else { // In the middle zoomBtnsController.setZoomInEnabled(true); zoomBtnsController.setZoomOutEnabled(true); } } protected void startProcessingRequests() { isDestroying = false; if (!userTouching) { tileProvider.startProcessingCommands(); } } private static final void notifyAboutPreZoomIn( ArrayList listeners) { for (MapEventsListener listener : listeners) { if (listener != null) { try { listener.onPreZoomIn(); } catch (Exception e) { Log.e(TAG, "Exception " + e + " on willZoomIn"); } } else { Log.w(TAG, "WillZoomIn: Map Events listener is null"); } } } private static final void notifyAboutPostZoomIn( ArrayList listeners) { for (MapEventsListener listener : listeners) { if (listener != null) { try { listener.onPostZoomIn(); } catch (Exception e) { e.printStackTrace(); Log.e(TAG, "Exception " + e + " on didlZoomIn"); } } else { Log.w(TAG, "DidZoomIn: Map Events listener is null"); } } } private static final void notifyAboutPreZoomOut( ArrayList listeners) { for (MapEventsListener listener : listeners) { if (listener != null) { try { listener.onPreZoomOut(); } catch (Exception e) { e.printStackTrace(); Log.e(TAG, "Exception " + e + " on willZoomOut"); } } else { Log.w(TAG, "WillZoomOut: Map Events listener is null"); } } } private static final void notifyAboutPostZoomOut( ArrayList listeners) { for (MapEventsListener listener : listeners) { if (listener != null) { try { listener.onPostZoomOut(); } catch (Exception e) { Log.e(TAG, "Exception " + e + " on didZoomOut"); } } else { Log.w(TAG, "DidZoomOut: Map Events listener is null"); } } } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); if (tileProvider != null) { tileProvider.startProcessingCommands(); } else { Log.e(TAG, "Tile manager is not initialized"); } } @Override protected void onDetachedFromWindow() { if (zoomBtnsController != null) { zoomBtnsController.setVisible(false); } if (tileProvider != null) { tileProvider.stopProcessingCommands(); } if (locationProvider != null) { locationProvider.stop(); } super.onDetachedFromWindow(); } @Override public void computeScroll() { if (scroller.computeScrollOffset()) { scrollTo(scroller.getCurrX(), scroller.getCurrY()); invalidate(); } super.computeScroll(); } @Override public void onMovePinTo(Location location) { PositionMarker pin = (PositionMarker) topmostLayer .getMapObject(POS_PIN_ID); if (pin != null) { pin.setAccuracy(location.getAccuracy()); pin.setBearing(location.getBearing()); pin.setBearingEnabled(location.hasBearing()); pin.moveTo(location); } notifyAboutLocationChanged(location); } private void notifyAboutLocationChanged(Location location) { if (locationChangeListener != null) { try { locationChangeListener.onLocationChanged(this, location); } catch (Exception e) { Log.w(TAG, "Exception while executing onLocationChanged. " + e); } } } @Override public void onChangePinVisibility(boolean visible) { topmostLayer.setVisible(visible); } /** * Scrolls the map to current location marker without animation. Location * marker should be visible in order for this method to work. */ public void jumpToCurrentLocation() { if (!topmostLayer.isVisible()) { Log.i(TAG, "Location marker is not visible. Jump to current location skipped"); return; } PositionMarker pin = (PositionMarker) topmostLayer .getMapObject(POS_PIN_ID); Point tempPoint = (pin.getPosition()); jumpTo(tempPoint); } /** * Scrolls the map to current location marker using scroll animation. * Location marker should be visible in order for this method to work. */ public void scrollToCurrentLocation() { if (!topmostLayer.isVisible()) { Log.i(TAG, "Location pin is not visible. Scroll to current location skipped"); return; } PositionMarker pin = (PositionMarker) topmostLayer .getMapObject(POS_PIN_ID); Point tempPoint = (pin.getPosition()); scrollMapTo(tempPoint); } /** * Scrolls the map to specific location without animation. * * @param location * - instance of {@link android.location.Location} object. * @throws IllegalStateException * if map was not calibrated. For more details see * MapWidget.setShowMyPosition(). */ public void jumpTo(Location location) { if (config == null) { Log.w(TAG, "Jump to skipped. Map is not initialized properly."); return; } if (!config.getGpsConfig().isMapCalibrated()) { throw new IllegalStateException("Map is not calibrated."); } Point point = new Point(); getGpsConfig().getCalibration().translate(location, point); point.set((int) (point.x * getScale()), (int) (point.y * getScale())); jumpTo(point); } /** * Scrolls the map to specific location. * * @param location * - instance of Point. The coordinates of the point should be * set in pixels in map coordinate system. */ public void jumpTo(Point location) { jumpTo(location.x, location.y); doCorrectPosition(false, false); } /** * Scrolls map to specific location * * @param x * - x coordinate in map coordinate system. * @param y * - y coordinate in map coordinate system. */ public void jumpTo(int x, int y) { int width = getWidth(); int height = getHeight(); scrollTo((int) (x * getScale() - width / 2), (int) (y * getScale() - height / 2)); doCorrectPosition(false, false); } /** * Scrolls the map to specific location using scroll animation. * * @param location * - instance of {@link android.location.Location}. * @throws IllegalStateException * if map is not calibrated. For more details see * MapWidget.setShowMyPosition(). */ public void scrollMapTo(Location location) { if (config == null) { Log.w(TAG, "Jump to skipped. Map is not initialized properly."); return; } if (!config.getGpsConfig().isMapCalibrated()) { throw new IllegalStateException("Map is not calibrated."); } Point point = new Point(); getGpsConfig().getCalibration().translate(location, point); scrollMapTo(point.x, point.y); } /** * Scrolls the map to specific location using scroll animation. * * @param location * - instance of {@link android.graphics.Point}. Coordinates of * the point should be set in pixels in map coordinates. */ public void scrollMapTo(Point location) { scrollMapTo(location.x, location.y); } /** * Scrolls the map to specific location using scroll animation. * * @param x * - x coordinate of the point in map coordinates. * @param y * - y coordinate of the point in map coordinates. */ public void scrollMapTo(int x, int y) { if (!isAnimationEnabled) { jumpTo(x, y); return; } int viewWidth = getWidth(); int viewHeight = getHeight(); if (isLayoutRequested()) return; int mapHeight = getMapHeight(); int mapWidth = getMapWidth(); float mapScale = getScale(); int scrollX = getScrollX(); int scrollY = getScrollY(); int newX = (int) (x * mapScale) - viewWidth / 2; int newY = (int) (y * mapScale) - viewHeight / 2; if (viewHeight < mapHeight && newY + viewHeight > mapHeight) { newY -= (newY + viewHeight - mapHeight); } if (mapWidth > viewWidth && newX + viewWidth > mapWidth) { newX -= (newX + viewWidth - mapWidth); } if (newX < 0) { newX = 0; } if (newY < 0) { newY = 0; } if (viewHeight > mapHeight) newY = scrollY; if (viewWidth > mapWidth) { newX = scrollX; } scroller.abortAnimation(); scroller.startScroll(scrollX, scrollY, newX - scrollX, newY - scrollY, 500); invalidate(); } static MapTouchedEvent mapTouchedEvent = new MapTouchedEvent(); private class MyGestureDetector extends SimpleOnGestureListener { @Override public boolean onDoubleTap(MotionEvent e) { boolean result = false; if (onDoubleTapListener != null) { updateMapTouchedEvent(e); result = onDoubleTapListener.onDoubleTap(MapWidget.this, mapTouchedEvent); } if (result == false) { float pivotX = e.getX(); float pivotY = e.getY(); zoomIn((int) pivotX, (int) pivotY); result = true; } return result; } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { if (config == null) { Log.w(TAG, "Jump to skipped. Map is not initialized properly."); return false; } if (!config.isFlingEnabled() || isZooming) { return false; } int speed = 800; if (Math.abs(velocityX) > speed) { if (velocityX > 0) { velocityX = speed; } else { velocityX = -speed; } } if (Math.abs(velocityY) > speed) { if (velocityY > 0) { velocityY = speed; } else { velocityY = -speed; } } int minX = 0; int minY = 0; int maxX = 0; int maxY = 0; if (config.isMapCenteringEnabled()) { minX = (getWidth() - getMapWidth()) / 2; minY = (getHeight() - getMapHeight()) / 2; maxX = (int) getMapWidth() - getWidth(); maxY = (int) getMapHeight() - getHeight(); if (minX < 0) { minX = 0; } if (minY < 0) { minY = 0; } minX *= -1; minY *= -1; } else { minX = -getMapWidth(); minY = -getMapHeight(); maxX = getMapWidth(); maxY = getMapHeight(); if (minX > -getWidth()) { minX = -getWidth(); } if (minY > -getHeight()) { minY = -getHeight(); } if (maxY < getHeight()) { maxY = getHeight(); } if (maxX < getWidth()) { maxX = getWidth(); } } scroller.fling(getScrollX(), getScrollY(), -(int) (velocityX), -(int) (velocityY), minX, // MinX maxX, // MaxX minY, // MinY maxY); // MaxY invalidate(); return true; } @Override public boolean onDown(MotionEvent e) { if (!scroller.isFinished()) { // is flinging scroller.forceFinished(true); // to stop flinging on touch } return true; // else won't work } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { if (mode != Mode.NONE || isZooming) return false; scrollBy((int) distanceX, (int) distanceY); // invalidate(); return true; } public boolean onSingleTapConfirmed(MotionEvent event) { if (mapTouchListener != null) { { updateMapTouchedEvent(event); if (debugEnabled) { lastTouchedRect = new RectF( translateXToMapCoordinate(event.getX()), translateYToMapCoordinate(event.getY()), translateXToMapCoordinate(event.getX()) + 10, translateYToMapCoordinate(event.getY() + 10)); } mapTouchListener.onTouch(MapWidget.this, mapTouchedEvent); } } return false; } @Override public void onLongPress(MotionEvent e) { if (longClickListener != null) { longClickListener.onLongClick(MapWidget.this); } if (mapLongClicklistener != null) { updateMapTouchedEvent(e); mapLongClicklistener.onLongClick(MapWidget.this, mapTouchedEvent); } }; } /** * Saves mapWidget internal state, that can be restored from onCreate(); * * @param bundle */ public void saveState(Bundle bundle) { float currPosOnMapX = translateXToMapCoordinate(getWidth() / 2.0f); float currPosOnMapY = translateYToMapCoordinate(getHeight() / 2.0f); bundle.putFloat("com.ls.curPosOnMapX", currPosOnMapX); bundle.putFloat("com.ls.curPosOnMapY", currPosOnMapY); if (grid != null) { bundle.putInt("com.ls.zoomLevel", grid.getZoomLevel()); } bundle.putFloat("com.ls.scale", scale); Log.d("MapWidget", "Saved point pos: [" + currPosOnMapX + ", " + currPosOnMapY + " ]"); } /** * Centers the map. */ public void centerMap() { int width = getWidth(); int height = getHeight(); if (width == 0 || height == 0) { requestCenterMap = true; } else { doCorrectPosition(true, isAnimationEnabled); jumpTo(getOriginalMapWidth() / 2, getOriginalMapHeight() / 2); } } private void updateMapTouchedEvent(MotionEvent event) { ArrayList touchedElementIds = getTouchedElementIds( (int) event.getX() + getScrollX(), (int) event.getY() + getScrollY()); mapTouchedEvent.setScreenX((int) event.getX()); mapTouchedEvent.setScreenY((int) event.getY()); mapTouchedEvent .setMapX((int) translateXToMapCoordinate(event .getX())); // X coordinate in map's // coordinates mapTouchedEvent .setMapY((int) translateYToMapCoordinate(event .getY())); // Y coordinate in map's // coordinates mapTouchedEvent.setTouchedObjectEvents(touchedElementIds); } } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/commands/GetTileTask.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.commands; import java.io.IOException; import java.io.InputStream; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.BitmapFactory.Options; import android.graphics.Color; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.TransitionDrawable; import android.os.AsyncTask; public class GetTileTask extends AsyncTask { private InputStream is; private Drawable result; private static final Drawable transparent = new ColorDrawable(Color.TRANSPARENT); public GetTileTask(InputStream is) { this.is = is; } public Drawable getResult() { return result; } public void closeStream() throws IOException { is.close(); } @Override protected Boolean doInBackground(Integer... params) { BitmapDrawable tileDrawable = null; try { Bitmap bitmap = BitmapFactory.decodeStream(is); if (bitmap != null) { tileDrawable = (BitmapDrawable) new BitmapDrawable(bitmap); result = new TransitionDrawable(new Drawable[]{transparent, tileDrawable}); result.setBounds(0, 0, tileDrawable.getBitmap().getWidth(), tileDrawable.getBitmap().getHeight()); return Boolean.TRUE; } else { return Boolean.FALSE; } } catch (OutOfMemoryError e) { return Boolean.FALSE; } } } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/commands/MapCommand.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.commands; import com.ls.widgets.map.config.OfflineMapConfig; public abstract class MapCommand implements Runnable { private OfflineMapConfig config; private MapCommandDelegate delegate; public MapCommand(OfflineMapConfig config, MapCommandDelegate delegate) { this.config = config; this.delegate = delegate; } public OfflineMapConfig getConfig() { return config; } public void onSuccess(Object data) { if (delegate != null) delegate.onSuccess(data); } public void onError(Exception e) { e.printStackTrace(); if (delegate != null) delegate.onError(e); } } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/commands/MapCommandDelegate.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.commands; public interface MapCommandDelegate { public void onSuccess(Object data); public void onError(Exception e); } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/config/GPSConfig.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.config; import com.ls.widgets.map.utils.MapCalibrationData; /** * GPSConfig class allows to configure the location aware aspects of the MapWidget. * Do not create this class directly. Instead use MapWidget.getGPSConfig() method. */ public class GPSConfig { private final static int DEFAULT_MIN_TIME = 1000; private final static int DEFAULT_MIN_DISTANCE = 10; private boolean passiveMode; private int minTime; private int minDistance; private MapCalibrationData calibrationData; public GPSConfig() { passiveMode = false; minTime = DEFAULT_MIN_TIME; minDistance = DEFAULT_MIN_DISTANCE; } public boolean getPassiveMode() { return passiveMode; } /** * Tells the map to not use GPS by itself, it will use "passive" location provider in order to display user's position. * You will need to call this method before call to setShowMyPosition. * @param passiveMode - true if you want the map to work in passive mode, false otherwise. */ public void setPassiveMode(boolean passiveMode) { this.passiveMode = passiveMode; } /** * Sets the calibration data for the map. This calibration data contains the top left and bottom right coordinate of the corners of the map. * @param geoArea - instance of RectGeo class. */ public void setGeoArea(MapCalibrationData geoArea) { this.calibrationData = geoArea; } /** * Sets the GPS sensor update time interval and distance. * @param minTime the minimum time interval for notifications, in milliseconds. This field is only used as a hint to conserve power, and actual time between location updates may be greater or lesser than this value. * @param minDistance the minimum distance interval for notifications, in meters */ public void setGPSUpdateInterval(int minTime, int minDistance) { this.minTime = minTime; this.minDistance = minDistance; } /** * Returns minimal refresh time in milliseconds. */ public int getMinTime() { return minTime; } /** * @return Returns minimal refresh distance in meters. Min distance is a distance that user should pass * in order to receive location update. */ public int getMinDistance() { return minDistance; } /** * Returns calibration data for the map. * @return instance of MapCalibratinData. */ public MapCalibrationData getCalibration() { return calibrationData; } /** * @return Returns true if config contains calibration data, false otherwise. */ public boolean isMapCalibrated() { return calibrationData != null; } } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/config/MapConfigParser.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.config; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.FactoryConfigurationError; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import android.content.Context; import android.graphics.Point; import android.graphics.Rect; import android.location.Location; import android.os.Debug; import android.util.DebugUtils; import android.util.Log; import android.util.Pair; import com.ls.widgets.map.utils.MapCalibrationData; public class MapConfigParser { private int imageWidth; private int imageHeight; private int tileSize; private int overlap; private String imageFormat; private String rootMapFolder; private MapCalibrationData geoArea; public MapConfigParser(String mapRoot) { this.rootMapFolder = mapRoot; } public OfflineMapConfig parse(Context context, File configFile) throws IOException, SAXException { InputStream is = null; try { if (configFile.exists()) { is = new FileInputStream(configFile); return parse(is); } else { Log.e("MapConfigParser", "Map config file not found."); return null; } } catch (ParserConfigurationException e) { Log.e("MapConfigParser", "Exception: " + e); e.printStackTrace(); } catch (FactoryConfigurationError e) { Log.e("MapConfigParser", "Exception: " + e); e.printStackTrace(); } finally { if (is != null) { is.close(); } } return null; } public OfflineMapConfig parse(Context context, String configPath) throws IOException, SAXException { InputStream is = null; try { is = context.getAssets().open(configPath); return parse(is); } catch (ParserConfigurationException e) { Log.e("MapConfigParser", "Exception: " + e); e.printStackTrace(); } catch (FactoryConfigurationError e) { Log.e("MapConfigParser", "Exception: " + e); e.printStackTrace(); } finally { if (is != null) { is.close(); } } return null; } private OfflineMapConfig parse(InputStream is) throws ParserConfigurationException, FactoryConfigurationError, SAXException, IOException { DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); Document doc = docBuilder.parse(is); NodeList images = doc.getElementsByTagName("Image"); Node image = images.item(0); NamedNodeMap attributes = image.getAttributes(); Node tileSizeNode = attributes.getNamedItem("TileSize"); Node overlapNode = attributes.getNamedItem("Overlap"); Node formatNode = attributes.getNamedItem("Format"); Node sizeNode = null; Node geoAreaNode = null; NodeList childNodes = image.getChildNodes(); for (int i=0; i topLeft = null; Pair bottomRight = null; for (int i=0; i(new Point(), new Location("config")); topLeft.first.set(x, y); topLeft.second.setLatitude(lat); topLeft.second.setLongitude(lon); } else { // bottom right point bottomRight = new Pair(new Point(), new Location("config")); bottomRight.first.set(x, y); bottomRight.second.setLatitude(lat); bottomRight.second.setLongitude(lon); } } } } if (topLeft != null && bottomRight != null) { geoArea = new MapCalibrationData(topLeft, bottomRight); } else { Log.w("MapConfigParser", "No geo area is set"); } } else { Log.w("MapConfigParser", "GeoArea is not configured."); } if (tileSizeNode != null) { tileSize = Integer.parseInt(tileSizeNode.getNodeValue()); } if (overlapNode != null) { overlap = Integer.parseInt(overlapNode.getNodeValue()); } if (formatNode != null) { imageFormat = formatNode.getNodeValue(); } if (widthNode != null) { imageWidth = Integer.parseInt(widthNode.getNodeValue()); } if (heightNode != null) { imageHeight = Integer.parseInt(heightNode.getNodeValue()); } OfflineMapConfig config = new OfflineMapConfig(rootMapFolder, imageWidth, imageHeight, tileSize, overlap, imageFormat); config.getGpsConfig().setGeoArea(geoArea); return config; } } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/config/MapGraphicsConfig.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.config; /** * Allows to configure the appearance of the map aspects. */ public class MapGraphicsConfig { public static final int DEFAULT_ACCURACY_AREA_COLOR = 0x331767e9; public static final int DEFAULT_ACCURACY_AREA_BORDER_COLOR = 0xFF1767e9; private int dotPointerDrawableId; private int arrowPointerDrawableId; private int accuracyAreaColor; private int accuracyAreaBorderColor; public MapGraphicsConfig() { dotPointerDrawableId = -1; arrowPointerDrawableId = -1; accuracyAreaColor = DEFAULT_ACCURACY_AREA_COLOR; accuracyAreaBorderColor = DEFAULT_ACCURACY_AREA_BORDER_COLOR; } public int getDotPointerDrawableId() { return dotPointerDrawableId; } /** * Configures the location pointer look when no bearing is available. * @param dotPointerDrawableId - id of the drawable resource. */ public void setDotPointerDrawableId(int dotPointerDrawableId) { this.dotPointerDrawableId = dotPointerDrawableId; } public int getArrowPointerDrawableId() { return arrowPointerDrawableId; } /** * Configures the location pointer look when bearing is available. * @param dotPointerDrawableId - id of the drawable resource. */ public void setArrowPointerDrawableId(int arrowPointerDrawableId) { this.arrowPointerDrawableId = arrowPointerDrawableId; } public int getAccuracyAreaColor() { return accuracyAreaColor; } /** * Configures the accuracy area color of location pointer. Use this template to set the color: 0xAARRGGBB. * AA - alpha channel, RR - red component, GG - green component, BB - blue component. * @param accuracyAreaColor - color. */ public void setAccuracyAreaColor(int accuracyAreaColor) { this.accuracyAreaColor = accuracyAreaColor; } public int getAccuracyAreaBorderColor() { return accuracyAreaBorderColor; } /** * Configures the accuracy area border color. * @param accuracyAreaBorderColor - color. */ public void setAccuracyAreaBorderColor(int accuracyAreaBorderColor) { this.accuracyAreaBorderColor = accuracyAreaBorderColor; } } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/config/OfflineMap.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.config; public class OfflineMap { public static final String MAP_ROOT = "map"; } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/config/OfflineMapConfig.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.config; import android.graphics.Rect; public class OfflineMapConfig { private String rootMapFolder; private int imageWidth; private int imageHeight; private Rect imageBounds; private int tileSize; private int overlap; private String imageFormat; private boolean flingEnabled; private boolean mapCenteringEnabled; private boolean pinchZoomEnabled; private boolean zoomBtnsVisible; private int trackballScrollStepX; private int trackballScrollStepY; private int minZoomLevelLimit; private int maxZoomLevelLimit; private int touchAreaSize; private boolean softwareZoomEnabled; private GPSConfig gpsConfig; private MapGraphicsConfig mapGraphics; public OfflineMapConfig(String rootMapFolder, int imageWidth, int imageHeight, int tileSize, int overlap, String imageFormat) { // Default values this.trackballScrollStepX = this.trackballScrollStepY = 64; this.minZoomLevelLimit = 0; this.maxZoomLevelLimit = 0; this.touchAreaSize = 5; this.softwareZoomEnabled = true; this.flingEnabled = false; this.zoomBtnsVisible = true; this.mapCenteringEnabled = true; // Other values this.rootMapFolder = rootMapFolder; this.imageFormat = imageFormat; this.imageWidth = imageWidth; this.imageHeight = imageHeight; this.imageBounds = new Rect(0,0, imageWidth, imageHeight); this.tileSize = tileSize; this.overlap = overlap; gpsConfig = new GPSConfig(); mapGraphics = new MapGraphicsConfig(); } public OfflineMapConfig(OfflineMapConfig config) { this.rootMapFolder = config.rootMapFolder; this.imageWidth = config.imageWidth; this.imageHeight = config.imageHeight; this.tileSize = config.tileSize; this.overlap = config.overlap; this.imageFormat = config.imageFormat; this.flingEnabled = config.flingEnabled; this.mapCenteringEnabled = config.mapCenteringEnabled; this.pinchZoomEnabled = config.pinchZoomEnabled; this.zoomBtnsVisible = config.zoomBtnsVisible; this.trackballScrollStepX = config.trackballScrollStepX; this.trackballScrollStepY = config.trackballScrollStepY; this.minZoomLevelLimit = config.minZoomLevelLimit; this.maxZoomLevelLimit = config.maxZoomLevelLimit; this.softwareZoomEnabled = config.softwareZoomEnabled; this.touchAreaSize = config.touchAreaSize; } /** * Returns original map image width in pixels * @return */ public int getImageWidth() { return imageWidth; } /** * Returns original map image height in pixels * @return */ public int getImageHeight() { return imageHeight; } public Rect getImageRect() { return imageBounds; } /** * Returns size of a tile in pixels. * @return */ public int getTileSize() { return tileSize; } public int getOverlap() { return overlap; } public String getImageFormat() { return imageFormat; } public boolean isFlingEnabled() { return flingEnabled; } /** * Controls inertial scrolling. * @param flingEnabled true to enable fling, false otherwise. */ public void setFlingEnabled(boolean flingEnabled) { this.flingEnabled = flingEnabled; } public boolean isMapCenteringEnabled() { return this.mapCenteringEnabled; } /** * Controls the ability to center the map. * @param enabled - if set to true map will center itself if it is smaller than screen. */ public void setMapCenteringEnabled(boolean enabled) { this.mapCenteringEnabled = enabled; } public boolean isPinchZoomEnabled() { return pinchZoomEnabled; } /** * Controls pinch zoom gesture. * @param pinchZoomEnabled - true to enable pinch zoom gesture, false otherwise. */ public void setPinchZoomEnabled(boolean pinchZoomEnabled) { this.pinchZoomEnabled = pinchZoomEnabled; } public boolean isZoomBtnsVisible() { return zoomBtnsVisible; } /** * Controls standard zoom buttons visibility. * @param zoomBtnsVisible - true to make standard zoom buttons visible, false otherwise. */ public void setZoomBtnsVisible(boolean zoomBtnsVisible) { this.zoomBtnsVisible = zoomBtnsVisible; } public int getTrackballScrollStepX() { return trackballScrollStepX; } /** * Set's track ball scroll step by X axis. * @param trackballScrollStepX scroll step in pixels. * @throws IllegalArgumentException if trackballScrollStepX < 0 */ public void setTrackballScrollStepX(int trackballScrollStepX) { if (trackballScrollStepX < 0) { throw new IllegalArgumentException(); } this.trackballScrollStepX = trackballScrollStepX; } public int getTrackballScrollStepY() { return trackballScrollStepY; } /** * Set's track ball scroll step by Y axis. * @param trackballScrollStepY - scroll step in pixels. * @throws IllegalArgumentException if trackballScrollStepY < 0 */ public void setTrackballScrollStepY(int trackballScrollStepY) { if (trackballScrollStepY < 0) { throw new IllegalArgumentException(); } this.trackballScrollStepY = trackballScrollStepY; } public int getMinZoomLevelLimit() { return minZoomLevelLimit; } /** * Sets minimal zoom level the user can zoom out to. * @param minZoomLevelLimit - represents zoom level number. * @throws IllegalArgumentException if minZoomLevelLimit < 0 */ public void setMinZoomLevelLimit(int minZoomLevelLimit) { if (minZoomLevelLimit < 0) { throw new IllegalArgumentException(); } this.minZoomLevelLimit = minZoomLevelLimit; } public int getMaxZoomLevelLimit() { return maxZoomLevelLimit; } /** * Sets max zoom level the user can zoom in to. * @param maxZoomLevelLimit - zoom level number. * @throws IllegalArgumentException if maxZoomLevelLimit < 0 */ public void setMaxZoomLevelLimit(int maxZoomLevelLimit) { if (maxZoomLevelLimit < 0) { throw new IllegalArgumentException(); } this.maxZoomLevelLimit = maxZoomLevelLimit; } public String getMapRootPath() { return rootMapFolder; } public boolean isSoftwareZoomEnabled() { return softwareZoomEnabled; } /** * Controls the ability to use software zoom if there is no zoom levels left during zooming in. * @param softwareZoomEnabled - Set true if you want to use software zoom, false otherwise. */ public void setSoftwareZoomEnabled(boolean softwareZoomEnabled) { this.softwareZoomEnabled = softwareZoomEnabled; } public int getTouchAreaSize() { return touchAreaSize; } /** * Sets touch area size * @param touchAreaSize - area size in pixels. Used when detecting objects that were hit by the user with a finger. * @throws IllegalArgumentException when touchAreaSize <= 0 */ public void setTouchAreaSize(int touchAreaSize) { if (touchAreaSize <= 0) throw new IllegalArgumentException(); this.touchAreaSize = touchAreaSize; } /** * You can use {@link GPSConfig} in order to control GPS sensor settings. * Please note, that you need to configure the GPS sensor before calling {@code MapWidget.setShowMyPosition(true);} * @return instance of {@link GPSConfig} class. */ public GPSConfig getGpsConfig() { return gpsConfig; } /** * You can use MapGraphicsConfig in order to configure the look of the position marker. * @return instance of {@link MapGraphicsConfig} */ public MapGraphicsConfig getGraphicsConfig() { return mapGraphics; } } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/events/MapScrolledEvent.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.events; /** * Represents scroll event. Event will be fired when map is scrolled. */ public class MapScrolledEvent { private int dx; private int dy; boolean byUser; public MapScrolledEvent(int originX, int originY) { this.dx = originX; this.dy = originY; } public void setData(int dx, int dy, boolean byUser) { this.dx = dx; this.dy = dy; this.byUser = byUser; } /** * @return * Returns the number of pixels the map was scrolled from it's last position by X axis. */ public int getDX() { return dx; } /** * @return * Returns the number of pixels the map was scrolled from it's last position by Y axis. */ public int getDY() { return dy; } /** * @returns Returns true if map scroll was caused by the user, false otherwise. */ public boolean isByUser() { return byUser; } } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/events/MapTouchedEvent.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.events; import java.util.ArrayList; /** *

Class Overview

* MapTouchedEvent represents the touch event that occurs on the map widget. * It contains the position of the touch in map coordinates, screen coordinates and a list of object touch events. */ public class MapTouchedEvent { private int screenX; private int screenY; private int mapX; private int mapY; private ArrayList touchedObjectIds; /** * Returns the X coordinate of a point where user has touched in screen coordinates. * It means that you can use this value to display something on the screen without coordinate transformation. * @return X coordinate of a point in screen coordinates. */ public int getScreenX() { return screenX; } public void setScreenX(int screenX) { this.screenX = screenX; } /** * Returns the Y coordinate of a point where user has touched in screen coordinates. * It means that you can use this value to display something on the screen without coordinate transformation. * @return Y coordinate of a point in screen coordinates. */ public int getScreenY() { return screenY; } public void setScreenY(int screenY) { this.screenY = screenY; } /** * Returns the X coordinate of a point where user has touched in your original image coordinates. * @return X coordinate of a point in map coordinates. */ public int getMapX() { return mapX; } public void setMapX(int mapX) { this.mapX = mapX; } /** * Returns the Y coordinate of a point where user has touched in your original image coordinates. * @return Y coordinate of a point in map coordinates. */ public int getMapY() { return mapY; } public void setMapY(int mapY) { this.mapY = mapY; } /** * @return ArrayList of ObjectTouchEvent objects. * @deprecated Use getTouchedObjectEvents() instead. */ public ArrayList getTouchedObjectIds() { return touchedObjectIds; } /** * Returns the list of ObjectTouchEvent objects. If user has touched the map * where no map objects are located - this will be an empty list. If user * touched on map object - array will contain ObjectTouchEvent for each * touched object. * * @return ArrayList of ObjectTouchEvent objects. */ public ArrayList getTouchedObjectEvents() { return touchedObjectIds; } public void setTouchedObjectEvents(ArrayList touchedObjectIds) { this.touchedObjectIds = touchedObjectIds; } } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/events/ObjectTouchEvent.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.events; /** * Object touch event occurs when user touches the map object. */ public class ObjectTouchEvent { private Object objectId; private long layerId; public ObjectTouchEvent(Object id, long layerId) { this.objectId = id; this.layerId = layerId; } /** * @return * Returns the ID of the map object. */ public Object getObjectId() { return objectId; } public void setObjectId(Object objectId) { this.objectId = objectId; } /** * @return * Returns the ID of the layer where map object is located. */ public long getLayerId() { return layerId; } public void setLayerId(int layerId) { this.layerId = layerId; } } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/interfaces/Layer.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.interfaces; import com.ls.widgets.map.model.MapObject; public interface Layer { /** * Adds map object to the layer. * @param mapObject - map object. */ public void addMapObject(MapObject mapObject); /** * Removes map object from the layer. * @param id - id of the map object. */ public void removeMapObject(Object id); /** * Returns map object. * @param id - id of the map object. */ public MapObject getMapObject(Object id); /** * Returns map object by index * @param index * @return instance of MapObject */ public MapObject getMapObjectByIndex(int index); /** * Returns the count of map objects on the layer * @return number of map objects */ public int getMapObjectCount(); /** * Removes all map objects from the layer. */ public void clearAll(); /** * Shows whether the layer is visible or not. * @return - true if layer is visible, false otherwise. */ public boolean isVisible(); /** * Sets layer visibility. * @param visible - true if layer should be visible, false otherwise. */ public void setVisible(boolean visible); } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/interfaces/MapEventsListener.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.interfaces; public interface MapEventsListener { /** * Is called before zoom in. */ public void onPreZoomIn(); /** * Is called when zoom in finished. */ public void onPostZoomIn(); /** * Is called before zoom out. */ public void onPreZoomOut(); /** * Is called when zoom out is finished. */ public void onPostZoomOut(); } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/interfaces/MapLocationListener.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.interfaces; import android.location.Location; public interface MapLocationListener { public void onMovePinTo(Location location); public void onChangePinVisibility(boolean visible); } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/interfaces/OnGridReadyListener.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.interfaces; public interface OnGridReadyListener { public void onReady(); } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/interfaces/OnLocationChangedListener.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.interfaces; import com.ls.widgets.map.MapWidget; import android.location.Location; public interface OnLocationChangedListener { public void onLocationChanged(MapWidget v, Location location); } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/interfaces/OnMapDoubleTapListener.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.interfaces; import com.ls.widgets.map.MapWidget; import com.ls.widgets.map.events.MapTouchedEvent; /** *

Interface overview

* Used for receiving notifications when MapWidget view is double tapped. */ public interface OnMapDoubleTapListener { /** * Will be called if user double taps the map widget. * @param v - map widget that was double tapped. * @param event - instance of {@link MapTouchedEvent}. * @return true - if you want to intercept this event and provide your own implementation.
* false - if you want to leave the default map widget behavior. */ public boolean onDoubleTap(MapWidget v, MapTouchedEvent event); } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/interfaces/OnMapLongClickListener.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.interfaces; import com.ls.widgets.map.MapWidget; import com.ls.widgets.map.events.MapTouchedEvent; public interface OnMapLongClickListener { public boolean onLongClick(MapWidget v, MapTouchedEvent e); } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/interfaces/OnMapScrollListener.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.interfaces; import com.ls.widgets.map.MapWidget; import com.ls.widgets.map.events.MapScrolledEvent; public interface OnMapScrollListener { public void onScrolledEvent(MapWidget v, MapScrolledEvent event); } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/interfaces/OnMapTilesFinishedLoadingListener.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.interfaces; public interface OnMapTilesFinishedLoadingListener { void onMapTilesFinishedLoading(); } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/interfaces/OnMapTouchListener.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.interfaces; import com.ls.widgets.map.MapWidget; import com.ls.widgets.map.events.MapTouchedEvent; public interface OnMapTouchListener { public void onTouch(MapWidget v, MapTouchedEvent event); } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/interfaces/TileManagerDelegate.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.interfaces; import android.graphics.drawable.Drawable; public interface TileManagerDelegate { public void onTileReady(int zoomLevel, int col, int row, Drawable drawable); public void onError(Exception e); } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/location/PositionMarker.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.location; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.shapes.OvalShape; import android.graphics.drawable.shapes.Shape; import com.ls.widgets.map.MapWidget; import com.ls.widgets.map.config.GPSConfig; import com.ls.widgets.map.model.MapObject; import com.ls.widgets.map.utils.MapCalibrationData; import com.ls.widgets.map.utils.PivotFactory; import com.ls.widgets.map.utils.PivotFactory.PivotPosition; public class PositionMarker extends MapObject { private float accuracy; private float bearing; private boolean hasBearing; private float accuracyRadius; private float pixelsInMeter; private double centerX; private double centerY; private ShapeDrawable accuracyDrawable; private Paint accuracyBorderPaint; private Drawable arrowPointerDrawable; private Drawable roundPointerDrawable; private MapWidget context; private Point roundPointerPivotPoint; private Point arrowPointerPivotPoint; public PositionMarker(MapWidget context, Object id, Drawable roundPointerDrawable, Drawable arrowPointerDrawable) { super(id, null, new Point(0, 0), false, false); this.context = context; accuracy = 500; pixelsInMeter = 0; hasBearing = false; arrowPointerPivotPoint = null; roundPointerPivotPoint= null; this.context = context; Shape accuracyShape = new OvalShape(); accuracyShape.resize(getAccuracyDiameter(), getAccuracyDiameter()); accuracyDrawable = new ShapeDrawable(accuracyShape); this.roundPointerDrawable = roundPointerDrawable; this.arrowPointerDrawable = arrowPointerDrawable; Paint accuracyAreaPaint = accuracyDrawable.getPaint(); accuracyAreaPaint.setStyle(Style.FILL); accuracyBorderPaint = new Paint(Paint.ANTI_ALIAS_FLAG); accuracyBorderPaint.setStyle(Style.STROKE); setDrawable(roundPointerDrawable); } private float calculatePixelsInMeter(MapWidget context) { if (context == null) return 0; GPSConfig config = context.getGpsConfig(); if (config != null && config.isMapCalibrated()) { MapCalibrationData geoArea = config.getCalibration(); return context.getConfig().getImageWidth() / geoArea.getWidthInMeters(); } return 0; } /** * Sets the color of accuracy area and border. * @param color - Color */ public void setColor(int area, int border) { Paint accuracyAreaPaint = accuracyDrawable.getPaint(); accuracyAreaPaint.setColor(area); accuracyBorderPaint.setColor(border); } /** * Sets the size of accuracy area. * @param accuracy - accuracy in meters. You can get this value from android.location.Location * @see android.location.Location */ public void setAccuracy(float accuracy) { invalidateSelf(); this.accuracy = accuracy; recalculateBounds(); invalidateSelf(); } /** * Rotates the position pointer by some degree. In order for this method to take effect you should call * GpsPositionMarker.setBearingEnabled(true) * * @param bearing - GPS bearing angle value in degrees. You can get it from {@link android.location.Location} object. */ public void setBearing(float bearing) { invalidateSelf(); this.bearing = bearing; invalidateSelf(); } public void setDotPointer(Drawable dotPointer, Point pivotPoint) { this.roundPointerDrawable = dotPointer; this.roundPointerPivotPoint = pivotPoint; } public void setArrowPointer(Drawable arrowPointer, Point pivotPoint) { this.arrowPointerDrawable = arrowPointer; this.arrowPointerPivotPoint = pivotPoint; } /** * Enables show direction mode. * @param hasBearing - true to show movement direction of false otherwise. */ public void setBearingEnabled(boolean hasBearing) { this.hasBearing = hasBearing; if (hasBearing) { setDrawable(arrowPointerDrawable); if (arrowPointerPivotPoint == null) { setPivotPoint(PivotFactory.createPivotPoint(arrowPointerDrawable, PivotPosition.PIVOT_CENTER)); } else { setPivotPoint(arrowPointerPivotPoint); } } else { setDrawable(roundPointerDrawable); if (roundPointerPivotPoint == null) { setPivotPoint(PivotFactory.createPivotPoint(roundPointerDrawable, PivotPosition.PIVOT_CENTER)); } else { setPivotPoint(roundPointerPivotPoint); } } recalculateBounds(); } @Override protected void recalculateBounds() { super.recalculateBounds(); float r = getAccuracyDiameter() / 2.0f; if (accuracyDrawable != null) { Rect bounds = super.getBounds(); float diameter = getAccuracyDiameter(); accuracyRadius = diameter / 2.0f; accuracyDrawable.getShape().resize(diameter, diameter); centerX = posScaled.x + bounds.width() / 2.0 - pivotPoint.x; centerY = posScaled.y + bounds.height() / 2.0 - pivotPoint.y; accuracyDrawable.setBounds((int) (centerX - r), (int) (centerY - r), (int) (centerX + r), (int) (centerY + r)); } } @Override public Rect getBounds() { Rect bounds = super.getBounds(); if ((accuracyRadius * 2.0) >= bounds.width()) { return accuracyDrawable.getBounds(); } if (hasBearing) { Rect newBounds = new Rect(bounds); int width = newBounds.width(); int height = bounds.height(); if ( width > height) { newBounds.bottom = newBounds.top + width; return newBounds; } else if (width > height) { newBounds.right = newBounds.left + height; return newBounds; } } return bounds; } @Override public void draw(Canvas canvas) { if (accuracy > 50.0f) { accuracyDrawable.draw(canvas); canvas.drawCircle((float) centerX, (float) centerY, accuracyRadius - 1, accuracyBorderPaint); } if (!hasBearing) { super.draw(canvas); } else { // Rotate the drawable canvas.save(Canvas.MATRIX_SAVE_FLAG); canvas.rotate(bearing, (float) centerX, (float) centerY); super.draw(canvas); canvas.restore(); } } private float getAccuracyDiameter() { if (pixelsInMeter == 0) { pixelsInMeter = calculatePixelsInMeter(context); } return (accuracy) * pixelsInMeter * scale * 2; } } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/model/Cell.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.model; import java.lang.ref.SoftReference; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable.Callback; import android.graphics.drawable.TransitionDrawable; import android.util.Log; import android.view.View; import com.ls.widgets.map.interfaces.TileManagerDelegate; import com.ls.widgets.map.providers.TileProvider; public class Cell implements Callback, TileManagerDelegate { // Model private int zoomLevel; private float width; private float height; private int col; private int row; // View private Grid parent; private View rootView; private double scale; private float density; protected Drawable image; private SoftReference imageCache; private TileProvider tileProvider; private Rect imageBounds; private float x1; private float y1; // Controller protected boolean invalidateRect; private boolean isImageLoading; private boolean loadImage; private boolean isReady; private String TAG = Cell.class.getSimpleName(); private boolean doNotProcessThisCell; public Cell(Grid parent, TileProvider tileProvider, int zoomLevel, int col, int row, int tileSize, double scale) { doNotProcessThisCell = false; this.width = tileSize; this.height = tileSize; this.col = col; this.row = row; this.scale = scale; this.zoomLevel = zoomLevel; invalidateRect = true; this.parent = parent; this.rootView = parent.getParentView(); this.tileProvider = tileProvider; loadImage = true; x1 = col * width; y1 = row * height; imageBounds = new Rect(); density = rootView.getResources().getDisplayMetrics().density; isImageLoading = false; isReady = false; } public void cacheImage(float dx, float dy) { if (image == null) { loadImage(dx, dy, false); } } public void draw (Canvas canvas, Paint paint, float dx, float dy) { try { if (image == null) loadImage(dx, dy, true); if (image != null) { if (invalidateRect) { recalculateDrawableRect(dx, dy); } image.draw(canvas); } } catch (Exception e) { e.printStackTrace(); Log.e("Cell", "Exception during cell painting. E: " + e); } } public void setLoadImage(boolean loadImage) { this.loadImage = loadImage; } public void setScale(double scale) { this.scale = scale; invalidateRect = true; if (image != null) { recalculateDrawableRect(0, 0); } } public void freeResources() { imageCache = new SoftReference(image); image = null; invalidateRect = true; } private void loadImage(final float dx, final float dy, final boolean invalidateOnDownload) { if (doNotProcessThisCell) return; if (isImageLoading || !loadImage) return; if (imageCache != null) { Drawable cached = imageCache.get(); if (cached != null) { processNewTile(0, 0, cached, true); return; } } isImageLoading = true; tileProvider.requestTile(zoomLevel, col, row, this); } private void processNewTile(final float dx, final float dy, Drawable drawable, boolean fromCache) { if (drawable != null) { image = drawable; if (!fromCache) { Rect bounds = image.getBounds(); width = (float)Math.round(bounds.width()); height = (float)Math.round(bounds.height()); } else { if (image instanceof TransitionDrawable) { TransitionDrawable transDrawable = (TransitionDrawable) drawable; BitmapDrawable tile = (BitmapDrawable) transDrawable.getDrawable(1); width = tile.getBitmap().getWidth(); height = tile.getBitmap().getHeight(); } } recalculateDrawableRect(dx, dy); if (image instanceof TransitionDrawable) { ((TransitionDrawable) image).setCallback(Cell.this); ((TransitionDrawable) image).startTransition(150); rootView.postDelayed(new Runnable(){public void run(){onIsReady();}}, 150); } } } private void onIsReady() { isReady = true; parent.onCellReady(this); } protected void recalculateDrawableRect(final float dx, final float dy) { imageBounds.left = (int)((x1) * scale); imageBounds.top = (int)((y1) * scale); imageBounds.right = (int)((x1 + width) * scale); imageBounds.bottom = (int)((y1 + height) * scale); image.setBounds(imageBounds); invalidateRect = false; } @Override public void invalidateDrawable(Drawable who) { Rect bounds = who.getBounds(); rootView.postInvalidate(bounds.left, bounds.top, bounds.right, bounds.bottom); } @Override public void scheduleDrawable(Drawable who, Runnable what, long when) { rootView.scheduleDrawable(who, what, when); } @Override public void unscheduleDrawable(Drawable who, Runnable what) { rootView.unscheduleDrawable(who, what); } public boolean isReady() { return isReady; } public void setTileProvider(TileProvider tileManager) { this.tileProvider = tileManager; } @Override public void onTileReady(int zoomLevel, int col, int row, Drawable drawable) { processNewTile(0, 0, drawable, false); //rootView.postInvalidate(imageBounds.left, imageBounds.top, imageBounds.right, imageBounds.bottom); isImageLoading = false; } @Override public void onError(Exception e) { isImageLoading = false; doNotProcessThisCell = true; onIsReady(); } } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/model/Grid.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.model; import java.util.ArrayList; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Point; import android.graphics.Rect; import android.os.Looper; import android.util.Log; import android.view.View; import com.ls.widgets.map.config.OfflineMapConfig; import com.ls.widgets.map.interfaces.OnGridReadyListener; import com.ls.widgets.map.providers.TileProvider; import com.ls.widgets.map.utils.OfflineMapUtil; public class Grid { private static final String TAG = "Grid"; //Tile manager is responsible for managing image tiles and free memory private TileProvider tileProvider; private OnGridReadyListener onReadyListener; //View where drawing will be occurred private View parentView; //Model of the grid. private ArrayListcells; //Amount of tile columns private int colCount; //Amount of tile rows private int rowCount; //Size of a square tile in pixels private int tileSize; //Zoom level private int zoomLevel; //Scale private double softScale; //Original image (max zoom) height in pixels private int imageHeight; //Original image (max zoom) width in pixels private int imageWidth; //Maximum possible zoom level private int maxZoomLevel; // For caching purpose only private boolean loadTiles; private Rect cachedDrawRect; private int screenXCapacity; private int screenYCapacity; private Point firstVisibleCell; private Point lastVisibleCell; private Rect gridWindow; private Rect gridWindowBigger; private int cellsToDrawCount; private int cellsReadyCount; private double intScale; private double scale; public Grid(View parent, OfflineMapConfig config, TileProvider tileProvider, int initZoomLevel) { this.zoomLevel = initZoomLevel; this.imageWidth = config.getImageWidth(); this.imageHeight = config.getImageHeight(); this.maxZoomLevel = OfflineMapUtil.getMaxZoomLevel(imageWidth, imageHeight); this.softScale = 1.0; this.intScale = 1.0; this.scale = 1.0; this.tileSize = config.getTileSize(); this.parentView = parent; this.tileProvider = tileProvider; loadTiles = true; rowCount = getRowCount(); colCount = getColCount(); calcCellsInScreen(); // Creating the two points here and then reusing them in onDraw() in order to // avoid of extensive use of garbage collector firstVisibleCell = new Point(0,0); lastVisibleCell = new Point(0,0); gridWindow = new Rect(0,0,0,0); gridWindowBigger = new Rect(0,0,0,0); initGrid(initZoomLevel, tileSize); } private void calcCellsInScreen() { screenXCapacity = (int)(parentView.getWidth() / ((float)tileSize * scale)) / 2; screenYCapacity = (int)(parentView.getHeight() / ((float)tileSize * scale)) / 2; } private void initGrid(int zoomLevel, int tileSize) { if (Looper.myLooper() == null) { throw new IllegalThreadStateException("Should be called from UI thread"); } int size = colCount*rowCount; cells = new ArrayList(size); for (int i=0; i= gridWindow.left && col <= gridWindow.right && row >= gridWindow.top && row <= gridWindow.bottom) { cell.draw(canvas, paint, 0, 0); if (!cell.isReady()) { cellsToDrawCount += 1; } } else { cell.cacheImage(0, 0); } } } } } private Rect getRridWindowBigger(Rect gridWindow) { gridWindowBigger.left = gridWindow.left - screenXCapacity; if (gridWindowBigger.left < 0) gridWindowBigger.left = 0; gridWindowBigger.right = gridWindow.right + screenXCapacity; if (gridWindowBigger.right >= colCount) gridWindowBigger.right = colCount - 1; gridWindowBigger.top = gridWindow.top - screenYCapacity; if (gridWindowBigger.top < 0) gridWindowBigger.top = 0; gridWindowBigger.bottom = gridWindow.bottom + screenYCapacity; if (gridWindowBigger.bottom >= rowCount) gridWindowBigger.bottom = rowCount - 1; return gridWindowBigger; } public int getHeight() { return (int)Math.ceil(imageHeight * getScale()); } public int getOriginalHeight() { return imageHeight; } public int getOriginalWidth() { return imageWidth; } public double getScale() { if (maxZoomLevel == zoomLevel) return 1.0f * scale; return (1.0/(Math.pow(2,maxZoomLevel - zoomLevel)))*scale; } public int getWidth() { return (int) Math.ceil(imageWidth * getScale()); } public int getZoomLevel() { return zoomLevel; } public int getMaxZoomLevel() { return maxZoomLevel; } public View getParentView() { return parentView; } public void setLoadTiles(boolean loadTiles) { applyLoadTileState(loadTiles); } public boolean isLoadTiles() { return this.loadTiles; } public int getMinZoomLevel() { return 0; } public void setSoftScale(float newScale) { if (newScale == 0.0) throw new IllegalArgumentException(); this.softScale = newScale; this.scale = softScale * intScale; applyScale(this.scale); } public void setOnReadyListener(OnGridReadyListener listener) { //tileManager.setOnReadyListener(listener); this.onReadyListener = listener; } protected int getColCount() { return (int)(Math.ceil(((float)OfflineMapUtil.getScaledImageSize(maxZoomLevel, zoomLevel, imageWidth) / (float)tileSize))); } protected int getRowCount() { return (int)((float)Math.ceil(((float)OfflineMapUtil.getScaledImageSize(maxZoomLevel, zoomLevel, imageHeight) / (float)tileSize))); } public double getSoftScale() { return softScale; } private Point getBottomRightVisibleCell(Rect drawingRect, Point point) { if (point == null || drawingRect == null) throw new IllegalArgumentException(); Point topLeft = getTopLeftVisibleCell(drawingRect, point); int cols = (int) Math.ceil(((float)drawingRect.width() / ((float)tileSize * scale))) + 2; int rows = (int) Math.ceil(((float)drawingRect.height() / ((float)tileSize * scale))) + 1; point.x = topLeft.x + cols; point.y = topLeft.y + rows; if (point.x >= colCount) point.x = colCount-1; if (point.y >= rowCount) point.y = rowCount-1; return point; } private Point getTopLeftVisibleCell(Rect drawingRect, Point point) { if (point == null || drawingRect == null) throw new IllegalArgumentException(); point.x = (int) Math.floor(((float)drawingRect.left / ((float)tileSize * scale))) - 1; point.y = (int) Math.floor(((float)drawingRect.top / ((float)tileSize * scale))) - 1; if (point.x < 0) { point.x = 0; } if (point.y < 0) { point.y = 0; } return point; } private synchronized Rect getGridWindow(Rect drawRect) { if (drawRect == null || firstVisibleCell == null || lastVisibleCell == null) throw new IllegalArgumentException(); getTopLeftVisibleCell(drawRect, firstVisibleCell); getBottomRightVisibleCell(drawRect, lastVisibleCell); // window.top = firstVisibleCell.y - screenYCapacity; // if (window.top< 0) window.top= 0; // // window.left = firstVisibleCell.x - screenYCapacity; // if (window.left < 0) window.left = 0; // // window.bottom = lastVisibleCell.y + screenYCapacity; // if (window.bottom > rowCount) window.bottom = rowCount; // // window.right = lastVisibleCell.x + screenYCapacity; // if (window.right > colCount) window.right = colCount; // return window; gridWindow.top = firstVisibleCell.y; gridWindow.left = firstVisibleCell.x; gridWindow.bottom = lastVisibleCell.y; gridWindow.right = lastVisibleCell.x; return gridWindow; } private void applyScale(double scale) { int size = cells.size(); calcCellsInScreen(); for (int i=0; i= gridWindowBigger.left && col <=gridWindowBigger.right && row >= gridWindowBigger.top && row <= gridWindowBigger.bottom)) { Cell cell = cells.get((int) (col + row*colCount)); if (cell != null) { cell.freeResources(); } } } } } void onCellReady(Cell cell) { cellsReadyCount += 1; if (cellsReadyCount == cellsToDrawCount) { if (onReadyListener != null) { Log.d(TAG, "OnReady!"); onReadyListener.onReady(); } } } public void setInternalScale(float newScale) { if (newScale == 0.0) throw new IllegalArgumentException(); intScale = newScale; this.scale = softScale * intScale; applyScale(this.scale); } public double getIntScale() { return intScale; } // // public void setInternalScale(float newScale) { // softScale *= newScale; // } } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/model/MapLayer.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.model; import java.util.ArrayList; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable.Callback; import android.os.Looper; import com.ls.widgets.map.MapWidget; import com.ls.widgets.map.config.OfflineMapConfig; import com.ls.widgets.map.interfaces.Layer; public class MapLayer implements Layer, Callback { private long layerId; private boolean isVisible; private ArrayList drawables; private ArrayList touchables; protected MapWidget parent; public MapLayer(long theLayerId, MapWidget parent) { drawables = new ArrayList(); touchables = new ArrayList(); //Visible by default isVisible = true; this.layerId = theLayerId; this.parent = parent; } @Override public void addMapObject(MapObject object) { if (object == null) { throw new IllegalArgumentException(); } if (Looper.myLooper() == null) { throw new RuntimeException("addMapObject should be called from UI thread."); } object.setParent(this); object.setScale(parent.getScale()); drawables.add(object); if (object.isTouchable()) { touchables.add(object); } parent.invalidate(object.getBounds()); } @Override public MapObject getMapObject(Object id) { if (id == null) { throw new IllegalArgumentException(); } // TODO: Re-factor this as this is extremely not efficient for (MapObject drawable:drawables) { if (drawable.getId().equals(id)) { return drawable; } } return null; } @Override public MapObject getMapObjectByIndex(int index) { return drawables.get(index); } @Override public int getMapObjectCount() { return drawables != null?drawables.size():0; } @Override public void removeMapObject(Object id) { if (id == null) { throw new IllegalArgumentException(); } if (Looper.myLooper() == null) { throw new RuntimeException("removeMapObject should be called from UI thread."); } MapObject objectToDelete = null; for (MapObject mapObject:drawables) { if (mapObject.getId().equals(id)) { objectToDelete = mapObject; break; } } if (objectToDelete != null) { drawables.remove(objectToDelete); touchables.remove(objectToDelete); Rect b = objectToDelete.getDrawable().getBounds(); parent.postInvalidate(b.left, b.top, b.right, b.bottom); } } // public ArrayList getTouched(int normX, int normY) // { // ArrayList result = new ArrayList(); // // for (MapObject touchable:touchables) { // if (touchable.isTouched(normX, normY)) { // result.add(touchable.getId()); // } // } // // return result; // } // /** * Returns Ids of map object that were touched. Intended for internal use * @param touchRect * @return */ public ArrayList getTouched(Rect touchRect) { ArrayList result = new ArrayList(); for (MapObject touchable:touchables) { if (touchable.isTouched(touchRect)) { result.add(touchable.getId()); } } return result; } public boolean isVisible() { return isVisible; } public void setVisible(boolean visible) { isVisible = visible; } public void setScale(float scale) { int size = drawables.size(); for (int i=0; i < size; ++i) { MapObject drawable = drawables.get(i); drawable.setScale(scale); } } public void draw(Canvas canvas, Rect drawingRect) { if (!isVisible) return; for (int i=0, size=drawables.size(); i(); touchables = new ArrayList(); } public boolean equals(Object o) { if (o == null) { return false; } if (!(o instanceof MapLayer)) { return false; } MapLayer inMapLayer = (MapLayer) o; return inMapLayer.layerId == this.layerId; } public int hashCode() { return (int)(layerId ^ (layerId >>> 32)); } // Package access methods OfflineMapConfig getConfig() { return parent.getConfig(); } void invalidate(MapObject object) { Rect bounds = object.getBounds(); parent.postInvalidate(bounds.left, bounds.top, bounds.right, bounds.bottom); } public void scheduleDrawable(Drawable who, Runnable what, long when) { parent.scheduleDrawable(who, what, when); } public void unscheduleDrawable(Drawable who, Runnable what) { parent.unscheduleDrawable(who, what); } public void invalidateDrawable(Drawable who) { Rect bounds = who.getBounds(); parent.postInvalidate(bounds.left, bounds.top, bounds.right, bounds.bottom); } public long getId() { return layerId; } } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/model/MapObject.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.model; import android.graphics.Canvas; import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.location.Location; import android.os.Looper; import android.util.Log; import com.ls.widgets.map.config.GPSConfig; import com.ls.widgets.map.utils.MapCalibrationData; public class MapObject { protected MapLayer parent; protected float scale; // Current map scale // Pivot point protected Point pivotPoint; private Object id; // Position in map coordinates protected Point pos; // Position in map coordinates taking the scale into account protected Point posScaled; private Drawable drawable; private boolean isScalable; // Map object is scalable or not (on map zoom) private boolean isTouchable; // Shows whether this object should respond to touch events. protected Rect touchRect; // Object's touch area. public MapObject(Object id) { this(id, null, 0, 0, false); } /** * Creates new MapObject. * @param id - id of the object. * @param drawable - image. * @param x - x coordinate of the object in map coordinates. * @param y - y coordinate of the object in map coordinates. */ public MapObject(Object id, Drawable drawable, int x, int y) { this(id, drawable, x, y, false); } /** * Creates new MapObject. * @param id - id of the object. * @param drawable - image * @param position - coordinate of the object in map coordinates. */ public MapObject(Object id, Drawable drawable, Point position) { this(id, drawable, position.x, position.y, false); } /** * Creates new MapObject. * @param id - id of the object. * @param drawable - image. * @param x - x coordinate of the object in map coordinates. * @param y - y coordinate of the object in map coordinates. * @param pivotX - x coordinate of pivot point in image coordinates. * @param pivotY - y coordinate of pivot point in image coordinates. */ public MapObject(Object id, Drawable drawable, int x, int y, int pivotX, int pivotY) { this(id, drawable, x, y, pivotX, pivotY, false, false); } /** * Creates new MapObject. * @param id - id of the object. * @param drawable - image. * @param position - coordinate of the object in map coordinates. * @param pivotPoint - coordinate of the pivot point in image coordinates. */ public MapObject(Object id, Drawable drawable, Point position, Point pivotPoint) { this(id, drawable, position.x, position.y, pivotPoint.x, pivotPoint.y); } /** * Creates new MapObject. * @param id - id of the object. * @param drawable - image. * @param x - x coordinate of the object in map coordinates. * @param y - y coordinate of the object in map coordinates. * @param isTouchable - true if the object should respond to touch events, false otherwise. */ public MapObject(Object id, Drawable drawable, int x, int y, boolean isTouchable) { this(id, drawable, x, y, isTouchable, true); } /** * Creates new MapObject. * @param id - id of the object. * @param drawable - image. * @param position - coordinate of the object in map coordinates. * @param isTouchable - true if the object should respond to touch events, false otherwise. */ public MapObject (Object id, Drawable drawable, Point position, boolean isTouchable) { this(id, drawable, position.x, position.y, true, true); } /** * Creates new MapObject. * @param id - id of the object. * @param drawable - image. * @param position - coordinate of the object in map coordinates. * @param pivotPoint - coordinate of the pivot point in image coordinates. * @param isTouchable - true if the object should respond to touch events, false otherwise. */ public MapObject (Object id, Drawable drawable, Point position, Point pivotPoint, boolean isTouchable) { this(id, drawable, position.x, position.y, pivotPoint.x, pivotPoint.y, isTouchable, true); } /** * Creates new MapObject. * @param id - id of the object. * @param drawable - image. * @param x - x coordinate of the object in map coordinates. * @param y - y coordinate of the object in map coordinates. * @param isTouchable - true if the object should respond to touch events, false otherwise. * @param isScalable - true, if map object should be scaled on map zoom, false otherwise. */ public MapObject(Object id, Drawable drawable, int x, int y, boolean isTouchable, boolean isScalable) { this(id, drawable, x, y, 0, 0, isTouchable, isScalable); } /** * Creates new MapObject. * @param id - id of the object. * @param drawable - image. * @param position - coordinate of the object in map coordinates. * @param isTouchable - true if the object should respond to touch events, false otherwise. * @param isScalable - true, if map object should be scaled on map zoom, false otherwise. */ public MapObject(Object id, Drawable drawable, Point position, boolean isTouchable, boolean isScalable) { this(id, drawable, position.x, position.y, 0, 0, isTouchable, isScalable); } /** * Creates new MapObject. * @param id - id of the object. * @param drawable - image. * @param position - coordinate of the object in map coordinates. * @param pivotPoint - coordinate of the pivot point in image coordinates. * @param isTouchable - true if the object should respond to touch events, false otherwise. * @param isScalable - true, if map object should be scaled on map zoom, false otherwise. */ public MapObject (Object id, Drawable drawable, Point position, Point pivotPoint, boolean isTouchable, boolean isScalable) { this(id, drawable, position.x, position.y, pivotPoint.x, pivotPoint.y, isTouchable, isScalable); } /** * Creates new MapObject. * @param id - id of the object. * @param drawable - image. * @param x - x coordinate of the object in map coordinates. * @param y - y coordinate of the object in map coordinates. * @param pivotX - x coordinate of pivot point in image coordinates. * @param pivotY - y coordinate of pivot point in image coordinates. * @param isTouchable - true if the object should respond to touch events, false otherwise. * @param isScalable - true, if map object should be scaled on map zoom, false otherwise. */ public MapObject(Object id, Drawable drawable, int x, int y, int pivotX, int pivotY, boolean isTouchable, boolean isScalable) { this.id = id; this.drawable = drawable; pos = new Point(x, y); posScaled = new Point(); this.pivotPoint = new Point(pivotX, pivotY); this.isTouchable = isTouchable; this.isScalable = isScalable; this.scale = 1.0f; this.touchRect = new Rect(); } /** * Returns image that was passed to the constructor. * @return instance of Drawable. */ public Drawable getDrawable() { return drawable; } /** * Sets object's image. * @param drawable instance of Drawable. * @throws IllegalArgumentException when null is set. */ public void setDrawable(Drawable drawable) { if (drawable == null) throw new IllegalArgumentException(); if (Looper.myLooper() == null) throw new IllegalThreadStateException("setDrawable should be called from UI thread"); this.drawable = drawable; recalculateBounds(); } /** * Draws the map object on the canvas * @param canvas - Canvas */ public void draw(Canvas canvas) { if (drawable != null) { drawable.draw(canvas); } } /** * Returns id of this object * @return Object that was passed as an id to the constructor. */ public Object getId() { return id; } /** * Determines whether touch rectangle intersects the bounds of this object. * Coordinates that are passed in touchRect are the screen coordinates + scroll offset. * @param touchRect - area inside of the map. * @return - true if touchRect intersects the map object's touch area, false otherwise. */ public boolean isTouched(Rect touchRect) { return Rect.intersects(this.touchRect, touchRect); } /** * Returns X coordinate of the map object in map coordinates. * @return X coordinate taking the map scale into account. */ public int getXScaled() { return posScaled.x; } /** * Returns Y coordinate of the map object in map coordinates. * @return Y coordinate taking the map scale into account. */ public int getYScaled() { return posScaled.y; } /** * Returns X coordinate of the map object in map coordinates. * @return X coordinate of the object. */ public int getX() { return pos.x; } /** * Returns Y coordinate of the map object in map coordinates. * @return Y coordinate of the object. */ public int getY() { return pos.y; } /** * Returns position of the map object in map coordinates in pixels of the original image. * @return instance of {@link android.graphics.Point}. */ public Point getPosition() { return pos; } /** * Shows whether this object is touchable. * @return true if this object should respond to touch events, false otherwise. */ public boolean isTouchable() { return isTouchable; } /** * Set's pivot point within the drawable * @param x - x coordinate in pixels * @param y - y coordinate in pixels */ public void setPivotPoint(int x, int y) { pivotPoint.x = x; pivotPoint.y = y; } /** * Set's pivot point within the drawable * @param pivotPoint position of pivot point within the drawable. * @throws java.lang.IllegalArgumentException if null is passed */ public void setPivotPoint(Point pivotPoint) { if (pivotPoint == null) { throw new IllegalArgumentException(); } this.pivotPoint = pivotPoint; } /** * Returns bounds of the image that represents the map object. * Note: for efficiency, the returned object may be the same object stored in the drawable (though this is not guaranteed), so if a persistent copy of the bounds is needed, call copyBounds(rect) instead. You should also not change the object returned by this method as it may be the same object stored in the drawable. * @return instance of Rect with size of the image in pixels taking the scale of the map into account. */ public Rect getBounds() { if (drawable != null) { return drawable.getBounds(); } else return null; } /** * This method is responsible for calculation of position and size of the map object. * It will be called when map changes its scale or when other event that may affect the size of the map object occurs. * It should perform quick and do not contain memory allocations as it may be called pretty often. */ protected void recalculateBounds() { posScaled.x = (int)(pos.x*scale); posScaled.y = (int)(pos.y*scale); int width = 0; int height = 0; if (drawable == null) return; width = drawable.getIntrinsicWidth(); height = drawable.getIntrinsicHeight(); if (!isScalable) { //ignore scale drawable.setBounds(posScaled.x - pivotPoint.x, posScaled.y-pivotPoint.y, posScaled.x + width - pivotPoint.x, posScaled.y + height - pivotPoint.y); } else { drawable.setBounds(posScaled.x - (int)(pivotPoint.x*scale), posScaled.y-(int)(pivotPoint.y*scale), posScaled.x + (int)(width*scale) - (int)(pivotPoint.x*scale), posScaled.y + (int)(height*scale) - (int)(pivotPoint.y*scale)); } if (isTouchable) { touchRect.set(drawable.getBounds()); } } /** * Moves object to another position on the map that is defined in pixels. * @param x - horizontal coordinate of the object within the map in pixels. * @param y - vertical coordinate of the object within the map in pixels. */ public void moveTo(int x, int y) { invalidateSelf(); pos.x = x; pos.y = y; recalculateBounds(); invalidateSelf(); } /** * Moves object to another position on the map that is defined in pixels. In order for this method to work you should * ensure that geo area is configured in /assets/map/map.xml file. * @param location - location of the object. * @throws IllegalStateException if geo area is not configured in map.xml file. * @see android.location.Location */ public void moveTo(Location location) { GPSConfig config = parent.getConfig().getGpsConfig(); if (!config.isMapCalibrated()) { Log.w("MapObject", "Can't move object to location because map has not been calibrated."); throw new IllegalStateException("Map is not calibrated. Please, add calibration info into map's configuration file."); } invalidateSelf(); if (config.isMapCalibrated()) { MapCalibrationData calibration = config.getCalibration(); calibration.translate(location, pos); recalculateBounds(); invalidateSelf(); } } @Override public boolean equals(Object o) { if (o == null) return false; if (!(o instanceof MapObject)) { return false; } return ((MapObject)o).id.equals(this.id); } @Override public int hashCode() { return id.hashCode(); } protected void invalidateSelf() { parent.invalidate(this); } /* * Sets the scale for drawable and recalculates bounds. * Package level visibility. Is called from MapLayer. */ void setScale(float scale) { this.scale = scale; recalculateBounds(); } /* * Used by the layer to set itself as a parent for this map object. * Not intended to use by the user. */ void setParent(MapLayer layer) { this.parent = layer; } } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/model/MapTouchable.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.model; import android.graphics.Rect; public class MapTouchable { private Object id; //Used to identify the touchable private Rect rect; //Used to define the position and a size of the touchable private MapObject drawableRef; public MapTouchable(Object id, MapObject drawable, Rect rect) { this.id = id; this.rect = rect; drawableRef = drawable; } public Object getId() { return id; } public boolean isTouched(int x, int y) { return rect.contains(x, y); } public boolean isTouched(Rect touchRect) { return Rect.intersects(rect, touchRect); } public MapObject getDrawable() { return drawableRef; } } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/providers/AssetTileProvider.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.providers; import java.io.IOException; import java.io.InputStream; import android.content.Context; import android.content.res.AssetManager; import com.ls.widgets.map.config.OfflineMapConfig; import com.ls.widgets.map.utils.OfflineMapUtil; public class AssetTileProvider extends TileProvider { private AssetManager assetManager; private StringBuilder sbuilder; public AssetTileProvider(Context context, OfflineMapConfig config) { super(config); this.assetManager = context.getAssets(); sbuilder = new StringBuilder(256); } @Override protected InputStream openTile(int zoomLevel, int col, int row) throws IOException { // This is more memory efficient than regular concatenation String filesFolder = OfflineMapUtil.getFilesPath(config.getMapRootPath()); sbuilder.delete(0, sbuilder.length()); sbuilder.append(filesFolder); sbuilder.append(zoomLevel); sbuilder.append("/"); sbuilder.append(col); sbuilder.append("_"); sbuilder.append(row); sbuilder.append("."); sbuilder.append(config.getImageFormat()); return assetManager.open(sbuilder.toString()); } } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/providers/ExternalStorageTileProvider.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.providers; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import com.ls.widgets.map.config.OfflineMapConfig; import com.ls.widgets.map.utils.OfflineMapUtil; public class ExternalStorageTileProvider extends TileProvider { private StringBuilder sbuilder; public ExternalStorageTileProvider(OfflineMapConfig config) { super(config); sbuilder = new StringBuilder(); } @Override protected InputStream openTile(int zoomLevel, int col, int row) throws IOException { // This is more memory efficient than regular concatenation String filesFolder = OfflineMapUtil.getFilesPath(config.getMapRootPath()); sbuilder.delete(0, sbuilder.length()); sbuilder.append(filesFolder); sbuilder.append(zoomLevel); sbuilder.append("/"); sbuilder.append(col); sbuilder.append("_"); sbuilder.append(row); sbuilder.append("."); sbuilder.append(config.getImageFormat()); File file = new File(sbuilder.toString()); if (file.exists()) { return new FileInputStream(file); } return null; } } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/providers/GPSLocationProvider.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.providers; import android.content.Context; import android.content.pm.PackageManager; import android.location.GpsStatus; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.location.LocationProvider; import android.os.Bundle; import android.os.SystemClock; import android.util.Log; import com.ls.widgets.map.interfaces.MapLocationListener; public final class GPSLocationProvider implements LocationListener { private static final String TAG = "GPSLocationProvider"; private LocationManager locManager; private int refreshRate; private int minDistance; private MapLocationListener listener; private long mLastLocationMillis; private Location mLastLocation; private boolean isGpsFix; private boolean filterNonGPSFix; private boolean started; private boolean permGranted; private boolean passiveMode; private MyGPSListener gpsStatusListener; public GPSLocationProvider(Context context) { locManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); started = false; passiveMode = false; gpsStatusListener = new MyGPSListener(); PackageManager mgr = context.getPackageManager(); if (mgr.checkPermission(android.Manifest.permission.ACCESS_FINE_LOCATION, context.getPackageName()) == PackageManager.PERMISSION_GRANTED) { permGranted = true; } else { permGranted = false; } } public void setMinRefreshTime(int refreshRate) { this.refreshRate = refreshRate; } public void setMinRefreshDistance(int minDistance) { this.minDistance = minDistance; } public void setMapLocationListener(MapLocationListener listener) { this.listener = listener; } /** * Registers location update listeners. * @param passiveMode */ public void start(boolean passiveMode) { if (!permGranted) { Log.w(TAG,"Can't start receiving the location updates. You have no ACCESS_FINE_LOCATION permission enabled."); return; } if (started) { Log.w(TAG, "Can't start receiving the location updates. Already started."); return; } started = true; try { this.passiveMode = passiveMode; if (passiveMode) { locManager.requestLocationUpdates("passive", 0, 0, this); Log.d(TAG, "Registering for receiving updates from passive provider."); } else { locManager.addGpsStatusListener(gpsStatusListener); if (locManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) { locManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, refreshRate, minDistance, this); } locManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, refreshRate, minDistance, this); } Location loc1 = locManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER); Location loc2 = locManager.getLastKnownLocation(LocationManager.GPS_PROVIDER); if (loc1 != null && loc2 != null) { if (loc1.getAccuracy() < loc2.getAccuracy()) { onLocationChanged(loc1); } else { onLocationChanged(loc2); } } else { Location loc = loc1 != null? loc1: loc2; if (loc != null) { onLocationChanged(loc); } } if (listener != null) { listener.onChangePinVisibility(true); } } catch (SecurityException e) { Log.w(TAG, "Can't get location provider due to " + e); } } public void stop() { locManager.removeGpsStatusListener(gpsStatusListener); if (listener != null) { listener.onChangePinVisibility(false); } started = false; locManager.removeUpdates(this); } @Override public void onLocationChanged(Location location) { if (location == null) return; if (!passiveMode) { mLastLocationMillis = SystemClock.elapsedRealtime(); if (location.getProvider().equals(LocationManager.NETWORK_PROVIDER)) { if (!filterNonGPSFix) { listener.onMovePinTo(location); } } else if (location.getProvider().equals(LocationManager.GPS_PROVIDER)){ listener.onMovePinTo(location); } mLastLocation = location; } else { listener.onMovePinTo(location); } } @Override public void onProviderDisabled(String name) { Log.d(TAG, "Provider disabled: " + name); } @Override public void onProviderEnabled(String name) { Log.d(TAG, "Provider enabled: " + name); if (started && !passiveMode) { if (name.equals(LocationManager.GPS_PROVIDER)) { locManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, refreshRate, minDistance, this); } } } @Override public void onStatusChanged(String name, int status, Bundle arg2) { Log.d(TAG, "Status of "+ name + " changed for " + statusToString(status)); } private String statusToString(int status) { switch (status) { case LocationProvider.OUT_OF_SERVICE: return "OUT_OF_SERVICE"; case LocationProvider.TEMPORARILY_UNAVAILABLE: return "TEMPORARILY_UNAVAILABLE"; case LocationProvider.AVAILABLE: return "AVAILABLE:"; } return "UNKNOWN"; } private class MyGPSListener implements GpsStatus.Listener { public void onGpsStatusChanged(int event) { if (passiveMode) { return; } switch (event) { case GpsStatus.GPS_EVENT_SATELLITE_STATUS: if (mLastLocation != null) isGpsFix = (SystemClock.elapsedRealtime() - mLastLocationMillis) < 3000; if (isGpsFix) { // A fix has been acquired. filterNonGPSFix = true; // Do something. } else { // The fix has been lost. filterNonGPSFix = false; // Do something. } break; case GpsStatus.GPS_EVENT_FIRST_FIX: // Do something. isGpsFix = true; filterNonGPSFix = true; break; } } } } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/providers/TileProvider.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.providers; import java.io.IOException; import java.io.InputStream; import java.util.LinkedList; import java.util.Queue; import java.util.concurrent.RejectedExecutionException; import android.util.Log; import com.ls.widgets.map.commands.GetTileTask; import com.ls.widgets.map.config.OfflineMapConfig; import com.ls.widgets.map.interfaces.TileManagerDelegate; public abstract class TileProvider { protected static final String TAG = TileProvider.class.getSimpleName(); protected boolean paused; protected OfflineMapConfig config; protected Queue commandQueue; public TileProvider(OfflineMapConfig config) { this.config = config; commandQueue = new LinkedList(); } public void requestTile(final int zoomLevel, final int col, final int row, final TileManagerDelegate delegate) { InputStream is = null; try { is = openTile(zoomLevel, col, row); if (is == null) { delegate.onError(null); return; } GetTileTask task = new GetTileTask(is) { @Override protected void onPostExecute(Boolean result) { try { if (result.equals(Boolean.TRUE)) { delegate.onTileReady(zoomLevel, col, row, getResult()); } else { Log.w(TAG, "Can't load tile " + zoomLevel + "\\" + col + "_" + row + "." + config.getImageFormat()); delegate.onError(null); } } finally { try { closeStream(); } catch (IOException e) { e.printStackTrace(); Log.w(TAG, "Can't close input stream due to exception:" + e); delegate.onError(null); } } } }; if (paused) { synchronized (commandQueue) { commandQueue.add(task); } return; } try { task.execute(); } catch (RejectedExecutionException e) { delegate.onError(e); } } catch (IOException e){ delegate.onError(e); } } protected abstract InputStream openTile(int zoomLevel, final int col, final int row) throws IOException; public void startProcessingCommands() { this.paused = false; synchronized (commandQueue) { for (GetTileTask task:commandQueue) { task.execute(); } commandQueue.clear(); } } public void pauseProcessingCommands() { this.paused = true; } public void stopProcessingCommands() { synchronized (commandQueue) { commandQueue.clear(); } } } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/utils/GeoUtils.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.utils; import android.graphics.Point; import android.location.Location; import android.util.Log; import com.ls.widgets.map.MapWidget; public class GeoUtils { /** * Helper function to translate position on the map to geographic coordinates. Map should be calibrated in order for * this method to take effect. Result will be returned in last parameter. * @param v - com.ls.MapWidget * @param x - x coordinate in map coordinate system. * @param y - y coordinate in map coordinate system. * @param location - out parameter. Will contain latitude and longitude of point on the map. */ public static void translate(MapWidget v, int x, int y, Location location) { MapCalibrationData calibration = v.getConfig().getGpsConfig().getCalibration(); if (calibration == null) { Log.w("GeoUtils", "Can't translate. No calibration data!"); } calibration.translate(x, y, location); } /** * Helper function to convert position on the map to geographic coordinates. Result will be returned in last parameter. * @param v - com.ls.MapWidget * @param point - position on the map in pixels. Instance of android.graphics.Point * @param location - out parameter. Will contain latitude and longitude of point on the map. */ public static void translate(MapWidget v, Point point, Location location) { MapCalibrationData calibration = v.getConfig().getGpsConfig().getCalibration(); if (calibration == null) { Log.w("GeoUtils", "Can't translate. No calibration data!"); } calibration.translate(point.x, point.y, location); } /** * Converts location to position on the map. Result will be returned in last parameter. * @param v - com.ls.MapWidget * @param location instance of android.location.Location object. * @param position - out parameter. Can be null. * @return returns the same object that was passed as position, or if it is null - returns new Point object. */ public static void translate(MapWidget v, Location location, Point point) { MapCalibrationData calibration = v.getConfig().getGpsConfig().getCalibration(); if (calibration == null) { Log.w("GeoUtils", "Can't translate. No calibration data!"); } calibration.translate(location, point); } } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/utils/Graphics.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.utils; public final class Graphics { public final static byte BLUE_DOT[] = {-119, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, 22, 0, 0, 0, 22, 8, 6, 0, 0, 0, -60, -76, 108, 59, 0, 0, 0, 1, 115, 82, 71, 66, 0, -82, -50, 28, -23, 0, 0, 0, 6, 98, 75, 71, 68, 0, -1, 0, -1, 0, -1, -96, -67, -89, -109, 0, 0, 0, 9, 112, 72, 89, 115, 0, 0, 11, 19, 0, 0, 11, 19, 1, 0, -102, -100, 24, 0, 0, 0, 7, 116, 73, 77, 69, 7, -37, 10, 29, 12, 6, 21, 110, 115, 107, -90, 0, 0, 5, 64, 73, 68, 65, 84, 56, -53, 125, -107, -55, -113, 92, 103, 21, -59, -49, -3, -90, 55, -44, 123, 85, -81, -70, 29, -9, -28, -10, -40, 114, 2, -119, -80, 49, -34, -64, -118, 29, 59, -60, 36, -40, -124, 21, 11, 118, 100, -111, 13, 127, 8, 11, 54, -39, 0, 11, -110, 69, 8, 34, 65, 68, 24, 41, -96, -56, 66, 14, -120, -104, 6, 55, 118, -20, -74, 122, -118, -37, 93, -43, 85, -43, 53, -67, -31, 27, 46, -117, 38, 36, 66, 50, 87, -70, -101, 43, -99, -77, 56, 58, -6, 93, -62, 51, -26, -6, -75, 31, -21, -94, 40, 90, 76, 33, -78, -42, -23, -70, 118, -62, 24, 4, 41, -31, -125, -29, 106, 56, -24, 77, -73, -18, -1, -44, 62, 75, 79, -1, 123, -40, -40, -8, 81, -53, 54, -19, -53, -25, -49, -81, 124, -83, -35, 73, -65, 14, -30, 69, -21, -126, 110, 26, -124, -56, -80, 80, -118, 44, 59, -22, -113, -122, -45, -73, -10, -10, 15, -2, -96, -93, -29, -57, -37, -37, -81, 77, -2, -81, -15, -91, -11, 87, 47, -89, 89, -15, -67, -107, -75, -27, 43, -21, 23, -98, -5, -30, -39, -91, -4, 70, -106, 107, -104, 72, -126, 0, 48, 24, 85, -23, 48, 26, 88, 60, 61, 28, -1, 101, 127, -81, 127, -73, 127, -12, 116, 123, 54, 61, 122, 125, 103, -1, 39, -113, 62, -21, -91, 0, 96, -3, -36, -73, 73, -118, -75, 85, 29, 21, -33, -115, -45, -10, 15, -105, 87, 23, 47, 108, -68, -80, -126, -49, -67, -76, -116, -107, -75, 54, -70, 11, 17, 71, 17, -95, -84, 2, 122, 71, 21, -19, 60, 62, -63, -67, -51, 39, 55, 43, 27, 110, -114, 39, -27, -74, 117, -63, -98, 91, 121, -7, -105, -77, 114, 115, 127, 56, -70, -53, 0, 32, 1, -32, -30, -123, 111, 94, 117, 33, 123, 37, 105, 47, 126, 99, -7, -46, -6, -107, 47, 127, 117, 67, -68, 120, 99, 25, -99, -77, 9, 26, 1, -116, 75, -121, -31, -84, -63, -55, -36, -95, 97, 38, -35, 82, 104, 117, 19, -80, 52, -24, 13, 67, -34, 84, 110, -107, -68, 94, 42, -118, -91, -99, 94, -1, 78, 31, 0, -28, 23, 54, 126, -112, -42, -68, -16, -107, -87, -49, -65, -33, 58, -69, 120, 109, -3, -13, 107, -30, -54, 75, 103, -112, 47, -58, 92, -70, 64, -109, -54, 97, 92, 89, -102, -108, -98, 38, -107, -89, -103, 13, -88, -104, 97, -91, -32, -119, 35, -22, -115, -125, 24, 13, -85, -91, -39, -52, 43, -119, -6, 97, 39, -50, -10, 70, -109, 71, -115, 42, 109, -66, 82, -117, -12, -14, 84, 117, -106, 23, 58, 5, -30, -75, -100, 7, -34, -61, -10, 74, -14, -98, -95, -108, -128, 20, 0, 17, -32, 61, 80, -69, -128, -70, 9, 104, -104, 105, 68, -52, 122, 37, -121, -35, -19, -46, -20, -40, 46, 75, 63, -69, -52, -95, 88, 7, 112, 79, -7, 120, -23, 58, 71, -83, -17, 84, 105, -66, 48, 110, 39, 24, 16, -93, 61, -9, 112, 16, -112, -126, -96, 1, 72, 65, 32, 102, -8, -64, -80, -98, 81, 58, 70, 105, 3, 38, -106, 81, 26, -96, 44, 82, 52, -35, -20, 12, 79, -69, -33, 34, 127, 97, 27, -64, -65, -44, 68, -104, 85, -81, -52, 13, -33, 77, 116, -109, -57, -104, 88, -90, -109, -118, -95, 36, 35, 54, 2, -127, 8, -126, 0, 48, 33, 0, -80, -98, -48, 120, 66, 105, -127, -71, 5, 85, 32, -8, 78, -116, -48, 77, -109, -78, 78, -82, 67, -90, 43, 0, 72, 13, -107, 73, 40, 50, 90, 118, 99, -88, -44, -80, -11, -126, -26, -106, 16, 59, 1, 72, 1, -49, 4, -94, -45, -78, 5, 16, 92, 32, -44, 30, -88, 125, 64, -29, 9, 46, 8, -88, -52, -80, -24, -58, 52, 29, -58, 38, 84, 81, 12, 64, 42, -41, -19, -112, -50, 59, -24, 118, 83, 20, 45, 13, 41, 20, 42, 39, 48, -86, -128, -46, 19, -108, 36, 40, 41, 32, -120, 16, -104, -31, 124, 64, -29, 24, -75, -109, -80, 33, -128, -124, 66, 20, 51, 116, 43, -58, 36, -53, 17, 108, 42, 0, 40, 37, 59, 49, -103, 60, 70, -111, -57, -24, -74, 34, 100, -79, -122, 54, 26, -112, 26, -127, 20, 88, 10, -80, -108, 96, 34, -128, 25, -96, 0, -126, -125, 98, -113, -56, 16, 90, 30, 40, 88, -96, -55, 44, -86, 78, -124, -58, 106, -63, -128, 80, 73, -22, 57, 109, 1, 121, 106, 80, 100, 9, 21, -87, -126, 49, 6, 90, 25, 40, -91, 96, -76, -124, -108, -97, -58, -31, 61, -61, 57, 15, -37, 88, 84, 70, -62, 104, 1, -87, 20, -71, -54, -94, -52, 8, -13, -71, -57, 28, -128, 74, 68, 85, -91, 58, -44, -19, 44, -117, -14, -84, -115, 60, 102, 68, -111, -127, -47, 26, -58, 40, 24, 77, -89, -58, 0, 24, -128, 115, 12, 107, 25, -115, 81, -120, 26, 5, -93, 53, -56, 0, 85, -29, 48, -48, 117, -27, 48, 107, -26, 0, 43, 101, -57, 7, -87, 58, -13, 65, -111, -89, 95, 106, -25, 105, -110, 71, -52, -111, 86, -120, -116, -92, -56, 8, 40, 13, 40, 9, 8, 2, 2, 3, -50, 3, 77, 3, 52, 86, -96, 49, -110, -115, -47, -128, 33, -102, -105, -13, 50, 66, -7, -31, -76, -20, 61, 5, -32, -107, 31, 124, -12, -49, -50, -59, -11, -33, 20, -103, -68, -108, -91, 114, 45, -49, 52, -116, 0, 34, -59, -120, 12, -125, 36, 65, -55, 83, -13, 0, -64, 57, -64, 40, 70, 99, 9, -50, 107, -40, -96, -31, -58, 22, 121, 44, -114, 19, 63, -4, -99, 61, -36, 124, 0, -96, 86, -119, 27, 29, 100, -86, -36, -29, -14, -55, 83, 69, -68, -42, -18, 62, 71, -111, 4, 52, 17, 107, 1, 34, -63, -112, 18, 16, 18, 8, 1, 8, 30, -16, 17, -63, 51, -40, 3, 100, 3, 48, -98, 12, 65, -27, -109, -93, 22, 77, 63, -42, -10, -28, 49, 0, 86, 59, 127, -6, -59, 120, -29, -38, -51, -69, -13, -31, -18, 123, -68, -92, -92, -92, -8, -59, 86, 43, 86, -119, 81, -92, 37, -99, -110, -107, 78, 75, 1, 6, -64, 32, 38, -64, 7, -90, -38, 57, 76, 102, -91, -27, -6, 112, 107, 118, -12, -16, -74, 59, 57, -8, 112, -8, -24, -50, -47, 127, -23, -42, -33, -39, -22, 79, -57, -11, 71, 23, -97, 95, -106, 105, 18, -82, 44, 116, 116, -69, -45, 81, 34, -53, 21, -94, 8, -120, -52, -23, 38, 49, -112, -90, -96, 40, 1, -108, 104, -48, -52, -121, -2, -8, -55, -18, -34, -63, -125, 127, -68, -15, -5, -97, -67, -10, -13, -99, 59, -65, -3, -69, -73, -11, -89, -40, 108, 102, 39, 24, 108, -1, 109, 116, -18, -123, -85, -3, 56, 49, 101, 26, -47, -114, -90, -64, 100, -101, 85, -40, 10, -111, 0, 101, -111, 38, 95, -49, -55, 78, -57, -80, -13, 41, 79, -121, -125, -65, -10, -10, -9, 110, -19, -36, -33, 122, -1, -34, -19, 63, -2, 122, -13, -35, 55, 55, -67, -83, -61, 39, -96, -105, -97, -91, -2, -42, -5, -17, -11, -120, -3, -67, -48, -52, -9, -115, -110, 53, 121, 79, -66, -100, 15, -123, -9, 61, 73, 116, 56, 25, 28, -9, 78, -6, -3, -113, -57, -57, -57, 91, -5, 15, -18, -65, -5, -63, -83, 91, 111, -1, -7, -19, 119, -34, -71, -3, -42, -81, 30, -2, 39, -88, 103, -1, -68, 79, -26, -4, -43, -85, 89, -34, -23, 20, 66, -120, -116, 4, 69, -126, -124, 118, -50, 6, -17, 124, -29, -100, -101, 76, 70, -93, -31, -31, -18, -18, -8, 89, -6, 127, 3, 21, 23, -87, 18, 52, -39, 118, 119, 0, 0, 0, 0, 73, 69, 78, 68, -82, 66, 96, -126, }; public final static byte BLUE_ARROW[] = {-119, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, 21, 0, 0, 0, 23, 8, 6, 0, 0, 0, -28, -33, 4, -99, 0, 0, 0, 1, 115, 82, 71, 66, 0, -82, -50, 28, -23, 0, 0, 0, 6, 98, 75, 71, 68, 0, 0, 0, 0, 0, 0, -7, 67, -69, 127, 0, 0, 0, 9, 112, 72, 89, 115, 0, 0, 11, 19, 0, 0, 11, 19, 1, 0, -102, -100, 24, 0, 0, 0, 7, 116, 73, 77, 69, 7, -37, 10, 29, 12, 26, 7, 123, -67, 71, -77, 0, 0, 3, 126, 73, 68, 65, 84, 56, -53, -91, -108, -49, 79, 92, 85, 20, -57, -65, -25, -66, 123, -33, 125, 63, -122, -103, 97, 40, 69, 22, -4, -88, 45, 63, 90, 52, -118, -76, 82, 74, -45, -96, 105, 10, -79, -94, 11, 19, 127, 110, -103, -83, 59, 87, 110, -4, 43, -20, -126, -58, -60, -65, -63, -51, -52, -62, -72, 53, -87, 77, -19, 10, 99, 109, -94, 77, 67, 0, -125, 69, -104, -57, -101, 119, -33, 125, -9, -72, -128, 26, 65, -22, 80, 60, -69, -101, -36, -13, 57, -33, -13, -109, -48, -63, -60, 82, 83, 121, -124, -79, -31, 30, -35, -1, -21, 102, -74, 81, 48, 86, -36, -19, 121, -13, -97, 62, -99, -96, 32, -88, -82, -48, -101, -83, 72, -13, -103, 16, -104, 6, 65, 117, 114, -111, -99, 62, 12, 87, -43, -87, -48, -89, -9, 13, -13, -44, 112, 77, 34, -51, -71, -15, 8, 72, -2, -105, -46, -99, 44, 63, -21, -61, -66, -70, 91, -96, 18, 123, 110, 50, -55, -20, 57, 81, 111, -48, -119, -95, -43, 79, -65, 125, -79, -65, -94, -33, 10, -107, -41, 85, 56, -64, 57, 87, -23, -119, -28, 2, 64, 103, 78, 12, 85, 40, 102, 52, -15, -37, -66, -110, -126, -119, -80, 91, 64, -108, 124, 90, -20, -117, -24, -14, -119, -95, -79, 47, 46, 41, 114, -93, 66, 8, -113, 0, 100, -114, 61, -63, -59, -88, 86, -30, -11, -25, 110, -108, -88, 55, -43, -71, 83, -2, -59, -118, -90, -117, -127, 34, -63, 96, 0, -128, 99, 66, 98, 10, 81, 9, -44, -44, -123, -49, -65, -69, -14, -45, 70, 118, -57, 45, 47, -28, -57, 83, -54, 28, -73, -115, -3, 32, -110, 52, -90, 125, 9, -30, -3, -23, 34, 32, 45, 24, -79, -62, 120, 110, -35, 71, 0, -107, -114, -99, -66, 86, 94, 53, -110, -34, -101, 30, 113, -51, 35, 2, 64, -5, 35, 11, 24, 7, 36, 109, -37, 3, -26, 55, 0, -108, -113, 5, 21, 75, -115, 120, -68, -41, -65, 126, -70, 44, -121, 66, 95, 9, -26, -61, -69, 0, -76, 50, 75, -35, 1, 13, 94, 29, -114, 110, -120, 122, 51, -18, -84, -108, 104, 98, 123, 55, 91, -44, -126, -61, 80, -53, -67, 106, -46, 65, 104, -37, 50, 92, 97, -125, 63, 90, -39, -69, 0, 94, -18, 8, 29, -23, 13, 102, -6, -54, -63, -84, 86, 82, 50, 31, -32, -19, 83, 25, 14, -116, 63, 83, 43, -75, -121, -103, -87, -127, -24, -22, 51, -69, 47, -106, -102, 4, -96, -20, 114, 51, 91, 42, -121, 61, -91, 64, 33, -77, -18, -56, 99, 64, 32, -92, -90, -96, 106, -28, 106, -62, -39, 89, 81, 111, 126, 5, -32, -119, 91, -98, -25, -125, 74, 9, 97, 57, -110, 55, 106, -111, -100, -120, 20, 65, 122, 2, 68, -49, -70, 49, -128, 117, 14, -83, 52, 7, -100, 59, 63, 122, 58, -70, 9, 70, 116, 84, -6, -70, 47, 22, -97, 116, -105, -44, 80, -88, 37, 14, 55, -120, -15, -17, 119, -110, 89, -128, -117, -127, -34, -120, 63, 6, -79, 127, 20, 116, -48, 39, 119, -87, 75, -53, 56, 10, 20, -98, 14, -4, 94, 81, 25, 12, -122, 99, -4, 29, 76, -128, 96, -14, 2, 105, 59, -113, 10, -21, 38, -107, 16, 67, 7, -96, -94, -34, 28, -102, -24, 15, 23, 107, -79, -84, -6, -110, -32, 9, 2, 97, 15, -112, 91, 7, -109, 91, 116, 73, -126, 22, -128, -92, 61, -107, 12, -96, 112, -116, -99, 52, -121, -75, 121, -41, -36, 72, -23, 67, 81, 111, 12, -1, 83, -23, 121, -55, -59, 123, -3, -107, 64, -105, 2, -123, -36, 58, -92, -58, -94, 109, 44, -104, -99, 85, -124, -43, -118, -94, 7, 85, -19, 61, 46, 7, -46, 68, 74, 64, -19, 7, 79, -115, -123, -55, -116, 22, -20, -34, 1, -45, 5, 0, 16, -94, -34, 20, -27, 64, 78, -62, 21, 19, 82, -112, -57, 12, -34, 78, -115, -39, -36, 105, 39, -101, 59, -23, -122, -16, -60, -67, -98, 90, -7, -21, 123, -21, -59, 23, -92, -93, 91, 113, 16, 124, 31, 107, 127, -75, -92, -3, 86, 24, 40, 35, 0, 78, -38, -71, -73, -67, 107, 70, 94, 25, -120, -81, 60, 29, -87, -23, -15, 62, 61, -41, 23, 123, 106, 43, 49, 120, -72, -66, -45, -126, 16, 43, 113, 24, 52, -18, -81, 39, -33, 108, -81, 37, 15, -127, -60, -128, 81, -36, -3, 109, -37, 3, -29, 22, -64, 103, 70, 95, -120, -26, 74, -70, -72, -87, -108, 122, -51, -27, 121, 117, 55, 53, 114, 108, -80, 124, -19, 126, -67, 121, 89, 2, -104, -31, -36, -124, -85, 79, -24, -57, -44, -119, 31, 30, 109, -15, -99, 86, 102, 30, 0, -26, 23, 48, 63, 118, -73, 23, 14, 55, 62, 17, 75, -115, -83, -97, -41, 118, 55, 64, 116, 23, -96, -77, 101, 29, -68, -92, -56, -101, 94, -37, 74, -117, 64, -47, 53, 18, -11, -26, -105, 96, 60, 1, 97, 5, 64, -45, 45, -49, -1, -114, -25, 52, 81, 111, -10, 2, -72, -66, -65, -78, -35, 127, 1, -10, 55, 102, -64, -66, 36, 61, 63, 0, 0, 0, 0, 73, 69, 78, 68, -82, 66, 96, -126, }; } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/utils/LogUtils.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.utils; public class LogUtils { public static final String APP = "MAP WIDGET"; public static void logHeap(Class clazz) { // Double allocated = new Double(Debug.getNativeHeapAllocatedSize())/new Double((1048576)); // Double available = new Double(Debug.getNativeHeapSize())/1048576.0; // Double free = new Double(Debug.getNativeHeapFreeSize())/1048576.0; // DecimalFormat df = new DecimalFormat(); // df.setMaximumFractionDigits(2); // df.setMinimumFractionDigits(2); // // Log.d(APP, "debug. ================================="); // Log.d(APP, "debug.heap native: allocated " + df.format(allocated) + "MB of " + df.format(available) + "MB (" + df.format(free) + "MB free) in [" + clazz.getName().replaceAll("com.myapp.android.","") + "]"); // Log.d(APP, "debug.memory: allocated: " + df.format(new Double(Runtime.getRuntime().totalMemory()/1048576)) + "MB of " + df.format(new Double(Runtime.getRuntime().maxMemory()/1048576))+ "MB (" + df.format(new Double(Runtime.getRuntime().freeMemory()/1048576)) +"MB free)"); // System.gc(); // System.gc(); // don't need to add the following lines, it's just an app specific handling in my app // if (allocated>=(new Double(Runtime.getRuntime().maxMemory())/new Double((1048576))-MEMORY_BUFFER_LIMIT_FOR_RESTART)) { // android.os.Process.killProcess(android.os.Process.myPid()); // } } } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/utils/MapCalibrationData.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.utils; import android.graphics.Point; import android.location.Location; import android.util.Pair; public class MapCalibrationData { private Pair topLeft; private Pair bottomRight; private float widthInMeters; private float heightInMeters; private float results[]; public MapCalibrationData(Pair topLeft, Pair bottomRight) { this.topLeft = topLeft; this.bottomRight = bottomRight; results = new float[3]; recalculateDistanceInMeters(); } /** * @return returns width of the calibration rectangle in pixels */ public int widthInPixels() { return bottomRight.first.x - topLeft.first.x; } /** * @return returns height of the calibration rectangle in pixels */ public int heightInPixels() { return bottomRight.first.y - topLeft.first.y; } /** * @return Returns width of the calibration rectangle in degrees; */ public double widthInDegrees() { return bottomRight.second.getLongitude() - topLeft.second.getLongitude(); } /** * @return Returns height of the calibration rectangle in degrees; */ public double heightInDegrees() { return topLeft.second.getLatitude() - bottomRight.second.getLatitude(); } /** * @return Returns width of the calibration rectangle in meters; */ public float getWidthInMeters() { return widthInMeters; } /** * @return Returns height of the calibration rectangle in meters; */ public float getHeightInMeters() { return heightInMeters; } /** * Converts location to position on the map. * @param location instance of android.location.Location object. * @param position - out parameter. Can be null. * @return returns the same object that was passed as position, or if it is null - returns new Point object. */ public Point translate(final Location location, /*out*/Point position) { double heightCoef = (topLeft.second.getLatitude() - location.getLatitude()) / heightInDegrees(); double widthCoef = (location.getLongitude() - topLeft.second.getLongitude()) / widthInDegrees(); if (position == null) { position = new Point(); } position.x = (int) (widthInPixels() * widthCoef + topLeft.first.x); position.y = (int) (heightInPixels()* heightCoef + topLeft.first.y); return position; } /** * Converts position on the map to location coordinates. * @param point - position on the map in pixels. Instance of android.graphics.Point * @param location - out parameter. Will contain latitude and longitude of point on the map. */ public void translate(Point point, Location location) { translate(point.x, point.y, location); } /** * Converts position on the map to location coordinates. * @param x - x coordinate in map coordinate system. * @param y - y coordinate in map coordinate system. * @param location - out parameter. Will contain latitude and longitude of point on the map. */ public void translate(int x, int y, Location location) { double heightCoef = (float)(topLeft.first.y - y) / (float)heightInPixels(); double widthCoef = (float)(x - topLeft.first.x) / (float)widthInPixels(); location.setLatitude(heightInDegrees() * heightCoef + topLeft.second.getLatitude()); location.setLongitude(widthInDegrees() * widthCoef + topLeft.second.getLongitude()); } private void recalculateDistanceInMeters() { // width Location.distanceBetween(topLeft.second.getLatitude(), topLeft.second.getLongitude(), topLeft.second.getLatitude(), bottomRight.second.getLongitude(), results); widthInMeters = results[0]; // height Location.distanceBetween(bottomRight.second.getLatitude(), topLeft.second.getLongitude(), topLeft.second.getLatitude(), topLeft.second.getLongitude(), results); heightInMeters = results[0]; } } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/utils/MathUtils.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.utils; import android.graphics.Point; import android.graphics.PointF; public class MathUtils { public static double distance(Point start, Point finish) { return Math.sqrt(Math.pow((start.x - finish.x),2) + Math.pow((start.y - finish.y), 2)); } public static double distance(float x1, float y1, float x2, float y2) { return Math.sqrt(Math.pow((x1 - x2),2) + Math.pow((y1 - y2), 2)); } public static Point middle(Point first, Point second) { return new Point((first.x + second.x)/2, (first.y + second.y)/2); } public static PointF middle(PointF first, PointF second) { return new PointF((first.x + second.x)/2.0f, (first.y + second.y)/2.0f); } public static PointF middle(float x1, float y1, float x2, float y2) { return new PointF((x1 + x2)/2.0f, (y1 + y2)/2.0f); } } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/utils/OfflineMapUtil.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.utils; public class OfflineMapUtil { private static String filesPathCache; private static String cachedRoot; public static String getConfigFilePath(String root) { if (root == null) { throw new IllegalArgumentException("root can't be null"); } if (root.lastIndexOf('/') == -1) { return root + "/" + root + ".xml"; } else { return root + "/" + extractMapName(root) + ".xml"; } } public static String getFilesPath(String root) { if (root == null) { throw new IllegalArgumentException("root can't be null"); } if (filesPathCache != null && cachedRoot.hashCode() == root.hashCode()) return filesPathCache; if (root.lastIndexOf('/') == -1) { filesPathCache = root + "/" + root + "_files/"; cachedRoot = root; return filesPathCache; } else { int indexOfSlash = root.lastIndexOf('/'); if (indexOfSlash != -1) { filesPathCache = root + "/" + extractMapName(root) + "_files/"; cachedRoot = root; } return filesPathCache; } } public static int getMaxZoomLevel(int imageWidth, int imageHeight) { int biggerSize = imageWidth > imageHeight ? imageWidth:imageHeight; if (biggerSize == 0) { return 0; } return (int) Math.ceil(Math.log((double)biggerSize) / Math.log(2.0)); } public static int getScaledImageSize(int maxZoomLevel, int currZoomLevel, int size) { double scale = 1.0 / Math.pow(2, maxZoomLevel - currZoomLevel); return (int)Math.ceil(size * scale); } private static String extractMapName(String root) { int indexOfSlash = root.lastIndexOf('/'); if (indexOfSlash != -1) { return root.substring(indexOfSlash+1); } return root; } } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/utils/PivotFactory.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.utils; import android.graphics.Point; import android.graphics.drawable.Drawable; public class PivotFactory { public enum PivotPosition { PIVOT_TOP_LEFT, PIVOT_CENTER, PIVOT_BOTTOM_CENTER } public static Point createPivotPoint(Drawable drawable, PivotPosition type) { if (drawable == null) { throw new IllegalArgumentException(); } switch (type) { case PIVOT_TOP_LEFT: return new Point(0,0); case PIVOT_CENTER: return new Point(drawable.getIntrinsicWidth() / 2, drawable.getIntrinsicHeight() / 2); case PIVOT_BOTTOM_CENTER: return new Point(drawable.getIntrinsicWidth() / 2, drawable.getIntrinsicHeight()); } return null; } } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/utils/Resources.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.utils; public final class Resources { public final static byte LOGO[] = {-119, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, -96, 0, 0, 0, 110, 8, 6, 0, 0, 0, -96, 45, 24, -23, 0, 0, 0, 1, 115, 82, 71, 66, 0, -82, -50, 28, -23, 0, 0, 0, 6, 98, 75, 71, 68, 0, -1, 0, -1, 0, -1, -96, -67, -89, -109, 0, 0, 0, 9, 112, 72, 89, 115, 0, 0, 11, 19, 0, 0, 11, 19, 1, 0, -102, -100, 24, 0, 0, 0, 7, 116, 73, 77, 69, 7, -37, 9, 27, 14, 23, 28, 37, 57, 1, 112, 0, 0, 32, 0, 73, 68, 65, 84, 120, -38, -19, -99, 121, 124, 21, -27, -67, -1, -33, -49, 89, -78, 64, 18, -62, -110, 0, -15, 4, -54, 38, 40, 84, 48, -118, -89, 22, -91, -126, -126, -43, -21, 82, -80, -83, -43, 107, -83, -30, 66, 93, -80, -83, -46, -34, 74, -75, -117, -2, 80, 106, -107, 94, -105, 122, 113, -93, -42, -123, -126, -74, -105, -42, -107, -53, -107, 10, 114, 65, -114, -72, -79, 111, 2, 9, -100, -124, 16, -78, -111, -28, -84, -77, 60, -65, 63, 102, -26, 100, 114, 114, -78, -97, 4, -119, -25, -5, 122, 77, -26, -52, 100, -26, 121, -98, -103, -7, -52, -25, -69, 60, -33, -25, 25, 65, 74, 98, -78, 96, -63, 2, 30, 122, -24, 33, 0, -42, -81, 95, -33, 111, -63, -126, 5, -33, -40, -66, 125, -5, -40, -102, -102, -102, -2, -83, -99, -105, -107, -107, 85, 63, 100, -56, -112, -118, 51, -50, 56, 99, -33, 107, -81, -67, -74, -51, -27, 114, 5, 83, 119, 51, 37, 29, -106, 57, 115, -26, -116, -49, -53, -53, 123, -84, 127, -1, -2, -59, -128, -76, 45, 122, -36, 118, -85, -1, -53, -55, -55, 57, -106, -97, -97, -1, -113, 81, -93, 70, -51, 121, -7, -27, -105, 7, -91, -18, 108, 74, 90, -107, -45, 78, 59, -19, -6, -2, -3, -5, 127, -47, 10, -56, 58, -77, -60, -128, 57, 112, -32, -64, -99, -89, -100, 114, -54, -4, 45, 91, -74, 100, 1, -36, 113, -57, 29, -87, -101, 110, -118, -8, 42, 94, -12, -36, -71, 115, 121, -26, -103, 103, 40, 42, 42, -70, 124, -17, -34, -67, 47, 52, 52, 52, -28, -103, 96, 17, 0, 30, -113, -121, -47, -93, 71, 83, 84, 84, -60, -8, -15, -29, -55, -49, -49, 39, 61, 61, 29, 33, 4, -118, -94, 112, -4, -8, 113, 106, 106, 106, -88, -86, -86, 2, 64, -41, 117, 2, -127, 0, 85, 85, 85, -108, -108, -108, 80, 81, 81, -63, -98, 61, 123, -120, 68, 34, 86, -107, 18, 16, 66, 8, -14, -14, -14, -34, -104, 54, 109, -38, 125, 43, 86, -84, -40, 118, -61, 13, 55, -16, -30, -117, 47, -90, 0, -8, 85, -110, -15, -29, -57, -77, 125, -5, 118, 119, 94, 94, -34, 91, -107, -107, -107, 51, -19, -32, -104, 57, 115, 38, 55, -35, 116, 19, 69, 69, 69, 20, 22, 22, -110, -106, -106, -42, 106, 89, 82, 74, 74, 75, 75, -39, -77, 103, 15, 59, 118, -20, -32, -24, -47, -93, -92, -91, -91, 33, -124, -64, -19, 118, 83, 94, 94, -50, -50, -99, 59, -15, -7, 124, -44, -41, -41, 55, 1, -29, -64, -127, 3, 55, -50, -104, 49, -29, -26, -27, -53, -105, -17, -102, 63, 127, 62, -113, 62, -6, 104, 10, -128, 95, 5, -103, 62, 125, 122, -31, -122, 13, 27, 118, 70, 34, -111, 44, 107, -33, -20, -39, -77, -7, -35, -17, 126, -57, -124, 9, 19, 98, -64, 18, -94, 125, -73, -58, 126, -20, -79, 99, -57, -8, -32, -125, 15, -8, -4, -13, -49, 113, 58, -99, -79, -3, 105, 105, 105, -108, -106, -106, -78, 110, -35, 58, -74, 111, -33, -34, 4, -120, 67, -122, 12, 89, -79, 121, -13, -26, 27, 10, 11, 11, -61, 41, 0, -10, 114, 41, 42, 42, 58, 109, -21, -42, -83, 59, 84, 85, 5, 16, 99, -57, -114, -27, -123, 23, 94, 96, -54, -108, 41, 73, 41, -33, 2, 99, 32, 16, -32, -115, 55, -34, 96, -57, -114, 29, -72, -35, -18, -40, -1, 29, 14, 7, -11, -11, -11, 108, -38, -76, -119, -11, -21, -41, -57, -10, -89, -91, -91, -23, -29, -58, -115, -5, -47, -42, -83, 91, 95, -7, -39, -49, 126, -58, 31, -1, -8, -57, 20, 0, 123, -109, -52, -102, 53, -117, 105, -45, -90, -11, -71, -25, -98, 123, -114, 43, -118, -30, 4, -60, 117, -41, 93, -57, -46, -91, 75, -101, 0, 36, 89, 98, 1, 113, -41, -82, 93, -68, -10, -38, 107, 9, -113, -79, 64, -70, 103, -49, -98, 24, 27, -26, -25, -25, -65, 125, -12, -24, -47, -53, 47, -70, -24, 34, -71, 102, -51, -102, 20, 0, 123, -109, -28, -28, -28, -84, -86, -85, -85, -101, 9, -120, 5, 11, 22, -80, 112, -31, -62, 14, -87, -38, -50, 74, 40, 20, 98, -55, -110, 37, 28, 63, 126, -68, 73, 93, 82, 74, 92, 46, 23, -121, 14, 29, -30, -49, 127, -2, 51, 38, 43, -109, -103, -103, 89, 51, 107, -42, -84, 9, -53, -106, 45, 43, 75, 1, -80, 23, -56, -80, 97, -61, -24, -37, -73, -17, 55, 119, -19, -38, -75, 1, -32, 59, -33, -7, 14, 43, 87, -82, 76, 58, -37, -75, -11, -1, -91, 75, -105, 82, 82, 82, -126, -61, -31, 72, -8, -1, -105, 95, 126, -103, -3, -5, -9, 3, 72, -73, -37, 45, -50, 57, -25, -100, 41, 27, 54, 108, -40, -104, 2, 96, 47, -112, -52, -52, -52, -125, -95, 80, 104, 56, 32, 116, 93, -17, 48, -21, 73, 41, -39, 120, -72, -114, -99, -27, -11, 84, 6, -94, 72, -87, -45, -57, 41, 24, -42, 63, -125, 51, 61, -71, -116, 24, -48, -73, 85, 32, 90, -1, 123, -27, -107, 87, -40, -73, 111, 95, 66, 16, -90, -91, -91, -79, 110, -35, 58, 86, -81, 94, 13, 32, -123, 16, 98, -14, -28, -55, -33, -7, -24, -93, -113, -2, -39, -101, -97, -115, -77, -73, -125, 47, 47, 47, -17, -25, -57, -113, 31, -1, 30, 32, 22, 47, 94, -52, -71, -25, -98, -37, -95, -13, 119, 87, -122, 88, -68, -79, -108, -35, -107, 65, -114, 71, 84, 20, 85, 67, -45, 117, -94, -102, 70, 69, 125, -124, -113, 75, -86, -39, 86, 90, 75, -31, -128, -66, -28, 100, 36, -74, 39, -123, 16, 72, 41, -103, 56, 113, 34, 7, 15, 30, 108, -90, -114, -123, 16, -24, -70, -50, -80, 97, -61, 40, 40, 40, 96, -37, -74, 109, 2, -112, -91, -91, -91, -41, 76, -98, 60, 121, 75, 89, 89, -39, -18, 20, 3, -98, -100, -22, -9, -20, 67, -121, 14, 109, 6, 100, 118, 118, -74, -88, -85, -85, -21, -48, -7, -97, 29, 13, -14, -4, -89, -57, -56, 112, 65, -70, -112, 124, -77, 48, -117, 97, -3, -46, 9, 69, 85, -74, -8, 107, 40, -87, 14, -31, 118, 11, 4, 16, -115, 106, 92, 48, 58, -113, 25, -89, 13, 105, -111, 13, -83, -3, -117, 23, 47, -74, -57, 5, -101, -119, -33, -17, -25, -7, -25, -97, -113, 57, 39, -25, -98, 123, -18, 5, 31, 126, -8, -31, -70, 20, 0, 79, 46, -16, -115, -14, -5, -3, -5, 116, 93, 7, 16, -69, 119, -17, 102, -20, -40, -79, -124, -61, 97, -22, -22, -22, 8, 6, -125, 68, -93, 81, -94, -47, 40, -125, 6, 13, 98, -56, -112, 33, 77, -128, 18, 84, 37, 119, -65, 87, -118, -37, 5, -29, 114, -45, -72, 115, -14, -32, 102, 54, -33, -114, -14, 58, 94, -3, -72, -124, 52, -105, -95, 82, 117, 77, 99, 92, 94, 54, -41, -98, 51, -94, 85, 16, 6, -125, 65, 30, 121, -28, -111, 102, -86, -40, 46, -91, -91, -91, 60, -9, -36, 115, 0, -46, -27, 114, -119, -117, 47, -66, 120, -60, -63, -125, 7, -117, 119, -18, -36, -103, 82, -63, 95, 118, 25, 52, 104, -112, -73, -68, -68, 124, -85, -108, 18, 64, -84, 88, -79, -126, 111, 125, -21, 91, 0, 76, -97, 62, -99, -41, 95, 127, -99, -73, -34, 122, -117, -43, -85, 87, -77, 118, -19, 90, 46, -72, -32, 2, 6, 14, 28, -40, 68, 37, -82, -40, 93, 75, 73, -67, 66, -106, -53, -63, 125, -25, 13, -115, 1, -54, 14, -86, -4, -84, 116, -94, 58, -108, 84, 7, 1, -119, 3, -88, 108, 8, 81, 113, 60, -60, -124, 83, -6, -73, -88, -114, -45, -46, -46, -56, -51, -51, 101, -25, -50, -99, 45, -126, 48, 39, 39, 7, -113, -57, -61, -42, -83, 91, -123, -82, -21, -14, -16, -31, -61, 55, -105, -107, -107, 61, 90, 89, 89, -87, 111, -34, -68, -71, -41, 60, 43, 71, 111, -71, 16, -113, -57, 3, 64, 86, 86, -42, -36, -54, -54, -54, 77, -46, 64, -97, 120, -6, -23, -89, -7, -2, -9, -65, 31, 59, 110, -63, -126, 5, 68, -93, 81, 84, 85, 101, -28, -56, -111, 44, 91, -74, -116, -47, -93, 71, 55, 43, -17, -29, 35, 33, 34, -102, -28, -46, 49, -71, 49, -32, -60, 81, 25, -1, -77, -73, -110, 117, -5, -85, -48, -123, 64, -45, 65, -109, 18, 4, 108, 47, -83, -30, 95, -69, -4, -83, -74, -9, -52, 51, -49, -92, -96, -96, -96, -43, 99, -58, -116, 25, -61, 101, -105, 93, 6, 32, -62, -31, 112, -33, 1, 3, 6, -84, -3, -45, -97, -2, -108, 98, -64, 47, -95, -93, 65, 121, 121, 57, 5, 5, 5, -81, 6, 2, -127, 123, 85, 85, -107, -128, 88, -74, 108, 25, 55, -34, 120, 99, -109, 99, 71, -113, 30, 77, 97, 97, 33, 51, 103, -50, -28, -10, -37, 111, -57, -19, 118, 39, 84, -107, -81, -18, 58, 78, 68, -123, -85, -57, -11, 35, 55, -67, -23, 109, -38, 85, 25, 98, -47, -6, -61, -20, -85, 10, 34, 1, -105, 0, 93, 98, -4, -47, 53, 28, 72, -10, -107, 85, 49, 102, 72, 127, -6, 101, -90, -75, -88, -118, -121, 15, 31, -50, -58, -115, 27, 113, 58, 91, 126, 12, -123, -123, -123, 84, 87, 87, 115, -12, -24, 81, 17, 14, -121, 11, -57, -114, 29, 91, 87, 85, 85, -75, 41, -59, -128, 95, 34, 81, 85, -43, 51, 104, -48, 32, -65, -86, -86, -41, -26, -28, -28, 80, 84, 84, 36, 62, -5, -20, 51, -82, -71, -26, -102, -124, -57, 95, 124, -15, -59, 76, -99, 58, 53, 49, -77, -103, -94, 73, 80, -92, 32, -86, 75, 76, 85, 14, -64, 71, 71, -126, -4, -63, 119, -116, -80, 112, 18, -43, 5, 87, -100, 62, -104, -2, 25, -23, 104, 82, -94, 74, -119, 38, 65, -45, 116, 92, 46, 7, 47, -81, -35, -118, 48, -63, -106, 72, 21, -25, -25, -25, 39, 100, -33, 38, -19, -48, 52, -82, -70, -22, 42, 43, 49, 66, -18, -37, -73, 111, -15, 61, -9, -36, 51, -8, -26, -101, 111, 78, 1, -16, 75, -31, 69, 9, -15, -77, 80, 40, 116, 56, 28, 14, 23, -44, -41, -41, -13, -13, -97, -1, -100, 79, 62, -7, -124, -119, 19, 39, 118, -87, -36, -127, 25, 110, -92, -61, -63, -57, -27, -111, 24, 72, 53, 41, 121, -22, -77, 90, -124, -53, 69, 68, 23, 76, 42, -56, -26, -68, -31, -71, 28, 56, 30, 70, -107, 14, 52, 4, -86, 102, 0, 81, -43, 52, 66, -118, -62, -101, 31, -17, 109, 53, 62, 120, -63, 5, 23, -60, 122, 65, 90, 3, -31, -83, -73, -34, -118, 25, -57, -108, 47, -68, -16, -62, -101, -90, -105, -100, 2, -32, 9, -108, -66, -64, -57, 82, -54, -59, -31, 112, 88, 14, 24, 48, 64, 108, -37, -74, -115, -7, -13, -25, -73, -54, 108, -19, 21, -17, 41, -103, 40, 58, 44, -33, 83, 79, 64, -47, 1, -8, -25, -2, 0, 42, -126, -120, 6, 19, -13, -5, 112, 75, -47, 96, -98, -1, -12, 40, 56, 93, 40, -70, 68, -47, 36, -86, 4, 77, -105, -26, -94, -13, -63, -114, -125, -44, -121, 34, 45, -78, -32, -120, 17, 35, -24, -45, -89, 79, 91, 47, 25, -7, -7, -7, -100, 117, -42, 89, 0, -94, -74, -74, 118, -14, -92, 73, -109, -82, 76, -39, -128, 39, 78, 10, -127, -61, -26, 90, -36, 120, -29, -115, 98, -19, -38, -75, -12, -17, -33, 63, 41, 125, -69, 82, -62, -8, -127, 105, -68, -78, -85, 1, -23, 116, -16, 63, -59, 65, 50, -35, 14, 94, -36, 89, -113, -108, -126, -17, -114, -50, -30, -58, -81, -9, -25, -71, -49, -85, -8, -65, -61, 13, 56, -84, 4, 104, 41, -111, -70, -114, -44, 84, -48, 53, -48, 53, -124, -128, 64, 32, -52, -124, -81, 13, 105, -79, -66, -70, -70, 58, 74, 75, 75, -37, -20, -46, 27, 63, 126, 60, 107, -41, -82, 69, 74, 41, 27, 26, 26, 102, 70, -93, -47, 71, 82, 0, 60, 49, 82, 6, 100, 2, 98, -50, -100, 57, 44, 93, -70, 52, -87, -119, 5, 66, -128, 67, 8, 70, -26, -70, 121, -89, 56, -120, 38, 4, 31, -107, -121, 113, 56, 28, -72, 28, -48, -57, 37, 120, -30, -77, 106, -118, -21, -94, 56, 4, 72, -87, -101, -64, -45, 65, 87, -111, -70, 110, 0, 80, -45, 64, -41, 57, 92, 81, -51, -123, -109, 70, -29, 76, 16, 114, -111, 82, -110, -98, -98, -50, 39, -97, 124, -46, 106, 92, -48, 82, -59, -39, -39, -39, -20, -35, -69, 87, 68, -93, -47, 62, -93, 71, -113, 46, -85, -82, -82, -2, 52, -91, -126, 123, 86, -82, 49, -43, -81, 0, -72, -6, -22, -85, -69, 45, -85, 101, -54, -48, 12, 46, 25, -34, -121, -120, 34, -47, -92, 32, -86, 73, -126, 26, 108, 56, 26, 65, -111, 2, 85, 10, 20, 41, 81, 36, 40, 58, 40, 82, 55, -42, 26, -88, -70, 68, -47, 117, 84, 77, 67, 74, -55, -121, -69, 75, 90, 84, -81, -61, -121, 15, 111, 87, -5, -123, 16, 76, -103, 50, -59, 74, 33, -109, -27, -27, -27, 15, -89, 108, -64, -98, -105, -3, -10, -115, -39, -77, 103, -29, -9, 55, -58, -36, -114, 28, 57, -110, -76, -118, -92, -108, 44, -104, -100, -117, 3, 73, 84, -41, 99, 118, 94, 84, -125, -88, 46, -51, 109, -127, -94, -117, 24, -16, 20, 13, 84, 105, -38, -125, -102, 68, -43, 116, 52, 4, -101, 118, 28, 108, -75, 46, 123, 79, 76, 107, 18, 10, -123, -72, -12, -46, 75, 1, 68, 67, 67, -61, -64, -119, 19, 39, -50, 74, 1, -80, 103, -27, 35, -96, 26, -93, -97, -108, 64, 32, -64, -80, 97, -61, -104, 58, 117, 42, 35, 70, -116, -96, -96, -96, -128, 80, 40, -108, 44, 15, 27, -128, -17, -113, -51, 66, 85, 33, -86, -101, -64, -45, 116, -29, -73, -122, -63, -128, -102, 9, 70, -109, -11, -116, 109, -35, -4, 109, -80, -32, -66, -14, 42, 20, 85, 107, -79, -82, -63, -125, 7, -93, -21, 122, 66, 103, 37, -66, 77, -33, -8, -58, 55, 44, 117, 45, 75, 75, 75, 127, -107, 2, 96, -49, -53, 116, 32, 108, -127, 16, 96, -3, -6, -11, -116, 29, 59, -106, -54, -54, 74, 50, 51, 51, -109, -56, -126, 112, -18, -112, 12, 54, -6, 67, 28, 9, -88, 70, 124, 80, 7, 69, 51, 24, -79, 9, 27, -22, -46, -8, -97, -82, -37, -114, -47, 81, 52, 13, 93, -43, -40, 91, 118, -84, 69, -128, 13, 29, 58, 20, 85, 85, -47, 52, -83, 9, 16, 19, 29, 31, -119, 68, -84, 56, -90, -88, -84, -84, 60, -21, -2, -5, -17, 31, 118, -1, -3, -9, -89, 0, -40, -125, -78, 5, 56, 7, 91, 50, -59, -51, 55, -33, -52, -86, 85, -85, 24, 48, 96, 64, -110, -29, -116, 48, 38, -41, 5, 18, -10, -41, 42, 108, 40, 11, 83, 82, -81, -96, 74, 12, -75, 108, -78, 95, 84, 51, 22, 69, 51, -9, -87, -74, -75, -86, -93, 11, -8, -94, -84, -78, 69, 91, -81, 95, -65, 126, 40, -118, -126, -82, -21, 49, 16, -102, -119, 20, 9, 65, 120, -10, -39, 103, -57, 126, 47, 89, -78, 100, -18, -125, 15, 62, -104, 2, 96, 15, -53, 118, -5, -58, 115, -49, 61, -41, 109, -50, 72, -102, 67, 24, 80, 23, 6, 24, 74, -22, 20, 54, -108, -122, 40, -87, 83, 81, -92, 36, -86, 89, -10, -95, 78, 84, -45, -119, -86, -102, 9, 78, -29, -73, -94, -23, -88, -70, -92, -92, -68, -70, -27, -96, 102, -33, -66, 40, -118, 18, 99, -63, 68, 32, -76, 3, 49, 55, 55, 55, -42, -1, -83, -86, -22, 117, 41, 21, -4, 37, -112, -18, 26, -33, 17, 84, -91, -39, -39, -37, -88, -12, 37, 80, 114, 60, -54, -122, -46, 6, -54, 27, 20, 52, 93, 18, 85, 117, -109, 1, 13, -42, -117, 42, -86, -71, 109, -128, -80, -94, -74, -27, 124, -60, -12, -12, -12, 102, 0, -76, 64, 104, -83, -19, -50, -111, -94, 40, -116, 31, 63, 30, -128, -102, -102, -102, 97, -113, 62, -6, 104, 65, 10, -128, 61, 47, -5, 44, 72, 44, 95, -66, -68, -37, 42, 57, -36, -96, 26, -58, -96, -76, 41, 125, 105, -52, -64, -95, -21, -110, 61, -57, 66, -8, 14, 31, -89, 62, -94, -96, -88, 26, 81, 85, 37, -86, -86, 6, -16, 84, -107, -88, 98, 108, -41, 52, -124, 91, 125, 121, 44, 0, -58, 47, 118, 54, -76, -128, 40, -124, -32, -76, -45, 78, -117, -99, -1, -44, 83, 79, 93, -102, 2, 96, -49, -53, 35, 22, 36, -82, -67, -10, 90, -34, 122, -21, -83, -92, 87, 32, -91, 100, 75, 101, 20, -100, -62, 4, 29, -74, -75, -11, 91, 39, -92, 104, 124, 114, -72, -122, 61, 71, -21, 12, -42, 51, 65, -89, 40, 26, -118, 106, 44, -95, -120, -46, 98, 61, -119, -64, 103, 103, -62, 120, -75, 44, -91, 100, -16, -32, -63, -79, 102, 6, -125, -63, 111, -89, 0, -40, -13, -14, 60, -80, -44, 2, -54, -100, 57, 115, -70, 69, -83, -65, 87, 28, 4, -105, -61, 64, 92, -52, 14, -45, 77, 86, 52, -25, 34, -110, 26, 32, 41, 63, 30, 100, -45, -2, -93, -44, 5, 35, 40, 74, 35, 27, 70, 85, 13, -67, -107, 16, 75, 67, 67, 3, -86, -86, -94, 40, 74, 12, -116, -10, -33, -119, 0, 25, -119, 68, -84, 108, 26, -95, 40, -54, 57, 41, 0, -98, 24, -71, -55, 64, 3, -52, -104, 49, -93, 91, 42, -8, -5, 23, 1, 3, 100, 66, -40, -116, 64, 11, 120, -70, -79, 88, -35, 111, 82, 67, 85, 21, 62, 59, 80, -58, -95, 99, 53, 104, -102, -118, -94, 26, -125, -103, 50, -36, -82, 22, 89, -74, -90, -90, 6, 77, -45, 98, 96, -117, 103, 68, -5, -1, 44, 0, 42, -118, 66, 94, 94, 30, 0, 117, 117, 117, -123, 41, 0, -98, 56, 89, 5, -56, -116, -116, -116, -92, -85, -33, 53, -2, 16, 117, 65, -51, 6, 56, -5, 98, 1, -49, 6, 64, 77, 3, 77, 5, -87, 83, 82, 94, -59, 103, -5, 75, -119, 40, 10, 81, 85, 37, -73, 111, 70, -117, 44, 123, -8, -16, -31, 24, -88, -20, 12, -104, 8, -112, 118, 22, -76, 122, 80, 52, 77, 99, -31, -62, -123, 39, 29, 8, 93, -67, 4, -128, 59, -128, 75, -37, 99, 3, 30, 57, 114, -124, 101, -53, -106, 81, 92, 92, -52, -87, -89, -98, -54, -83, -73, -34, 26, -101, -47, 42, 17, 48, 22, -84, -81, -126, 52, 97, -38, 123, 122, 35, -5, 89, -32, -109, -70, -95, 126, 99, -55, 7, -86, -79, 104, 10, -24, 26, 13, -63, 8, -66, -35, -121, 56, -35, -109, 79, -63, -32, -106, 99, -108, -5, -10, -19, -117, -59, 1, 93, 46, 23, -70, -82, -29, 116, 58, -111, 82, 54, -7, -19, 112, 56, 98, 107, -121, -61, 65, 86, 86, 108, -114, 37, 124, 62, -97, 7, 35, 75, 40, -59, -128, 61, 44, 107, 1, 42, 42, 42, -104, 55, 111, 94, -116, -67, -102, 29, -76, 118, 45, 5, 5, 5, -52, -97, 63, -97, -89, -98, 122, -118, -69, -18, -70, -117, -52, -52, 76, -118, -117, -117, -101, 29, 47, -91, 100, 99, 121, -104, -113, -4, 97, 83, -21, -38, 88, -49, -82, 114, 117, 13, 84, -75, 41, -16, 84, -43, 96, 65, 93, 53, 109, 69, -99, -99, 123, 15, 49, 98, -16, 0, 90, 50, 3, -73, 111, -33, 30, 99, -65, 104, 52, 26, -5, -35, -110, 103, 108, 45, -10, -71, 109, -114, 28, 57, -46, 63, -91, -126, 79, -116, -68, 3, -44, 2, -14, -87, -89, -98, 98, -22, -44, -87, -60, 15, 95, 124, -23, -91, -105, -104, 54, 109, -102, 65, -5, 46, 23, -9, -35, 119, 31, 119, -33, 125, 55, 82, 74, -34, 126, -5, -19, -72, 121, 91, 12, -10, -5, -34, -101, -27, -112, 46, 64, -56, -90, -74, -97, -44, 76, -10, -45, 76, -90, 83, 65, 85, 12, -48, -87, -118, -79, 79, 83, 76, -112, -102, -64, -43, 97, -14, -87, -123, 36, 10, 85, 30, 59, 118, -116, -46, -46, -46, 38, -64, -77, -122, -116, -38, -105, 68, -96, -76, 75, 40, 20, 74, 79, -87, -32, 19, 39, -77, -127, 127, -127, -47, 47, 60, 97, -62, 4, 50, 51, 51, 41, 40, 40, -32, -16, -31, -61, 68, -93, -47, -40, -127, -101, 54, 109, -78, -78, -117, 121, -20, -79, -57, 98, -116, 103, -127, 80, 8, -8, -23, -38, 74, -54, 2, 90, 35, -8, 116, 25, 99, -77, 24, -13, 105, 54, -80, -87, 10, -88, 17, -48, -94, -58, -94, -85, 49, -49, 24, 96, -24, -48, 1, 12, -55, -51, 78, -40, -16, 15, 62, -8, 0, -121, -61, -47, 76, 5, -57, 47, -106, -22, -75, -74, -99, 78, 39, -118, -94, -40, 77, 6, -103, 2, -32, -119, -109, -9, -127, 123, -128, -57, -52, -89, 46, 66, -95, -112, 53, -31, 79, 76, -90, 79, -97, 30, 3, 95, -68, -67, 103, -55, -54, -3, 1, 30, -1, -72, 22, -36, 54, -43, -117, -87, 118, 53, -43, -76, -11, -20, -64, -117, 26, -32, 83, 34, -26, -74, 98, 122, -60, 22, 96, -31, -102, -23, 103, -73, -40, -16, 55, -33, 124, 19, 85, 85, 99, 51, 102, 89, -32, 114, 58, -99, -51, -64, -24, 116, 58, -101, 108, -37, 51, 127, -6, -12, -23, -45, -112, 2, -32, -119, -107, -59, 24, -67, 35, -1, -35, -46, -75, 61, -5, -20, -77, -83, -10, 25, -65, 119, 56, -60, -20, -107, 101, -112, -26, 48, 1, 100, 119, 58, -84, 84, 123, -43, 0, -99, 18, 53, 64, 23, 13, 27, 0, 84, -93, -26, 98, 2, 80, 55, -25, 41, 15, -122, -71, -19, -78, -13, -102, -43, 43, -91, -92, -86, -86, -118, -113, 62, -6, -120, -84, -84, -84, 24, -88, 18, -127, -82, 37, 86, 108, 104, 104, -60, -36, -48, -95, 67, 43, 82, 54, -32, -119, -105, 55, -127, 116, -32, 55, 64, -87, -3, 31, -117, 22, 45, 98, -44, -88, 81, -51, -64, 103, 57, 32, -49, 110, -81, 99, -58, 10, 127, 28, -8, 52, -45, -42, 83, 27, -99, 11, 37, 2, -47, 8, 40, -31, -58, 37, 26, 50, 65, 24, 49, -43, -81, 53, 73, -66, -32, -52, -15, 35, 25, 61, 116, 96, -77, 122, -123, 16, 60, -15, -60, 19, -72, 92, -82, 38, 118, 94, 34, -69, 47, -34, 6, -76, -106, -118, -118, 70, -52, -3, -32, 7, 63, 40, 62, -39, 30, 86, -81, -97, -98, -19, -40, -79, 99, -69, 107, 106, 106, -58, 14, 27, 54, -116, -12, -12, -12, -124, -79, 62, 33, 4, -41, -67, 91, -63, -85, 59, -21, -116, -112, -117, -59, 92, -70, 108, 100, 61, -51, -12, 112, -107, -80, 1, 64, 37, 12, -47, 32, 68, -126, 16, 110, 48, -42, -47, -112, -63, -122, -70, -23, -112, 8, 1, 13, 33, 86, 63, 49, -97, -117, 38, -114, 105, -58, 126, 82, 74, -100, 78, 39, -125, 6, 13, 98, -16, -32, -63, 56, 28, -114, 24, -37, 89, 42, -72, -75, 109, -105, -53, -59, -89, -97, 126, -54, -2, -3, -5, 73, 79, 79, 15, 69, 34, -111, 62, 41, 21, 124, -94, -27, -55, -125, 48, 111, 4, -4, -31, -117, -63, 59, 110, 42, -68, 118, 80, -1, -76, -79, -125, 6, -75, -4, -83, -104, -22, -120, -50, -103, -81, -108, 114, -72, 65, 49, 108, 62, 43, -21, -60, 2, -97, 102, 83, -71, -106, -99, 23, 53, 25, 47, 18, -124, 72, -64, -40, 86, -52, -1, 89, -25, 8, 35, 127, 107, -62, 105, -61, -103, 49, -23, -44, -124, 54, -25, 47, 127, -7, 75, 0, 42, 43, 43, 73, 75, 75, 35, 39, 39, -89, 69, -75, -101, 104, 91, 8, 65, 89, 89, 25, -128, -52, -52, -52, -36, 108, -5, 44, 68, -118, 1, 79, 48, 8, 31, 2, -18, 69, -47, 105, -104, -9, 53, -39, -41, -27, 72, 120, -99, -1, 87, 22, -26, -4, -27, -91, -32, 22, 54, -107, 107, 5, -104, 85, 80, -29, -63, 23, 54, -63, 23, 108, 4, 96, 52, 12, -118, -71, 86, 21, -29, 60, 93, 3, -100, 16, 8, -80, 107, -39, 66, -58, -98, -110, -41, -116, -3, -86, -85, -85, -119, 127, 49, -122, 15, 31, 78, 122, 122, 122, 19, -74, 107, -115, 1, 29, 14, 7, -85, 86, -83, 2, 32, 59, 59, -5, 87, -11, -11, -11, 15, -91, 24, -16, 68, -77, 31, 124, 29, -72, -41, 96, 49, -40, 84, 30, 21, 23, 122, -102, 119, -127, -67, -70, -89, -127, -21, -34, 60, 106, -60, -7, -102, -128, 79, 107, -86, 114, 45, -57, 66, 9, 67, 36, 4, 74, -88, 17, 124, -106, -19, -89, -124, -115, 99, -91, 106, 50, -88, 3, 84, -107, -97, 94, 123, 9, -29, 60, -7, 9, -39, -17, -54, 43, -101, -113, 43, 63, 116, -24, 16, -61, -121, 15, 111, 6, -74, 68, 94, -79, -45, -23, -92, -68, -68, 60, 118, -18, 89, 103, -99, -11, -33, -47, 104, -108, -115, 27, 55, -90, 0, 120, -62, 100, -34, 8, 120, -14, -32, -4, -58, -128, -79, -92, -96, 111, 115, 63, -21, -39, -19, 117, -52, 93, 117, -52, 4, -97, -103, -25, 23, 11, 44, -85, -115, -127, 101, 53, 108, 122, -70, -31, 70, -75, 27, 13, 54, -2, -74, -20, 65, -53, -26, -109, 38, -8, -112, -116, 40, -56, -29, -113, 63, -98, -99, -48, -29, 94, -78, 100, 9, 27, 54, 108, 72, 104, -113, 30, 57, 114, -124, -95, 67, -121, -58, -70, -32, -84, -75, -3, -73, -45, -23, 36, 61, 61, -99, 3, 7, 14, -128, -47, 7, 94, -74, 118, -19, -38, -109, 114, 22, -43, -34, 103, 3, -62, 101, 86, 52, -71, -80, -65, -101, -45, -6, 55, -3, -38, -47, -33, -9, 7, -104, -69, -86, 2, -46, 29, -115, 73, 5, -70, -51, -45, -43, -84, 16, 75, -60, 0, 96, 12, 120, -90, -22, 85, 34, -115, -32, 83, -93, -115, -128, -115, -127, 79, 39, 77, -105, 124, -10, 95, -1, -111, 48, -20, -78, 123, -9, 110, 110, -69, -19, -74, 22, 27, 31, -119, 68, -88, -83, -83, 37, 59, 59, 59, -42, -17, -101, 40, 14, 88, 95, 95, 111, 5, -41, -123, -37, -19, 126, 50, 28, 62, 57, -65, 115, -45, -69, -62, 48, 79, 30, -52, 1, -116, 30, 127, 77, 114, 87, 81, -65, -90, 15, -65, 70, -31, -69, 43, -53, 27, -63, -121, -107, 66, 101, -122, 88, -44, -88, -95, 98, 35, 38, -45, -123, -125, 16, 14, 24, 75, 36, 96, 58, 29, 13, -115, 33, 23, 93, 105, 4, -97, -60, -24, 53, 9, 43, -20, -4, -53, 111, -55, -55, 76, 111, 6, -66, -38, -38, 90, -50, 60, -13, -52, 54, 47, -93, -74, -74, -106, 72, 36, -110, -80, 75, -50, -22, -122, 43, 41, 41, -119, -87, -13, 117, -21, -42, 61, 118, -78, 62, -78, -34, -58, -128, -89, -101, -113, 27, 84, -55, 15, -57, 102, 53, 9, -75, -100, -13, -41, -46, 70, -75, 107, -123, 89, -92, 102, 56, 27, 106, -60, 80, -73, -86, -59, 118, 97, 3, -116, 74, 8, 34, -31, 56, -42, -77, -87, 92, -117, 69, -123, -64, 17, 81, -40, -11, -22, 3, -116, 26, 50, -80, -103, 106, -83, -83, -83, 101, -8, -16, -31, -76, -41, 83, -83, -82, -82, 102, -32, -64, -127, -79, 112, -115, 93, 5, 7, 2, 1, 2, -127, 0, 0, -103, -103, -103, -113, 21, 21, 21, -87, 39, -21, 3, 115, -12, 34, -10, 3, -8, -102, -27, -36, 15, -17, -17, 102, 112, 31, 103, -116, 37, 110, 88, 93, 65, -67, -86, 55, -75, -7, 116, -45, -45, -43, -52, 16, -118, 98, -78, 94, 36, 96, 48, 93, -72, 1, 66, 38, -5, 89, -22, -41, 2, 96, 44, 35, -38, 72, 56, -56, 77, 115, -29, -1, -37, 34, -58, 12, 109, 30, -14, 41, 46, 46, -90, -96, -96, -96, -43, -119, -55, -29, 69, 85, 85, 2, -127, 64, -109, -28, 3, 107, 102, -41, 67, -121, 14, 1, 72, -121, -61, -95, 87, 84, 84, -4, 98, -46, -92, 73, -92, 0, -8, 101, 112, 64, 96, -120, 69, -128, -105, -116, -56, -116, -79, -49, -82, 26, -123, -65, 108, -83, 51, -126, 78, 18, -64, -20, 38, -45, 76, -16, 69, 35, 54, -43, 107, -86, -38, -80, -71, 88, 33, 23, 37, -46, -104, 100, 96, 79, -51, 10, -122, -8, 118, -47, 56, -114, -83, -4, 3, 67, 114, -77, 99, 106, -41, -22, 93, 121, -29, -115, 55, 24, 57, 114, 36, -99, -79, -47, -30, -45, -12, 53, 77, -93, -94, -94, -62, 42, 91, 100, 102, 102, -34, -100, -107, -107, -91, 127, -2, -7, -25, 39, -19, 99, -21, 109, 42, -40, -120, 121, 40, 58, 51, -121, 103, -58, 84, -17, 109, 107, -114, 65, -70, -45, 102, -9, 89, 65, -26, 104, 99, -81, 70, -60, 4, 91, -60, 10, -79, -124, 108, 125, -68, -74, -8, -98, 21, -82, -119, 42, 100, -70, -35, -68, -76, -16, 14, -66, 123, -2, -103, 77, 28, 14, -21, -9, 29, 119, -36, -63, -45, 79, 63, -35, -91, 11, 10, 6, -125, 100, 100, 100, 32, -91, 36, 20, 10, 17, 12, 6, 1, -92, -45, -23, -4, 48, 16, 8, -4, -7, 100, 127, 96, -67, 13, -128, 70, -66, -109, 46, 57, 43, -49, -24, 118, 43, -87, 87, 89, 87, 28, -126, 52, 26, -103, -53, 74, 28, -115, -123, 88, 66, 77, 123, 55, -84, 80, -117, 5, -66, 88, 106, -107, -128, 112, 20, -31, -112, -36, -9, -61, 75, 120, -32, -122, 43, -102, -60, -10, 44, -32, 109, -34, -68, -103, -53, 47, -65, -68, 73, 63, 109, 103, 37, 26, -115, -30, 114, -71, 80, 85, 53, 6, 62, 33, 68, 120, -62, -124, 9, 23, 30, 57, 114, 36, 41, 117, -92, 0, -104, 60, 113, 3, 18, -73, 16, -61, -78, -115, 75, -5, -45, -42, 58, -93, -89, -61, 26, 64, 100, -91, -50, 39, -22, -35, -80, 84, -82, 98, -87, 92, -43, -16, 116, -91, -124, -122, 32, 5, 67, 6, 113, -17, -113, 103, 115, -25, 21, 83, -101, 57, 25, 66, 8, 74, 74, 74, -72, -29, -114, 59, 120, -25, -99, 119, -110, 122, 81, -31, 112, -40, 26, 15, 44, 1, -111, -106, -106, 118, -42, -106, 45, 91, 122, -59, -9, -123, 123, 27, 0, 117, 64, 100, -89, 53, -50, -69, -7, -6, -98, 6, -45, -10, 51, -39, 79, 51, 85, -81, 26, 109, -22, -19, 90, -67, 28, 74, -40, -80, 9, 53, -59, 96, -67, -6, 0, -25, 23, -115, 99, -31, 77, 87, 112, -2, -41, 71, 55, 3, 29, -64, -58, -115, 27, 121, -32, -127, 7, -84, -17, -68, 37, -1, -94, 108, -32, 115, -71, 92, 23, 71, 34, -111, 93, -67, -27, -127, -11, 54, 0, 86, 1, 100, -104, -8, 11, 41, 58, -59, 53, 102, -110, -127, 21, 112, -42, -43, -58, 60, 62, -59, -58, -128, -79, 44, 23, -45, -47, 8, -122, -104, 52, -58, -61, 43, -9, -2, -126, -15, -61, -121, 52, 25, 51, -94, -21, 58, -17, -65, -1, 62, 43, 87, -82, 100, -7, -14, -27, 84, 85, 85, 117, -9, 117, 73, 67, -53, -117, 43, 84, 85, 93, -35, -101, 30, 88, 111, 3, -32, -127, -40, -29, 2, -74, -43, -104, -29, 50, -92, 48, 123, 58, -84, 52, 122, -45, -10, 83, 35, 77, -63, 103, 5, -105, 27, 2, 44, -103, 127, 45, 115, 47, -99, 18, 3, -36, -22, -43, -85, 121, -9, -35, 119, 89, -77, 102, 13, 59, 118, -20, -24, -55, 107, -78, 38, 4, 57, 79, 74, -71, -95, -105, 61, -81, 94, 7, -64, 61, 96, 76, 36, 9, 112, -88, 94, -59, -100, -60, -71, 49, -93, -39, -22, -15, -48, -44, -58, 49, 28, 86, -120, 69, 83, -23, -25, -128, 29, -81, 63, -60, 41, 3, -5, -15, -50, 59, -17, -80, 104, -47, 34, -42, -81, 95, 127, 34, -81, -87, 12, 56, 27, 40, -89, 23, 74, 111, 3, -32, 22, -128, 58, -43, 64, 96, -64, 66, 34, -10, 97, -108, 74, 35, -8, 84, 5, 20, 37, 54, -78, -83, -97, 19, 74, 95, 125, -112, 112, -96, -98, -119, 19, 39, -78, 117, -21, -42, 19, 117, 29, 22, -21, 45, 1, 110, -93, 23, 75, -17, -22, 11, -98, 55, 34, 0, -78, 76, 42, -110, -128, -94, -29, 116, -120, -58, -18, 50, 123, -118, -107, 125, 48, -111, 22, -115, -87, -35, -99, -49, -35, 75, -55, -127, 47, -56, -49, -49, 63, 81, -32, -109, 54, 83, 98, 82, 111, 7, 95, -17, 3, -96, 17, 37, 91, -123, 46, -39, 89, -93, 48, 40, -61, -47, 56, 46, -41, -98, -21, 23, 27, 68, 110, -126, 50, 24, -30, -15, 59, 103, 35, -62, 13, -116, 31, 63, -66, -55, 92, 124, 54, 80, -12, -124, 124, 1, 92, 5, -116, -74, -40, -68, -73, 75, 111, 76, 72, -3, 43, 110, 49, -25, 125, 127, -120, -17, -114, -22, 107, -101, -51, -64, 54, -96, -36, -4, 126, -121, 21, 96, 30, -36, -65, 47, 119, 93, 126, 94, -4, 76, -11, -106, 26, 60, -126, 49, 3, -41, 58, -45, 30, -45, -128, -127, -64, 24, -32, 92, -116, -7, -86, -57, 116, 82, -59, 2, 52, 0, 43, -127, 39, -127, -51, 124, -59, -92, -105, -90, -28, 31, -120, 78, -20, -25, 114, 127, -2, -61, 66, -60, -30, 61, 102, -24, 37, 100, -90, 86, -43, 27, 75, -88, -63, 88, 31, -81, -31, -39, 121, 87, 82, -74, -31, 109, 126, -5, -37, -33, -58, 3, 100, 46, -16, 108, 59, 106, -52, 2, 102, 0, -105, 0, -33, 0, 78, 107, -27, -27, -82, 4, 62, -58, -104, 78, 100, -43, 87, -123, -23, -66, 42, 78, -120, 37, 75, -74, -108, 71, -26, 85, -122, 53, -68, -7, -23, -8, -54, -107, -90, -77, 91, 53, -103, -51, 74, -27, -106, -117, -50, 70, -52, -104, 108, 7, 95, 0, 24, 71, -36, -80, -50, 86, -60, 98, -79, -107, -74, 125, 125, 48, -6, -90, 51, -128, -88, 89, -26, 81, 82, -46, 68, 122, -33, 23, -45, -97, 60, 0, -80, 25, -89, -8, -123, -94, 74, -66, -23, -55, 96, -11, 23, 117, 32, -29, 103, 48, 48, -42, 69, -123, -125, 24, 18, 60, -60, -14, -41, 94, -73, -37, 123, -123, 73, 8, 123, 40, 24, -13, -43, 84, 2, 53, 38, 0, 83, -46, -21, -99, -112, 121, 35, 97, -34, -56, 74, 4, 43, -2, -13, -29, 90, -82, 63, -67, -97, -15, 97, 15, -53, -38, -80, 39, -93, -22, 58, -45, 38, -97, -50, 10, 3, 124, -106, 73, 114, 13, 112, 44, 5, -115, 20, 0, 59, 47, 79, 28, 0, -28, -113, 113, -62, -93, -101, 107, -104, 57, 58, -121, -58, 105, -87, 68, -29, 116, 107, -86, -58, -23, -123, 121, 124, 106, -28, -45, 73, -45, 11, 93, -111, -126, 69, 10, -128, 93, -109, -69, 70, -62, 93, -93, 107, 113, 56, -18, -3, -3, -58, 99, -36, 62, 121, 16, -124, 52, 16, -26, -27, -118, 70, 54, -52, 112, -69, 9, 52, 4, 44, -10, 123, 40, 5, -119, 20, 0, -109, 39, 63, 25, -75, -120, 76, -41, -50, 59, 87, -105, -55, 91, -67, -125, 13, -114, 115, -120, 70, 117, -20, 114, 114, -88, -70, -114, -36, -36, -40, -32, -91, 55, 83, -112, 72, 1, 48, 121, -14, -8, 94, 0, -81, 63, -88, 41, 27, -113, -124, 101, 78, 102, 26, 8, -89, 113, -43, 66, -128, -61, -63, -70, 79, -9, 113, -58, -23, -29, -20, 33, -110, -108, -92, 0, -104, 44, 6, 60, 21, 52, -67, 1, -63, -60, -19, 85, 17, 81, -89, 72, -119, -61, 97, 92, -74, 16, 32, 4, -85, 54, -17, 96, -31, -61, -65, -57, -19, 118, 63, -106, -126, 67, 10, -128, -55, -105, -69, -57, -63, 61, -29, 119, -29, 16, -109, 112, 58, 5, -62, 41, 113, -70, -64, -31, 50, 64, -88, 104, -44, -69, -77, -119, 70, -93, -1, -5, -16, -61, 15, -89, 16, -111, 2, 96, 55, -55, 47, 38, 110, 65, 112, 10, 46, 119, 37, 78, 55, 56, 93, -32, 116, -125, -61, -55, -64, -84, 62, 0, -45, -18, -67, -9, -34, 20, 34, 82, 0, -20, 70, 81, -43, 50, 30, -104, -106, -113, -45, -3, 48, -18, 12, 13, 87, 58, 119, -52, -2, 22, 5, 3, -78, -63, -104, -44, 50, 37, 41, -23, 57, 57, 127, -47, -37, 125, -92, -108, 79, -21, -70, -66, 75, 74, 57, -84, -83, -81, -107, -89, 36, 37, 73, 21, 59, -32, 82, -32, 75, 73, 74, -66, -54, 44, -48, 29, 12, -112, -20, 50, -69, -109, -91, 122, -14, -6, -69, -13, -66, 36, -93, -20, -98, -46, 6, 82, 74, -124, -108, 114, 4, -16, 93, -32, 93, 33, -60, -10, 36, -33, -108, 124, -32, -38, 36, 57, 59, 2, -40, 34, -124, 120, -49, 26, -109, 43, -91, -68, 28, 40, 22, 66, 108, 107, -19, -45, 11, 109, -76, 51, 29, -72, 5, 88, 47, -124, -40, 98, -37, 127, 13, 48, -76, 43, -73, 0, 40, 6, -34, 18, 66, 40, 113, 117, -98, 11, 76, -63, -4, -54, 103, 18, -28, -17, 64, -119, 121, 79, -58, 2, 23, -48, -7, 76, 110, 1, -44, 1, -17, 11, 33, -54, 19, -51, 113, 104, -42, -109, 99, 94, -61, 80, 58, -105, -42, 39, -128, -43, 72, 41, -53, -92, 41, -35, -128, -16, 61, 50, -7, 50, -46, 44, 123, -119, 109, -33, 51, 29, 121, 115, 109, -84, 127, -123, -108, 50, 106, 43, -25, 20, 115, -1, -84, 36, -73, -7, 26, 91, -35, -39, -35, 112, 79, 106, 109, -27, 39, 83, -98, -117, -65, 111, 82, -54, 62, 82, -54, 119, -110, 84, -2, 81, 7, -42, 124, 42, 70, 5, 105, 73, -58, -32, -64, 100, -30, -39, 92, 91, -97, 34, -8, -90, -19, 127, -73, 74, 41, 107, 0, 111, 59, -127, -24, -110, 82, -2, 3, -8, 103, -36, -37, 59, -54, 92, -9, 79, 114, -69, -105, 73, 41, 11, -51, 118, 37, -5, -125, -126, 18, 35, -23, -75, 59, -98, -33, -51, 82, -54, -97, -40, -103, 15, 56, 4, 124, 59, 73, -19, 110, -10, 5, 101, -39, 13, 55, -57, -82, 38, 66, 93, 84, 13, -101, 108, 102, -126, 22, -9, -1, 126, -64, 38, 41, -27, -77, 66, -120, -71, -83, -88, -114, 111, 99, 100, 46, 103, -40, -54, -115, 111, -81, -67, -115, -59, 24, -29, 65, 58, 106, 70, -92, 3, -33, -73, 109, -33, 46, -124, -72, 55, -18, -27, 8, 3, 127, -91, -13, -103, -23, -62, 84, -29, 45, 13, 27, 120, -119, -114, 15, -69, 112, 0, -1, 102, -34, 79, -128, 59, -127, -57, -51, 123, -9, -37, 56, 82, 41, 3, 86, 3, -111, 78, -74, -3, 69, -92, -108, -11, 54, 74, 116, 39, 89, 5, 31, -77, -107, 61, 56, -55, 101, 127, 102, -106, -85, -57, -83, -91, -108, -78, 70, 74, -23, -115, 83, -73, 78, 41, -27, 10, -37, -79, -42, -15, -86, -19, -68, -13, -51, 99, 111, -76, -19, 123, -87, 11, 109, -100, 103, -85, 111, -91, -71, 111, -104, 93, 5, 37, -7, -98, -92, -39, -11, 91, 23, -54, 25, 108, 43, 70, -73, -19, -33, 111, -37, -1, -41, 100, -76, -71, 39, 123, 66, -46, 58, 121, 51, -38, -13, 38, -51, 4, -2, -49, -58, 94, 22, 27, 62, 99, -66, -71, -45, -127, 122, 27, 35, 9, -109, 65, 103, 2, 31, -75, 81, 126, 87, -122, 45, 84, -38, -22, 115, -76, -48, -10, 47, 99, 84, -92, -70, -123, 54, -114, -76, -3, -98, -105, -24, -39, 116, 20, -9, 61, 57, 40, -23, 7, -90, -99, -42, 81, -102, -34, 101, 3, 87, 75, 18, 16, 66, 76, -107, 82, -34, 4, 60, 111, 83, -95, -73, 74, 41, -81, 51, -19, 70, -21, -93, -65, 2, 120, 15, -72, 76, 8, 17, -111, 82, 46, 106, -93, -20, -47, 82, -54, 27, 58, 120, -81, 36, -112, 11, -4, -54, 86, -25, -95, 4, -57, 101, -104, -19, -53, -24, -60, 125, 121, 23, -16, -73, -26, -7, 75, 41, 111, -20, -60, 11, 36, -127, -101, 108, -37, -91, -74, 104, 65, 99, 3, -124, 104, 41, 117, -19, 84, 41, -27, -65, -75, 81, 71, 4, 120, 79, 8, -79, -73, -89, 84, -80, -34, 69, 111, -23, 107, -83, -88, 96, 41, -91, -4, -90, 109, 127, -82, -108, 114, 115, -126, 122, 117, 41, -91, 38, -91, -4, -9, 56, -43, -4, 73, 27, 42, -72, 43, 98, -43, 31, -106, 82, -114, 49, -67, -56, 97, 73, 42, -69, -86, 13, 21, -84, 39, -87, -98, 69, 22, 0, 19, -87, -27, 4, 109, -48, 58, 80, -10, 92, 71, -110, -87, -69, 37, 10, -18, -84, -86, -79, 10, -53, -18, -64, 57, -75, 66, -120, -55, -64, 93, -40, 102, -123, 6, 62, 4, -6, 9, 33, 94, 53, -33, -32, -98, 96, 125, -85, -110, 32, -55, 77, 118, -107, 64, -33, 118, -42, -35, -111, -5, 44, -29, 28, -80, 79, -124, 16, -65, -20, -96, 90, 117, 116, -64, -47, -4, -99, 43, -119, -32, 27, 44, -91, -68, -42, 84, -105, -119, 70, -8, -1, -125, -114, 15, 77, 20, -64, -25, 66, -120, 109, -19, 62, 33, -10, -43, 115, -15, -92, -108, -14, -97, 24, -29, 60, -2, 37, -124, 88, -38, 73, -69, -68, 24, 88, -33, 9, 123, 57, 19, -29, 43, -18, -46, 12, -67, -4, 84, 8, -15, -101, -72, 54, 68, -128, -27, -99, 48, -123, 28, -64, 115, -19, 56, -18, -107, 4, 64, -44, 77, 47, 119, 64, -36, 125, 62, 106, 70, 7, 114, -52, -105, -27, 109, 33, -60, 106, 91, -12, -96, -67, -128, -98, 9, 92, -33, -58, 11, -16, -17, -26, 58, 55, 105, 42, 88, 74, -7, -123, -83, -100, -52, 30, -12, -126, -101, -88, -32, -114, 26, -60, 82, -54, -11, 61, -28, 5, 47, -5, 50, 121, -63, 82, -54, 7, 18, 68, 4, 62, -106, 82, -98, -35, -46, -67, -117, 83, -97, -103, 93, 104, -89, 37, -31, 100, 122, -63, 57, -74, -33, -3, -109, -27, 5, 119, 73, -1, -75, 79, -51, -74, -123, -46, -82, 120, -63, -43, 109, 68, 28, 68, 23, 1, -41, -107, 123, -13, 107, 32, 15, -8, -105, -51, 84, 41, 2, 54, 75, 41, -41, 90, 30, 111, 92, 29, -10, 23, -26, 63, -110, -15, -116, -30, -87, 127, -114, -108, -78, 51, 95, -35, -119, -60, 1, 76, -74, -32, 5, 87, 119, -47, -98, -38, 38, -124, -16, -11, 48, -114, 79, -107, 82, -50, 107, -89, 10, -50, -79, 93, 123, 58, -16, 19, -102, 78, 114, -44, -110, 23, -100, -34, 69, 16, -66, 69, -25, -90, -3, -88, 20, 66, 92, 36, -91, -68, 16, 88, -122, -11, -103, 11, -104, 10, -20, 55, 89, 123, -82, -108, -78, -63, 124, -103, -1, -127, 49, 95, -114, 4, 126, 99, -58, 90, -33, -64, -104, 122, -92, -45, 23, 80, -97, 100, -81, 73, 74, 41, -121, 38, -39, 11, -74, -53, -80, 4, 42, 120, 74, 23, -82, -1, -125, 86, 84, -80, -98, 36, 47, 88, 74, 41, 39, 37, -39, 11, -74, 75, 101, 87, 2, -47, 54, 7, -14, 87, 9, -54, 62, 102, 29, 99, -21, -57, -42, -69, 120, 127, -84, -13, 106, 29, 38, 122, 101, -110, -126, -94, -46, -58, -120, 96, 116, 53, 37, -69, -20, 92, -101, 49, 109, -19, -21, -54, 84, 26, -10, 76, -107, 96, -36, 62, -47, -59, -10, 90, -25, -1, 63, 33, -60, -25, 38, -117, 4, -37, -87, -6, 59, 82, 79, -74, -87, 86, -93, -74, 125, 74, 39, 28, -73, -123, -26, -3, 125, -53, 86, 78, -125, 117, -116, 16, -94, 30, 24, 107, 51, 45, 68, 23, -81, -29, 25, 23, -16, 61, -32, -26, 36, -35, 16, 1, -84, 17, 66, 88, 13, -100, 5, -36, -99, -60, -78, 63, 23, 66, 88, 83, -105, 94, 11, -4, -36, 12, 21, -20, -19, -126, -6, -70, -34, 12, 24, -17, 20, 66, 124, 98, -13, 30, 39, -47, -75, 116, -84, -80, -23, 65, -65, 38, -124, -40, 99, 15, -32, 74, 41, -25, -46, -75, -108, -87, -8, -5, -14, -126, 109, 123, 22, 112, 37, -58, -100, -122, -99, -111, -29, 66, -120, -53, 77, -83, -14, 99, -32, -105, 113, -3, -22, 123, -123, 16, -125, -92, -108, 51, -128, 11, -127, 83, 58, 17, 33, 80, 77, 47, -5, 53, 78, 86, 73, 86, -10, 88, 50, -70, -109, 122, -94, -99, 39, -22, -2, -10, 84, 82, 109, 74, 82, -110, -110, -108, -92, 36, 37, 41, 73, 73, 74, 82, -110, 28, -15, -7, 123, -26, -100, -8, -13, 58, 82, 70, 71, -50, -21, 108, 29, 39, -78, -116, -8, 99, 123, -14, -103, -76, -45, -123, -17, 110, -16, -67, 10, 92, -122, -47, 57, 47, -37, 104, -53, 110, -68, -98, 51, -52, -13, 111, 4, -18, -61, -104, -81, -71, -91, -13, 74, -128, -121, -15, 122, -2, 108, -98, -13, 30, 112, 6, 94, 79, 126, 7, -37, 106, 124, -4, -51, -21, 25, -33, -114, 99, -85, 1, 31, 94, -49, 37, -8, -4, -91, 64, 65, 7, 106, 122, 25, -81, -25, 122, 124, -2, 16, -16, 6, 94, -49, -43, 102, -103, -13, 48, 66, 74, 67, 90, -72, 86, -119, -15, -15, -102, 95, -29, -11, -4, -51, -42, 102, 7, -16, 109, -68, -98, -110, 54, -38, 124, 4, -40, -118, -41, 115, 49, 62, -1, 32, -116, 30, -115, 51, 105, 59, 9, -62, 1, 60, -125, -41, 115, 103, 119, -63, -92, -5, 18, 82, -67, 30, -16, -7, 23, 97, -60, -21, 116, -116, -82, 34, -47, 6, 0, -113, -102, 55, 108, 22, -115, 113, -84, 74, -116, -72, 81, -68, -72, 49, -66, -49, -79, 20, -97, 63, -120, -41, -77, -62, -36, -50, -61, -25, 23, 120, 61, 29, -119, 19, -116, 108, 39, 80, 29, 24, -3, -36, -42, -124, -126, 7, 48, -6, -118, -19, -23, 76, -3, 48, 18, 96, -85, 105, -38, 69, 37, -128, -61, -8, -4, 105, 24, 9, -88, 99, -51, 50, 111, 1, -98, 48, -113, -87, 32, -15, 80, -51, 116, -116, 79, 63, -68, -114, -49, 127, 17, 94, -49, 26, -64, -125, 17, -128, 46, -58, -25, 127, 16, -81, -25, -41, -8, -4, -58, 125, 111, -38, 102, -105, 9, 108, 43, 0, -2, 22, 112, 14, 70, -100, -78, -70, 29, 0, -20, -42, 79, -127, 118, 119, 70, -12, 55, -52, -75, 7, -81, -25, 72, 7, -50, -5, -87, -71, -2, 94, -20, -115, 79, 12, -120, -85, 49, -46, -103, 126, -122, 49, -73, 115, -94, 65, 69, -19, -111, -50, 29, -17, -11, -100, -97, -96, 77, 127, 0, -26, 3, -77, -16, 122, 62, 72, -16, -1, -52, -72, 58, -83, 107, -67, 16, 34, 122, 42, -82, 0, 0, 2, -107, 73, 68, 65, 84, -81, -25, 95, -83, 92, -21, -19, -64, -97, -128, 123, -128, 53, -26, 94, -51, 124, -122, -9, -29, -13, -49, -63, 72, -123, -38, 25, 7, -60, -8, 123, 82, 4, 68, -16, 122, -6, 116, 72, -101, -59, 3, -5, 36, 1, 96, -67, -71, -66, 27, -97, 127, 67, 27, 12, -72, 25, -81, -57, 31, 3, -84, -15, -128, -1, -42, 6, -53, -82, -64, -25, 95, 14, 12, -1, 18, 89, -66, 86, 98, 65, 123, -45, -107, 60, 64, -76, 85, -16, 25, -14, -84, 9, 64, -5, -75, -22, 38, 35, 111, 0, 78, 7, 118, -32, -13, 63, -114, -41, -13, -45, 86, 64, 19, 0, 114, 77, -26, 109, 45, 73, 86, 1, -42, -32, -11, -124, -70, 11, 124, 61, 1, -64, -57, 76, -5, 111, -66, -71, -76, -11, -90, -35, -126, -41, -13, 124, 15, 48, -40, -105, 73, -100, -19, 108, -65, 35, -127, -35, -18, -64, -21, -87, 5, -58, -29, -13, -1, 2, -8, 61, -16, 19, 124, -2, -21, 49, -110, 78, 63, 76, 80, -50, 98, -32, 1, -38, -9, 5, 40, -16, -7, -65, 14, 108, 63, 57, 25, -48, -21, 89, -117, -49, 127, 42, 112, -123, 105, -77, -75, 36, -89, 96, -116, 63, -3, 61, -58, -96, -94, -114, 58, 71, 29, 119, -90, 12, 6, 80, -16, 122, 94, -20, -64, 89, 122, 55, 0, 94, 38, -27, 56, -81, -25, 17, 124, -2, -65, 0, -1, 11, 124, 29, -40, -120, -49, -1, 18, 94, -49, -113, -102, 120, -79, 94, -49, -125, -8, -4, -21, 109, -26, 81, 75, -41, 121, 30, 112, 57, -80, 16, -81, -25, -54, -109, -107, 1, -63, -21, -39, 103, 50, 97, 91, -128, -72, -119, -58, 76, -105, 106, 96, 36, 62, -1, -39, 120, 61, 31, -73, 114, -50, 68, -13, -41, -47, 102, 14, 80, -37, -14, 95, -90, 97, -2, -94, 121, -2, -41, -16, -7, 71, -32, -11, 28, 108, -27, 28, 43, -19, -85, 52, 9, 119, 70, -73, -75, 125, 20, 62, -1, 72, -68, -98, 3, -83, 28, 111, -39, -101, -83, 125, -63, -23, 40, 94, -49, 25, -8, -4, 63, 1, -2, 19, -72, 30, -97, -1, -86, 102, -114, -115, -41, -77, 22, -29, 91, 117, -83, 61, -113, 103, 48, -66, -12, 52, -80, 59, -31, -47, -67, 0, 52, 88, -26, 71, -76, 61, 9, -49, 0, -45, 102, -78, 30, -2, 75, 24, 95, 9, -33, -116, -49, -1, 97, 11, 94, -80, 11, -29, 107, -107, 70, 120, -61, -50, -124, -58, 27, -34, 26, 99, 104, -26, -79, 81, 91, 125, -65, 6, 14, -104, -74, -86, -34, -126, -41, 109, -79, -58, 95, -38, -63, 84, 109, -79, 114, -56, 86, -9, -17, -128, 47, -16, -7, 55, -74, 80, 119, 26, -26, -76, 35, -26, -15, 45, 71, 30, -116, -11, -29, -8, -4, -81, 98, 124, 12, -15, -84, 38, -9, -62, -25, 127, 18, 35, -45, 71, -74, 113, 13, -93, -51, -11, -2, -109, -39, 11, -66, -54, -58, 26, 109, 73, 29, -42, -64, 113, -81, -25, 73, 124, -2, 97, -90, -35, 120, 110, 27, -25, 61, -114, -41, 99, 49, -84, 21, 106, 56, -81, -99, 117, 106, 102, 125, -65, -63, -25, 31, -118, 49, 75, -42, -108, 54, 30, -52, 67, 120, 61, 47, -76, 114, 76, -125, -71, 110, 105, 12, 116, -92, -55, -38, -21, 121, 0, -97, -65, 0, 35, -45, 120, 74, 27, -116, -71, -48, 102, 50, 68, -38, 120, 126, -107, 120, 61, 103, -29, -13, -33, 6, 60, 109, 123, -119, 111, -95, -3, 25, -40, 95, 0, 119, 117, -89, 23, -36, -99, -20, -41, 115, -25, 118, 54, -62, -33, -39, 115, 91, 58, -66, 61, -27, 36, -69, 103, -94, -77, -67, 62, 61, -15, 28, -37, 33, -1, 31, -104, -78, -26, 106, 89, 21, 20, 3, 0, 0, 0, 0, 73, 69, 78, 68, -82, 66, 96, -126, }; //public final static byte LOGO[] = null; } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/utils/Size.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.utils; public class Size { private int width; private int height; public Size(int width, int height) { this.width = width; this.height = height; } public int getWidth() { return width; } public void setWidth(int width) { this.width = width; } public int getHeight() { return height; } public void setHeight(int height) { this.height = height; } } ================================================ FILE: mAppWidget/mappwidgetlib/src/main/java/com/ls/widgets/map/utils/TransformUtils.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.widgets.map.utils; import android.graphics.Matrix; import android.graphics.Rect; public class TransformUtils { private static final Matrix matrix = new Matrix(); public static Rect scaleRect(Rect coords, float scale, int pivotX, int pivotY) { matrix.reset(); matrix.setScale(scale, scale, pivotX, pivotY); float[] result = {coords.left, coords.top, coords.right, coords.bottom}; matrix.mapPoints(result); return new Rect((int)result[0], (int)result[1], (int)result[2], (int)result[3]); } } ================================================ FILE: mAppWidget/settings.gradle ================================================ include ':demo1app', ':demo2app', ':mappwidgetlib' ================================================ FILE: slicingtool/.classpath ================================================ ================================================ FILE: slicingtool/.project ================================================ com.ls.mappwidget.slicingtool org.eclipse.jdt.core.javabuilder org.eclipse.pde.ManifestBuilder org.eclipse.pde.SchemaBuilder org.eclipse.pde.PluginNature org.eclipse.jdt.core.javanature ================================================ FILE: slicingtool/.settings/org.eclipse.jdt.core.prefs ================================================ eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve org.eclipse.jdt.core.compiler.compliance=1.6 org.eclipse.jdt.core.compiler.debug.lineNumber=generate org.eclipse.jdt.core.compiler.debug.localVariable=generate org.eclipse.jdt.core.compiler.debug.sourceFile=generate org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.compiler.problem.enumIdentifier=error org.eclipse.jdt.core.compiler.source=1.6 ================================================ FILE: slicingtool/META-INF/MANIFEST.MF ================================================ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Slicingtool Bundle-SymbolicName: com.ls.mappwidget.slicingtool;singleton:=true Bundle-Version: 1.0.0.qualifier Bundle-Activator: com.ls.mappwidget.slicingtool.Activator Bundle-Vendor: LS Require-Bundle: org.eclipse.ui, org.eclipse.core.runtime,org.eclipse.core.resources Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Bundle-ActivationPolicy: lazy Bundle-ClassPath: libs/org.eclipse.core.resources.jar, . ================================================ FILE: slicingtool/build.properties ================================================ source.. = src/ output.. = bin/ bin.includes = plugin.xml,\ META-INF/,\ .,\ icons/,\ contexts.xml,\ libs/org.eclipse.core.resources.jar ================================================ FILE: slicingtool/contexts.xml ================================================ This is the context help for the sample view with a table viewer. It was generated by a PDE template. ================================================ FILE: slicingtool/plugin.xml ================================================ ================================================ FILE: slicingtool/src/com/ls/mappwidget/slicingtool/Activator.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.mappwidget.slicingtool; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.ui.plugin.AbstractUIPlugin; import org.osgi.framework.BundleContext; /** * The activator class controls the plug-in life cycle */ public class Activator extends AbstractUIPlugin { // The plug-in ID public static final String PLUGIN_ID = "com.ls.anymaps.slicingtool"; //$NON-NLS-1$ // The shared instance private static Activator plugin; /** * The constructor */ public Activator() { } /* * (non-Javadoc) * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) */ public void start(BundleContext context) throws Exception { super.start(context); plugin = this; } /* * (non-Javadoc) * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) */ public void stop(BundleContext context) throws Exception { plugin = null; super.stop(context); } /** * Returns the shared instance * * @return the shared instance */ public static Activator getDefault() { return plugin; } /** * Returns an image descriptor for the image file at the given * plug-in relative path * * @param path the path * @return the image descriptor */ public static ImageDescriptor getImageDescriptor(String path) { return imageDescriptorFromPlugin(PLUGIN_ID, path); } } ================================================ FILE: slicingtool/src/com/ls/mappwidget/slicingtool/cutter/Constants.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.mappwidget.slicingtool.cutter; public class Constants { public static class Tag { public static final String IMAGE = "Image"; public static final String SIZE = "Size"; public static final String CALIBRATIONRECT = "CalibrationRect"; public static final String POINT = "Point"; } public static class Attr { public static final String TILE_SIZE = "TileSize"; public static final String OVERLAP = "Overlap"; public static final String FORMAT = "Format"; public static final String WIDTH = "Width"; public static final String HEIGHT = "Height"; public static final String X = "x"; public static final String Y = "y"; public static final String LAT = "lat"; public static final String LON = "lon"; public static final String TOPLEFT = "topLeft"; } } ================================================ FILE: slicingtool/src/com/ls/mappwidget/slicingtool/cutter/Cutter.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.mappwidget.slicingtool.cutter; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; import com.ls.mappwidget.slicingtool.views.MainView; import com.ls.mappwidget.slicingtool.vo.PointVO; public class Cutter { private OnProgressUpdateListener onUpdate; private OnCompliteListener onComplite; public Cutter(OnProgressUpdateListener listener, OnCompliteListener onComplite) { this.onUpdate = listener; this.onComplite = onComplite; } public void startCuttingAndroid(final String inFile, final String outDir, final String mapName, final int tileSize, final PointVO pointTopLeft, final PointVO pointBottomRight) { new Thread(new Runnable() { @Override public void run() { File fileXml = new File(outDir, mapName); createDir(fileXml); File file = new File(outDir, mapName + File.separator + mapName + "_files"); createDir(file); String temp = file.getAbsolutePath() + File.separator + "tmp.png"; saveImage(inFile, "png", temp); int count = (int) Math.ceil(Math.log(getMaxSide(temp)) / Math.log(2)); MainView.PROGRESS_MAX = count; for (int i = count; i >= 0; i--) { File fdir = new File(file.getAbsoluteFile(), File.separator + i); createDir(fdir); if (i == count) { imageCut(temp, fdir.getAbsolutePath(), tileSize, fileXml.getAbsoluteFile() + File.separator + mapName + ".xml", true, "", pointTopLeft, pointBottomRight); } else { imageCut(temp, fdir.getAbsolutePath(), tileSize, null, false, "", pointTopLeft, pointBottomRight); } imageResize(temp, temp, 50); onUpdate.onProgressUpdate(count - i + 1); } File tmp = new File(temp); tmp.delete(); onComplite.onComplite(); } }).start(); } private void createDir(File file) { if (!file.exists()) { file.mkdirs(); } } private void imageCut(String inFile, String outDir, int tileSize, String mapName, boolean xml, String concut, PointVO pointTopLeft, PointVO pointBottomRight) { String s = ""; if (!outDir.endsWith(File.separator)) { s = File.separator; } BufferedImage image = getImage(inFile); int w = image.getWidth(); int h = image.getHeight(); if (xml) { ImageXML.createXML(mapName, tileSize, w, h, pointTopLeft, pointBottomRight); } if (w < tileSize && h < tileSize) { saveImage(image, "png", outDir + s + concut + "0_0.png"); return; } for (int i = 0, k = 0; i < w - 1; i += tileSize, k++) { for (int j = 0, l = 0; j < h - 1; j += tileSize, l++) { int tileWidth = tileSize; int tileHeight = tileSize; if (tileWidth > (w - i - 1)) { tileWidth = w - i - 1; } if (tileHeight > (h - j - 1)) { tileHeight = h - j - 1; } BufferedImage part = image.getSubimage(i, j, tileWidth, tileHeight); saveImage(part, "png", outDir + s + concut + k + "_" + l + ".png"); } } } private boolean imageResize(String outFile, String inFile, int percents) { BufferedImage originalImage; originalImage = getImage(inFile); int type = originalImage.getType() == 0 ? BufferedImage.TYPE_INT_ARGB : originalImage.getType(); BufferedImage resizedImage = doResize(originalImage, type, percents); saveImage(resizedImage, "png", outFile); return true; } private BufferedImage doResize(BufferedImage originalImage, int type, int percents) { int h = (int) Math.round((originalImage.getHeight() * percents / 100.0)); int w = (int) Math.round((originalImage.getWidth() * percents / 100.0)); if (h <= 0) { h = 1; } if (w <= 0) { w = 1; } BufferedImage resizedImage = new BufferedImage(w, h, type); Graphics2D g = resizedImage.createGraphics(); g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_BICUBIC); g.setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON); g.drawImage(originalImage, 0, 0, w, h, null); g.dispose(); return resizedImage; } public int getWidth(String fileName) { BufferedImage image = getImage(fileName); if (image == null) { return -1; } int width = image.getWidth(); return width; } private int getMaxSide(String fileName) { BufferedImage image = getImage(fileName); int width = image.getWidth(); int height = image.getHeight(); if (width > height) { return width; } else { return height; } } private BufferedImage getImage(String fileName) { try { BufferedImage image = ImageIO.read(new File(fileName)); return image; } catch (IOException e) { e.printStackTrace(); } return null; } private boolean saveImage(String inFile, String formatName, String fileName) { try { BufferedImage image = getImage(inFile); ImageIO.write(image, formatName, new File(fileName)); return true; } catch (IOException e) { e.printStackTrace(); } return false; } private boolean saveImage(BufferedImage image, String formatName, String fileName) { try { ImageIO.write(image, formatName, new File(fileName)); return true; } catch (IOException e) { e.printStackTrace(); } return false; } } ================================================ FILE: slicingtool/src/com/ls/mappwidget/slicingtool/cutter/ImageXML.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.mappwidget.slicingtool.cutter; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; import com.ls.mappwidget.slicingtool.utils.XMLUtils; import com.ls.mappwidget.slicingtool.vo.PointVO; public class ImageXML { public static void createXML(String fileName, int tileSize, int w, int h, PointVO pointTopLeft, PointVO pointBottomRight) { Document doc = XMLUtils.createDoc(); Element rootElement = doc.createElement(Constants.Tag.IMAGE); doc.appendChild(rootElement); addRootAttr(rootElement, doc, tileSize); Element sizeElement = doc.createElement(Constants.Tag.SIZE); rootElement.appendChild(sizeElement); addSizeAttr(sizeElement, doc, w, h); if (pointTopLeft != null) { Element colibrElement = doc.createElement(Constants.Tag.CALIBRATIONRECT); rootElement.appendChild(colibrElement); Element pointElement = doc.createElement(Constants.Tag.POINT); colibrElement.appendChild(pointElement); addPointAttr(pointElement, doc, pointTopLeft, true); pointElement = doc.createElement(Constants.Tag.POINT); colibrElement.appendChild(pointElement); addPointAttr(pointElement, doc, pointBottomRight, false); } XMLUtils.saveDoc(doc, fileName); } private static void addPointAttr(Element pointElement, Document doc, PointVO point, boolean b) { Attr attr = doc.createAttribute(Constants.Attr.X); attr.setValue(point.getX() + ""); pointElement.setAttributeNode(attr); attr = doc.createAttribute(Constants.Attr.Y); attr.setValue(point.getY() + ""); pointElement.setAttributeNode(attr); attr = doc.createAttribute(Constants.Attr.LAT); attr.setValue(point.getLat() + ""); pointElement.setAttributeNode(attr); attr = doc.createAttribute(Constants.Attr.LON); attr.setValue(point.getLon() + ""); pointElement.setAttributeNode(attr); if (b) { attr = doc.createAttribute(Constants.Attr.TOPLEFT); attr.setValue("1"); pointElement.setAttributeNode(attr); } } private static void addSizeAttr(Element sizeElement, Document doc, int w, int h) { Attr attr = doc.createAttribute(Constants.Attr.WIDTH); attr.setValue("" + w); sizeElement.setAttributeNode(attr); attr = doc.createAttribute(Constants.Attr.HEIGHT); attr.setValue("" + h); sizeElement.setAttributeNode(attr); } private static void addRootAttr(Element rootElement, Document doc, int tileSize) { Attr attr = doc.createAttribute(Constants.Attr.TILE_SIZE); attr.setValue("" + tileSize); rootElement.setAttributeNode(attr); attr = doc.createAttribute(Constants.Attr.OVERLAP); attr.setValue("1"); rootElement.setAttributeNode(attr); attr = doc.createAttribute(Constants.Attr.FORMAT); attr.setValue("png"); rootElement.setAttributeNode(attr); } } ================================================ FILE: slicingtool/src/com/ls/mappwidget/slicingtool/cutter/OnCompliteListener.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.mappwidget.slicingtool.cutter; public interface OnCompliteListener { public void onComplite(); } ================================================ FILE: slicingtool/src/com/ls/mappwidget/slicingtool/cutter/OnProgressUpdateListener.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.mappwidget.slicingtool.cutter; public interface OnProgressUpdateListener { public void onProgressUpdate(int value); } ================================================ FILE: slicingtool/src/com/ls/mappwidget/slicingtool/utils/EclipseUtils.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.mappwidget.slicingtool.utils; import java.io.File; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IPath; public class EclipseUtils { private EclipseUtils() { } public static File getCurrentWorkspace() { IWorkspace workspace = ResourcesPlugin.getWorkspace(); IWorkspaceRoot root = workspace.getRoot(); IPath location = root.getLocation(); location.toFile(); return location.toFile(); } } ================================================ FILE: slicingtool/src/com/ls/mappwidget/slicingtool/utils/FileUtils.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.mappwidget.slicingtool.utils; import java.io.File; public class FileUtils { public static boolean deleteDir(File dir) { if (dir.isDirectory()) { String[] children = dir.list(); for (int i = 0; i < children.length; i++) { boolean success = deleteDir(new File(dir, children[i])); if (!success) { return false; } } } return dir.delete(); } } ================================================ FILE: slicingtool/src/com/ls/mappwidget/slicingtool/utils/XMLUtils.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.mappwidget.slicingtool.utils; import java.io.File; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.w3c.dom.Document; public class XMLUtils { public static Document createDoc() { Document document = null; DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); try { DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); document = docBuilder.newDocument(); } catch (ParserConfigurationException e) { e.printStackTrace(); } return document; } public static void saveDoc(Document doc, String fileName) { try { TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer transformer = transformerFactory.newTransformer(); DOMSource source = new DOMSource(doc); StreamResult result = new StreamResult(new File(fileName)); transformer.transform(source, result); } catch (TransformerException e) { e.printStackTrace(); } } } ================================================ FILE: slicingtool/src/com/ls/mappwidget/slicingtool/views/DirectoryChooser.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.mappwidget.slicingtool.views; import java.io.File; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.DirectoryDialog; import org.eclipse.swt.widgets.Text; public class DirectoryChooser extends Composite { public static final String DIR_PATH = "directorychooser.file_path"; private static final String BTN_TXT = "..."; private Text text; private Button button; private String title = null; public DirectoryChooser(Composite parent) { super(parent, SWT.NONE); createContent(); } public void createContent() { GridLayout layout = new GridLayout(2, false); layout.marginRight = 4; setLayout(layout); text = new Text(this, SWT.SINGLE | SWT.BORDER); GridData gd = new GridData(GridData.FILL_BOTH); gd.grabExcessHorizontalSpace = true; gd.horizontalAlignment = GridData.FILL; text.setLayoutData(gd); button = new Button(this, SWT.NONE); button.setText(BTN_TXT); button.addSelectionListener(new SelectionListener() { public void widgetDefaultSelected(SelectionEvent e) { } public void widgetSelected(SelectionEvent e) { DirectoryDialog dirDialog = new DirectoryDialog(button.getShell()); dirDialog.setFilterPath(text.getText()); dirDialog.setText("Select your home directory"); String selectedDir = dirDialog.open(); if (selectedDir == null) { return; } text.setText(selectedDir); FileChooser.savePath(DirectoryChooser.this, DIR_PATH, selectedDir); } }); } public void setText(String text) { this.text.setText(text); } public String getText() { return text.getText(); } public Text getTextControl() { return text; } public File getFile() { String text = this.text.getText(); if (text.length() == 0) { return null; } return new File(text); } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } } ================================================ FILE: slicingtool/src/com/ls/mappwidget/slicingtool/views/FileChooser.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.mappwidget.slicingtool.views; import java.io.File; import java.util.prefs.Preferences; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.FileDialog; import org.eclipse.swt.widgets.Text; public class FileChooser extends Composite { public static final String FILE_PATH = "filechooser.file.path"; private static final String OPEN = "Open"; private static final String OK = "Ok"; private static final String BTN_TXT = "..."; private Text text; private Button button; private String title = null; private int mode; public FileChooser(Composite parent, int mode) { super(parent, SWT.NONE); this.mode = mode; createContent(); } public void createContent() { GridLayout layout = new GridLayout(); layout.numColumns = 2; layout.marginHeight = 0; layout.marginWidth = 0; setLayout(layout); text = new Text(this, SWT.SINGLE | SWT.BORDER); GridData gd = new GridData(); gd.grabExcessHorizontalSpace = true; gd.horizontalAlignment = GridData.FILL; text.setLayoutData(gd); button = new Button(this, SWT.NONE); button.setText(BTN_TXT); button.addSelectionListener(new SelectionListener() { public void widgetDefaultSelected(SelectionEvent e) { } public void widgetSelected(SelectionEvent e) { String path = null; switch (mode) { case Mode.OPEN: path = openFile(); break; case Mode.SAVE: path = saveFile(); break; } if (path == null) { return; } text.setText(path); savePath(FileChooser.this, FILE_PATH, path); } }); } public static void savePath(Composite composite, String key, String value) { Preferences prefs = Preferences.userNodeForPackage(composite.getClass()); prefs.put(key, value); } public String openFile() { FileDialog dlg = new FileDialog(button.getShell(), SWT.OPEN); dlg.setFileName(text.getText()); dlg.setFilterExtensions(new String[] { "*.gif; *.jpg; *.png; *.ico; *.bmp" }); dlg.setText(OPEN); String path = dlg.open(); return path; } public String saveFile() { FileDialog dlg = new FileDialog(button.getShell(), SWT.SAVE); dlg.setFileName(text.getText()); dlg.setText(OK); String path = dlg.open(); return path; } public void setText(String text) { this.text.setText(text); } public String getText() { return text.getText(); } public Text getTextControl() { return text; } public File getFile() { String text = this.text.getText(); if (text.length() == 0) { return null; } return new File(text); } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public static class Mode { public static final int OPEN = 1; public static final int SAVE = 2; } } ================================================ FILE: slicingtool/src/com/ls/mappwidget/slicingtool/views/GPSChooser.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.mappwidget.slicingtool.views; import org.eclipse.swt.SWT; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Text; import com.ls.mappwidget.slicingtool.vo.PointVO; public class GPSChooser extends Composite { private String title; private Text x; private Text y; private Text longitude; private Text latitude; public GPSChooser(Composite parent, String title) { super(parent, SWT.NONE); this.title = title; createContent(); } public void createContent() { GridData gd = new GridData(); gd.grabExcessHorizontalSpace = true; gd.horizontalAlignment = GridData.FILL; GridLayout contentLayout = new GridLayout(); contentLayout.numColumns = 1; contentLayout.marginHeight = 0; contentLayout.marginWidth = 0; setLayout(contentLayout); setLayoutData(gd); Group group = new Group(this, SWT.NONE); GridLayout layout = new GridLayout(); layout.marginWidth = 15; layout.numColumns = 4; group.setLayoutData(gd); group.setLayout(layout); group.setText(title); GridData textData = new GridData(); textData.grabExcessHorizontalSpace = true; textData.horizontalAlignment = GridData.FILL; Label label = new Label(group, SWT.WRAP); label.setText("X"); x = new Text(group, SWT.SINGLE | SWT.BORDER); x.setLayoutData(textData); label = new Label(group, SWT.WRAP); label.setText("Longitude"); longitude = new Text(group, SWT.SINGLE | SWT.BORDER); longitude.setLayoutData(textData); label = new Label(group, SWT.WRAP); label.setText("Y"); y = new Text(group, SWT.SINGLE | SWT.BORDER); y.setLayoutData(textData); label = new Label(group, SWT.WRAP); label.setText("Latitude"); latitude = new Text(group, SWT.SINGLE | SWT.BORDER); latitude.setLayoutData(textData); } public void setActive(boolean enabled) { x.setEnabled(enabled); y.setEnabled(enabled); latitude.setEnabled(enabled); longitude.setEnabled(enabled); } public PointVO getPoint() { PointVO result = new PointVO(); try { result.setX(Double.valueOf(x.getText())); result.setY(Double.valueOf(y.getText())); result.setLat(Double.valueOf(latitude.getText())); result.setLon(Double.valueOf(longitude.getText())); } catch (NumberFormatException e) { return null; } return result; } } ================================================ FILE: slicingtool/src/com/ls/mappwidget/slicingtool/views/MainView.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.mappwidget.slicingtool.views; import java.io.File; import java.util.prefs.Preferences; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.ScrolledComposite; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.MessageBox; import org.eclipse.swt.widgets.ProgressBar; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.part.ViewPart; import com.ls.mappwidget.slicingtool.cutter.Cutter; import com.ls.mappwidget.slicingtool.cutter.OnCompliteListener; import com.ls.mappwidget.slicingtool.cutter.OnProgressUpdateListener; import com.ls.mappwidget.slicingtool.utils.FileUtils; import com.ls.mappwidget.slicingtool.views.FileChooser.Mode; import com.ls.mappwidget.slicingtool.vo.PointVO; public class MainView extends ViewPart { private static final String GPS_POSITIONING = "GPS Positioning"; private static final String BOTTOM_RIGHT_POINT = "Bottom right point"; private static final String TOP_LEFT_POINT = "Top left point"; private static final String ATTENTION = "Attention :"; private static final String EXPORT_OPTIONS = "Export Options"; private static final String MAP_SOURCE = "Map Source"; private static final String DEFAULT_NAME = "map"; private static final String INCORECT_FILE_NAME = "Incorect file name!"; private static final String CHOOSE_SAVE_DIR_FIRST = "Choose save dir first!"; private static final String ENTER_NAME_FIRST = "Enter name first!"; private static final String CHOOSE_IMAGE_FIRST = "Choose image first!"; private static final String NAME = "Name"; private static final String TILE_SIZE = "Tile Size"; private static final String CUT = "Export"; public static final String ID = "com.ls.map.image.views.SampleView"; public static int PROGRESS_MAX = 10; public MainView() { } @Override public void createPartControl(Composite parent) { initWidgets(parent); } @Override public void setFocus() { } private void initWidgets(final Composite parent) { ScrolledComposite scrolledComposite = new ScrolledComposite(parent, SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER); final Composite top = new Composite(scrolledComposite, SWT.NONE); fillTop(top, scrolledComposite); Preferences prefs = Preferences.userNodeForPackage(this.getClass()); Composite mapSource = new Composite(top, SWT.NONE); final FileChooser fileChooser = fillMapSource(mapSource, prefs); final Group group = new Group(top, SWT.NONE); fillGroup(group); GridLayout layout = new GridLayout(); layout.numColumns = 2; GridData fileDir = new GridData(); fileDir.grabExcessHorizontalSpace = true; fileDir.horizontalAlignment = GridData.FILL; Composite composite = new Composite(group, SWT.NONE); composite.setLayoutData(fileDir); composite.setLayout(layout); Label label = new Label(composite, SWT.WRAP); label.setText(NAME); final Text text = fillName(composite); final RadioGroup radioGroup = new RadioGroup(group); radioGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); radioGroup.setDirText(prefs.get(RadioGroup.DIR_PATH, "")); layout = new GridLayout(); layout.numColumns = 2; composite = new Composite(group, SWT.NONE); composite.setLayoutData(fileDir); composite.setLayout(layout); GridData dataCombo = new GridData(); dataCombo.widthHint = 50; label = new Label(composite, SWT.WRAP); label.setText(TILE_SIZE); final Combo comboTile = new Combo(composite, SWT.DROP_DOWN | SWT.READ_ONLY); comboTile.setItems(new String[] { "128", "256" }); comboTile.select(1); comboTile.setLayoutData(dataCombo); composite = new Composite(group, SWT.NONE); composite.setLayoutData(fileDir); composite.setLayout(layout); label = new Label(composite, SWT.WRAP); label.setText(GPS_POSITIONING); final Button gps = new Button(composite, SWT.CHECK); fileDir = new GridData(); fileDir.grabExcessHorizontalSpace = true; fileDir.horizontalAlignment = GridData.FILL; layout = new GridLayout(); layout.numColumns = 2; composite = new Composite(group, SWT.NONE); composite.setLayoutData(fileDir); composite.setLayout(layout); final GPSChooser gpsChooserTopLeft = new GPSChooser(composite, TOP_LEFT_POINT); gpsChooserTopLeft.setActive(false); final GPSChooser gpsChooserBottomRight = new GPSChooser(composite, BOTTOM_RIGHT_POINT); gpsChooserBottomRight.setActive(false); final Button runButton = new Button(top, SWT.NONE); runButton.setText(CUT); final ProgressBar progressBar = new ProgressBar(top, SWT.SMOOTH); progressBar.setMinimum(0); progressBar.setVisible(false); progressBar.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); final Cutter cutter = new Cutter(new OnProgressUpdateListener() { @Override public void onProgressUpdate(int value) { setValue(top, progressBar, value); } }, new OnCompliteListener() { @Override public void onComplite() { setCompliete(top, runButton, progressBar); } }); runButton.addListener(SWT.Selection, new Listener() { public void handleEvent(Event event) { PointVO pointTopLeft = gpsChooserTopLeft.getPoint(); PointVO pointBottomRight = gpsChooserBottomRight.getPoint(); if (gps.getSelection() && (pointTopLeft == null || pointBottomRight == null)) { MessageBox dialog = new MessageBox(parent.getShell()); dialog.setMessage("Please enter a valid values for points!."); dialog.open(); return; } String dir = getDir(radioGroup); if (dir == null) { MessageBox dialog = new MessageBox(parent.getShell()); dialog.setMessage("Incorect location path!"); dialog.open(); return; } if (!checkDir(parent, dir, text.getText())) { return; } progressBar.setSelection(0); runButton.setEnabled(false); progressBar.setMaximum(PROGRESS_MAX); progressBar.setVisible(true); top.update(); getTiles(parent, group, fileChooser.getText(), dir, text.getText(), comboTile, cutter, pointTopLeft, pointBottomRight); } }); gps.addListener(SWT.Selection, new Listener() { public void handleEvent(Event event) { boolean selection = gps.getSelection(); gpsChooserTopLeft.setActive(selection); gpsChooserBottomRight.setActive(selection); } }); } private String getDir(RadioGroup radioGroup) { switch (radioGroup.getSelected()) { case RadioGroup.LOCATION: return radioGroup.getDirText(); case RadioGroup.PROJECT: return radioGroup.getSelectedPath(); } return null; } protected boolean checkDir(Composite parent, String dir, String name) { String sign = ""; if (!dir.endsWith("\\")) { sign = "\\"; } String fileName = dir + sign + name; File file = new File(fileName); if (file.exists()) { boolean answer = MessageDialog.openQuestion(parent.getShell(), ATTENTION, "Directory \"" + fileName + "\"" + " already exist. Do you want to continue(its will remove old files!)?"); if (answer) { FileUtils.deleteDir(file); } return answer; } return true; } private void setValue(Composite top, final ProgressBar progressBar, final int value) { top.getDisplay().asyncExec(new Runnable() { @Override public void run() { progressBar.setSelection(value); } }); } private void setCompliete(Composite top, final Button runButton, final ProgressBar progressBar) { top.getDisplay().asyncExec(new Runnable() { @Override public void run() { runButton.setEnabled(true); } }); } private FileChooser fillMapSource(Composite mapSource, Preferences prefs) { GridLayout layout = new GridLayout(); layout.numColumns = 2; GridData fileDir = new GridData(); fileDir.grabExcessHorizontalSpace = true; fileDir.horizontalAlignment = GridData.FILL; mapSource.setLayoutData(fileDir); mapSource.setLayout(layout); Label label = new Label(mapSource, SWT.WRAP); label.setText(MAP_SOURCE); final FileChooser fileChooser = new FileChooser(mapSource, Mode.OPEN); fileChooser.setLayoutData(fileDir); fileChooser.setText(prefs.get(FileChooser.FILE_PATH, "")); return fileChooser; } private Text fillName(Composite parent) { Text text = new Text(parent, SWT.SINGLE | SWT.BORDER); GridData gd = new GridData(); gd.grabExcessHorizontalSpace = true; gd.horizontalAlignment = GridData.FILL; text.setLayoutData(gd); text.setText(DEFAULT_NAME); return text; } private void fillGroup(Group group) { GridLayout layout = new GridLayout(); layout.marginWidth = 15; layout.numColumns = 1; GridData gd = new GridData(); gd.grabExcessHorizontalSpace = true; gd.horizontalAlignment = GridData.FILL; group.setLayoutData(gd); group.setLayout(layout); group.setText(EXPORT_OPTIONS); } private void fillTop(Composite top, ScrolledComposite scrolledComposite) { GridLayout layout = new GridLayout(); layout.numColumns = 1; GridData data = new GridData(); data.grabExcessHorizontalSpace = true; data.horizontalAlignment = GridData.FILL; top.setLayoutData(data); top.setLayout(layout); scrolledComposite.setMinSize(500, 250); scrolledComposite.setExpandHorizontal(true); scrolledComposite.setExpandVertical(true); scrolledComposite.setContent(top); } protected void getTiles(final Composite parent, final Composite banner, final String imgPath, final String saveDirPath, final String name, final Combo combo, final Cutter cutter, final PointVO pointTopLeft, final PointVO pointBottomRight) { MessageBox dialog = new MessageBox(parent.getShell()); if (name == null || name.contentEquals("")) { dialog.setMessage(ENTER_NAME_FIRST); dialog.open(); return; } else if (imgPath == null || imgPath.contentEquals("")) { dialog.setMessage(CHOOSE_IMAGE_FIRST); dialog.open(); return; } else if (saveDirPath == null || saveDirPath.contentEquals("")) { dialog.setMessage(CHOOSE_SAVE_DIR_FIRST); dialog.open(); return; } File f = new File(imgPath); if (f.exists()) { cutter.startCuttingAndroid(imgPath, saveDirPath, name, Integer.parseInt(combo.getItem(combo.getSelectionIndex())), pointTopLeft, pointBottomRight); } else { dialog.setMessage(INCORECT_FILE_NAME); dialog.open(); return; } } public Image loadImage(Composite composite, String filename) { File f = new File(filename); Image sourceImage = null; if (f.exists()) { sourceImage = new Image(composite.getDisplay(), filename); } return sourceImage; } } ================================================ FILE: slicingtool/src/com/ls/mappwidget/slicingtool/views/RadioGroup.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.mappwidget.slicingtool.views; import java.io.File; import java.util.ArrayList; import java.util.List; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.DirectoryDialog; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Text; import com.ls.mappwidget.slicingtool.utils.EclipseUtils; public class RadioGroup extends Composite { public static final int LOCATION = 1; public static final int PROJECT = 2; private static final String BTN_LOCATION_TXT = "Export to location"; private static final String BTN_PROJECT_TXT = "Export to Project"; private Button btnLocation; private Button btnProject; public static final String DIR_PATH = "directorychooser.file_path"; private static final String BTN_TXT = "..."; private Text text; private Button button; private List result; private File workspace; private Combo comboPath; public RadioGroup(Composite parent) { super(parent, SWT.NULL); createContent(); } public void createContent() { GridLayout layout = new GridLayout(3, false); setLayout(layout); GridData gd = new GridData(GridData.FILL_BOTH); gd.horizontalAlignment = SWT.FILL; gd.minimumWidth = 400; gd.minimumHeight = 50; btnLocation = new Button(this, SWT.RADIO); btnLocation.setText(BTN_LOCATION_TXT); text = new Text(this, SWT.SINGLE | SWT.BORDER); GridData gd2 = new GridData(GridData.FILL_BOTH); gd.grabExcessHorizontalSpace = true; gd.horizontalAlignment = GridData.FILL; text.setLayoutData(gd2); button = new Button(this, SWT.NONE); button.setText(BTN_TXT); button.addSelectionListener(new SelectionListener() { public void widgetDefaultSelected(SelectionEvent e) { } public void widgetSelected(SelectionEvent e) { DirectoryDialog dirDialog = new DirectoryDialog(button.getShell()); dirDialog.setFilterPath(text.getText()); dirDialog.setText("Select your home directory"); String selectedDir = dirDialog.open(); if (selectedDir == null) { return; } text.setText(selectedDir); FileChooser.savePath(RadioGroup.this, DIR_PATH, selectedDir); } }); btnProject = new Button(this, SWT.RADIO); btnProject.setText(BTN_PROJECT_TXT); btnProject.setSelection(true); text.setEnabled(false); button.setEnabled(false); GridData dataCombo = new GridData(); dataCombo.grabExcessHorizontalSpace = true; dataCombo.horizontalAlignment = GridData.FILL; workspace = EclipseUtils.getCurrentWorkspace(); String[] list = workspace.list(); result = new ArrayList(); for (int i = 0; i < list.length; i++) { File f = new File(workspace, list[i] + "\\" + "AndroidManifest.xml"); if (f.exists()) { result.add(list[i]); } } comboPath = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY); comboPath.setItems(result.toArray(new String[result.size()])); comboPath.select(0); comboPath.setLayoutData(dataCombo); Label label = new Label(this, SWT.WRAP); label.setText(""); btnProject.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { comboPath.setEnabled(true); text.setEnabled(false); button.setEnabled(false); } }); btnLocation.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { comboPath.setEnabled(false); text.setEnabled(true); button.setEnabled(true); } }); } public void setDirText(String text) { this.text.setText(text); } public String getDirText() { return text.getText(); } public String getSelectedPath() { int pos = comboPath.getSelectionIndex(); if (pos == -1) { return null; } return workspace.getAbsolutePath() + "\\" + result.get(pos) + "\\assets"; } public int getSelected() { if (btnLocation.getSelection()) { return LOCATION; } return PROJECT; } } ================================================ FILE: slicingtool/src/com/ls/mappwidget/slicingtool/vo/PointVO.java ================================================ /************************************************************************* * Copyright (c) 2015 Lemberg Solutions * * 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.ls.mappwidget.slicingtool.vo; public class PointVO { private double x; private double y; private double lat; private double lon; private int topLeft; public double getX() { return x; } public void setX(double x) { this.x = x; } public double getY() { return y; } public void setY(double y) { this.y = y; } public double getLat() { return lat; } public void setLat(double lat) { this.lat = lat; } public double getLon() { return lon; } public void setLon(double lon) { this.lon = lon; } public int getTopLeft() { return topLeft; } public void setTopLeft(int topLeft) { this.topLeft = topLeft; } }