Java Read File Chosen Into Formatted String Array String Format

A string resource provides text strings for your application with optional text styling and formatting. At that place are three types of resources that can provide your application with strings:

Cord
XML resources that provides a single string.
String Assortment
XML resources that provides an array of strings.
Quantity Strings (Plurals)
XML resource that carries different strings for pluralization.

All strings are capable of applying some styling markup and formatting arguments. For information virtually styling and formatting strings, run across the section nearly Formatting and Styling.

String

A unmarried cord that can be referenced from the application or from other resource files (such as an XML layout).

Note: A string is a simple resource that is referenced using the value provided in the name aspect (not the proper noun of the XML file). And then, you tin can combine string resource with other simple resources in the one XML file, nether one <resource> element.

file location:
res/values/filename.xml
The filename is arbitrary. The <cord> element'south name is used as the resource ID.
compiled resource datatype:
Resource arrow to a Cord.
resource reference:
In Java: R.string.string_name
In XML:@string/string_name
syntax:
<?xml version="1.0" encoding="utf-8"?> <resources>     <cord         name="string_name"         >text_string</string> </resource>            
elements:
<resources>
Required. This must exist the root node.

No attributes.

<string>
A string, which tin can include styling tags. Beware that yous must escape apostrophes and quotation marks. For more than data well-nigh how to properly style and format your strings run into Formatting and Styling, below.

attributes:

name
String. A name for the string. This name is used as the resources ID.
example:
XML file saved at res/values/strings.xml:
<?xml version="ane.0" encoding="utf-8"?> <resources>     <string name="hello">Hello!</string> </resources>            

This layout XML applies a cord to a View:

<TextView     android:layout_width="fill_parent"     android:layout_height="wrap_content"              android:text="@string/hello"              />            

This application lawmaking retrieves a string:

Kotlin

val string: String =                  getString(R.cord.hello)                

Coffee

Cord string =                  getString(R.string.howdy);                

You lot tin apply either getString(int) or getText(int) to call back a cord. getText(int) retains whatever rich text styling applied to the string.

String array

An array of strings that can exist referenced from the application.

Note: A string array is a uncomplicated resources that is referenced using the value provided in the name aspect (non the proper noun of the XML file). As such, you can combine cord array resources with other simple resources in the one XML file, under one <resources> element.

file location:
res/values/filename.xml
The filename is arbitrary. The <string-assortment> element'due south proper name is used as the resource ID.
compiled resource datatype:
Resources pointer to an array of Cords.
resource reference:
In Java: R.array.string_array_name
In XML: @[bundle:]array/string_array_name
syntax:
<?xml version="1.0" encoding="utf-eight"?> <resources>     <cord-assortment         name="string_array_name">         <detail             >text_string</item>     </string-array> </resources>            
elements:
<resources>
Required. This must be the root node.

No attributes.

<string-array>
Defines an assortment of strings. Contains one or more <item> elements.

attributes:

name
Cord. A name for the assortment. This name is used as the resource ID to reference the array.
<particular>
A cord, which can include styling tags. The value can be a reference to some other string resource. Must be a child of a <string-array> element. Beware that you must escape apostrophes and quotation marks. See Formatting and Styling, below, for data about to properly mode and format your strings.

No attributes.

case:
XML file saved at res/values/strings.xml:
<?xml version="1.0" encoding="utf-8"?> <resources>     <string-array name="planets_array">         <item>Mercury</item>         <item>Venus</particular>         <item>Globe</particular>         <particular>Mars</item>     </string-array> </resources>            

This application code retrieves a string array:

Quantity strings (plurals)

Different languages have dissimilar rules for grammatical agreement with quantity. In English, for instance, the quantity 1 is a special case. We write "one book", simply for any other quantity nosotros'd write "n books". This distinction betwixt singular and plural is very common, but other languages make finer distinctions. The full fix supported by Android is null, 1, two, few, many, and other.

The rules for deciding which example to apply for a given language and quantity can be very complex, and so Android provides you with methods such as getQuantityString() to select the appropriate resource for you.

Although historically chosen "quantity strings" (and still called that in API), quantity strings should only exist used for plurals. It would be a mistake to employ quantity strings to implement something like Gmail'due south "Inbox" versus "Inbox (12)" when there are unread letters, for example. It might seem user-friendly to use quantity strings instead of an if argument, but information technology'south important to note that some languages (such every bit Chinese) don't make these grammatical distinctions at all, so yous'll always get the other string.

The selection of which string to use is made solely based on grammatical necessity. In English, a string for nothing is ignored even if the quantity is 0, because 0 isn't grammatically dissimilar from 2, or whatsoever other number except 1 ("naught books", "one volume", "two books", and so on). Conversely, in Korean but the other string is e'er used.

Don't be misled either by the fact that, say, two sounds like information technology could merely apply to the quantity 2: a language may require that 2, 12, 102 (and and so on) are all treated like one another but differently to other quantities. Rely on your translator to know what distinctions their language really insists upon.

If your message doesn't contain the quantity number, it is probably non a skillful candidate for a plural. For example, in Lithuanian the atypical form is used for both 1 and 101, so "i book" is translated equally "ane knyga", and "101 books" is translated equally "101 knyga". Meanwhile "a volume" is "knyga" and "many books" is "daug knygų". If an English plural message contains "a book" (singular) and "many books" (plural) without the actual number, it tin can be translated as "knyga" (a book)/"daug knygų" (many books), but with Lithuanian rules, it will evidence "knyga" (a unmarried book), when the number happens to be 101.

It's often possible to avoid quantity strings by using quantity-neutral formulations such equally "Books: 1". This makes your life and your translators' lives easier, if information technology's an acceptable style for your application.

On API 24+ you can use the much more powerful ICU MessageFormat course instead.

Note: A plurals collection is a uncomplicated resource that is referenced using the value provided in the name attribute (non the name of the XML file). As such, you can combine plurals resources with other simple resource in the i XML file, under one <resource> element.

file location:
res/values/filename.xml
The filename is capricious. The <plurals> element's name is used every bit the resource ID.
resources reference:
In Coffee: R.plurals.plural_name
syntax:
<?xml version="1.0" encoding="utf-eight"?> <resources>     <plurals         name="plural_name">         <item             quantity=["naught" | "ane" | "two" | "few" | "many" | "other"]             >text_string</particular>     </plurals> </resources>            
elements:
<resources>
Required. This must be the root node.

No attributes.

<plurals>
A drove of strings, of which, ane string is provided depending on the amount of something. Contains 1 or more than <particular> elements.

attributes:

proper noun
String. A name for the pair of strings. This name is used as the resource ID.
<item>
A plural or singular string. The value can be a reference to another string resource. Must exist a child of a <plurals> element. Beware that y'all must escape apostrophes and quotation marks. See Formatting and Styling, below, for information most to properly style and format your strings.

attributes:

quantity
Keyword. A value indicating when this string should be used. Valid values, with non-exhaustive examples in parentheses:
Value Description
zero When the language requires special handling of the number 0 (as in Arabic).
ane When the language requires special treatment of numbers similar ane (as with the number ane in English and near other languages; in Russian, any number catastrophe in 1 but non ending in 11 is in this form).
ii When the language requires special treatment of numbers like ii (as with 2 in Welsh, or 102 in Slovenian).
few When the language requires special treatment of "small" numbers (as with two, 3, and 4 in Czech; or numbers ending 2, iii, or 4 but not 12, 13, or fourteen in Polish).
many When the language requires special treatment of "big" numbers (as with numbers ending 11-99 in Maltese).
other When the language does not require special treatment of the given quantity (as with all numbers in Chinese, or 42 in English language).
example:
XML file saved at res/values/strings.xml:
<?xml version="1.0" encoding="utf-eight"?> <resource>     <plurals proper name="numberOfSongsAvailable">         <!--              As a developer, you lot should e'er supply "one" and "other"              strings. Your translators volition know which strings are actually              needed for their language. Always include %d in "one" because              translators will demand to use %d for languages where "ane"              doesn't mean 1 (equally explained to a higher place).           -->         <item quantity="one">%d song institute.</item>         <item quantity="other">%d songs found.</particular>     </plurals> </resources>            

XML file saved at res/values-pl/strings.xml:

<?xml version="1.0" encoding="utf-viii"?> <resources>     <plurals name="numberOfSongsAvailable">         <detail quantity="i">Znaleziono %d piosenkę.</item>         <particular quantity="few">Znaleziono %d piosenki.</item>         <item quantity="other">Znaleziono %d piosenek.</item>     </plurals> </resources>            

Usage:

Kotlin

val count = getNumberOfSongsAvailable() val songsFound = resources.getQuantityString(R.plurals.numberOfSongsAvailable, count, count)                

Java

int count = getNumberOfSongsAvailable(); Resources res =                  getResources(); String songsFound = res.getQuantityString(R.plurals.numberOfSongsAvailable, count, count);                

When using the getQuantityString() method, you need to pass the count twice if your string includes cord formatting with a number. For instance, for the cord %d songs found, the first count parameter selects the appropriate plural cord and the 2d count parameter is inserted into the %d placeholder. If your plural strings practice not include string formatting, you lot don't need to laissez passer the third parameter to getQuantityString.

Format and style

Here are a few important things you should know nigh how to properly format and style your string resources.

Handle special characters

When a string contains characters that take special usage in XML, you must escape the characters co-ordinate to the standard XML/HTML escaping rules. If you need to escape a graphic symbol that has special pregnant in Android y'all should apply a preceding backslash.

By default Android will collapse sequences of whitespace characters into a single space. You tin avert this by enclosing the relevant part of your cord in double quotes. In this case all whitespace characters (including new lines) volition become preserved within the quoted region. Double quotes will allow you to use regular single unescaped quotes as well.

Character Escaped form(s)
@ \@
? \?
New line \n
Tab \t
U+XXXX Unicode character \uXXXX
Single quote (')

Any of the post-obit:

  • \'
  • Enclose the unabridged string in double quotes ("This'll work", for example)
Double quote (") \"

Note that surrounding the string with single quotes does not work.

Whitespace collapsing and Android escaping happens subsequently your resource file gets parsed as XML. This means that <cord> &#32; &#8200; &#8195;</string> (space, punctuation space, Unicode Em space) all plummet to a unmarried space (" "), considering they are all Unicode spaces later the file is parsed as an XML. To preserve those spaces equally they are, you can either quote them (<string>" &#32; &#8200; &#8195;"</string>) or apply Android escaping (<cord> \u0032 \u8200 \u8195</cord>).

Notation: From XML parser'southward perspective, there is no deviation between <cord>"Test this"</string> and <string>&quot;Test this&quot;</string> whatsoever. Both forms will non prove whatsoever quotes but trigger Android whitespace-preserving quoting (that volition take no practical upshot in this case).

Formatting strings

If y'all need to format your strings, and then you lot tin do so past putting your format arguments in the string resource, as demonstrated past the following case resource.

<string name="welcome_messages">Howdy, %i$s! You have %2$d new letters.</string>        

In this example, the format cord has two arguments: %1$s is a string and %2$d is a decimal number. Then, format the string past calling getString(int, Object...). For case:

Kotlin

var text = getString(R.cord.welcome_messages, username, mailCount)            

Java

String text = getString(R.string.welcome_messages, username, mailCount);            

Styling with HTML markup

You tin add styling to your strings with HTML markup. For example:

<?xml version="1.0" encoding="utf-viii"?> <resources>     <string proper noun="welcome">Welcome to <b>Android</b>!</string> </resources>        

The following HTML elements are supported:

  • Bold: <b>, <em>
  • Italic: <i>, <cite>, <dfn>
  • 25% larger text: <large>
  • 20% smaller text: <pocket-sized>
  • Setting font properties: <font confront="font_family" color="hex_color">. Examples of possible font families include monospace, serif, and sans_serif.
  • Setting a monospace font family: <tt>
  • Strikethrough: <southward>, <strike>, <del>
  • Underline: <u>
  • Superscript: <sup>
  • Subscript: <sub>
  • Bullet points: <ul>, <li>
  • Line breaks: <br>
  • Division: <div>
  • CSS manner: <span style="color|background_color|text-ornamentation">
  • Paragraphs: <p dir="rtl | ltr" style="…">

If y'all aren't applying formatting, you tin can fix TextView text direct by calling setText(java.lang.CharSequence). In some cases, all the same, y'all may want to create a styled text resource that is also used as a format string. Ordinarily, this doesn't work because the format(String, Object...) and getString(int, Object...) methods strip all the style information from the string. The work-around to this is to write the HTML tags with escaped entities, which are then recovered with fromHtml(String), subsequently the formatting takes place. For example:

  1. Shop your styled text resources as an HTML-escaped string:
    <resources>   <string name="welcome_messages">Hello, %1$due south! You accept &lt;b>%2$d new letters&lt;/b>.</string> </resources>            

    In this formatted string, a <b> element is added. Notice that the opening bracket is HTML-escaped, using the &lt; annotation.

  2. And so format the string as usual, just likewise telephone call fromHtml(String) to catechumen the HTML text into styled text:

    Kotlin

    val text: String = getString(R.string.welcome_messages, username, mailCount) val styledText: Spanned = Html.fromHtml(text, FROM_HTML_MODE_LEGACY)                

    Java

    String text = getString(R.cord.welcome_messages, username, mailCount); Spanned styledText = Html.fromHtml(text, FROM_HTML_MODE_LEGACY);                

Because the fromHtml(Cord) method formats all HTML entities, be sure to escape whatever possible HTML characters in the strings you use with the formatted text, using htmlEncode(String). For example, if y'all are formatting a string that contains characters such as "<" or "&", then they must be escaped before formatting, so that when the formatted string is passed through fromHtml(Cord), the characters come out the mode they were originally written. For case:

Kotlin

val escapedUsername: String = TextUtils.htmlEncode(username)  val text: String = getString(R.string.welcome_messages, escapedUsername, mailCount) val styledText: Spanned = Html.fromHtml(text, FROM_HTML_MODE_LEGACY)            

Coffee

Cord escapedUsername = TextUtils.htmlEncode(username);  Cord text = getString(R.string.welcome_messages, escapedUsername, mailCount); Spanned styledText = Html.fromHtml(text);            

Styling with spannables

A Spannable is a text object that y'all can style with typeface backdrop such every bit color and font weight. You use SpannableStringBuilder to build your text and then apply styles divers in the android.text.manner package to the text.

You can use the post-obit helper methods to set upward much of the piece of work of creating spannable text:

Kotlin

/**  * Returns a CharSequence that concatenates the specified array of CharSequence  * objects and and then applies a listing of zippo or more tags to the entire range.  *  * @param content an array of graphic symbol sequences to employ a fashion to  * @param tags the styled span objects to utilize to the content  *        such as android.text.manner.StyleSpan  */ individual fun apply(content: Array<out CharSequence>, vararg tags: Any): CharSequence {     return SpannableStringBuilder().apply {         openTags(tags)         content.forEach { charSequence ->             append(charSequence)         }         closeTags(tags)     } }  /**  * Iterates over an assortment of tags and applies them to the beginning of the specified  * Spannable object and so that future text appended to the text will have the styling  * applied to it. Do not call this method directly.  */ individual fun Spannable.openTags(tags: Array<out Whatever>) {     tags.forEach { tag ->         setSpan(tag, 0, 0, Spannable.SPAN_MARK_MARK)     } }  /**  * "Closes" the specified tags on a Spannable by updating the spans to be  * endpoint-exclusive so that future text appended to the end will not take  * on the aforementioned styling. Do not call this method direct.  */ private fun Spannable.closeTags(tags: Assortment<out Whatsoever>) {     tags.forEach { tag ->     if (length > 0) {             setSpan(tag, 0, length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)         } else {             removeSpan(tag)         }     } }            

Java

/**  * Returns a CharSequence that concatenates the specified assortment of CharSequence  * objects and and so applies a list of nix or more tags to the unabridged range.  *  * @param content an array of character sequences to apply a style to  * @param tags the styled span objects to apply to the content  *        such equally android.text.style.StyleSpan  *  */ private static CharSequence applyStyles(CharSequence[] content, Object[] tags) {     SpannableStringBuilder text = new SpannableStringBuilder();     openTags(text, tags);     for (CharSequence item : content) {         text.append(item);     }     closeTags(text, tags);     return text; }  /**  * Iterates over an array of tags and applies them to the beginning of the specified  * Spannable object so that hereafter text appended to the text will take the styling  * practical to it. Do non call this method directly.  */ private static void openTags(Spannable text, Object[] tags) {     for (Object tag : tags) {         text.setSpan(tag, 0, 0, Spannable.SPAN_MARK_MARK);     } }  /**  * "Closes" the specified tags on a Spannable by updating the spans to exist  * endpoint-exclusive so that future text appended to the terminate will not take  * on the same styling. Do not phone call this method directly.  */ private static void closeTags(Spannable text, Object[] tags) {     int len = text.length();     for (Object tag : tags) {         if (len > 0) {             text.setSpan(tag, 0, len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);         } else {             text.removeSpan(tag);         }     } }            

The following bold, italic, and color methods wrap the helper methods to a higher place and demonstrate specific examples of applying styles defined in the android.text.style parcel. You tin can create similar methods to do other types of text styling.

Kotlin

/**  * Returns a CharSequence that applies boldface to the concatenation  * of the specified CharSequence objects.  */ fun bold(vararg content: CharSequence): CharSequence = apply(content, StyleSpan(Typeface.Assuming))  /**  * Returns a CharSequence that applies italics to the concatenation  * of the specified CharSequence objects.  */ fun italic(vararg content: CharSequence): CharSequence = apply(content, StyleSpan(Typeface.ITALIC))  /**  * Returns a CharSequence that applies a foreground color to the  * concatenation of the specified CharSequence objects.  */ fun color(color: Int, vararg content: CharSequence): CharSequence =         apply(content, ForegroundColorSpan(color))            

Java

/**  * Returns a CharSequence that applies boldface to the concatenation  * of the specified CharSequence objects.  */ public static CharSequence assuming(CharSequence... content) {     return apply(content, new StyleSpan(Typeface.Bold)); }  /**  * Returns a CharSequence that applies italics to the concatenation  * of the specified CharSequence objects.  */ public static CharSequence italic(CharSequence... content) {     render use(content, new StyleSpan(Typeface.ITALIC)); }  /**  * Returns a CharSequence that applies a foreground color to the  * concatenation of the specified CharSequence objects.  */ public static CharSequence color(int colour, CharSequence... content) {     return apply(content, new ForegroundColorSpan(colour)); }            

Here'southward an example of how to chain these methods together to employ various styles to individual words inside a phrase:

Kotlin

// Create an italic "hello, " a red "world", // and bold the entire sequence. val text: CharSequence = assuming(italic(getString(R.string.how-do-you-do)),         color(Color.Scarlet, getString(R.string.globe)))            

Java

// Create an italic "hello, " a red "world", // and assuming the entire sequence. var text = bold(italic(getString(R.string.how-do-you-do)),   colour(Color.Ruby, getString(R.string.globe))) </pre> </section><section><h3 id="java">Java</h3> <pre class="prettyprint lang-java"> // Create an italic "hello, " a red "world", // and bold the entire sequence. CharSequence text = bold(italic(getString(R.string.howdy)),     colour(Colour.RED, getString(R.string.world)));            

The core-ktx Kotlin module as well contains extension functions that make working with spans fifty-fifty easier. You can check out the android.text packet documentation on GitHub to acquire more.

For more information on working with spans, run into the following links:

  • Spantastic text styling with Spans
  • Agreement spans

Styling with annotations

You lot tin apply complex or custom styling past using the Annotation class along with the <note> tag in your strings.xml resources files. The annotation tag allows you to marker parts of the cord for custom styling by defining custom key-value pairs in the XML that the framework and then converts into Annotation spans. You lot can then retrieve these annotations and use the primal and value to apply the styling.

When creating annotations, brand certain you add together the <annotation> tag to all translations of the string in every strings.xml file.


Applying a custom typeface to the word "text" in all languages

Example - adding a custom typeface

  1. Add together the <notation> tag, and define the fundamental-value pair. In this case, the key is font, and the value is the type of font we desire to employ: title_emphasis

    // values/strings.xml <cord name="championship">Best practices for <annotation font="title_emphasis">text</annotation> on Android</cord>  // values-es/strings.xml <string name="title"><annotation font="title_emphasis">Texto</note> en Android: mejores prácticas</string>            
  2. Load the string resources and find the annotations with the font primal. Then create a custom bridge and replace the existing bridge.

    Kotlin

    // become the text as SpannedString then we tin can get the spans attached to the text val titleText = getText(R.string.title) as SpannedString  // go all the annotation spans from the text val annotations = titleText.getSpans(0, titleText.length, Notation::class.java)  // create a copy of the title text as a SpannableString. // the constructor copies both the text and the spans. then we can add and remove spans val spannableString = SpannableString(titleText)  // iterate through all the annotation spans for (annotation in annotations) {    // await for the bridge with the key font    if (note.fundamental == "font") {       val fontName = notation.value       // check the value associated to the annotation key       if (fontName == "title_emphasis") {          // create the typeface          val typeface = getFontCompat(R.font.permanent_marker)          // ready the bridge at the same indices every bit the annotation          spannableString.setSpan(CustomTypefaceSpan(typeface),             titleText.getSpanStart(annotation),             titleText.getSpanEnd(note),             Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)       }    } }  // now, the spannableString contains both the note spans and the CustomTypefaceSpan styledText.text = spannableString                

    Java

    // get the text as SpannedString so nosotros tin can get the spans fastened to the text SpannedString titleText = (SpannedString) getText(R.string.title);  // become all the annotation spans from the text Note[] annotations = titleText.getSpans(0, titleText.length(), Note.form);  // create a re-create of the title text every bit a SpannableString. // the constructor copies both the text and the spans. so we can add and remove spans SpannableString spannableString = new SpannableString(titleText);  // iterate through all the notation spans for (Annotation annotation: annotations) {   // look for the span with the key font   if (annotation.getKey().equals("font")) {     String fontName = note.getValue();     // bank check the value associated to the annotation cardinal     if (fontName.equals("title_emphasis")) {     // create the typeface     Typeface typeface = ResourcesCompat.getFont(this, R.font.roboto_mono);     // gear up the span at the same indices as the note     spannableString.setSpan(new CustomTypefaceSpan(typeface),       titleText.getSpanStart(annotation),       titleText.getSpanEnd(annotation),       Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);     }   } }  // now, the spannableString contains both the annotation spans and the CustomTypefaceSpan styledText.text = spannableString;                

If you lot're using the aforementioned text multiple times, you should construct the SpannableString object once and reuse it as needed to avoid potential functioning and memory issues.

For more than examples of annotation usage, see Styling internationalized text in Android

Notation spans and text parceling

Considering Notation spans are also ParcelableSpans, the fundamental-value pairs are parceled and unparceled. Equally long as the receiver of the parcel knows how to interpret the annotations, yous tin can utilise Annotation spans to apply custom styling to the parceled text.

To proceed your custom styling when y'all laissez passer the text to an Intent Packet, you start demand to add together Annotation spans to your text. You can do this in the XML resource via the <notation> tag, as shown in the example above, or in code past creating a new Annotation and setting it as a span, as shown below:

Kotlin

val spannableString = SpannableString("My spantastic text") val annotation = Note("font", "title_emphasis") spannableString.setSpan(notation, 3, 7, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)  // start Action with text with spans val intent = Intent(this, MainActivity::grade.java) intent.putExtra(TEXT_EXTRA, spannableString) startActivity(intent)            

Coffee

SpannableString spannableString = new SpannableString("My spantastic text"); Annotation annotation = new Annotation("font", "title_emphasis"); spannableString.setSpan(annotation, 3, 7, 33);  // outset Activity with text with spans Intent intent = new Intent(this, MainActivity.class); intent.putExtra(TEXT_EXTRA, spannableString); this.startActivity(intent);            

Retrieve the text from the Bundle equally a SpannableString and then parse the annotations attached, every bit shown in the instance above.

Kotlin

// read text with Spans val intentCharSequence = intent.getCharSequenceExtra(TEXT_EXTRA) as SpannableString            

Java

// read text with Spans SpannableString intentCharSequence = (SpannableString)intent.getCharSequenceExtra(TEXT_EXTRA);            

For more data on text styling, meet the following links:

  • Google I/O 2018 talk - All-time practices for text on Android
  • Understanding spans

matthewsthenteme1974.blogspot.com

Source: https://developer.android.com/guide/topics/resources/string-resource

0 Response to "Java Read File Chosen Into Formatted String Array String Format"

إرسال تعليق

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel