blob: 6e22dde542d05527a938c5dc1ae53827ef1a1a35 [file] [log] [blame]
/*
* Copyright (C) 2015 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.systemui.qs.customize;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Looper;
import android.service.quicksettings.TileService;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.GridLayout;
import android.widget.ImageView;
import android.widget.TextView;
import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
import com.android.systemui.qs.QSTile.Icon;
import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.statusbar.phone.QSTileHost;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
public class TileAdapter extends BaseAdapter {
private static final String TAG = "TileAdapter";
private final ArrayList<TileGroup> mGroups = new ArrayList<>();
private final Context mContext;
private TileSelectedListener mListener;
private ArrayList<String> mCurrentTiles;
public TileAdapter(Context context, Collection<QSTile<?>> currentTiles, QSTileHost host) {
mContext = context;
addSystemTiles(currentTiles, host);
// TODO: Live?
}
private void addSystemTiles(Collection<QSTile<?>> currentTiles, QSTileHost host) {
try {
ArrayList<String> tileSpecs = new ArrayList<>();
for (QSTile<?> tile : currentTiles) {
tileSpecs.add(tile.getTileSpec());
}
mCurrentTiles = tileSpecs;
final TileGroup group = new TileGroup("com.android.settings", mContext);
String possible = mContext.getString(R.string.quick_settings_tiles_default)
+ ",user,hotspot,inversion";
String[] possibleTiles = possible.split(",");
for (int i = 0; i < possibleTiles.length; i++) {
final String spec = possibleTiles[i];
if (spec.startsWith("q")) {
// Quick tiles can't be customized.
continue;
}
if (tileSpecs.contains(spec)) {
Log.d(TAG, "Skipping " + spec);
continue;
}
Log.d(TAG, "Trying " + spec);
final QSTile<?> tile = host.createTile(spec);
if (tile == null) {
continue;
}
// Bad, bad, very bad.
tile.setListening(true);
tile.clearState();
tile.refreshState();
tile.setListening(false);
new Handler(host.getLooper()).post(new Runnable() {
@Override
public void run() {
group.addTile(spec, tile.getState().icon, tile.getState().label, mContext);
}
});
}
// Error: Badness (10000).
// Serialize this work after the host's looper's queue is empty.
new Handler(host.getLooper()).post(new Runnable() {
@Override
public void run() {
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
if (group.mTiles.size() > 0) {
mGroups.add(group);
notifyDataSetChanged();
}
new QueryTilesTask().execute();
}
});
}
});
} catch (NameNotFoundException e) {
Log.e(TAG, "Couldn't load system tiles", e);
}
}
public void setListener(TileSelectedListener listener) {
mListener = listener;
}
@Override
public int getCount() {
return mGroups.size();
}
@Override
public Object getItem(int position) {
return mGroups.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return mGroups.get(position).getView(mContext, convertView, parent, mListener);
}
private static class TileGroup {
private final ArrayList<TileInfo> mTiles = new ArrayList<>();
private CharSequence mLabel;
private Drawable mIcon;
public TileGroup(String pkg, Context context) throws NameNotFoundException {
PackageManager pm = context.getPackageManager();
ApplicationInfo info = pm.getApplicationInfo(pkg, 0);
mLabel = info.loadLabel(pm);
mIcon = info.loadIcon(pm);
Log.d(TAG, "Added " + mLabel);
}
private void addTile(String spec, Drawable icon, CharSequence label) {
TileInfo info = new TileInfo();
info.label = label;
info.drawable = icon;
info.spec = spec;
mTiles.add(info);
}
private void addTile(String spec, Icon icon, CharSequence label, Context context) {
addTile(spec, icon.getDrawable(context), label);
}
private View getView(Context context, View convertView, ViewGroup parent,
final TileSelectedListener listener) {
if (convertView == null) {
convertView = LayoutInflater.from(context).inflate(R.layout.tile_listing, parent,
false);
}
((TextView) convertView.findViewById(android.R.id.title)).setText(mLabel);
((ImageView) convertView.findViewById(android.R.id.icon)).setImageDrawable(mIcon);
GridLayout grid = (GridLayout) convertView.findViewById(R.id.tile_grid);
final int N = mTiles.size();
if (grid.getChildCount() != N) {
grid.removeAllViews();
}
for (int i = 0; i < N; i++) {
if (grid.getChildCount() <= i) {
grid.addView(createTile(context));
}
View view = grid.getChildAt(i);
final TileInfo tileInfo = mTiles.get(i);
((ImageView) view.findViewById(R.id.tile_icon)).setImageDrawable(tileInfo.drawable);
((TextView) view.findViewById(R.id.tile_label)).setText(tileInfo.label);
view.setClickable(true);
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
listener.onTileSelected(tileInfo.spec);
}
});
}
return convertView;
}
private View createTile(Context context) {
return LayoutInflater.from(context).inflate(R.layout.qs_add_tile_layout, null);
}
}
private static class TileInfo {
private String spec;
private Drawable drawable;
private CharSequence label;
}
private class QueryTilesTask extends AsyncTask<Void, Void, Collection<TileGroup>> {
@Override
protected Collection<TileGroup> doInBackground(Void... params) {
HashMap<String, TileGroup> pkgMap = new HashMap<>();
PackageManager pm = mContext.getPackageManager();
// TODO: Handle userness.
List<ResolveInfo> services = pm.queryIntentServices(
new Intent(TileService.ACTION_QS_TILE), 0);
for (ResolveInfo info : services) {
String packageName = info.serviceInfo.packageName;
ComponentName componentName = new ComponentName(packageName, info.serviceInfo.name);
String spec = CustomTile.PREFIX + componentName.flattenToShortString() + ")";
if (mCurrentTiles.contains(spec)) {
continue;
}
try {
TileGroup group = pkgMap.get(packageName);
if (group == null) {
group = new TileGroup(packageName, mContext);
pkgMap.put(packageName, group);
}
Drawable icon = info.serviceInfo.loadIcon(pm);
CharSequence label = info.serviceInfo.loadLabel(pm);
group.addTile(spec, icon, label != null ? label.toString() : "null");
} catch (NameNotFoundException e) {
Log.w(TAG, "Couldn't find resolved package... " + packageName, e);
}
}
return pkgMap.values();
}
@Override
protected void onPostExecute(Collection<TileGroup> result) {
mGroups.addAll(result);
notifyDataSetChanged();
}
}
public interface TileSelectedListener {
void onTileSelected(String spec);
}
}