blob: 816fd882f6761ad1cbd7b5d6b6f8c133e26507d1 [file] [log] [blame]
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.loaderapp.model;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.util.AttributeSet;
import android.util.Xml;
import java.io.IOException;
import java.util.List;
/*
<!-- example of what SourceConstraints would look like in XML -->
<!-- NOTE: may not directly match the current structure version -->
<DataKind
mimeType="vnd.android.cursor.item/email"
title="@string/title_postal"
icon="@drawable/icon_postal"
weight="12"
editable="true">
<!-- these are defined using string-builder-ish -->
<ActionHeader></ActionHeader>
<ActionBody socialSummary="true" /> <!-- can pull together various columns -->
<!-- ordering handles precedence the "insert/add" case -->
<!-- assume uniform type when missing "column", use title in place -->
<EditTypes column="data5" overallMax="-1">
<EditType rawValue="0" label="@string/type_home" specificMax="-1" />
<EditType rawValue="1" label="@string/type_work" specificMax="-1" secondary="true" />
<EditType rawValue="4" label="@string/type_custom" customColumn="data6" specificMax="-1" secondary="true" />
</EditTypes>
<!-- when single edit field, simplifies edit case -->
<EditField column="data1" title="@string/field_family_name" android:inputType="textCapWords|textPhonetic" />
<EditField column="data2" title="@string/field_given_name" android:minLines="2" />
<EditField column="data3" title="@string/field_suffix" />
</DataKind>
*/
/**
* Internal structure that represents constraints and styles for a specific data
* source, such as the various data types they support, including details on how
* those types should be rendered and edited.
* <p>
* In the future this may be inflated from XML defined by a data source.
*/
public class ExternalSource extends FallbackSource {
private static final String ACTION_SYNC_ADAPTER = "android.content.SyncAdapter";
private static final String METADATA_CONTACTS = "android.provider.CONTACTS_STRUCTURE";
private interface InflateTags {
final String CONTACTS_SOURCE = "ContactsSource";
final String CONTACTS_DATA_KIND = "ContactsDataKind";
}
public ExternalSource(String resPackageName) {
this.resPackageName = resPackageName;
this.summaryResPackageName = resPackageName;
}
/**
* Ensure that the constraint rules behind this {@link ContactsSource} have
* been inflated. Because this may involve parsing meta-data from
* {@link PackageManager}, it shouldn't be called from a UI thread.
*/
@Override
public void inflate(Context context, int inflateLevel) {
// Handle unknown sources by searching their package
final PackageManager pm = context.getPackageManager();
final Intent syncAdapter = new Intent(ACTION_SYNC_ADAPTER);
final List<ResolveInfo> matches = pm.queryIntentServices(syncAdapter,
PackageManager.GET_META_DATA);
for (ResolveInfo info : matches) {
final XmlResourceParser parser = info.serviceInfo.loadXmlMetaData(pm,
METADATA_CONTACTS);
if (parser == null) continue;
inflate(context, parser);
}
// Bring in name and photo from fallback source, which are non-optional
inflateStructuredName(context, inflateLevel);
inflatePhoto(context, inflateLevel);
setInflatedLevel(inflateLevel);
}
/**
* Inflate this {@link ContactsSource} from the given parser. This may only
* load details matching the publicly-defined schema.
*/
protected void inflate(Context context, XmlPullParser parser) {
final AttributeSet attrs = Xml.asAttributeSet(parser);
try {
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG
&& type != XmlPullParser.END_DOCUMENT) {
// Drain comments and whitespace
}
if (type != XmlPullParser.START_TAG) {
throw new IllegalStateException("No start tag found");
}
if (!InflateTags.CONTACTS_SOURCE.equals(parser.getName())) {
throw new IllegalStateException("Top level element must be "
+ InflateTags.CONTACTS_SOURCE);
}
// Parse all children kinds
final int depth = parser.getDepth();
while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
&& type != XmlPullParser.END_DOCUMENT) {
if (type == XmlPullParser.END_TAG
|| !InflateTags.CONTACTS_DATA_KIND.equals(parser.getName())) {
continue;
}
final TypedArray a = context.obtainStyledAttributes(attrs,
android.R.styleable.ContactsDataKind);
final DataKind kind = new DataKind();
kind.mimeType = a
.getString(com.android.internal.R.styleable.ContactsDataKind_mimeType);
kind.iconRes = a.getResourceId(
com.android.internal.R.styleable.ContactsDataKind_icon, -1);
final String summaryColumn = a
.getString(com.android.internal.R.styleable.ContactsDataKind_summaryColumn);
if (summaryColumn != null) {
// Inflate a specific column as summary when requested
kind.actionHeader = new FallbackSource.SimpleInflater(summaryColumn);
}
final String detailColumn = a
.getString(com.android.internal.R.styleable.ContactsDataKind_detailColumn);
final boolean detailSocialSummary = a.getBoolean(
com.android.internal.R.styleable.ContactsDataKind_detailSocialSummary,
false);
if (detailSocialSummary) {
// Inflate social summary when requested
kind.actionBodySocial = true;
}
if (detailColumn != null) {
// Inflate specific column as summary
kind.actionBody = new FallbackSource.SimpleInflater(detailColumn);
}
addKind(kind);
}
} catch (XmlPullParserException e) {
throw new IllegalStateException("Problem reading XML", e);
} catch (IOException e) {
throw new IllegalStateException("Problem reading XML", e);
}
}
@Override
public int getHeaderColor(Context context) {
return 0xff6d86b4;
}
@Override
public int getSideBarColor(Context context) {
return 0xff6d86b4;
}
}