/*
* Copyright (C) 2016 The DirtyUnicorns 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.
*
* GridView displaying all the available icons in a Icon pack.
*/
package com.android.settings.du.utils;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;
import com.android.settings.R;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Bitmap.Config;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.LruCache;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
public class IconPackGridActivity extends Activity {
private IconGridAdapter mAdapter;
private ArrayList<IconInfo> mGridData;
private GridView mGridView;
private ProgressBar mProgressBar;
private LruCache<String, Bitmap> mMemoryCache;
private String mPackageName;
private Resources mIconRes;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// intent with package name is REQUIRED here
mPackageName = getIntent().getStringExtra("icon_package_name");
if (mPackageName == null) {
setResult(RESULT_CANCELED);
finish();
}
try {
PackageManager pm = getPackageManager();
String title = pm.getApplicationInfo(mPackageName, 0).loadLabel(pm).toString();
if (title != null) {
setTitle(title);
}
} catch (Exception e) {
setTitle(R.string.icon_pack_picker_dialog_title);
}
try {
PackageInfo info = getPackageManager().getPackageInfo(mPackageName, 0);
String iconApk = info.applicationInfo.publicSourceDir;
AssetManager assets = new AssetManager();
assets.addAssetPath(iconApk);
DisplayMetrics dm = getResources().getDisplayMetrics();
Configuration config = getResources().getConfiguration();
mIconRes = new Resources(assets, dm, config);
} catch (Exception e) {
setResult(RESULT_CANCELED);
finish();
}
getActionBar().setDisplayHomeAsUpEnabled(true);
setContentView(R.layout.icon_picker_grid);
// Get memory class of this device, exceeding this amount will throw an
// OutOfMemory exception.
final int memClass = ((ActivityManager) getSystemService(Context.ACTIVITY_SERVICE))
.getMemoryClass();
// Use 1/8th of the available memory for this memory cache.
final int cacheSize = 1024 * 1024 * memClass / 8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
protected int sizeOf(String key, Bitmap bitmap) {
// The cache size will be measured in bytes rather than number of items.
return bitmap.getByteCount();
}
};
mGridView = (GridView) findViewById(R.id.icon_grid);
mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
mGridData = new ArrayList<>();
mAdapter = new IconGridAdapter(this);
mGridView.setAdapter(mAdapter);
mGridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
IconInfo info = (IconInfo) parent.getItemAtPosition(position);
Intent resultIntent = new Intent();
resultIntent.putExtra("icon_data_type", "iconpack");
resultIntent.putExtra("icon_data_package", mPackageName);
resultIntent.putExtra("icon_data_name", info.name);
IconPackGridActivity.this.setResult(RESULT_OK, resultIntent);
IconPackGridActivity.this.finish();
}
});
new AsyncIconLoaderTask().execute(mPackageName);
mProgressBar.setVisibility(View.VISIBLE);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
setResult(RESULT_CANCELED);
finish();
return true;
}
return false;
}
public class AsyncIconLoaderTask extends AsyncTask<String, Void, Boolean> {
@Override
protected Boolean doInBackground(String... params) {
String packageName = params[0];
Set<String> set = new HashSet<>();
//From assets
try {
InputStream appfilterstream = mIconRes.getAssets().open("drawable.xml");
set.addAll(xmlInputStreamToSet(appfilterstream, packageName));
} catch (Exception e) {
e.printStackTrace();
}
//From res/xml
try {
int xmlId = mIconRes.getIdentifier("drawable", "xml", packageName);
set.addAll(xmlPullParserToSet(mIconRes.getXml(xmlId)));
} catch (Exception e) {
e.printStackTrace();
}
for (String drawableName : set) {
int resId = mIconRes.getIdentifier(drawableName, "drawable", packageName);
if (resId > 0) {
Drawable d = mIconRes.getDrawable(resId);
if (d != null) {
IconInfo info = new IconInfo();
info.id = resId;
info.name = drawableName;
mGridData.add(info);
}
}
}
Collections.sort(mGridData, new Comparator<IconInfo>() {
@Override
public int compare(IconInfo lhs, IconInfo rhs) {
return lhs.name.compareTo(rhs.name);
}
});
return !mGridData.isEmpty();
}
@Override
protected void onPostExecute(Boolean result) {
if (result) {
mAdapter.setGridData(mGridData);
} else {
setResult(RESULT_CANCELED);
finish();
}
mProgressBar.setVisibility(View.GONE);
}
private Set<String> xmlInputStreamToSet(InputStream inputStream, String packageName) {
Set<String> set = new HashSet<>();
try {
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
XmlPullParser xpp = factory.newPullParser();
xpp.setInput(inputStream, "utf-8");
set.addAll(xmlPullParserToSet(xpp));
} catch (Exception e) {
e.printStackTrace();
}
return set;
}
private Set<String> xmlPullParserToSet(XmlPullParser xpp) {
Set<String> set = new HashSet<>();
try {
if (xpp != null) {
int eventType = xpp.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG) {
if (xpp.getName().equals("item")) {
for (int i = 0; i < xpp.getAttributeCount(); i++) {
if (xpp.getAttributeName(i).startsWith("drawable")) {
String drawableName = xpp.getAttributeValue(i);
set.add(drawableName);
}
}
}
}
eventType = xpp.next();
}
}
} catch (Exception e) {
e.printStackTrace();
}
return set;
}
}
public static Bitmap drawableToBitmap(Drawable drawable) {
if (drawable == null) {
return null;
} else if (drawable instanceof BitmapDrawable) {
return ((BitmapDrawable) drawable).getBitmap();
}
Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
drawable.getIntrinsicHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
}
private class IconGridAdapter extends BaseAdapter {
private LayoutInflater mInflater;
private ArrayList<IconInfo> mGridDataInternal = new ArrayList<IconInfo>();
public IconGridAdapter(Context ctx) {
mInflater = LayoutInflater.from(ctx);
}
void setGridData(ArrayList<IconInfo> gridData) {
mGridDataInternal = gridData;
notifyDataSetChanged();
}
@Override
public int getCount() {
return mGridDataInternal.size();
}
@Override
public IconInfo getItem(int position) {
return mGridDataInternal.get(position);
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView != null) {
holder = (ViewHolder) convertView.getTag();
} else {
convertView = mInflater.inflate(R.layout.icon_picker_item, null, false);
holder = new ViewHolder();
convertView.setTag(holder);
holder.icon = (ImageView) convertView.findViewById(R.id.grid_item_image);
}
IconInfo info = (IconInfo) getItem(position);
Bitmap b = mMemoryCache.get(info.name);
Drawable d = null;
if (b == null) {
try {
d = mIconRes.getDrawable(info.id);
Bitmap tmp = drawableToBitmap(d);
mMemoryCache.put(info.name, tmp);
} catch (Exception e) {
}
} else {
d = new BitmapDrawable(getResources(), b);
}
holder.icon.setImageDrawable(d);
return convertView;
}
}
private static class ViewHolder {
ImageView icon;
}
private static class IconInfo {
Integer id;
String name;
}
}