michael@0: /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: package org.mozilla.gecko.webapp; michael@0: michael@0: import java.util.ArrayList; michael@0: michael@0: import org.mozilla.gecko.GeckoAppShell; michael@0: michael@0: import android.content.Context; michael@0: import android.content.SharedPreferences; michael@0: import android.content.SharedPreferences.Editor; michael@0: import android.util.Log; michael@0: michael@0: public class Allocator { michael@0: michael@0: private final String LOGTAG = "GeckoWebappAllocator"; michael@0: michael@0: private static final String PREFIX_ORIGIN = "webapp-origin-"; michael@0: private static final String PREFIX_PACKAGE_NAME = "webapp-package-name-"; michael@0: michael@0: // These prefixes are for prefs used by the old shortcut-based runtime. michael@0: // We define them here so maybeMigrateOldPrefs can migrate them to their michael@0: // new equivalents if this app was originally installed as a shortcut. michael@0: // Maybe we can remove this code in the future! michael@0: private static final String PREFIX_OLD_APP = "app"; michael@0: private static final String PREFIX_OLD_ICON = "icon"; michael@0: michael@0: // The number of Webapp# and WEBAPP# activities/apps/intents michael@0: private final static int MAX_WEB_APPS = 100; michael@0: michael@0: protected static Allocator sInstance = null; michael@0: public static Allocator getInstance() { michael@0: return getInstance(GeckoAppShell.getContext()); michael@0: } michael@0: michael@0: public static synchronized Allocator getInstance(Context cx) { michael@0: if (sInstance == null) { michael@0: sInstance = new Allocator(cx); michael@0: } michael@0: michael@0: return sInstance; michael@0: } michael@0: michael@0: SharedPreferences mPrefs; michael@0: michael@0: protected Allocator(Context context) { michael@0: mPrefs = context.getSharedPreferences("webapps", Context.MODE_PRIVATE | Context.MODE_MULTI_PROCESS); michael@0: } michael@0: michael@0: private static String appKey(int index) { michael@0: return PREFIX_PACKAGE_NAME + index; michael@0: } michael@0: michael@0: public static String iconKey(int index) { michael@0: return "web-app-color-" + index; michael@0: } michael@0: michael@0: public static String originKey(int i) { michael@0: return PREFIX_ORIGIN + i; michael@0: } michael@0: michael@0: private static String oldAppKey(int index) { michael@0: return PREFIX_OLD_APP + index; michael@0: } michael@0: michael@0: private static String oldIconKey(int index) { michael@0: return PREFIX_OLD_ICON + index; michael@0: } michael@0: michael@0: private static void save(Editor editor) { michael@0: // Use SharedPreferences.Editor.apply() where available and commit() michael@0: // where it isn't. We could also use a background thread with commit(), michael@0: // but our callers might expect the changes we make to be available michael@0: // immediately, so we instead take the commit() performance hit michael@0: // on the small percentage of extant devices that don't support apply(). michael@0: if (android.os.Build.VERSION.SDK_INT > 8) { michael@0: editor.apply(); michael@0: } else { michael@0: editor.commit(); michael@0: } michael@0: } michael@0: michael@0: public ArrayList getInstalledPackageNames() { michael@0: ArrayList installedPackages = new ArrayList(); michael@0: michael@0: for (int i = 0; i < MAX_WEB_APPS; ++i) { michael@0: if (mPrefs.contains(appKey(i))) { michael@0: installedPackages.add(mPrefs.getString(appKey(i), "")); michael@0: } michael@0: } michael@0: return installedPackages; michael@0: } michael@0: michael@0: public synchronized int findOrAllocatePackage(final String packageName) { michael@0: int index = getIndexForApp(packageName); michael@0: if (index != -1) michael@0: return index; michael@0: michael@0: for (int i = 0; i < MAX_WEB_APPS; ++i) { michael@0: if (!mPrefs.contains(appKey(i))) { michael@0: // found unused index i michael@0: putPackageName(i, packageName); michael@0: return i; michael@0: } michael@0: } michael@0: michael@0: // no more apps! michael@0: return -1; michael@0: } michael@0: michael@0: public synchronized void putPackageName(final int index, final String packageName) { michael@0: save(mPrefs.edit().putString(appKey(index), packageName)); michael@0: } michael@0: michael@0: public void updateColor(int index, int color) { michael@0: save(mPrefs.edit().putInt(iconKey(index), color)); michael@0: } michael@0: michael@0: public synchronized int getIndexForApp(String packageName) { michael@0: return findSlotForPrefix(PREFIX_PACKAGE_NAME, packageName); michael@0: } michael@0: michael@0: public synchronized int getIndexForOrigin(String origin) { michael@0: return findSlotForPrefix(PREFIX_ORIGIN, origin); michael@0: } michael@0: michael@0: protected int findSlotForPrefix(String prefix, String value) { michael@0: for (int i = 0; i < MAX_WEB_APPS; ++i) { michael@0: if (mPrefs.getString(prefix + i, "").equals(value)) { michael@0: return i; michael@0: } michael@0: } michael@0: return -1; michael@0: } michael@0: michael@0: public synchronized String getAppForIndex(int index) { michael@0: return mPrefs.getString(appKey(index), null); michael@0: } michael@0: michael@0: public synchronized int releaseIndexForApp(String app) { michael@0: int index = getIndexForApp(app); michael@0: if (index == -1) michael@0: return -1; michael@0: michael@0: releaseIndex(index); michael@0: return index; michael@0: } michael@0: michael@0: public synchronized void releaseIndex(final int index) { michael@0: save(mPrefs.edit().remove(appKey(index)).remove(iconKey(index)).remove(originKey(index))); michael@0: } michael@0: michael@0: public void putOrigin(int index, String origin) { michael@0: save(mPrefs.edit().putString(originKey(index), origin)); michael@0: } michael@0: michael@0: public String getOrigin(int index) { michael@0: return mPrefs.getString(originKey(index), null); michael@0: } michael@0: michael@0: public int getColor(int index) { michael@0: return mPrefs.getInt(iconKey(index), -1); michael@0: } michael@0: michael@0: /** michael@0: * Migrate old prefs to their new equivalents if this app was originally michael@0: * installed by the shortcut-based implementation. michael@0: */ michael@0: public void maybeMigrateOldPrefs(int index) { michael@0: if (!mPrefs.contains(oldAppKey(index))) { michael@0: return; michael@0: } michael@0: michael@0: Log.i(LOGTAG, "migrating old prefs"); michael@0: michael@0: // The old appKey pref stored the origin, while the new appKey pref michael@0: // stores the packageName, so we migrate oldAppKey to the origin pref. michael@0: putOrigin(index, mPrefs.getString(oldAppKey(index), null)); michael@0: michael@0: // The old iconKey pref actually stored the splash screen background michael@0: // color, so we migrate oldIconKey to the color pref. michael@0: updateColor(index, mPrefs.getInt(oldIconKey(index), -1)); michael@0: michael@0: // Remove the old prefs so we don't migrate them the next time around. michael@0: save(mPrefs.edit().remove(oldAppKey(index)).remove(oldIconKey(index))); michael@0: } michael@0: }