mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-19 00:05:36 +00:00
Bug 715307 - Read and write profiles.ini for Android. r=mfinkle
This commit is contained in:
parent
7122fce81e
commit
096e6e91e8
@ -1722,6 +1722,8 @@ abstract public class GeckoApp
|
||||
}
|
||||
}
|
||||
|
||||
BrowserDB.initialize(getProfile().getName());
|
||||
|
||||
if (ACTION_UPDATE.equals(action) || args != null && args.contains("-alert update-app")) {
|
||||
Log.i(LOGTAG,"onCreate: Update request");
|
||||
checkAndLaunchUpdate();
|
||||
|
@ -9,12 +9,15 @@
|
||||
|
||||
package org.mozilla.gecko;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.util.HashMap;
|
||||
import java.util.Enumeration;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.SystemClock;
|
||||
@ -34,19 +37,44 @@ public final class GeckoProfile {
|
||||
// this short timeout is a temporary fix until bug 735399 is implemented
|
||||
private static final long SESSION_TIMEOUT = 30 * 1000; // 30 seconds
|
||||
|
||||
static private INIParser getProfilesINI(Context context) {
|
||||
File filesDir = context.getFilesDir();
|
||||
File mozillaDir = new File(filesDir, "mozilla");
|
||||
File profilesIni = new File(mozillaDir, "profiles.ini");
|
||||
return new INIParser(profilesIni);
|
||||
}
|
||||
|
||||
public static GeckoProfile get(Context context) {
|
||||
return get(context, null);
|
||||
return get(context, "");
|
||||
}
|
||||
|
||||
public static GeckoProfile get(Context context, String profileName) {
|
||||
if (context == null) {
|
||||
throw new IllegalArgumentException("context must be non-null");
|
||||
}
|
||||
|
||||
// if no profile was passed in, look for the default profile listed in profiles.ini
|
||||
// if that doesn't exist, look for a profile called 'default'
|
||||
if (TextUtils.isEmpty(profileName)) {
|
||||
// XXX: TO-DO read profiles.ini to get the default profile. bug 715307
|
||||
profileName = "default";
|
||||
|
||||
INIParser parser = getProfilesINI(context);
|
||||
|
||||
String profile = "";
|
||||
boolean foundDefault = false;
|
||||
for (Enumeration<INISection> e = parser.getSections().elements(); e.hasMoreElements();) {
|
||||
INISection section = e.nextElement();
|
||||
if (section.getIntProperty("Default") == 1) {
|
||||
profile = section.getStringProperty("Name");
|
||||
foundDefault = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (foundDefault)
|
||||
profileName = profile;
|
||||
}
|
||||
|
||||
// actually try to look up the profile
|
||||
synchronized (sProfileCache) {
|
||||
GeckoProfile profile = sProfileCache.get(profileName);
|
||||
if (profile == null) {
|
||||
@ -75,6 +103,10 @@ public final class GeckoProfile {
|
||||
mName = profileName;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
public synchronized File getDir() {
|
||||
if (mDir != null) {
|
||||
return mDir;
|
||||
@ -88,9 +120,11 @@ public final class GeckoProfile {
|
||||
profileMigrator.launchMoveProfile();
|
||||
}
|
||||
|
||||
// now check if a profile with this name that already exists
|
||||
File mozillaDir = ensureMozillaDirectory(mContext);
|
||||
mDir = findProfileDir(mozillaDir);
|
||||
if (mDir == null) {
|
||||
// otherwise create it
|
||||
mDir = createProfileDir(mozillaDir);
|
||||
} else {
|
||||
Log.d(LOGTAG, "Found profile dir: " + mDir.getAbsolutePath());
|
||||
@ -177,16 +211,20 @@ public final class GeckoProfile {
|
||||
}
|
||||
|
||||
private File findProfileDir(File mozillaDir) {
|
||||
String suffix = '.' + mName;
|
||||
File[] candidates = mozillaDir.listFiles();
|
||||
if (candidates == null) {
|
||||
return null;
|
||||
}
|
||||
for (File f : candidates) {
|
||||
if (f.isDirectory() && f.getName().endsWith(suffix)) {
|
||||
return f;
|
||||
// Open profiles.ini to find the correct path
|
||||
INIParser parser = getProfilesINI(mContext);
|
||||
|
||||
for (Enumeration<INISection> e = parser.getSections().elements(); e.hasMoreElements();) {
|
||||
INISection section = e.nextElement();
|
||||
String name = section.getStringProperty("Name");
|
||||
if (name != null && name.equals(mName)) {
|
||||
if (section.getIntProperty("IsRelative") == 1) {
|
||||
return new File(mozillaDir, section.getStringProperty("Path"));
|
||||
}
|
||||
return new File(section.getStringProperty("Path"));
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -202,14 +240,9 @@ public final class GeckoProfile {
|
||||
}
|
||||
|
||||
private File createProfileDir(File mozillaDir) throws IOException {
|
||||
// XXX: TO-DO If we already have an ini file, we should append the
|
||||
// new profile information to it. For now we just throw an exception.
|
||||
// see bug 715391
|
||||
File profileIniFile = new File(mozillaDir, "profiles.ini");
|
||||
if (profileIniFile.exists()) {
|
||||
throw new IOException("Can't create new profiles");
|
||||
}
|
||||
INIParser parser = getProfilesINI(mContext);
|
||||
|
||||
// Salt the name of our requested profile
|
||||
String saltedName = saltProfileName(mName);
|
||||
File profileDir = new File(mozillaDir, saltedName);
|
||||
while (profileDir.exists()) {
|
||||
@ -217,25 +250,38 @@ public final class GeckoProfile {
|
||||
profileDir = new File(mozillaDir, saltedName);
|
||||
}
|
||||
|
||||
// Attempt to create the salted profile dir
|
||||
if (! profileDir.mkdirs()) {
|
||||
throw new IOException("Unable to create profile at " + profileDir.getAbsolutePath());
|
||||
}
|
||||
Log.d(LOGTAG, "Created new profile dir at " + profileDir.getAbsolutePath());
|
||||
|
||||
FileWriter out = new FileWriter(profileIniFile, true);
|
||||
try {
|
||||
out.write("[General]\n" +
|
||||
"StartWithLastProfile=1\n" +
|
||||
"\n" +
|
||||
"[Profile0]\n" +
|
||||
"Name=" + mName + "\n" +
|
||||
"IsRelative=1\n" +
|
||||
"Path=" + saltedName + "\n" +
|
||||
"Default=1\n");
|
||||
} finally {
|
||||
out.close();
|
||||
// Now update profiles.ini
|
||||
// If this is the first time its created, we also add a General section
|
||||
// look for the first profile number that isn't taken yet
|
||||
int profileNum = 0;
|
||||
while (parser.getSection("Profile" + profileNum) != null) {
|
||||
profileNum++;
|
||||
}
|
||||
|
||||
INISection profileSection = new INISection("Profile" + profileNum);
|
||||
profileSection.setProperty("Name", mName);
|
||||
profileSection.setProperty("IsRelative", 1);
|
||||
profileSection.setProperty("Path", saltedName);
|
||||
|
||||
if (parser.getSection("General") == null) {
|
||||
INISection generalSection = new INISection("General");
|
||||
generalSection.setProperty("StartWithLastProfile", 1);
|
||||
parser.addSection(generalSection);
|
||||
|
||||
// only set as default if this is the first profile we're creating
|
||||
Log.i(LOGTAG, "WESJ - SET DEFAULT");
|
||||
profileSection.setProperty("Default", 1);
|
||||
}
|
||||
|
||||
parser.addSection(profileSection);
|
||||
parser.write();
|
||||
|
||||
return profileDir;
|
||||
}
|
||||
}
|
||||
|
163
mobile/android/base/INIParser.java
Normal file
163
mobile/android/base/INIParser.java
Normal file
@ -0,0 +1,163 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Hashtable;
|
||||
|
||||
public class INIParser extends INISection {
|
||||
// default file to read and write to
|
||||
private File mFile = null;
|
||||
|
||||
// List of sections in the current iniFile. null if the file has not been parsed yet
|
||||
private Hashtable<String, INISection> mSections = null;
|
||||
|
||||
// create a parser. The file will not be read until you attempt to
|
||||
// access sections or properties inside it. At that point its read synchronously
|
||||
public INIParser(File iniFile) {
|
||||
super("");
|
||||
mFile = iniFile;
|
||||
}
|
||||
|
||||
// write ini data to the default file. Will overwrite anything current inside
|
||||
public void write() {
|
||||
writeTo(mFile);
|
||||
}
|
||||
|
||||
// write to the specified file. Will overwrite anything current inside
|
||||
public void writeTo(File f) {
|
||||
if (f == null)
|
||||
return;
|
||||
|
||||
FileWriter outputStream = null;
|
||||
try {
|
||||
outputStream = new FileWriter(f);
|
||||
} catch (IOException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
|
||||
BufferedWriter writer = new BufferedWriter(outputStream);
|
||||
try {
|
||||
write(writer);
|
||||
writer.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(BufferedWriter writer) throws IOException {
|
||||
super.write(writer);
|
||||
|
||||
if (mSections != null) {
|
||||
for (Enumeration<INISection> e = mSections.elements(); e.hasMoreElements();) {
|
||||
INISection section = e.nextElement();
|
||||
section.write(writer);
|
||||
writer.newLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// return all of the sections inside this file
|
||||
public Hashtable<String, INISection> getSections() {
|
||||
if (mSections == null) {
|
||||
try {
|
||||
parse();
|
||||
} catch (IOException e) {
|
||||
debug("Error parsing: " + e);
|
||||
}
|
||||
}
|
||||
return mSections;
|
||||
}
|
||||
|
||||
// parse the default file
|
||||
protected void parse() throws IOException {
|
||||
super.parse();
|
||||
parse(mFile);
|
||||
}
|
||||
|
||||
// parse a passed in file
|
||||
private void parse(File f) throws IOException {
|
||||
// Set up internal data members
|
||||
mSections = new Hashtable<String, INISection>();
|
||||
|
||||
if (f == null || !f.exists())
|
||||
return;
|
||||
|
||||
FileReader inputStream = null;
|
||||
try {
|
||||
inputStream = new FileReader(f);
|
||||
} catch (FileNotFoundException e1) {
|
||||
// If the file doesn't exist. Just return;
|
||||
return;
|
||||
}
|
||||
|
||||
BufferedReader buf = new BufferedReader(inputStream);
|
||||
String line = null; // current line of text we are parsing
|
||||
INISection currentSection = null; // section we are currently parsing
|
||||
|
||||
while ((line = buf.readLine()) != null) {
|
||||
|
||||
if (line != null)
|
||||
line = line.trim();
|
||||
|
||||
// blank line or a comment. ignore it
|
||||
if (line == null || line.length() == 0 || line.charAt(0) == ';') {
|
||||
debug("Ignore line: " + line);
|
||||
} else if (line.charAt(0) == '[') {
|
||||
debug("Parse as section: " + line);
|
||||
currentSection = new INISection(line.substring(1, line.length()-1));
|
||||
mSections.put(currentSection.getName(), currentSection);
|
||||
} else {
|
||||
debug("Parse as property: " + line);
|
||||
|
||||
String[] pieces = line.split("=");
|
||||
if (pieces.length != 2)
|
||||
continue;
|
||||
|
||||
String key = pieces[0].trim();
|
||||
String value = pieces[1].trim();
|
||||
if (currentSection != null) {
|
||||
currentSection.setProperty(key, value);
|
||||
} else {
|
||||
mProperties.put(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
buf.close();
|
||||
}
|
||||
|
||||
// add a section to the file
|
||||
public void addSection(INISection sect) {
|
||||
// ensure that we have parsed the file
|
||||
getSections();
|
||||
mSections.put(sect.getName(), sect);
|
||||
}
|
||||
|
||||
// get a section from the file. will return null if the section doesn't exist
|
||||
public INISection getSection(String key) {
|
||||
// ensure that we have parsed the file
|
||||
getSections();
|
||||
return mSections.get(key);
|
||||
}
|
||||
|
||||
// remove an entire section from the file
|
||||
public void removeSection(String name) {
|
||||
// ensure that we have parsed the file
|
||||
getSections();
|
||||
mSections.remove(name);
|
||||
}
|
||||
|
||||
}
|
128
mobile/android/base/INISection.java
Normal file
128
mobile/android/base/INISection.java
Normal file
@ -0,0 +1,128 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Hashtable;
|
||||
|
||||
class INISection {
|
||||
private static final String LOGTAG = "INIParser";
|
||||
|
||||
// default file to read and write to
|
||||
private String mName = null;
|
||||
public String getName() { return mName; }
|
||||
|
||||
// show or hide debug logging
|
||||
private boolean mDebug = false;
|
||||
|
||||
// Global properties that aren't inside a section in the file
|
||||
protected Hashtable<String, Object> mProperties = null;
|
||||
|
||||
// create a parser. The file will not be read until you attempt to
|
||||
// access sections or properties inside it. At that point its read synchronously
|
||||
public INISection(String name) {
|
||||
mName = name;
|
||||
}
|
||||
|
||||
// log a debug string to the console
|
||||
protected void debug(String msg) {
|
||||
if (mDebug) {
|
||||
Log.i(LOGTAG, msg);
|
||||
}
|
||||
}
|
||||
|
||||
// get a global property out of the hash table. will return null if the property doesn't exist
|
||||
public Object getProperty(String key) {
|
||||
getProperties(); // ensure that we have parsed the file
|
||||
return mProperties.get(key);
|
||||
}
|
||||
|
||||
// get a global property out of the hash table. will return null if the property doesn't exist
|
||||
public int getIntProperty(String key) {
|
||||
Object val = getProperty(key);
|
||||
if (val == null)
|
||||
return -1;
|
||||
|
||||
Integer i = new Integer(val.toString());
|
||||
return i.intValue();
|
||||
}
|
||||
|
||||
// get a global property out of the hash table. will return null if the property doesn't exist
|
||||
public String getStringProperty(String key) {
|
||||
Object val = getProperty(key);
|
||||
if (val == null)
|
||||
return null;
|
||||
|
||||
return val.toString();
|
||||
}
|
||||
|
||||
// get a hashtable of all the global properties in this file
|
||||
public Hashtable<String, Object> getProperties() {
|
||||
if (mProperties == null) {
|
||||
try {
|
||||
parse();
|
||||
} catch (IOException e) {
|
||||
debug("Error parsing: " + e);
|
||||
}
|
||||
}
|
||||
return mProperties;
|
||||
}
|
||||
|
||||
// do nothing for generic sections
|
||||
protected void parse() throws IOException {
|
||||
mProperties = new Hashtable<String, Object>();
|
||||
}
|
||||
|
||||
// set a property. Will erase the property if value = null
|
||||
public void setProperty(String key, Object value) {
|
||||
getProperties(); // ensure that we have parsed the file
|
||||
if (value == null)
|
||||
removeProperty(key);
|
||||
else
|
||||
mProperties.put(key.trim(), value);
|
||||
}
|
||||
|
||||
// remove a property
|
||||
public void removeProperty(String name) {
|
||||
// ensure that we have parsed the file
|
||||
getProperties();
|
||||
mProperties.remove(name);
|
||||
}
|
||||
|
||||
public void write(BufferedWriter writer) throws IOException {
|
||||
if (!TextUtils.isEmpty(mName)) {
|
||||
writer.write("[" + mName + "]");
|
||||
writer.newLine();
|
||||
}
|
||||
|
||||
if (mProperties != null) {
|
||||
for (Enumeration<String> e = mProperties.keys(); e.hasMoreElements();) {
|
||||
String key = e.nextElement();
|
||||
writeProperty(writer, key, mProperties.get(key));
|
||||
}
|
||||
}
|
||||
writer.newLine();
|
||||
}
|
||||
|
||||
// Helper function to write out a property
|
||||
private void writeProperty(BufferedWriter writer, String key, Object value) {
|
||||
try {
|
||||
writer.write(key + "=" + value);
|
||||
writer.newLine();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
@ -63,6 +63,8 @@ FENNEC_JAVA_FILES = \
|
||||
GeckoThread.java \
|
||||
GlobalHistory.java \
|
||||
GeckoViewsFactory.java \
|
||||
INIParser.java \
|
||||
INISection.java \
|
||||
LinkPreference.java \
|
||||
LinkTextView.java \
|
||||
MenuItemActionBar.java \
|
||||
|
@ -21,7 +21,6 @@ public class BrowserContract {
|
||||
public static final String TABS_AUTHORITY = "@ANDROID_PACKAGE_NAME@.db.tabs";
|
||||
public static final Uri TABS_AUTHORITY_URI = Uri.parse("content://" + TABS_AUTHORITY);
|
||||
|
||||
public static final String DEFAULT_PROFILE = "default";
|
||||
public static final String PARAM_PROFILE = "profile";
|
||||
public static final String PARAM_PROFILE_PATH = "profilePath";
|
||||
public static final String PARAM_LIMIT = "limit";
|
||||
|
@ -10,6 +10,8 @@ import android.database.ContentObserver;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
|
||||
import org.mozilla.gecko.GeckoProfile;
|
||||
|
||||
public class BrowserDB {
|
||||
public static String ABOUT_PAGES_URL_FILTER = "about:%";
|
||||
|
||||
@ -23,7 +25,7 @@ public class BrowserDB {
|
||||
public static String KEYWORD = "keyword";
|
||||
}
|
||||
|
||||
private static BrowserDBIface sDb;
|
||||
private static BrowserDBIface sDb = null;
|
||||
|
||||
public interface BrowserDBIface {
|
||||
public void invalidateCachedState();
|
||||
@ -78,7 +80,11 @@ public class BrowserDB {
|
||||
|
||||
static {
|
||||
// Forcing local DB no option to switch to Android DB for now
|
||||
sDb = new LocalBrowserDB(BrowserContract.DEFAULT_PROFILE);
|
||||
sDb = null;
|
||||
}
|
||||
|
||||
public static void initialize(String profile) {
|
||||
sDb = new LocalBrowserDB(profile);
|
||||
}
|
||||
|
||||
public static void invalidateCachedState() {
|
||||
|
@ -1036,7 +1036,7 @@ public class BrowserProvider extends ContentProvider {
|
||||
|
||||
// Always fallback to default profile if none has been provided.
|
||||
if (TextUtils.isEmpty(profile)) {
|
||||
profile = BrowserContract.DEFAULT_PROFILE;
|
||||
profile = GeckoProfile.get(mContext).getName();
|
||||
}
|
||||
|
||||
DatabaseHelper dbHelper;
|
||||
|
@ -153,7 +153,8 @@ public abstract class GeckoProvider extends ContentProvider {
|
||||
|
||||
private SQLiteBridge getDatabaseForProfile(String profile) {
|
||||
if (TextUtils.isEmpty(profile)) {
|
||||
profile = BrowserContract.DEFAULT_PROFILE;
|
||||
profile = GeckoProfile.get(mContext).getName();
|
||||
Log.d(mLogTag, "No profile provided, using '" + profile + "'");
|
||||
}
|
||||
|
||||
SQLiteBridge db = null;
|
||||
|
@ -198,7 +198,7 @@ public class TabsProvider extends ContentProvider {
|
||||
|
||||
// Always fallback to default profile if none has been provided.
|
||||
if (TextUtils.isEmpty(profile)) {
|
||||
profile = BrowserContract.DEFAULT_PROFILE;
|
||||
profile = GeckoProfile.get(getContext()).getName();
|
||||
}
|
||||
|
||||
DatabaseHelper dbHelper;
|
||||
|
@ -224,7 +224,7 @@ abstract class ContentProviderTest extends AndroidTestCase {
|
||||
Method getDatabasePath =
|
||||
mProviderClass.getDeclaredMethod("getDatabasePath", String.class, boolean.class);
|
||||
|
||||
String defaultProfile = (String) mProviderContract.getField("DEFAULT_PROFILE").get(null);
|
||||
String defaultProfile = "default";
|
||||
databaseName = (String) getDatabasePath.invoke(mProvider, defaultProfile, true /* is test */);
|
||||
} catch (Exception e) {}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user