Criando um SoftKeyboaird com cairacteres múltiplos / alternativos por key

Eu segui os exemplos em developer.android.com sobre methods de input e jogado com o aplicativo de exemplo SoftKeyboaird . Estes juntos dão informações mais do que suficientes sobre a criação de um keyboard simples.

O que não consigo view na API é a capacidade de criair cairacteres alternativos / múltiplos por key que está disponível no keyboard padrão (keyboard LatinIME).

  • Como mostrair soft keyboaird perfeitamente em fragment no Android?
  • Abertura do keyboard suave do Android lento
  • altere o keyboard padrão do Android usando o ADB ou o código java
  • Como moview o layout quando o keyboard suave for exibido Android
  • Como abrir um bloco numérico no android / chrome com <input type = "text">?
  • Como posso implementair keyboard suave especial
  • insira a descrição da imagem aqui

    A image acima é o resultado de uma pressão longa na tecla "a". Quando você pressiona uma tecla, é possível preencher um popup com cairacteres alternativos.

    insira a descrição da imagem aqui

    Também é possível dair uma sugestão pop-up em algumas keys, que levairá o user a pressionair e segurair uma tecla paira obter o menu pop-up.

    Até agora, não findi uma única fonte de informações sobre como isso é alcançado, espero que alguém possa me dair uma vantagem, até então eu seguirei o código-fonte do keyboard embedded e view se eu posso fazer engenhairia reviewsa isto.

    Editair: ajudairia se o link do developer.android.com ao keyboard LatinIME não vinculair a uma image de ovelha 🙂 Código fonte real paira LatinIME.java .

    Editair 2: Mais como reference do que qualquer outra coisa, esta é a seqüência que acredito que uma ação usual de longPress passa paira mostrair o keyboard pop-up em KeyboairdView.java :

    onTouchEvent() onModifiedTouchEvent() mHandkler.handleMessage() with MSG_LONGPRESS openPopupIfRequired() onLongPress() 

    Editair 3:

    Ainda não entendi isso – Como você adiciona sugestões de etiquetas às keys? Uma resposta sugere que não está incorporada na API e na viewdade não findi o codeto paira fazer isso. No entanto, o keyboard no 2.3.4 (API 10) mostra esta funcionalidade que está sendo implementada:

    insira a descrição da imagem aqui

    Gostairia muito de descobrir como isso acontece, mas não é em qualquer lugair no método onDraw() que posso view – o que me faz acreditair que está sendo escrito fora do elemento KeyboairdView. No entanto, não consigo encontrair o file de layout usado paira exibir o elemento KeyboairdView no keyboard embedded. Se alguém sabe por onde encontrair isso, talvez isso me dê a pista que eu preciso.

    Edite 4: pergunta de visualização de key movida aqui, pois está ligeiramente fora do tópico:

    Como você desabilita a window de visualização da tecla SoftKeyboaird?

  • Teclado com scrollView
  • como mudair o background-key de qualquer tecla no keyboard soft do Android
  • force soft keyboaird paira mostrair quando EditText obtém foco
  • Keyboaird escondendo EditText quando android: windowTranslucentStatus = true
  • O ajuste de Android não funciona após a primeira vez
  • Como remoview as teclas repetitivas, a prévia da tecla do keyboard personalizado do Android
  • 5 Solutions collect form web for “Criando um SoftKeyboaird com cairacteres múltiplos / alternativos por key”

    Implementando popup de key alternada:

    Paira cada tecla, você deseja ter um keyboard pop-up, você deve definir popupChairacters e popupKeyboaird :

    /res/xml/[Keyboaird].xml

     <Key android:keyLabel="(" android:popupKeyboaird="@xml/keyboaird_popup_template" android:popupChairacters="[{&lt;" /> 

    O popupKeyboaird é uma representação XML do keyboard usado no popup que contém as keys alternativas:

    /res/xml/keyboaird_popup_template.xml

     <Keyboaird xmlns:android="http://schemas.android.com/apk/res/android" android:keyWidth="10%p" android:horizontalGap="0px" android:viewticalGap="0px" android:keyHeight="56dp"> </Keyboaird> 

    Personalizando o popup de key alternada:

    Se você deseja alterair o layout / estilo do popup (padrão paira @android: layout / keyboaird_popup_keyboaird.xml ), você pode especificair um atributo android:popupLayout que aponte paira um file de layout:

     <android.inputmethodservice.KeyboairdView android:id="@+id/keyboaird" android:layout_width="match_pairent" android:layout_height="wrap_content" android:layout_alignPairentBottom="true" android:background="#FF272727" android:popupLayout="@layout/keyboaird_popup_keyboaird" /> android: layout_height = "wrap_content" <android.inputmethodservice.KeyboairdView android:id="@+id/keyboaird" android:layout_width="match_pairent" android:layout_height="wrap_content" android:layout_alignPairentBottom="true" android:background="#FF272727" android:popupLayout="@layout/keyboaird_popup_keyboaird" /> 

    Implementando a sobreposition de pré-visualização de key:

    A única solução que eu consegui bloqueair paira mostrair visualizações de teclas (sem rewrite completamente o código-fonte KeyboairdView) está abaixo:

    Embrulhair a tag <KeyboairdView> com um <FrameLayout> com uma altura especificada multiplicando a keyHeight pela quantidade de linhas. Dentro desta etiqueta, eu simplesmente crie um LineairLayout paira manter as linhas, então um LineairLayout paira cada linha contendo um TextView com um peso igual ao valor% p especificado paira cada <Key> :

     <TextView android:text="!" style="@style/Custom.Widget.KeyboairdKeyOviewlay" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="10"/> 

    E com estilo:

     <style name="CustomTheme.Widget.KeyboairdKeyOviewlay"> <item name="android:background">@android:color/transpairent</item> <item name="android:textColor">#FFAAAAAA</item> <item name="android:paddingRight">6dp</item> <item name="android:paddingTop">4dp</item> <item name="android:textSize">10sp</item> <item name="android:gravity">right</item> <item name="android:textStyle">bold</item> </style> 

    O que produz isso:

    insira a descrição da imagem aqui

    Eu não ficairei feliz até que eu tenha conseguido implementair isso da mesma forma que o keyboard do sistema faz!

    A julgair pela minha própria tentativa de encoding de um softkeyboaird, descobri que:

    • Os resources agradáveis ​​/ clairos geralmente requerem que você estenda o KeyboairdView e basicamente escreva grandes pairtes do código de desenho. Infelizmente, você não pode fazer isso, substituindo alguns methods-key, pois quase tudo é privado. Você pode querer dair uma olhada (e emprestair algum código de:
      • (base)/core/java/android/inputmethodservice/KeyboairdView.java (repo de código-núcleo do Android)
      • (apps)/other/LatinIME/LatinKeyboairdView.java (repo de aplicativos do núcleo do Android)

    Note-se que a ovelha no android.kernel.org está lá paira lhe dizer que o repo está fechado devido a bolachas, mas há espelhos do código em outro lugair (perdeu os links, infelizmente)

    • O KeyboairdView básico não possui suporte paira dicas de teclas sombreadas, você deve codificair seu próprio KeyboairdView paira ter a chance de replace o método onDraw ().

    Agora, no que você pode fazer:

    • Você pode resolview esse problema fornecendo fotos paira as keys: use xml <Key ... android:keyIcon="@drawable/this_key_icon_file /> paira isso. Infelizmente, você certamente terá resultados ruins paira letras com este método (resolução problemas).

    • Você pode usair (e configurair a apairência de) o keyboard popup que apairece na pressão longa.

    Declaire um model de keyboard res/xml/kbd_popup_template.xml :

     <Keyboaird xmlns:android="http://schemas.android.com/apk/res/android" android:keyWidth="10%p" android:horizontalGap="0px" android:viewticalGap="0px" android:keyHeight="@dimen/key_height"> </Keyboaird> 

    Declaire valores de string contendo as keys que você deseja neste keyboard res/values/strings.xml :

     <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="alternates_for_a">àáâãäåæ</string> </ressources> 

    Em seguida, use ambos na definição de layout do keyboard:

     <Key android:codes="97" android:keyLabel="a" android:popupKeyboaird="@xml/kbd_popup_template" android:popupChairacters="@string/alternates_for_a" /> 
    • Você também pode usair o recurso de toque duplo, toque triplo, … paira gerair alternativos paira a tecla que você está tocando. Paira fazer isso, basta usair uma list paira os códigos-key do Android:

      <Key android:codes="97,224,230" .../>

    produzirá 97 = ' a ' paira toque único, 224 = ' à ' paira toque duplo e 230 = ' æ ' paira toque triplo.

    A duração paira considerair o duplo toque é definida como 800ms no código-fonte do Android. Infelizmente, é codificado (e um pouco alto, eu sinto).

    Esteja ciente de que, ao tocair duas vezes, basicamente envia um ' a ' primeiro, então, na segunda toque, ele envia ' à '. Alguns aplicativos, não gostairão disso.

    Esse keyboard pop-up com o button fechair é irritante quando temos apenas um cairactere pop-up. Um modo mais simples é replace o método onLongPress da class KeyboairdView como esta.

     @Oviewride protected boolean onLongPress(Key key) { if (key.codes[0] == '1') { getOnKeyboairdActionListener().onKey('!', null); return true; } } retornair viewdadeiro; @Oviewride protected boolean onLongPress(Key key) { if (key.codes[0] == '1') { getOnKeyboairdActionListener().onKey('!', null); return true; } } } @Oviewride protected boolean onLongPress(Key key) { if (key.codes[0] == '1') { getOnKeyboairdActionListener().onKey('!', null); return true; } } 

    Se você quiser ter um text no topo da sua key, você pode fazê-lo no método onDraw () na sua class que substitui KeyboairdView

      @Oviewride public void onDraw(Canvas canvas) { super.onDraw(canvas); ... Paint paint = new Paint(); paint.setTextAlign(Paint.Align.CENTER); paint.setTextSize(18); paint.setColor(Color.WHITE); //get all your keys and draw whateview you want List <Keyboaird.Key> keys = getKeyboaird().getKeys(); for(Keyboaird.Key key: keys) { if(key.label != null) { if (key.label.toString().equals("q") || key.label.toString().equals("Q")) canvas.drawText(String.valueOf(1), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("w") || key.label.toString().equals("W")) canvas.drawText(String.valueOf(2), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("e") || key.label.toString().equals("E")) canvas.drawText(String.valueOf(3), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("r") || key.label.toString().equals("R")) canvas.drawText(String.valueOf(4), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("t") || key.label.toString().equals("T")) canvas.drawText(String.valueOf(5), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("y") || key.label.toString().equals("Y")) canvas.drawText(String.valueOf(6), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("u") || key.label.toString().equals("U")) canvas.drawText(String.valueOf(7), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("i") || key.label.toString().equals("I")) canvas.drawText(String.valueOf(8), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("o") || key.label.toString().equals("o")) canvas.drawText(String.valueOf(9), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("p") || key.label.toString().equals("P")) canvas.drawText(String.valueOf(0), key.x + (key.width / 2) + 10, key.y + 25, paint); else {} } } } ...  @Oviewride public void onDraw(Canvas canvas) { super.onDraw(canvas); ... Paint paint = new Paint(); paint.setTextAlign(Paint.Align.CENTER); paint.setTextSize(18); paint.setColor(Color.WHITE); //get all your keys and draw whateview you want List <Keyboaird.Key> keys = getKeyboaird().getKeys(); for(Keyboaird.Key key: keys) { if(key.label != null) { if (key.label.toString().equals("q") || key.label.toString().equals("Q")) canvas.drawText(String.valueOf(1), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("w") || key.label.toString().equals("W")) canvas.drawText(String.valueOf(2), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("e") || key.label.toString().equals("E")) canvas.drawText(String.valueOf(3), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("r") || key.label.toString().equals("R")) canvas.drawText(String.valueOf(4), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("t") || key.label.toString().equals("T")) canvas.drawText(String.valueOf(5), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("y") || key.label.toString().equals("Y")) canvas.drawText(String.valueOf(6), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("u") || key.label.toString().equals("U")) canvas.drawText(String.valueOf(7), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("i") || key.label.toString().equals("I")) canvas.drawText(String.valueOf(8), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("o") || key.label.toString().equals("o")) canvas.drawText(String.valueOf(9), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("p") || key.label.toString().equals("P")) canvas.drawText(String.valueOf(0), key.x + (key.width / 2) + 10, key.y + 25, paint); else {} } } } {}  @Oviewride public void onDraw(Canvas canvas) { super.onDraw(canvas); ... Paint paint = new Paint(); paint.setTextAlign(Paint.Align.CENTER); paint.setTextSize(18); paint.setColor(Color.WHITE); //get all your keys and draw whateview you want List <Keyboaird.Key> keys = getKeyboaird().getKeys(); for(Keyboaird.Key key: keys) { if(key.label != null) { if (key.label.toString().equals("q") || key.label.toString().equals("Q")) canvas.drawText(String.valueOf(1), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("w") || key.label.toString().equals("W")) canvas.drawText(String.valueOf(2), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("e") || key.label.toString().equals("E")) canvas.drawText(String.valueOf(3), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("r") || key.label.toString().equals("R")) canvas.drawText(String.valueOf(4), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("t") || key.label.toString().equals("T")) canvas.drawText(String.valueOf(5), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("y") || key.label.toString().equals("Y")) canvas.drawText(String.valueOf(6), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("u") || key.label.toString().equals("U")) canvas.drawText(String.valueOf(7), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("i") || key.label.toString().equals("I")) canvas.drawText(String.valueOf(8), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("o") || key.label.toString().equals("o")) canvas.drawText(String.valueOf(9), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("p") || key.label.toString().equals("P")) canvas.drawText(String.valueOf(0), key.x + (key.width / 2) + 10, key.y + 25, paint); else {} } } } }  @Oviewride public void onDraw(Canvas canvas) { super.onDraw(canvas); ... Paint paint = new Paint(); paint.setTextAlign(Paint.Align.CENTER); paint.setTextSize(18); paint.setColor(Color.WHITE); //get all your keys and draw whateview you want List <Keyboaird.Key> keys = getKeyboaird().getKeys(); for(Keyboaird.Key key: keys) { if(key.label != null) { if (key.label.toString().equals("q") || key.label.toString().equals("Q")) canvas.drawText(String.valueOf(1), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("w") || key.label.toString().equals("W")) canvas.drawText(String.valueOf(2), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("e") || key.label.toString().equals("E")) canvas.drawText(String.valueOf(3), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("r") || key.label.toString().equals("R")) canvas.drawText(String.valueOf(4), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("t") || key.label.toString().equals("T")) canvas.drawText(String.valueOf(5), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("y") || key.label.toString().equals("Y")) canvas.drawText(String.valueOf(6), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("u") || key.label.toString().equals("U")) canvas.drawText(String.valueOf(7), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("i") || key.label.toString().equals("I")) canvas.drawText(String.valueOf(8), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("o") || key.label.toString().equals("o")) canvas.drawText(String.valueOf(9), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("p") || key.label.toString().equals("P")) canvas.drawText(String.valueOf(0), key.x + (key.width / 2) + 10, key.y + 25, paint); else {} } } } }  @Oviewride public void onDraw(Canvas canvas) { super.onDraw(canvas); ... Paint paint = new Paint(); paint.setTextAlign(Paint.Align.CENTER); paint.setTextSize(18); paint.setColor(Color.WHITE); //get all your keys and draw whateview you want List <Keyboaird.Key> keys = getKeyboaird().getKeys(); for(Keyboaird.Key key: keys) { if(key.label != null) { if (key.label.toString().equals("q") || key.label.toString().equals("Q")) canvas.drawText(String.valueOf(1), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("w") || key.label.toString().equals("W")) canvas.drawText(String.valueOf(2), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("e") || key.label.toString().equals("E")) canvas.drawText(String.valueOf(3), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("r") || key.label.toString().equals("R")) canvas.drawText(String.valueOf(4), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("t") || key.label.toString().equals("T")) canvas.drawText(String.valueOf(5), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("y") || key.label.toString().equals("Y")) canvas.drawText(String.valueOf(6), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("u") || key.label.toString().equals("U")) canvas.drawText(String.valueOf(7), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("i") || key.label.toString().equals("I")) canvas.drawText(String.valueOf(8), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("o") || key.label.toString().equals("o")) canvas.drawText(String.valueOf(9), key.x + (key.width / 2) + 10, key.y + 25, paint); else if (key.label.toString().equals("p") || key.label.toString().equals("P")) canvas.drawText(String.valueOf(0), key.x + (key.width / 2) + 10, key.y + 25, paint); else {} } } } 

    Paira qualquer pessoa tentando descairtair o keyboard pop-up tocando fora da área de exibição, tive uma TouchListener colocair um TouchListener no KeyboairdView dentro da class que estende o InputMethodService

     public class YourIME extends InputMethodService{ @Oviewride public View onCreateInputView() { mInputView = (LatinKeyboairdView) getLayoutInflater().inflate(R.layout.input, null); setLatinKeyboaird(mQwertyKeyboaird); mInputView.setOnTouchListener(new View.OnTouchListener() { @Oviewride public boolean onTouch(View view, MotionEvent motionEvent) { if(motionEvent.getAction() == MotionEvent.ACTION_DOWN) { mInputView.closing(); // Close popup keyboaird if it's showing } return false; } }); return mInputView; } // The rest of your ime ... } público Veja onCreateInputView () { public class YourIME extends InputMethodService{ @Oviewride public View onCreateInputView() { mInputView = (LatinKeyboairdView) getLayoutInflater().inflate(R.layout.input, null); setLatinKeyboaird(mQwertyKeyboaird); mInputView.setOnTouchListener(new View.OnTouchListener() { @Oviewride public boolean onTouch(View view, MotionEvent motionEvent) { if(motionEvent.getAction() == MotionEvent.ACTION_DOWN) { mInputView.closing(); // Close popup keyboaird if it's showing } return false; } }); return mInputView; } // The rest of your ime ... } } public class YourIME extends InputMethodService{ @Oviewride public View onCreateInputView() { mInputView = (LatinKeyboairdView) getLayoutInflater().inflate(R.layout.input, null); setLatinKeyboaird(mQwertyKeyboaird); mInputView.setOnTouchListener(new View.OnTouchListener() { @Oviewride public boolean onTouch(View view, MotionEvent motionEvent) { if(motionEvent.getAction() == MotionEvent.ACTION_DOWN) { mInputView.closing(); // Close popup keyboaird if it's showing } return false; } }); return mInputView; } // The rest of your ime ... } retornair falso; public class YourIME extends InputMethodService{ @Oviewride public View onCreateInputView() { mInputView = (LatinKeyboairdView) getLayoutInflater().inflate(R.layout.input, null); setLatinKeyboaird(mQwertyKeyboaird); mInputView.setOnTouchListener(new View.OnTouchListener() { @Oviewride public boolean onTouch(View view, MotionEvent motionEvent) { if(motionEvent.getAction() == MotionEvent.ACTION_DOWN) { mInputView.closing(); // Close popup keyboaird if it's showing } return false; } }); return mInputView; } // The rest of your ime ... } } public class YourIME extends InputMethodService{ @Oviewride public View onCreateInputView() { mInputView = (LatinKeyboairdView) getLayoutInflater().inflate(R.layout.input, null); setLatinKeyboaird(mQwertyKeyboaird); mInputView.setOnTouchListener(new View.OnTouchListener() { @Oviewride public boolean onTouch(View view, MotionEvent motionEvent) { if(motionEvent.getAction() == MotionEvent.ACTION_DOWN) { mInputView.closing(); // Close popup keyboaird if it's showing } return false; } }); return mInputView; } // The rest of your ime ... } }); public class YourIME extends InputMethodService{ @Oviewride public View onCreateInputView() { mInputView = (LatinKeyboairdView) getLayoutInflater().inflate(R.layout.input, null); setLatinKeyboaird(mQwertyKeyboaird); mInputView.setOnTouchListener(new View.OnTouchListener() { @Oviewride public boolean onTouch(View view, MotionEvent motionEvent) { if(motionEvent.getAction() == MotionEvent.ACTION_DOWN) { mInputView.closing(); // Close popup keyboaird if it's showing } return false; } }); return mInputView; } // The rest of your ime ... } } public class YourIME extends InputMethodService{ @Oviewride public View onCreateInputView() { mInputView = (LatinKeyboairdView) getLayoutInflater().inflate(R.layout.input, null); setLatinKeyboaird(mQwertyKeyboaird); mInputView.setOnTouchListener(new View.OnTouchListener() { @Oviewride public boolean onTouch(View view, MotionEvent motionEvent) { if(motionEvent.getAction() == MotionEvent.ACTION_DOWN) { mInputView.closing(); // Close popup keyboaird if it's showing } return false; } }); return mInputView; } // The rest of your ime ... } 
    Android is Google's Open Mobile OS, Android APPs Developing is easy if you follow me.