Клиринговое намерение


112

Мое приложение для Android вызывается намерением, которое передает информацию (ожидающее намерения в строке состояния).

Когда я нажимаю кнопку «Домой» и снова открываю свое приложение, удерживая кнопку «Домой», оно снова вызывает намерение, и те же дополнительные функции все еще присутствуют.

    @Override
    public void onSaveInstanceState(Bundle savedInstanceState) {
      super.onSaveInstanceState(savedInstanceState);
    }
    @Override
    public void onRestoreInstanceState(Bundle savedInstanceState) {
      super.onRestoreInstanceState(savedInstanceState);
    }

это код, который работает не так, как предполагалось

    String imgUrl;
    Bundle extras = this.getIntent().getExtras();


    if(extras != null){
        imgUrl = extras.getString("imgUrl");
        if( !imgUrl.equals(textView01.getText().toString()) ){

            imageView.setImageDrawable( getImageFromUrl( imgUrl ) );
            layout1.setVisibility(0);
            textView01.setText(imgUrl);//textview to hold the url

        }

    }

И мое намерение:

public void showNotification(String ticker, String title, String message, 
    String imgUrl){
    String ns = Context.NOTIFICATION_SERVICE;
    NotificationManager mNotificationManager = 
        (NotificationManager) getSystemService(ns);
    int icon = R.drawable.icon;        // icon from resources
    long when = System.currentTimeMillis();         // notification time
    CharSequence tickerText = ticker;              // ticker-text

    //make intent
    Intent notificationIntent = new Intent(this, activity.class);
    notificationIntent.putExtra("imgUrl", imgUrl);
    notificationIntent.setFlags(
        PendingIntent.FLAG_UPDATE_CURRENT | 
        PendingIntent.FLAG_ONE_SHOT);
    PendingIntent contentIntent = 
        PendingIntent.getActivity(this, 0, 
        notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT | 
        PendingIntent.FLAG_ONE_SHOT);

    //make notification
    Notification notification = new Notification(icon, tickerText, when);
    notification.setLatestEventInfo(this, title, message, contentIntent);
    //flags
    notification.flags = Notification.FLAG_SHOW_LIGHTS | 
        Notification.FLAG_ONGOING_EVENT | 
        Notification.FLAG_ONLY_ALERT_ONCE | 
        Notification.FLAG_AUTO_CANCEL;
    //sounds
    notification.defaults |= Notification.DEFAULT_SOUND;
    //notify
    mNotificationManager.notify(1, notification);
}

Есть ли способ очистить намерение или проверить, использовалось ли оно раньше?


Вы можете разместить свои коды?
xandy

Я добавил код к своему вопросу
Марс

Вместо очистки намерения вы можете определить тип запуска и соответствующим образом обработать поток приложения. Получайте Extras только в том случае, если он запускается для уведомления, а не из фона. stackoverflow.com/questions/4116110/clearing-intent/…
BB

Ответы:


169

ОБНОВИТЬ:

Я не понимал, что этот ответ будет так много упоминаться, когда впервые написал его более 5 лет назад!

Я поясню, чтобы указать, что в соответствии с ответом @ tato-rodrigo это не поможет вам обнаружить уже обработанное намерение в некоторых ситуациях.

Также я должен указать, что я поставил "clear" в кавычки по какой-то причине - вы на самом деле не очищаете намерение, делая это, вы просто используете удаление лишнего в качестве флага того, что это намерение уже было замечено действием. .


У меня была точно такая же проблема.

Приведенный выше ответ направил меня на правильный путь, и я нашел еще более простое решение, используйте:

getIntent().removeExtra("key"); 

вызов метода, чтобы «очистить» намерение.

Немного поздно ответить, так как об этом спросили год назад, но, надеюсь, это поможет другим в будущем.


4
Разве метод removeExtra () не принимает параметр String? как это getIntent (). removeExtra ("String");
tony9099

25
@Maks Я могу ошибаться, но я думаю, что это не сработает в следующем сценарии: 1) Откройте активность через уведомление; 2) Завершите действие, нажав кнопку «Назад»; 3) Снова откройте действие через историю (последние приложения). Другой случай - когда система убивает приложение из-за нехватки ресурсов (включите «Не сохранять действия» в параметрах разработчика, а после этого просто нажмите «Домой», затем снова откройте действие из истории). Я разместил решение, которое использую ниже. Если бы вы могли это прокомментировать, было бы неплохо.
tato.rodrigo

2
К сожалению, у нас это не работает. Мы обнаружили, что запуск нового действия, которое, в свою очередь, запускает начальное действие, заставляет OnNewIntent снова срабатывать с тем же намерением.
Le-roy Staines

2
Вместо очистки намерения вы можете определить тип запуска и соответствующим образом обработать поток приложения. Получайте Extras только в том случае, если он запускается для уведомления, а не из фона. stackoverflow.com/questions/4116110/clearing-intent/…
BB

2
У меня не сработало. Я столкнулся с той же проблемой, что и @ tato.rodrigo, где упоминалось о том, что намерение не очищалось, если действие открывается через уведомление, из истории или по другим причинам, которые он упомянул, поэтому после использования информации о намерениях я сбросил намерение, например это setIntent(new Intent())и теперь работает нормально.
Shubhral

43

РЕДАКТИРОВАТЬ: я редактирую, чтобы опубликовать полное решение, которое я использую.

Это решение будет работать, если возникнет проблема «Не выполнить некоторый код при запуске действия из истории (недавние приложения)» .

Прежде всего, объявите booleanв своем, Activityчтобы указать, Intentбыл ли уже использован:

    private boolean consumedIntent;

Затем, безопасно хранить и восстанавливать это значение , используя onSaveInstanceStateи onCreateметоды для обработки изменений конфигурации и случаи , что система может убивать ваших , Activityкогда она идет в фоновом режиме.

    private final String SAVED_INSTANCE_STATE_CONSUMED_INTENT = "SAVED_INSTANCE_STATE_CONSUMED_INTENT";

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putBoolean(SAVED_INSTANCE_STATE_CONSUMED_INTENT, consumedIntent);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //set content view ...

        if( savedInstanceState != null ) {
            consumedIntent = savedInstanceState.getBoolean(SAVED_INSTANCE_STATE_CONSUMED_INTENT);
        }

        //other initializations
    }

Теперь проверьте, можете ли вы запустить свой код под onResumeметодом.

    @Override
    protected void onResume() {
        super.onResume();

        //check if this intent should run your code
        //for example, check the Intent action
        boolean shouldThisIntentTriggerMyCode = [...];
        Intent intent = getIntent();
        boolean launchedFromHistory = intent != null ? (intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0 : false;
        if( !launchedFromHistory && shouldThisIntentTriggerMyCode && !consumedIntent ) {
            consumedIntent = true;
            //execute the code that should be executed if the activity was not launched from history
        }
    }

Кроме того, если вы Activityнастроены на singleTop, вы должны сбросить свой флаг при Intentдоставке нового .

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        setIntent(intent);
        consumedIntent = false;
    }

12
Большое спасибо! Мне помог этот код, (intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY)поэтому теперь я могу понять, когда начало активности находится в истории, и могу игнорировать свои дополнения.
Роман Назаревич

1
@Lemberg У меня такая же проблема, и я решил ее так же, как и вы. Если вы используете некоторые дополнения, полученные из push-уведомлений, есть риск начать свою деятельность из истории и всегда использовать ваши дополнения и перенаправлять на то же действие, что и ваше Отправить уведомление. Флаг, запущенный FromHistory, может помочь вам узнать об этом
Стойчо Андреев

будет ли он работать, даже если действие было уничтожено и мы снова откроем его из стека истории?
user25

здорово! кажется, работает, даже если приложение было уничтожено ... но, пожалуйста, @ tato.rodrigo удалите boolean shouldThisIntentTriggerMyCode = [...];из ответа (для чего используется?)
user25

В моем случае с мульти-уведомлениями для определенного пользователя лучше использовать user consumedIntentкак Stringсодержащий Uid уведомления. Этот Uid можно просто добавить к уведомлению на бэкэнде как текущую отметку времени. Также вы должны сохранять этот Uid onSaveInstanceStateтолько в том случае, если намерение было сформировано onCreate. Это означает, что вам не следует сохранять Uid из файлов onNewIntent.
Константин Конопко

22

Ответ Макс работает для очистки лишнего:

    getIntent().removeExtra("key"); 

Еще одна полезная команда:

    getIntent().setAction("");

Вы также можете пометить намерение, позвонив:

    getIntent().putExtra("used", true);

а затем просто проверьте значение.


21

Когда мы запускаем приложения для Android из истории (последние приложения), приложение может запускаться в основном с тремя разными флагами намерений.

  1. FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
    Это когда активность запускается из истории приложения, которое было свернуто (долгое нажатие клавиши возврата домой).
    Постоянное значение: 1048576 (0x00100000)
  2. FLAG_ACTIVITY_NEW_TASK
    Это когда действие запускается через «щелчок по значку приложения» или через « фильтры намерений ». Здесь действие станет началом новой задачи в этом стеке истории.
    Постоянное значение: 268435456 ( 0x10000000 )
  3. FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY | FLAG_ACTIVITY_NEW_TASK
    Это когда приложение было закрыто нажатием кнопки «Назад», а затем возобновилось из истории (последние приложения).
    Постоянное значение: 269484032 (0x10100000)

Постоянное значение можно получить через getIntent().getFlags()

В третьем случае Android перезагружает последние значения Intent из своей памяти. Таким образом, intent ( getIntent) вашего приложения будет иметь значения из последнего намерения, запустившего приложение.

Фактически, приложение должно вести себя так, как будто это новый запуск, со значениями намерений для нового запуска, а не значениями намерений предыдущего запуска. Это поведение можно увидеть, если вы запустите приложение, щелкнув значок приложения, оно никогда не будет иметь старых значений намерений. Это потому, что Android использует следующий фильтр намерений для этого сценария

 <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER"/>
 </intent-filter>

Но в третьем случае (приложение, которое было закрыто, запускается из истории последних приложений), ОС Android использует последнее намерение, которое запустило приложение до его выхода (путем нажатия кнопки возврата). Таким образом, у вас останутся старые значения намерений, и поток приложения будет неправильным.

Удаление намерения - это один из способов ее решения, но это не решит проблему полностью! Поскольку ОС Android перезагружает намерение из последнего запуска приложений, а не из последнего экземпляра намерения запуска.

Чистый способ избежать этого - справиться с этим, получив тип Intent для определения типа запуска.

Так что в вашем LaunchActivity (тот , который имеет намерение фильтра , определенный в манифесте), вы можете использовать следующий код в onCreate(), onStart()или onResume()методах.

if(getIntent().getFlags() == (Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY)) {
    //app is launched from recent apps after it was closed
        normalLaunch();
    } else {
        String intentAction = getIntent().getAction();
        String scheme = getIntent().getScheme();
        //app is launched via other means
        // URL intent scheme, Intent action etc
        if("https".equalsIgnoreCase(scheme)) {
            // URL intent for browser
        } else if("com.example.bb".equalsIgnoreCase(intentAction)) {
            // App launched via package name
        } else {
            // App was launched via Click on App Icon, or other means
            normalLaunch();
        }
    }

Я полагаю normalLaunch(), не следует использовать параметры из Intent; в противном случае вам нужно будет отделить и оптимизировать метод запуска по умолчанию, чтобы не использовать параметры Intent.


1
Не все герои носят кепки!
Sdghasemi

Я не знаю, но он всегда возвращает истину, getIntent().getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORYне имеет значения, начинаю ли я действие из другого действия (метод startActivity) или повторно открываю его из стека истории (недавние приложения).
user25

вам придется использовать его в сочетании с другими флагами, возможно FLAG_ACTIVITY_NEW_TASK
BB

1
Это не работает, если активность была отключена в фоновом режиме из-за настроек Dev «Не сохранять действия». В этом случае getIntent () getFlags () идентичен тому, как когда действие запускалось в первый раз.
Malachiasz

ценю объяснение, но оно не такое, как задумано
Азлан Джамал

18

Очистка намеренного объекта :

intent.replaceExtras(new Bundle());
intent.setAction("");
intent.setData(null);
intent.setFlags(0);

2
Это должен быть принятый ответ. Очень хорошо работает!
Мартин Эрлик

2
Не работает, когда в параметрах разработчика
отмечено

8

Короткий ответ: нет

Длинный ответ. Не существует такой вещи, как одноразовое намерение. Экспериментально показывает, что недавняя история активности современных Android-устройств - это не что иное, как «история намерений». Последнее намерение, переданное активности, просто регистрируется в системе, и в этом дело. Люди выше предлагают использовать

setAction("")

Но это не работает, потому что намерение уже зарегистрировано до того момента, когда вы получите его внутри метода onNewIntent () или onStart ().

Я решил проблему, отказавшись от намерений. Моя проблема была похожа на проблему, опубликованную автором. Я попытался реализовать глобальный выход из приложения через элемент управления в области уведомлений. Он должен остановить базовую службу и закрыть все действия приложения. Вы можете найти такое же поведение в приложении Waze.

Алгоритм:

  1. Создайте PendingIntent для контроля уведомлений, который передает действие «Выход». Но к специальной деятельности, которая представляет собой простой прокси.
  2. Код активности прокси onStart () анализирует намерение, проверяет действие и устанавливает состояние некоторой модели на «Завершено».
  3. Код активности прокси onStart () очищает исходное намерение с помощью setIntent (""), а затем перенаправляет его в целевую "корневую" активность, вызывая startActivity (intent).
  4. Активность прокси в коде onStart () вызывает finish ().
  5. Внутри onStart () и onNewIntent () целевой активности проверьте состояние модели и вызовите finish (), если она «Exited» (а также вызовите stopService () в моем случае).

Надеюсь, это кому-то поможет, потому что я не нашел ответа в Интернете.


Я считаю это наиболее точным ответом, поскольку «очистка намерения» не обязательно означает «удалить некоторые лишние». Я также не думаю, что есть простой способ сделать это.
mdelolmo

5

Убедитесь , что вы используете PendingIntent.FLAG_UPDATE_CURRENT флаг PendingIntent .

PendingIntent pendingIntent = PendingIntent.getActivity(this, 100, mPutIntent, PendingIntent.FLAG_UPDATE_CURRENT);

Где mPutIntentтвой Intent.

Надеюсь, что это поможет вам.


1
спас мою жизнь!!
eren130 09

1
Я не понимаю, почему это не принятый ответ. Единственное, о чем я сожалею: я получил один голос за. Ура.
Энди

2

Недавно у меня была эта проблема, и я решил ее, добавив метку времени в качестве дополнительного параметра к намерению:

private void launchActivity(Context context) {
    Intent intent = new Intent(context, MainActivity.class);
    intent.putExtra("KEY_EXTRA_TIMESTAMP", System.currentTimeMillis());
    context.startActivity(intent);
}

После этого сохраните метку времени в общих настройках:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    long time = getIntent().getLongExtra("KEY_EXTRA_TIMESTAMP", -1);
    long previousTime = getPreferences(MODE_PRIVATE).getLong("timestamp", -1);

    //ignore if the timestamp is the same as the previous one  
    if (time != previousTime) {
        handleIntent(getIntent());
        if (time != -1) {
            //save the timestamp
            getPreferences(MODE_PRIVATE).edit().putLong("timestamp", time).apply();
        }
    }
}

1

У меня точно такая же проблема. Мое решение заключалось в том, чтобы добавить booleanпеременную, которая была установлена, когда она Intentбыла «использована», и ifутверждение на основе этого, booleanчтобы проверить, следует ли вам использовать Intentили нет.


3
это может не сработать, поскольку логические значения будут воссозданы (если объявлены глобальными в активе), когда действие будет остановлено, а затем перезапущено. (например, нажав кнопку «Домой»)
tony9099

1

Когда вы закончите обработку намерения, сделайте следующее:

setIntent(null);

Вы больше не увидите это обработанное намерение и не будете маскировать проблему, редактируя содержимое обработанного намерения.


1

Я не смог найти способ удалить Intent Extra . Ни один из ответов об удалении лишнего из намерения не работает, если вы включите «Не сохранять действия » в параметрах разработчика (таким образом вы можете уничтожить действие и вернуться, чтобы проверить, есть ли еще дополнительные действия).

В качестве решения проблемы я сохранил логическое значение в SharedPreferences после обработки Intent Extras. Когда то же намерение повторно доставляется в действие, я проверяю значение SharedPreference и решаю обработать дополнительное намерение. Если вы отправляете другое новое Intent Extra в то же действие, вы устанавливаете значение SharedPreference false, и Activity обработает его. Пример :

// Start Activity with Intent Extras
Intent intent = new Intent(context, MyActivity.class);
intent.putExtra("someData", "my Data");
// Set data as not processed
context.getSharedPreferences(BuildConfig.APPLICATION_ID, Context.MODE_PRIVATE).edit().putBoolean("myActivityExtraProccessed", false).commit();
context.startActivity(intent);

...

public class MyActivity{

    ...
    public void someMethod(){
        boolean isExtrasProcessed = context.getSharedPreferences(BuildConfig.APPLICATION_ID, Context.MODE_PRIVATE).getBoolean("myActivityExtraProccessed", false);  
         if (!isExtrasProcessed) {
              // Use Extras

              //Set data as processed
              context.getSharedPreferences(BuildConfig.APPLICATION_ID, Context.MODE_PRIVATE).edit().putBoolean("myActivityExtraProccessed", true).commit();
         }
    }

}

предпочтение не имеет смысла, поскольку вы не знаете, начали ли вы действие с помощью startActivity или повторно открыли его из стека истории ...
user25

@ user25 Думаю, был способ определить, запущен ли он из недавних приложений. Но имеет ли значение, потребляются дополнительные намерения или нет, если они потребляются, вы знаете это из общих предпочтений. Я использовал это для дополнительного использования push-уведомлений, и в моем случае это не имеет значения.
Джемшит Искендеров

0

Даже после ручной очистки дополнительных компонентов Intent и Intent после их анализа кажется, что Activity.getIntent () всегда будет возвращать исходное намерение, которое запустило действие.

Чтобы обойти это, я рекомендую что-то вроде этого:

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // The Intent provided by getIntent() (and its extras) will persist through a restore
    // via savedInstance.  Because of this, restoring this activity from a
    // an instance that was originally started with extras (deep-link or 
    // pre-defined destination) may cause un-desired behavior
    // (ie...infinite loop of sending the user directly to somewhere else because of a
    // pre-defined alternate destination in the Intent's extras).
    //
    // To get around this, if restoring from savedInstanceState, we explicitly
    // set a new Intent *** to override the original Intent that started the activity.***
    // Note...it is still possible to re-use the original Intent values...simply
    // set them in the savedInstanceState Bundle in onSavedInstanceState.
    if (savedInstanceState != null) {
        // Place savedInstanceState Bundle as the Intent "extras"
        setIntent(new Intent().putExtras(savedInstanceState));
    }

    processIntent(getIntent())
}

private void processIntent(Intent intent) {
    if (getIntent().getExtras() == null) {
        // Protection condition
        return;
    }

    doSomething(intent.getExtras.getString("SOMETHING_I_REALLY_NEED_TO_PERSIST"));

    final String somethingIDontWantToPersist = 
        intent.getExtras.getString("SOMETHING_I_DONT_WANT_TO_PERSIST");

    if(somethingIDontWantToPersist != null) {
        doSomething(somethingIDontWantToPersist);
    }
}

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
    // Save selective extras from original Intent...
    savedInstanceState.putString("SOMETHING_I_REALLY_NEED_TO_PERSIST", "persistedValued");
    super.onSaveInstanceState(savedInstanceState);
}

Таким образом, существует механизм для сброса исходного Intent, сохраняя при этом возможность явно сохранять определенные части исходных Intent / Intent дополнений.

Обратите внимание, что я не тестировал все режимы запуска Activity.


0

Самый простой способ - избежать вызова getIntent () из других методов, кроме onCreate (). Но это вызовет проблемы при следующем запуске, если пользователь покинул нашу Activity, нажав кнопку «Домой». Думаю, у этой проблемы нет полнофункционального решения.


0

Я сталкиваюсь с той же проблемой и пытаюсь использовать вышеуказанные методы, но это не работает.

Думаю, это может быть причиной режима запуска acitvity, который я использовал в режиме singleTop.

Когда я использую приложение в фоновом режиме и использую RamEater для имитации проблемы, у намерения всегда есть лишнее, даже если я устанавливаю его на ноль или удаляю ключ.

Проблема исчезла, если использовать хранилище предпочтений на Android, чтобы проверить, что прошло.


0

Не рекомендуется добавлять еще одну добавку, чтобы знать, были ли израсходованы дополнительные продукты или нет, почему бы не сделать это ?:

if (intent.hasExtra(EXTRA_NAME) && intent.getBooleanExtra(EXTRA_NAME, false)) {
    // consume extra here after that set it to false
    putExtra(EXTRA_NAME, false)
}   

-1

Как насчет этого? Устанавливает newIntent как намерение.

@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    setIntent(intent);
}

1
Я не думаю, что это работает. Когда действие воссоздается из истории, намерение остается неизменным.
mdelolmo

-1

Как насчет того, чтобы очистить намерение - заменить его пустым?

например.

@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    setIntent(intent);
}

@Override 
public void onResume() {
    super.onResume();

    Intent theIntent = getIntent();
    if ("myaction".equals(theIntent.getAction()) {
         handleIntent();
         onNewIntent(new Intent());  // <--- "clear" the intent by setting empty one
    }
}

-1

Надеюсь, это поможет всем. Итак, сначала мы понимаем намерение

//globally
Intent myIntent;

Поместите это где-нибудь на Create

myIntent = getIntent();
String data = myIntent.getStringExtra("yourdata");
//Your process here

Теперь давайте установим это так, что каждый раз, когда наше приложение будет уничтожено или завершено, мы будем удалять данные.

@Override
protected void onDestroy() {
    //TODO: Clear intents
    super.onDestroy();
    myIntent.removeExtra("data");
}
@Override
protected void onBackPressed() {
    //TODO: Clear intents
    super.onBackPressed();
    myIntent.removeExtra("data");
}

Вы понимаете, если этого недостаточно, просто найдите больше обратных вызовов


Намерение очищается только при выходе из приложения, например. Листая недавние.
Pixeldroid Modding

-2

Хотя Intent.removeExtra("key")он удалит один конкретный ключ из дополнений, существует также метод Intent.replaceExtras (Bundle) , который можно использовать для удаления всего дополнения из Intent, если nullон передается в качестве параметра.

Из документов:

Полностью замените дополнения в Намерении данным Комплектом дополнений.

Параметры
extras Новый набор дополнений в Intent или null, чтобы удалить все дополнения.

Поскольку методы putXXX () инициализируют дополнительные функции свежим Bundle, если он равен нулю, это не проблема.


-3
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 
intent.addCategory(Intent.CATEGORY_HOME);  
startActivity(intent);

2
Вам следует подумать над уточнением своего ответа, предоставив некоторые сведения о том, что вы делаете и как он отвечает на вопрос OP.
forsvarir

1
@Ramkumar, это приведет пользователя домой. Очевидно, что это всего лишь единичный случай из 10000, который может вызвать вызовы onPause, onStop или onDestroy.
tony9099

Это даже отдаленно не связано с вопросом
Хоссейн Шахдуст
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.