语言设置

属性 :

persist.sys.locale


布局:

res/layout/locale_order_list.xml 

       android:id="@+id/dragList"
src/com/android/settings/localepicker/LocaleListEditor.java 

      final RecyclerView list = (RecyclerView) view.findViewById(R.id.dragList);


语言切换事件:

packages\apps\Settings\src\com\android\settings\localepicker\LocaleRecyclerView.java

@Override
public boolean onTouchEvent(MotionEvent e) {
    if (e.getAction() == MotionEvent.ACTION_UP) {
        LocaleDragAndDropAdapter adapter = (LocaleDragAndDropAdapter) this.getAdapter();
        if (adapter != null) {
            adapter.doTheUpdate();
        }
    }
    return super.onTouchEvent(e);
}

packages\apps\Settings\src\com\android\settings\localepicker\LocaleDragAndDropAdapter.java

public void doTheUpdate() {
    int count = mFeedItemList.size();
    final Locale[] newList = new Locale[count];

    for (int i = 0; i < count; i++) {
        final LocaleStore.LocaleInfo li = mFeedItemList.get(i);
        newList[i] = li.getLocale();
    }

    final LocaleList ll = new LocaleList(newList);
    updateLocalesWhenAnimationStops(ll);
}

public void updateLocalesWhenAnimationStops(final LocaleList localeList) {
    if (localeList.equals(mLocalesToSetNext)) {
        return;
    }

    // This will only update the Settings application to make things feel more responsive,
    // the system will be updated later, when animation stopped.
    LocaleList.setDefault(localeList);

    mLocalesToSetNext = localeList;
    final RecyclerView.ItemAnimator itemAnimator = mParentView.getItemAnimator();
    itemAnimator.isRunning(new RecyclerView.ItemAnimator.ItemAnimatorFinishedListener() {
        @Override
        public void onAnimationsFinished() {
            if (mLocalesToSetNext == null || mLocalesToSetNext.equals(mLocalesSetLast)) {
                // All animations finished, but the locale list did not change
                return;
            }

            LocalePicker.updateLocales(mLocalesToSetNext);
            mLocalesSetLast = mLocalesToSetNext;
            mLocalesToSetNext = null;

            mNumberFormatter = NumberFormat.getNumberInstance(Locale.getDefault());
        }
    });
}

frameworks\base\core\java\com\android\internal\app\LocalePicker.java

/**
 * Requests the system to update the list of system locales.
 * Note that the system looks halted for a while during the Locale migration,
 * so the caller need to take care of it.
 */
public static void updateLocales(LocaleList locales) {
    try {
        final IActivityManager am = ActivityManagerNative.getDefault();
        final Configuration config = am.getConfiguration();

        config.setLocales(locales);
        config.userSetLocale = true;

        am.updatePersistentConfiguration(config);
        // Trigger the dirty bit for the Settings Provider.
        BackupManager.dataChanged("com.android.providers.settings");
    } catch (RemoteException e) {
        // Intentionally left blank
    }
}
 
 


frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java

// 实现更新类似以下调用关系 ----begin

ActivityManagerNative.getDefault().updateConfiguration(mConfiguration);--》
updateConfigurationLocked(values, null, false);
boolean updateConfigurationLocked(Configuration values,
        ActivityRecord starting, boolean initLocale) {
    // pass UserHandle.USER_NULL as userId because we don't persist configuration for any user
    return updateConfigurationLocked(values, starting, initLocale, false,
            UserHandle.USER_NULL);
}

/ /实现更新类似以下调用关系 ----end


----- updatePersistentConfiguration begin


@Override
public void updatePersistentConfiguration(Configuration values) {
    enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
            "updateConfiguration()");
    enforceWriteSettingsPermission("updateConfiguration()");
    if (values == null) {
        throw new NullPointerException("Configuration must not be null");
    }

    int userId = UserHandle.getCallingUserId();

    synchronized(this) {
        final long origId = Binder.clearCallingIdentity();
        updateConfigurationLocked(values, null, false, true, userId);
        Binder.restoreCallingIdentity(origId);
    }
}
 
/**
 * Do either or both things: (1) change the current configuration, and (2)
 * make sure the given activity is running with the (now) current
 * configuration.  Returns true if the activity has been left running, or
 * false if <var>starting</var> is being destroyed to match the new
 * configuration.
 *
 * @param userId is only used when persistent parameter is set to true to persist configuration
 *               for that particular user
 */
private boolean updateConfigurationLocked(Configuration values,
        ActivityRecord starting, boolean initLocale, boolean persistent, int userId) {
    int changes = 0;
  boolean localeChanged =  false;
   // 判断local是否改变
  if((mConfiguration != null) && (values != null)){
      Locale beforeLocale = (mConfiguration.getLocales()== null) ? 
          null:mConfiguration.getLocales().get(0);
      Locale newLocale = (values.getLocales()== null) ? 
          null:values.getLocales().get(0);
       。。。

if (values != null) {
    Configuration newConfig = new Configuration(mConfiguration);
    changes = newConfig.updateFrom(values);                          ----获取配置更新项
      。。。
     会发送msg, 设置persist.sys.locale

// Make sure all resources in our process are updated
// right now, so that anyone who is going to retrieve
// resource values after we return will be sure to get
// the new ones.  This is especially important during
// boot, where the first config change needs to guarantee
// all resources have that config before following boot
// code is executed.
     mSystemThread.applyConfigurationToResources(configCopy);

---JUMP TO
frameworks\base\core\java\android\app\ActivityThread.java
public final void applyConfigurationToResources(Configuration config) {
    synchronized (mResourcesManager) {
        mResourcesManager.applyConfigurationToResourcesLocked(config, null);
    }
}
/**
 * The global configuration upon which all Resources are based. Multi-window Resources
 * apply their overrides to this configuration.
 */
private final Configuration mResConfiguration = new Configuration();

public final boolean applyConfigurationToResourcesLocked(@NonNull Configuration config,
                                                         @Nullable CompatibilityInfo compat) {
      int changes = mResConfiguration.updateFrom(config);
      
      Resources.updateSystemConfiguration(config, defaultDisplayMetrics, compat);
 
---JUMP TO
frameworks\base\core\java\android\content\res\ResourcesImpl.java
mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc,
        adjustLanguageTag(mConfiguration.getLocales().get(0).toLanguageTag()),           -----调用native层更新配置资源
        mConfiguration.orientation,
        mConfiguration.touchscreen,
        mConfiguration.densityDpi, mConfiguration.keyboard,
        keyboardHidden, mConfiguration.navigation, width, height,
        mConfiguration.smallestScreenWidthDp,
        mConfiguration.screenWidthDp, mConfiguration.screenHeightDp,
        mConfiguration.screenLayout, mConfiguration.uiMode,
        Build.VERSION.RESOURCES_SDK_INT);

---JUMP BACK

for (int i = mResourceImpls.size() - 1; i >= 0; i--) {        ----分别针对每一个配置更新调用
    ResourcesKey key = mResourceImpls.keyAt(i);
    ResourcesImpl r = mResourceImpls.valueAt(i).get();
    r.updateConfiguration(tmpConfig, dm, compat);             ----- 调用 mAssets.setConfiguration(
}

---JUMP BACK

     
   Message msg = mHandler.obtainMessage(UPDATE_CONFIGURATION_MSG);  ---如果是永久性设置字体大小、语言,发送消息
   final boolean isDensityChange = (changes & ActivityInfo.CONFIG_DENSITY) != 0;
if (isDensityChange) {
    // Reset the unsupported display size dialog.
    mUiHandler.sendEmptyMessage(SHOW_UNSUPPORTED_DISPLAY_SIZE_DIALOG_MSG);      ---屏膜密度改变

    killAllBackgroundProcessesExcept(Build.VERSION_CODES.N,
            ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
}
for (int i=mLruProcesses.size()-1; i>=0; i--) {
    ProcessRecord app = mLruProcesses.get(i);
    try {
        if (app.thread != null) {
            if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Sending to proc "
                    + app.processName + " new config " + mConfiguration);
            app.thread.scheduleConfigurationChanged(configCopy);          ----对最近运行列表中每个进程发送消息
        }
    } catch (Exception e) {
    }
}

public void scheduleConfigurationChanged(Configuration config) {
    updatePendingConfiguration(config);
    sendMessage(H.CONFIGURATION_CHANGED, config);             ----发送消息
}
Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED);           ----发送广播ACTION_CONFIGURATION_CHANGED

intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
        | Intent.FLAG_RECEIVER_REPLACE_PENDING
        | Intent.FLAG_RECEIVER_FOREGROUND);
broadcastIntentLocked(null, null, intent, null, null, 0, null, null,
        null, AppOpsManager.OP_NONE, null, false, false,
        MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);

if ((changes&ActivityInfo.CONFIG_LOCALE) != 0) {                             -----语言改变发送广播
        // Tell the shortcut manager that the system locale changed.  It needs to know
        // it before any other apps receive ACTION_LOCALE_CHANGED, which is why
        // we "push" from here, rather than having the service listen to the broadcast.
        final ShortcutServiceInternal shortcutService =
                LocalServices.getService(ShortcutServiceInternal.class);
        if (shortcutService != null) {
            shortcutService.onSystemLocaleChangedNoLock();
        }
        intent = new Intent(Intent.ACTION_LOCALE_CHANGED);
        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
        if (!mProcessesReady) {
            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
        }
        broadcastIntentLocked(null, null, intent,
                null, null, 0, null, null, null, AppOpsManager.OP_NONE,
                null, false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
    }
}

boolean kept = true;
final ActivityStack mainStack = mStackSupervisor.getFocusedStack();
// mainStack is null during startup.
if (mainStack != null) {
    if (changes != 0 && starting == null) {
        // If the configuration changed, and the caller is not already
        // in the process of starting an activity, then find the top
        // activity to check if its configuration needs to change.
        starting = mainStack.topRunningActivityLocked();
    }

    if (starting != null) {
        kept = mainStack.ensureActivityConfigurationLocked(starting, changes, false);      -----更新activity 栈
        // And we need to make sure at this point that all other activities
        // are made visible with the correct configuration.
        mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes,
                !PRESERVE_WINDOWS);
    }
}

}
----- updatePersistentConfiguration end 

frameworks\base\core\java\android\app\ResourcesManager.java
frameworks\base\core\java\android\content\res\Configuration.java        
 ---系统配置项,如字体、mcc、mnc、键盘、屏膜方向等
frameworks\base\core\java\android\content\pm\ActivityInfo.java

/**
 * Information you can retrieve about a particular application
 * activity or receiver. This corresponds to information collected
 * from the AndroidManifest.xml's &lt;activity&gt; and
 * &lt;receiver&gt; tags.
 */
/**
 * A style resource identifier (in the package's resources) of this
 * activity's theme.  From the "theme" attribute or, if not set, 0.
 */
public int theme;
/**
 * M: Theme feature
 * Bit in {@link #configChanges} that indicates that the activity
 * can itself handle changes to the skin. Set from the
 * {@link android.R.attr#configChanges} attribute.
 * @hide
 */
public static final int CONFIG_SKIN = 0x80000000;


夜间白天切换模式以及沉浸式模式

http://blog.csdn.net/pengyu1801/article/details/51176279

 

Android app应用多语言切换功能实现

http://www.cnblogs.com/popqq520/p/5498990.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值