11c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov/* 21c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov * Copyright (C) 2010 The Android Open Source Project 31c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov * 41c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov * Licensed under the Apache License, Version 2.0 (the "License"); 51c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov * you may not use this file except in compliance with the License. 61c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov * You may obtain a copy of the License at 71c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov * 81c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov * http://www.apache.org/licenses/LICENSE-2.0 91c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov * 101c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov * Unless required by applicable law or agreed to in writing, software 111c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov * distributed under the License is distributed on an "AS IS" BASIS, 121c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 131c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov * See the License for the specific language governing permissions and 141c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov * limitations under the License. 151c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov */ 161c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikovpackage com.android.contacts.list; 171c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov 181c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikovimport com.android.contacts.R; 191c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov 201c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikovimport android.content.AsyncTaskLoader; 211c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikovimport android.content.Context; 221c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikovimport android.content.pm.PackageManager; 231c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikovimport android.database.ContentObserver; 241c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikovimport android.database.Cursor; 251c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikovimport android.database.MatrixCursor; 261c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikovimport android.net.Uri; 271c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikovimport android.os.Handler; 281c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikovimport android.provider.ContactsContract.Directory; 291c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikovimport android.text.TextUtils; 301c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikovimport android.util.Log; 311c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov 321c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov/** 331c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov * A specialized loader for the list of directories, see {@link Directory}. 341c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov */ 351c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikovpublic class DirectoryListLoader extends AsyncTaskLoader<Cursor> { 361c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov 371c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov private static final String TAG = "ContactEntryListAdapter"; 381c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov 394d174aad97cd382f810e3bf1a7f1f4f4772be118Dmitri Plotnikov public static final int SEARCH_MODE_NONE = 0; 404d174aad97cd382f810e3bf1a7f1f4f4772be118Dmitri Plotnikov public static final int SEARCH_MODE_DEFAULT = 1; 414d174aad97cd382f810e3bf1a7f1f4f4772be118Dmitri Plotnikov public static final int SEARCH_MODE_CONTACT_SHORTCUT = 2; 424d174aad97cd382f810e3bf1a7f1f4f4772be118Dmitri Plotnikov public static final int SEARCH_MODE_DATA_SHORTCUT = 3; 434d174aad97cd382f810e3bf1a7f1f4f4772be118Dmitri Plotnikov 441c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov private static final class DirectoryQuery { 451c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov public static final Uri URI = Directory.CONTENT_URI; 461c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov public static final String ORDER_BY = Directory._ID; 471c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov 481c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov public static final String[] PROJECTION = { 491c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov Directory._ID, 501c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov Directory.PACKAGE_NAME, 511c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov Directory.TYPE_RESOURCE_ID, 521c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov Directory.DISPLAY_NAME, 531228c817c5b4850a1ea52a95a860fe6a329462b1Dmitri Plotnikov Directory.PHOTO_SUPPORT, 541c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov }; 551c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov 561c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov public static final int ID = 0; 571c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov public static final int PACKAGE_NAME = 1; 581c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov public static final int TYPE_RESOURCE_ID = 2; 591c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov public static final int DISPLAY_NAME = 3; 601228c817c5b4850a1ea52a95a860fe6a329462b1Dmitri Plotnikov public static final int PHOTO_SUPPORT = 4; 611c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov } 621c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov 631c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov public static final String DIRECTORY_TYPE = "directoryType"; 641c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov 651c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov private static final String[] RESULT_PROJECTION = { 661c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov Directory._ID, 671c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov DIRECTORY_TYPE, 681c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov Directory.DISPLAY_NAME, 691228c817c5b4850a1ea52a95a860fe6a329462b1Dmitri Plotnikov Directory.PHOTO_SUPPORT, 701c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov }; 711c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov 721c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov private final ContentObserver mObserver = new ContentObserver(new Handler()) { 731c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov @Override 741c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov public void onChange(boolean selfChange) { 751c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov forceLoad(); 761c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov } 771c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov }; 781c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov 794d174aad97cd382f810e3bf1a7f1f4f4772be118Dmitri Plotnikov private int mDirectorySearchMode; 805b1d28f27b73f7f167dfb67af642a3aaf70fbd5dDmitri Plotnikov private boolean mLocalInvisibleDirectoryEnabled; 811c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov 821c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov private MatrixCursor mDefaultDirectoryList; 831c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov 841c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov public DirectoryListLoader(Context context) { 851c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov super(context); 861c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov } 871c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov 884d174aad97cd382f810e3bf1a7f1f4f4772be118Dmitri Plotnikov public void setDirectorySearchMode(int mode) { 894d174aad97cd382f810e3bf1a7f1f4f4772be118Dmitri Plotnikov mDirectorySearchMode = mode; 901c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov } 911c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov 925b1d28f27b73f7f167dfb67af642a3aaf70fbd5dDmitri Plotnikov /** 935b1d28f27b73f7f167dfb67af642a3aaf70fbd5dDmitri Plotnikov * A flag that indicates whether the {@link Directory#LOCAL_INVISIBLE} directory should 945b1d28f27b73f7f167dfb67af642a3aaf70fbd5dDmitri Plotnikov * be included in the results. 955b1d28f27b73f7f167dfb67af642a3aaf70fbd5dDmitri Plotnikov */ 965b1d28f27b73f7f167dfb67af642a3aaf70fbd5dDmitri Plotnikov public void setLocalInvisibleDirectoryEnabled(boolean flag) { 975b1d28f27b73f7f167dfb67af642a3aaf70fbd5dDmitri Plotnikov this.mLocalInvisibleDirectoryEnabled = flag; 985b1d28f27b73f7f167dfb67af642a3aaf70fbd5dDmitri Plotnikov } 995b1d28f27b73f7f167dfb67af642a3aaf70fbd5dDmitri Plotnikov 1001c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov @Override 101c04fc2704faad2c045cfbfea34611dfbe4519cf6Dianne Hackborn protected void onStartLoading() { 1021c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov getContext().getContentResolver(). 1031c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov registerContentObserver(Directory.CONTENT_URI, false, mObserver); 1041c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov forceLoad(); 1051c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov } 1061c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov 1071c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov @Override 108c04fc2704faad2c045cfbfea34611dfbe4519cf6Dianne Hackborn protected void onStopLoading() { 1091c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov getContext().getContentResolver().unregisterContentObserver(mObserver); 1101c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov } 1111c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov 1121c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov @Override 1131c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov public Cursor loadInBackground() { 1144d174aad97cd382f810e3bf1a7f1f4f4772be118Dmitri Plotnikov if (mDirectorySearchMode == SEARCH_MODE_NONE) { 1151c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov return getDefaultDirectories(); 1161c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov } 1171c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov 1181c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov MatrixCursor result = new MatrixCursor(RESULT_PROJECTION); 1191c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov Context context = getContext(); 1201c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov PackageManager pm = context.getPackageManager(); 1214d174aad97cd382f810e3bf1a7f1f4f4772be118Dmitri Plotnikov String selection; 1224d174aad97cd382f810e3bf1a7f1f4f4772be118Dmitri Plotnikov switch (mDirectorySearchMode) { 1234d174aad97cd382f810e3bf1a7f1f4f4772be118Dmitri Plotnikov case SEARCH_MODE_DEFAULT: 1244d174aad97cd382f810e3bf1a7f1f4f4772be118Dmitri Plotnikov selection = mLocalInvisibleDirectoryEnabled ? null 1254d174aad97cd382f810e3bf1a7f1f4f4772be118Dmitri Plotnikov : (Directory._ID + "!=" + Directory.LOCAL_INVISIBLE); 1264d174aad97cd382f810e3bf1a7f1f4f4772be118Dmitri Plotnikov break; 1274d174aad97cd382f810e3bf1a7f1f4f4772be118Dmitri Plotnikov 1284d174aad97cd382f810e3bf1a7f1f4f4772be118Dmitri Plotnikov case SEARCH_MODE_CONTACT_SHORTCUT: 1294d174aad97cd382f810e3bf1a7f1f4f4772be118Dmitri Plotnikov selection = Directory.SHORTCUT_SUPPORT + "=" + Directory.SHORTCUT_SUPPORT_FULL 1304d174aad97cd382f810e3bf1a7f1f4f4772be118Dmitri Plotnikov + (mLocalInvisibleDirectoryEnabled ? "" 1314d174aad97cd382f810e3bf1a7f1f4f4772be118Dmitri Plotnikov : (" AND " + Directory._ID + "!=" + Directory.LOCAL_INVISIBLE)); 1324d174aad97cd382f810e3bf1a7f1f4f4772be118Dmitri Plotnikov break; 1334d174aad97cd382f810e3bf1a7f1f4f4772be118Dmitri Plotnikov 1344d174aad97cd382f810e3bf1a7f1f4f4772be118Dmitri Plotnikov case SEARCH_MODE_DATA_SHORTCUT: 1354d174aad97cd382f810e3bf1a7f1f4f4772be118Dmitri Plotnikov selection = Directory.SHORTCUT_SUPPORT + " IN (" 1364d174aad97cd382f810e3bf1a7f1f4f4772be118Dmitri Plotnikov + Directory.SHORTCUT_SUPPORT_FULL + ", " 1374d174aad97cd382f810e3bf1a7f1f4f4772be118Dmitri Plotnikov + Directory.SHORTCUT_SUPPORT_DATA_ITEMS_ONLY + ")" 1384d174aad97cd382f810e3bf1a7f1f4f4772be118Dmitri Plotnikov + (mLocalInvisibleDirectoryEnabled ? "" 1394d174aad97cd382f810e3bf1a7f1f4f4772be118Dmitri Plotnikov : (" AND " + Directory._ID + "!=" + Directory.LOCAL_INVISIBLE)); 1404d174aad97cd382f810e3bf1a7f1f4f4772be118Dmitri Plotnikov break; 1414d174aad97cd382f810e3bf1a7f1f4f4772be118Dmitri Plotnikov 1424d174aad97cd382f810e3bf1a7f1f4f4772be118Dmitri Plotnikov default: 1434d174aad97cd382f810e3bf1a7f1f4f4772be118Dmitri Plotnikov throw new RuntimeException( 1444d174aad97cd382f810e3bf1a7f1f4f4772be118Dmitri Plotnikov "Unsupported directory search mode: " + mDirectorySearchMode); 1454d174aad97cd382f810e3bf1a7f1f4f4772be118Dmitri Plotnikov } 1464d174aad97cd382f810e3bf1a7f1f4f4772be118Dmitri Plotnikov 1471c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov Cursor cursor = context.getContentResolver().query(DirectoryQuery.URI, 1485b1d28f27b73f7f167dfb67af642a3aaf70fbd5dDmitri Plotnikov DirectoryQuery.PROJECTION, selection, null, DirectoryQuery.ORDER_BY); 1491c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov try { 1501c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov while(cursor.moveToNext()) { 1511c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov long directoryId = cursor.getLong(DirectoryQuery.ID); 1521c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov String directoryType = null; 1531c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov 1541c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov String packageName = cursor.getString(DirectoryQuery.PACKAGE_NAME); 1551c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov int typeResourceId = cursor.getInt(DirectoryQuery.TYPE_RESOURCE_ID); 1561c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov if (!TextUtils.isEmpty(packageName) && typeResourceId != 0) { 1571c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov try { 1581c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov directoryType = pm.getResourcesForApplication(packageName) 1591c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov .getString(typeResourceId); 1601c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov } catch (Exception e) { 1611c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov Log.e(TAG, "Cannot obtain directory type from package: " + packageName); 1621c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov } 1631c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov } 1641c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov String displayName = cursor.getString(DirectoryQuery.DISPLAY_NAME); 1651228c817c5b4850a1ea52a95a860fe6a329462b1Dmitri Plotnikov int photoSupport = cursor.getInt(DirectoryQuery.PHOTO_SUPPORT); 1661228c817c5b4850a1ea52a95a860fe6a329462b1Dmitri Plotnikov result.addRow(new Object[]{directoryId, directoryType, displayName, photoSupport}); 1671c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov } 1681c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov } finally { 1691c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov cursor.close(); 1701c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov } 1714d174aad97cd382f810e3bf1a7f1f4f4772be118Dmitri Plotnikov 1721c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov return result; 1731c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov } 1741c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov 1751c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov private Cursor getDefaultDirectories() { 1761c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov if (mDefaultDirectoryList == null) { 1771c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov mDefaultDirectoryList = new MatrixCursor(RESULT_PROJECTION); 1781c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov mDefaultDirectoryList.addRow(new Object[] { 1791c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov Directory.DEFAULT, 1801c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov getContext().getString(R.string.contactsList), 1811c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov null 1821c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov }); 1831c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov mDefaultDirectoryList.addRow(new Object[] { 1841c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov Directory.LOCAL_INVISIBLE, 1851c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov getContext().getString(R.string.local_invisible_directory), 1861c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov null 1871c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov }); 1881c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov } 1891c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov return mDefaultDirectoryList; 1901c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov } 1911c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov 1921c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov @Override 193c04fc2704faad2c045cfbfea34611dfbe4519cf6Dianne Hackborn protected void onReset() { 1941c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov stopLoading(); 1951c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov } 1961c5c8e23849ac3babb2bdec65ec63c190a5c40dfDmitri Plotnikov} 197