Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3871f509 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Use language tags to store Configuration's locale list."

parents 4a39a74c bd0ce4d4
Loading
Loading
Loading
Loading
+10 −1
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import static android.content.ConfigurationProto.HARD_KEYBOARD_HIDDEN;
import static android.content.ConfigurationProto.KEYBOARD;
import static android.content.ConfigurationProto.KEYBOARD_HIDDEN;
import static android.content.ConfigurationProto.LOCALES;
import static android.content.ConfigurationProto.LOCALE_LIST;
import static android.content.ConfigurationProto.MCC;
import static android.content.ConfigurationProto.MNC;
import static android.content.ConfigurationProto.NAVIGATION;
@@ -1111,7 +1112,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
            protoOutputStream.write(MCC, mcc);
            protoOutputStream.write(MNC, mnc);
            if (mLocaleList != null) {
                mLocaleList.writeToProto(protoOutputStream, LOCALES);
                protoOutputStream.write(LOCALE_LIST, mLocaleList.toLanguageTags());
            }
            protoOutputStream.write(SCREEN_LAYOUT, screenLayout);
            protoOutputStream.write(COLOR_MODE, colorMode);
@@ -1283,6 +1284,14 @@ public final class Configuration implements Parcelable, Comparable<Configuration
                    case (int) WINDOW_CONFIGURATION:
                        windowConfiguration.readFromProto(protoInputStream, WINDOW_CONFIGURATION);
                        break;
                    case (int) LOCALE_LIST:
                        try {
                            setLocales(LocaleList.forLanguageTags(protoInputStream.readString(
                                    LOCALE_LIST)));
                        } catch (Exception e) {
                            Slog.e(TAG, "error parsing locale list in configuration.", e);
                        }
                        break;
                }
            }
        } finally {
+0 −22
Original line number Diff line number Diff line
@@ -21,9 +21,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Size;
import android.annotation.UnsupportedAppUsage;
import android.content.LocaleProto;
import android.icu.util.ULocale;
import android.util.proto.ProtoOutputStream;

import com.android.internal.annotations.GuardedBy;

@@ -142,26 +140,6 @@ public final class LocaleList implements Parcelable {
        dest.writeString(mStringRepresentation);
    }

    /**
     * Helper to write LocaleList to a protocol buffer output stream.  Assumes the parent
     * protobuf has declared the locale as repeated.
     *
     * @param protoOutputStream Stream to write the locale to.
     * @param fieldId Field Id of the Locale as defined in the parent message.
     * @hide
     */
    public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) {
        for (int i = 0; i < mList.length; i++) {
            final Locale locale = mList[i];
            final long token = protoOutputStream.start(fieldId);
            protoOutputStream.write(LocaleProto.LANGUAGE, locale.getLanguage());
            protoOutputStream.write(LocaleProto.COUNTRY, locale.getCountry());
            protoOutputStream.write(LocaleProto.VARIANT, locale.getVariant());
            protoOutputStream.write(LocaleProto.SCRIPT, locale.getScript());
            protoOutputStream.end(token);
        }
    }

    /**
     * Retrieves a String representation of the language tags in this list.
     */
+2 −1
Original line number Diff line number Diff line
@@ -32,7 +32,7 @@ message ConfigurationProto {
    optional float font_scale = 1;
    optional uint32 mcc = 2;
    optional uint32 mnc = 3 [ (.android.privacy).dest = DEST_EXPLICIT ];
    repeated LocaleProto locales = 4;
    repeated LocaleProto locales = 4 [deprecated = true];
    optional uint32 screen_layout = 5;
    optional uint32 color_mode = 6;
    optional uint32 touchscreen = 7;
@@ -48,6 +48,7 @@ message ConfigurationProto {
    optional uint32 smallest_screen_width_dp = 17;
    optional uint32 density_dpi = 18;
    optional .android.app.WindowConfigurationProto window_configuration = 19;
    optional string locale_list = 20;
}

/**
+1 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import "frameworks/base/core/proto/android/privacy.proto";
package android.content;

message LocaleProto {
    option deprecated = true;
    option (.android.msg_privacy).dest = DEST_AUTOMATIC;

    optional string language = 1;
+79 −0
Original line number Diff line number Diff line
@@ -16,16 +16,29 @@

package android.content.res;

import android.content.Context;
import android.os.LocaleList;
import android.platform.test.annotations.Presubmit;
import android.util.AtomicFile;
import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoOutputStream;

import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;

import com.android.server.usage.IntervalStatsProto;

import junit.framework.TestCase;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.Locale;

/**
 * Build/install/run: bit FrameworksCoreTests:android.content.res.ConfigurationTest
 */
@@ -54,4 +67,70 @@ public class ConfigurationTest extends TestCase {
        config2.updateFrom(config);
        assertEquals(config2.screenLayout, Configuration.SCREENLAYOUT_COMPAT_NEEDED);
    }

    @Test
    public void testReadWriteProto() throws Exception {
        final Context context = InstrumentationRegistry.getTargetContext();
        final File testDir = new File(context.getFilesDir(), "ConfigurationTest");
        testDir.mkdirs();
        final File proto = new File(testDir, "configs");
        if (proto.exists()) {
            proto.delete();
        }

        final Locale arabic = new Locale.Builder().setLocale(new Locale("ar", "AE")).build();
        final Locale urdu = new Locale.Builder().setLocale(new Locale("ur", "IN")).build();
        final Locale urduExtension = new Locale.Builder().setLocale(new Locale("ur", "IN"))
                .setExtension('u', "nu-latn").build();
        Configuration write = new Configuration();
        write.setLocales(new LocaleList(arabic, urdu, urduExtension));
        writeToProto(proto, write);
        assertTrue("Failed to write configs to proto.", proto.exists());

        final Configuration read = new Configuration();
        try {
            readFromProto(proto, read);
        } finally {
            proto.delete();
        }

        assertEquals("Missing locales in proto file written to disk.",
                read.getLocales().size(), write.getLocales().size());
        assertTrue("Arabic locale not found in Configuration locale list.",
                read.getLocales().indexOf(arabic) != -1);
        assertTrue("Urdu locale not found in Configuration locale list.",
                read.getLocales().indexOf(urdu) != -1);
        assertTrue("Urdu locale with extensions not found in Configuration locale list.",
                read.getLocales().indexOf(urduExtension) != -1);
    }

    private void writeToProto(File f, Configuration config) throws Exception {
        final AtomicFile af = new AtomicFile(f);
        FileOutputStream fos = af.startWrite();
        try {
            final ProtoOutputStream protoOut = new ProtoOutputStream(fos);
            final long token = protoOut.start(IntervalStatsProto.CONFIGURATIONS);
            config.writeToProto(protoOut, IntervalStatsProto.Configuration.CONFIG, false, false);
            protoOut.end(token);
            protoOut.flush();
            af.finishWrite(fos);
            fos = null;
        } finally {
            af.failWrite(fos);
        }
    }

    private void readFromProto(File f, Configuration config) throws Exception {
        final AtomicFile afRead = new AtomicFile(f);
        try (FileInputStream in = afRead.openRead()) {
            final ProtoInputStream protoIn = new ProtoInputStream(in);
            if (protoIn.nextField(IntervalStatsProto.CONFIGURATIONS)) {
                final long token = protoIn.start(IntervalStatsProto.CONFIGURATIONS);
                if (protoIn.nextField(IntervalStatsProto.Configuration.CONFIG)) {
                    config.readFromProto(protoIn, IntervalStatsProto.Configuration.CONFIG);
                    protoIn.end(token);
                }
            }
        }
    }
}