eu.nubomedia.nubomedia_kurento_health_communicator_android.kc_and_communicator.util.FileUtils.java Source code

Java tutorial

Introduction

Here is the source code for eu.nubomedia.nubomedia_kurento_health_communicator_android.kc_and_communicator.util.FileUtils.java

Source

// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you 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 eu.nubomedia.nubomedia_kurento_health_communicator_android.kc_and_communicator.util;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

import org.apache.http.HttpEntity;
import org.apache.http.client.methods.HttpGet;
import org.json.JSONException;
import eu.nubomedia.nubomedia_kurento_health_communicator_android.R;
import eu.nubomedia.nubomedia_kurento_health_communicator_android.kc_and_communicator.database.DataBasesAccess;
import eu.nubomedia.nubomedia_kurento_health_communicator_android.kc_and_communicator.database.MessageObject;
import eu.nubomedia.nubomedia_kurento_health_communicator_android.kc_and_communicator.services.CommandStoreService;
import eu.nubomedia.nubomedia_kurento_health_communicator_android.kc_and_communicator.services.MessagingClientService;
import eu.nubomedia.nubomedia_kurento_health_communicator_android.kc_and_communicator.ui.activity.Preferences;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.media.ThumbnailUtils;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Environment;
import android.provider.MediaStore;
import android.widget.ImageView;
import android.widget.Toast;

import com.kurento.agenda.datamodel.pojo.Command;
import com.kurento.agenda.services.pojo.MessageSend;

public class FileUtils {

    private static final Logger log = LoggerFactory.getLogger(FileUtils.class.getSimpleName());

    public static void deleteTemp(String fileTmp) {
        if (fileTmp == null) {
            log.warn("Invalid file: {}", fileTmp);
            return;
        }

        File file = new File(fileTmp);
        file.delete();
    }

    public static String entityToString(HttpEntity entity) throws IOException {
        InputStream is = entity.getContent();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is));
        StringBuilder str = new StringBuilder();

        String line = null;
        try {
            while ((line = bufferedReader.readLine()) != null) {
                str.append(line + "\n");
            }
        } finally {
            try {
                is.close();
            } catch (IOException ignore) {
            }
        }
        return str.toString();
    }

    public static Bitmap entityToBitmap(Context context, HttpEntity entity, final String contentId,
            boolean withProgressBar, Long contentSize) {
        try {
            File file = new File(getDir(), contentId);
            file.createNewFile();
            InputStream is = entity.getContent();

            /*
             * Read bytes to the Buffer until there is nothing more to read(-1)
             * and write on the fly in the file.
             */
            final FileOutputStream fos = new FileOutputStream(file);
            final int BUFFER_SIZE = 23 * 1024;
            BufferedInputStream bis = new BufferedInputStream(is, BUFFER_SIZE);
            byte[] baf = new byte[BUFFER_SIZE];
            int actual = 0;
            long total = 0;
            int totalpercent = 0;
            int auxtotal = 0;

            while (actual != -1) {
                fos.write(baf, 0, actual);
                actual = bis.read(baf, 0, BUFFER_SIZE);

                if (withProgressBar) {
                    try {
                        total = total + actual;
                        auxtotal = (int) (((total * 100) / contentSize)) / 10;
                        if ((totalpercent != auxtotal) && (totalpercent != 10)) {
                            totalpercent = auxtotal;
                            Intent intent = new Intent();
                            intent.setAction(ConstantKeys.BROADCAST_DIALOG_PROGRESSBAR);
                            intent.putExtra(ConstantKeys.TOTAL, totalpercent * 10);
                            intent.putExtra(ConstantKeys.LOCALID,
                                    contentId.replace(ConstantKeys.EXTENSION_3GP, ConstantKeys.STRING_DEFAULT)
                                            .replace(ConstantKeys.EXTENSION_JPG, ConstantKeys.STRING_DEFAULT));
                            context.sendBroadcast(intent);
                        }
                    } catch (Exception e) {
                        log.error("Error tryng to send broadcast to progressbar", e);
                    }
                }
            }

            fos.close();

            if (contentId.contains(ConstantKeys.EXTENSION_JPG)) {
                return decodeSampledBitmapFromPath(file.getAbsolutePath(), 200, 200);
            } else {
                return ThumbnailUtils.createVideoThumbnail(file.getAbsolutePath(),
                        MediaStore.Images.Thumbnails.MINI_KIND);
            }

        } catch (Exception e) {
            new File(getDir(), contentId).delete();
            return null;
        }
    }

    public static void FreeUpSpace(Context ctx, Long fileSize) {
        File dir = new File(FileUtils.getDir());
        File[] files = dir.listFiles();
        long actualSize = Long.valueOf(0);
        for (int i = 0; i < files.length; i++) {
            if (!files[i].getName().contains("_")) {
                actualSize = actualSize + files[i].length();
            }
        }

        long futureSize = actualSize + fileSize;
        long totalSize = Long.valueOf(Preferences.getMediaTotalSize(ctx)) * 1024 * 1024;
        if (futureSize < totalSize) {
            return;
        }

        log.debug("We need more space, let's delete some files");
        orderFiles(files);
        for (int i = 0; i < files.length; i++) {
            if (!files[i].getName().contains("_")) {
                futureSize = futureSize - (files[i].length());
                files[i].delete();
                if (futureSize < totalSize) {
                    return;
                }
            }
        }

    }

    @SuppressWarnings("unchecked")
    public static void orderFiles(File[] files) {
        Arrays.sort(files, new Comparator() {
            public int compare(Object o1, Object o2) {

                if (((File) o1).lastModified() < ((File) o2).lastModified()) {
                    return -1;
                } else if (((File) o1).lastModified() > ((File) o2).lastModified()) {
                    return 1;
                } else {
                    return 0;
                }
            }

        });
    }

    public static void DownloadFromUrl(final String media, final String messageId, final Context ctx,
            final ImageView container, final Object object, final String timelineId, final String localId,
            final Long fileSize) {

        new AsyncTask<Void, Void, Boolean>() {
            private boolean retry = true;
            private Bitmap imageDownloaded;
            private BroadcastReceiver mDownloadCancelReceiver;
            private HttpGet job;
            private AccountManager am;
            private Account account;
            private String authToken;

            @Override
            protected void onPreExecute() {
                IntentFilter downloadFilter = new IntentFilter(ConstantKeys.BROADCAST_CANCEL_PROCESS);
                mDownloadCancelReceiver = new BroadcastReceiver() {
                    @Override
                    public void onReceive(Context context, Intent intent) {
                        String localIdToClose = (String) intent.getExtras().get(ConstantKeys.LOCALID);
                        if (localId.equals(localIdToClose)) {
                            try {
                                job.abort();
                            } catch (Exception e) {
                                log.debug("The process was canceled");
                            }
                            cancel(false);
                        }
                    }
                };

                // registering our receiver
                ctx.getApplicationContext().registerReceiver(mDownloadCancelReceiver, downloadFilter);
            }

            @Override
            protected void onCancelled() {
                File file1 = new File(FileUtils.getDir(), localId + ConstantKeys.EXTENSION_JPG);
                File file2 = new File(FileUtils.getDir(), localId + ConstantKeys.EXTENSION_3GP);

                if (file1.exists()) {
                    file1.delete();
                }
                if (file2.exists()) {
                    file2.delete();
                }

                file1 = null;
                file2 = null;
                System.gc();
                try {
                    ctx.getApplicationContext().unregisterReceiver(mDownloadCancelReceiver);
                } catch (Exception e) {
                    log.debug("Receriver unregister from another code");
                }

                for (int i = 0; i < AppUtils.getlistOfDownload().size(); i++) {
                    if (AppUtils.getlistOfDownload().get(i).equals(localId)) {
                        AppUtils.getlistOfDownload().remove(i);
                    }
                }

                DataBasesAccess.getInstance(ctx.getApplicationContext()).MessagesDataBaseWriteTotal(localId, 100);

                Intent intent = new Intent();
                intent.setAction(ConstantKeys.BROADCAST_DIALOG_DOWNLOAD_FINISH);
                intent.putExtra(ConstantKeys.LOCALID, localId);
                ctx.sendBroadcast(intent);

                if (object != null) {
                    ((ProgressDialog) object).dismiss();
                }
            }

            @Override
            protected Boolean doInBackground(Void... params) {
                try {
                    File file1 = new File(FileUtils.getDir(), localId + ConstantKeys.EXTENSION_JPG);
                    File file2 = new File(FileUtils.getDir(), localId + ConstantKeys.EXTENSION_3GP);
                    // firt we are goint to search the local files
                    if ((!file1.exists()) && (!file2.exists())) {
                        account = AccountUtils.getAccount(ctx.getApplicationContext(), false);
                        am = (AccountManager) ctx.getSystemService(Context.ACCOUNT_SERVICE);
                        authToken = ConstantKeys.STRING_DEFAULT;
                        authToken = am.blockingGetAuthToken(account, ctx.getString(R.string.account_type), true);

                        MessagingClientService messageService = new MessagingClientService(
                                ctx.getApplicationContext());

                        URL urlObj = new URL(Preferences.getServerProtocol(ctx), Preferences.getServerAddress(ctx),
                                Preferences.getServerPort(ctx), ctx.getString(R.string.url_get_content));

                        String url = ConstantKeys.STRING_DEFAULT;
                        url = Uri.parse(urlObj.toString()).buildUpon().build().toString() + timelineId + "/"
                                + messageId + "/" + "content";

                        job = new HttpGet(url);
                        // first, get free space
                        FreeUpSpace(ctx, fileSize);
                        messageService.getContent(authToken, media, messageId, timelineId, localId, false, false,
                                fileSize, job);
                    }

                    if (file1.exists()) {
                        imageDownloaded = decodeSampledBitmapFromPath(file1.getAbsolutePath(), 200, 200);
                    } else if (file2.exists()) {
                        imageDownloaded = ThumbnailUtils.createVideoThumbnail(file2.getAbsolutePath(),
                                MediaStore.Images.Thumbnails.MINI_KIND);
                    }

                    if (imageDownloaded == null) {
                        return false;
                    }
                    return true;
                } catch (Exception e) {
                    deleteFiles();
                    return false;
                }
            }

            @Override
            protected void onPostExecute(Boolean result) {
                // We have the media
                try {
                    ctx.getApplicationContext().unregisterReceiver(mDownloadCancelReceiver);
                } catch (Exception e) {
                    log.debug("Receiver was closed on cancel");
                }

                if (!(localId.contains(ConstantKeys.AVATAR))) {
                    for (int i = 0; i < AppUtils.getlistOfDownload().size(); i++) {
                        if (AppUtils.getlistOfDownload().get(i).equals(localId)) {
                            AppUtils.getlistOfDownload().remove(i);
                        }
                    }

                    DataBasesAccess.getInstance(ctx.getApplicationContext()).MessagesDataBaseWriteTotal(localId,
                            100);

                    Intent intent = new Intent();
                    intent.setAction(ConstantKeys.BROADCAST_DIALOG_DOWNLOAD_FINISH);
                    intent.putExtra(ConstantKeys.LOCALID, localId);
                    ctx.sendBroadcast(intent);
                }

                if (object != null) {
                    ((ProgressDialog) object).dismiss();
                }

                // Now the only container could be the avatar in edit screen
                if (container != null) {
                    if (imageDownloaded != null) {
                        container.setImageBitmap(imageDownloaded);
                    } else {
                        deleteFiles();
                        imageDownloaded = decodeSampledBitmapFromResource(ctx.getResources(),
                                R.drawable.ic_error_loading, 200, 200);
                        container.setImageBitmap(imageDownloaded);
                        Toast.makeText(ctx.getApplicationContext(),
                                ctx.getApplicationContext().getText(R.string.donwload_fail), Toast.LENGTH_SHORT)
                                .show();
                    }
                } else {
                    showMedia(localId, ctx, (ProgressDialog) object);
                }

            }

            private void deleteFiles() {
                File file1 = new File(FileUtils.getDir(), localId + ConstantKeys.EXTENSION_JPG);
                File file2 = new File(FileUtils.getDir(), localId + ConstantKeys.EXTENSION_3GP);
                if (file1.exists()) {
                    file1.delete();
                }
                if (file2.exists()) {
                    file2.delete();
                }
            }

        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    }

    private static void showMedia(final String localId, final Context ctx, final ProgressDialog dialog) {
        new AsyncTask<Void, Void, Boolean>() {

            private boolean noMedia = false;

            @Override
            protected Boolean doInBackground(Void... params) {
                File file1 = new File(FileUtils.getDir(), localId + ConstantKeys.EXTENSION_JPG);
                File file2 = new File(FileUtils.getDir(), localId + ConstantKeys.EXTENSION_3GP);

                Intent mediaPlayer = new Intent(Intent.ACTION_VIEW);

                if (file1.exists()) {
                    mediaPlayer.setDataAndType(Uri.fromFile(file1), "image/*");
                } else if (file2.exists()) {
                    mediaPlayer.setDataAndType(Uri.fromFile(file2), "video/*");

                } else {
                    noMedia = true;
                    return false;
                }
                ctx.startActivity(mediaPlayer);
                return true;
            }

            @Override
            protected void onPostExecute(Boolean result) {
                if (dialog != null) {
                    dialog.dismiss();
                }
                if (noMedia) {
                    Toast.makeText(ctx.getApplicationContext(),
                            ctx.getApplicationContext().getText(R.string.no_media), Toast.LENGTH_SHORT).show();
                    noMedia = false;
                }
            }

        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);

    }

    public static String sendMessageToServer(final Context ctx, final String path, final Long toId,
            final String toType, final Long timelineId, boolean retry, Long localId, String messageBody,
            Object messageApp) {

        Account account = AccountUtils.getAccount(ctx.getApplicationContext(), true);
        AccountManager am = (AccountManager) ctx.getSystemService(Context.ACCOUNT_SERVICE);

        MessageSend m = new MessageSend();
        m.setBody(messageBody);
        m.setFrom(Long.valueOf(am.getUserData(account, JsonKeys.ID_STORED)));
        m.setTo(toId);
        m.setLocalId(localId);
        if (messageApp != null)
            m.setApp(messageApp);

        CommandStoreService cs = new CommandStoreService(ctx.getApplicationContext());
        try {
            boolean result = false;
            if (toType.equals(JsonKeys.GROUP)) {
                result = cs.createCommand(JsonParser.MessageSendToJson(m), Command.METHOD_SEND_MESSAGE_TO_GROUP,
                        path);
            } else if (toType.equals(JsonKeys.USER)) {
                result = cs.createCommand(JsonParser.MessageSendToJson(m), Command.METHOD_SEND_MESSAGE_TO_USER,
                        path);
            }

            if (result) {
                return ConstantKeys.SENDING_OK;
            } else {
                return ConstantKeys.SENDING_OFFLINE;
            }
        } catch (JSONException e) {
            log.error("Cannot create command", e);
            return ConstantKeys.SENDING_FAIL;
        }
    }

    public static String getRealPathFromURI(Uri contentUri, Context ctx) {
        String[] proj = { MediaStore.Video.Media.DATA };
        Cursor cursor = ((Activity) ctx).managedQuery(contentUri, proj, null, null, null);
        if (cursor != null) {
            int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA);
            cursor.moveToFirst();
            String ret = cursor.getString(columnIndex);

            return ret;
        } else {
            return contentUri.getPath();
        }
    }

    public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) {

        // First decode with inJustDecodeBounds=true to check dimensions
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(res, resId, options);

        // Calculate inSampleSize
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeResource(res, resId, options);
    }

    public static Bitmap decodeSampledBitmapFromPath(String path, int reqWidth, int reqHeight) {
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(path, options);
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
        options.inJustDecodeBounds = false;

        Bitmap b = BitmapFactory.decodeFile(path, options);

        ExifInterface exif;
        try {
            exif = new ExifInterface(path);
        } catch (IOException e) {
            return null;
        }
        int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);
        Matrix matrix = new Matrix();
        if (orientation == 6)
            matrix.postRotate(90);
        else if (orientation == 3)
            matrix.postRotate(180);
        else if (orientation == 8)
            matrix.postRotate(270);

        return Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(), matrix, true);
    }

    public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {
            final int heightRatio = Math.round((float) height / (float) reqHeight);
            final int widthRatio = Math.round((float) width / (float) reqWidth);
            inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
        }

        return inSampleSize;
    }

    public static void takePicture(Context ctx, Uri mFileCaptureUri) {
        if (!isIntentAvailable(ctx, MediaStore.ACTION_IMAGE_CAPTURE)) {
            log.warn("Cannot take picture (Intent is not available)");
            return;
        }

        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, mFileCaptureUri);
        intent.putExtra("return-data", true);

        try {
            ((Activity) ctx).startActivityForResult(intent, AppUtils.ACTION_TAKE_PICTURE);
        } catch (ActivityNotFoundException e) {
            log.warn("Cannot take picture", e);
        }
    }

    public static void recordVideo(Context ctx, Uri mFileCaptureUri) {
        if (!isIntentAvailable(ctx, MediaStore.ACTION_VIDEO_CAPTURE)) {
            log.warn("Cannot record video (Intent is not available)");
            return;
        }

        Intent takeVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
        takeVideoIntent.putExtra(MediaStore.EXTRA_OUTPUT, mFileCaptureUri);

        int durationLimit = ctx.getResources().getInteger(R.integer.video_recording_duration_limit);
        if (durationLimit > 0) {
            takeVideoIntent.putExtra("android.intent.extra.durationLimit", durationLimit);
        }

        try {
            ((Activity) ctx).startActivityForResult(takeVideoIntent, AppUtils.ACTION_RECORD_VIDEO);
        } catch (ActivityNotFoundException e) {
            log.warn("Cannot record video", e);
        }
    }

    public static void copyFile(File src, File dst) throws IOException {
        FileChannel inChannel = new FileInputStream(src).getChannel();
        FileChannel outChannel = new FileOutputStream(dst).getChannel();

        try {
            inChannel.transferTo(0, inChannel.size(), outChannel);
        } finally {
            if (inChannel != null)
                inChannel.close();
            if (outChannel != null)
                outChannel.close();
        }
    }

    public static void createThumnail(File file) {
        String name = file.getName();
        String filePath = file.getAbsolutePath();
        Bitmap bm;

        if ((file.toString().contains(ConstantKeys.EXTENSION_JPG))
                || (file.toString().contains(ConstantKeys.EXTENSION_PNG))
                || (file.toString().contains(ConstantKeys.EXTENSION_JPEG))) {
            bm = decodeSampledBitmapFromPath(filePath, 250, 250);
            name = name.replace(ConstantKeys.EXTENSION_JPG, ConstantKeys.STRING_DEFAULT);

        } else {
            bm = ThumbnailUtils.createVideoThumbnail(filePath, MediaStore.Images.Thumbnails.MINI_KIND);
            name = name.replace(ConstantKeys.EXTENSION_3GP, ConstantKeys.STRING_DEFAULT);

        }

        if (bm == null) {
            log.error("Error creating thumbnail");
            return;
        }

        File f = new File(getDir(), name + ConstantKeys.THUMBNAIL + ConstantKeys.EXTENSION_JPG);
        try {
            f.createNewFile();
        } catch (IOException e1) {
            log.error("Error creating file for thumbnail", e1);
            return;
        }
        // Convert bitmap to byte array
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        bm.compress(CompressFormat.PNG, 0, bos);
        byte[] bitmapdata = bos.toByteArray();
        // write the bytes in file
        FileOutputStream fos;
        try {
            fos = new FileOutputStream(f);
            fos.write(bitmapdata);
            fos.flush();
            fos.close();
        } catch (Exception e) {
            log.debug("Error creagint thumnail", e);
        }

        bm = null;
        System.gc();
    }

    public static String getDir() {
        File dir = new File(Environment.getExternalStorageDirectory().toString() + "/.kurento");
        if (!dir.exists()) {
            dir.mkdir();
        }
        return dir.getAbsolutePath();
    }

    private static void showError(Context ctx) {
        Toast.makeText(ctx.getApplicationContext(), ctx.getApplicationContext().getText(R.string.auth_fail),
                Toast.LENGTH_SHORT).show();
    }

    private static boolean isIntentAvailable(Context context, String action) {
        final PackageManager packageManager = context.getPackageManager();
        final Intent intent = new Intent(action);
        List<ResolveInfo> list = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
        return list.size() > 0;
    }

    public static void deleteMediaFromTimeline(Context ctx, Long timeLine) {

        ArrayList<MessageObject> list = DataBasesAccess.getInstance(ctx.getApplicationContext())
                .MessagesDataBaseReadSelected(timeLine, null);

        for (int i = 0; i < list.size(); i++) {
            File toDeleteJpg = new File(
                    FileUtils.getDir() + "/" + list.get(i).getLocalId() + ConstantKeys.EXTENSION_JPG);
            File toDelete3gp = new File(
                    FileUtils.getDir() + "/" + list.get(i).getLocalId() + ConstantKeys.EXTENSION_3GP);
            if (toDeleteJpg.exists()) {
                toDeleteJpg.delete();
                toDeleteJpg = null;
            }

            if (toDelete3gp.exists()) {
                toDelete3gp.delete();
                toDelete3gp = null;
            }

            DataBasesAccess.getInstance(ctx.getApplicationContext())
                    .MessagesDataBaseDelete(list.get(i).getLocalId());
        }
    }
}