提交 90846984 编写于 作者: S Simon Fels 提交者: GitHub

Merge pull request #17 from morphis/app-view

Implement simple application view
......@@ -6,14 +6,18 @@
<application
android:name="org.anbox.appmgr.MainApplication">
<activity
android:name=".LauncherActivity"
android:launchMode="singleTask">
android:name=".LauncherActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.HOME"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<activity
android:name=".AppViewActivity"
android:label="@string/app_name"
android:excludeFromRecents="true">
</activity>
<service
android:name=".LauncherService"
android:exported="false">
......
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context=".AppViewActivity">
<fragment
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="org.anbox.appmgr.AppsGridFragment"
android:id="@+id/apps_grid" />
</RelativeLayout>
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2007 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView android:id="@+id/icon"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center_horizontal"/>
<TextView android:id="@+id/text"
android:layout_gravity="center_horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:singleLine="true"
android:gravity="center"
android:textColor="@android:color/black"
android:textSize="14sp"
/>
</LinearLayout>
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
</resources>
/*
* The MIT License (MIT)
*
* Copyright 2016 Arnab Chakraborty. http://arnab.ch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
* software and associated documentation files (the "Software"), to deal in the Software
* without restriction, including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be included in all copies
* or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
* THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package org.anbox.appmgr;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.Collection;
/**
* Created by Arnab Chakraborty
*/
public class AppListAdapter extends ArrayAdapter<AppModel> {
private final LayoutInflater mInflater;
public AppListAdapter (Context context) {
super(context, android.R.layout.simple_list_item_2);
mInflater = LayoutInflater.from(context);
}
public void setData(ArrayList<AppModel> data) {
clear();
if (data != null) {
addAll(data);
}
}
@Override
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public void addAll(Collection<? extends AppModel> items) {
//If the platform supports it, use addAll, otherwise add in loop
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
super.addAll(items);
}else{
for(AppModel item: items){
super.add(item);
}
}
}
/**
* Populate new items in the list.
*/
@Override public View getView(int position, View convertView, ViewGroup parent) {
View view;
if (convertView == null) {
view = mInflater.inflate(R.layout.list_item_icon_text, parent, false);
} else {
view = convertView;
}
AppModel item = getItem(position);
((ImageView)view.findViewById(R.id.icon)).setImageDrawable(item.getIcon());
((TextView)view.findViewById(R.id.text)).setText(item.getLabel());
return view;
}
}
/*
* The MIT License (MIT)
*
* Copyright 2016 Arnab Chakraborty. http://arnab.ch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
* software and associated documentation files (the "Software"), to deal in the Software
* without restriction, including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be included in all copies
* or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
* THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package org.anbox.appmgr;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import java.util.ArrayList;
/**
* Created by Arnab Chakraborty
*/
public class AppListFragment extends ListFragment implements LoaderManager.LoaderCallbacks<ArrayList<AppModel>> {
AppListAdapter mAdapter;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setEmptyText("No Applications");
mAdapter = new AppListAdapter(getActivity());
setListAdapter(mAdapter);
// till the data is loaded display a spinner
setListShown(false);
// create the loader to load the apps list in background
getLoaderManager().initLoader(0, null, this);
}
@Override
public Loader<ArrayList<AppModel>> onCreateLoader(int id, Bundle bundle) {
return new AppsLoader(getActivity());
}
@Override
public void onLoadFinished(Loader<ArrayList<AppModel>> loader, ArrayList<AppModel> apps) {
mAdapter.setData(apps);
if (isResumed()) {
setListShown(true);
} else {
setListShownNoAnimation(true);
}
}
@Override
public void onLoaderReset(Loader<ArrayList<AppModel>> loader) {
mAdapter.setData(null);
}
}
/*
* The MIT License (MIT)
*
* Copyright 2016 Arnab Chakraborty. http://arnab.ch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
* software and associated documentation files (the "Software"), to deal in the Software
* without restriction, including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be included in all copies
* or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
* THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package org.anbox.appmgr;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.graphics.drawable.Drawable;
import java.io.File;
/**
* @credit http://developer.android.com/reference/android/content/AsyncTaskLoader.html
*/
public class AppModel {
private final Context mContext;
private final ApplicationInfo mInfo;
private String mAppLabel;
private Drawable mIcon;
private boolean mMounted;
private final File mApkFile;
public AppModel(Context context, ApplicationInfo info) {
mContext = context;
mInfo = info;
mApkFile = new File(info.sourceDir);
}
public ApplicationInfo getAppInfo() {
return mInfo;
}
public String getApplicationPackageName() {
return getAppInfo().packageName;
}
public String getLabel() {
return mAppLabel;
}
public Drawable getIcon() {
if (mIcon == null) {
if (mApkFile.exists()) {
mIcon = mInfo.loadIcon(mContext.getPackageManager());
return mIcon;
} else {
mMounted = false;
}
} else if (!mMounted) {
// If the app wasn't mounted but is now mounted, reload
// its icon.
if (mApkFile.exists()) {
mMounted = true;
mIcon = mInfo.loadIcon(mContext.getPackageManager());
return mIcon;
}
} else {
return mIcon;
}
return mContext.getResources().getDrawable(android.R.drawable.sym_def_app_icon);
}
void loadLabel(Context context) {
if (mAppLabel == null || !mMounted) {
if (!mApkFile.exists()) {
mMounted = false;
mAppLabel = mInfo.packageName;
} else {
mMounted = true;
CharSequence label = mInfo.loadLabel(context.getPackageManager());
mAppLabel = label != null ? label.toString() : mInfo.packageName;
}
}
}
}
/*
* Copyright (C) 2016 Simon Fels <morphis@gravedo.de>
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3, as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranties of
* MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.anbox.appmgr;
import android.os.Bundle;
import android.util.Log;
import android.support.v4.app.FragmentActivity;
public final class AppViewActivity extends FragmentActivity {
private static final String TAG = "AnboxAppView";
@Override
public void onCreate(Bundle info) {
super.onCreate(info);
setContentView(R.layout.app_view);
Log.i(TAG, "Created application view activity");
}
@Override
public void onDestroy() {
Log.i(TAG, "Destroying application view activity");
super.onDestroy();
}
}
/*
* The MIT License (MIT)
*
* Copyright 2016 Arnab Chakraborty. http://arnab.ch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
* software and associated documentation files (the "Software"), to deal in the Software
* without restriction, including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be included in all copies
* or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
* THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package org.anbox.appmgr;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.view.View;
import android.widget.GridView;
import java.util.ArrayList;
/**
* Created by Arnab Chakraborty
*/
public class AppsGridFragment extends GridFragment implements LoaderManager.LoaderCallbacks<ArrayList<AppModel>> {
AppListAdapter mAdapter;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setEmptyText("No Applications");
mAdapter = new AppListAdapter(getActivity());
setGridAdapter(mAdapter);
// till the data is loaded display a spinner
setGridShown(false);
// create the loader to load the apps list in background
getLoaderManager().initLoader(0, null, this);
}
@Override
public Loader<ArrayList<AppModel>> onCreateLoader(int id, Bundle bundle) {
return new AppsLoader(getActivity());
}
@Override
public void onLoadFinished(Loader<ArrayList<AppModel>> loader, ArrayList<AppModel> apps) {
mAdapter.setData(apps);
if (isResumed()) {
setGridShown(true);
} else {
setGridShownNoAnimation(true);
}
}
@Override
public void onLoaderReset(Loader<ArrayList<AppModel>> loader) {
mAdapter.setData(null);
}
@Override
public void onGridItemClick(GridView g, View v, int position, long id) {
AppModel app = (AppModel) getGridAdapter().getItem(position);
if (app != null) {
Intent intent = getActivity().getPackageManager().getLaunchIntentForPackage(app.getApplicationPackageName());
if (intent != null) {
startActivity(intent);
}
}
}
}
/*
* The MIT License (MIT)
*
* Copyright 2016 Arnab Chakraborty. http://arnab.ch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
* software and associated documentation files (the "Software"), to deal in the Software
* without restriction, including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be included in all copies
* or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
* THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package org.anbox.appmgr;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.support.v4.content.AsyncTaskLoader;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Comparator;
/**
* @credit http://developer.android.com/reference/android/content/AsyncTaskLoader.html
*/
public class AppsLoader extends AsyncTaskLoader<ArrayList<AppModel>> {
ArrayList<AppModel> mInstalledApps;
final PackageManager mPm;
PackageIntentReceiver mPackageObserver;
public AppsLoader(Context context) {
super(context);
mPm = context.getPackageManager();
}
@Override
public ArrayList<AppModel> loadInBackground() {
// retrieve the list of installed applications
List<ApplicationInfo> apps = mPm.getInstalledApplications(0);
if (apps == null) {
apps = new ArrayList<ApplicationInfo>();
}
final Context context = getContext();
// create corresponding apps and load their labels
ArrayList<AppModel> items = new ArrayList<AppModel>(apps.size());
for (int i = 0; i < apps.size(); i++) {
String pkg = apps.get(i).packageName;
// only apps which are launchable
if (context.getPackageManager().getLaunchIntentForPackage(pkg) != null) {
AppModel app = new AppModel(context, apps.get(i));
app.loadLabel(context);
items.add(app);
}
}
// sort the list
Collections.sort(items, ALPHA_COMPARATOR);
return items;
}
@Override
public void deliverResult(ArrayList<AppModel> apps) {
if (isReset()) {
// An async query came in while the loader is stopped. We
// don't need the result.
if (apps != null) {
onReleaseResources(apps);
}
}
ArrayList<AppModel> oldApps = apps;
mInstalledApps = apps;
if (isStarted()) {
// If the Loader is currently started, we can immediately
// deliver its results.
super.deliverResult(apps);
}
// At this point we can release the resources associated with
// 'oldApps' if needed; now that the new result is delivered we
// know that it is no longer in use.
if (oldApps != null) {
onReleaseResources(oldApps);
}
}
@Override
protected void onStartLoading() {
if (mInstalledApps != null) {
// If we currently have a result available, deliver it
// immediately.
deliverResult(mInstalledApps);
}
// watch for changes in app install and uninstall operation
if (mPackageObserver == null) {
mPackageObserver = new PackageIntentReceiver(this);
}
if (takeContentChanged() || mInstalledApps == null ) {
// If the data has changed since the last time it was loaded
// or is not currently available, start a load.
forceLoad();
}
}
@Override
protected void onStopLoading() {
// Attempt to cancel the current load task if possible.
cancelLoad();
}
@Override
public void onCanceled(ArrayList<AppModel> apps) {
super.onCanceled(apps);
// At this point we can release the resources associated with 'apps'
// if needed.
onReleaseResources(apps);
}
@Override
protected void onReset() {
// Ensure the loader is stopped
onStopLoading();
// At this point we can release the resources associated with 'apps'
// if needed.
if (mInstalledApps != null) {
onReleaseResources(mInstalledApps);
mInstalledApps = null;
}
// Stop monitoring for changes.
if (mPackageObserver != null) {
getContext().unregisterReceiver(mPackageObserver);
mPackageObserver = null;
}
}
/**
* Helper method to do the cleanup work if needed, for example if we're
* using Cursor, then we should be closing it here
*
* @param apps
*/
protected void onReleaseResources(ArrayList<AppModel> apps) {
// do nothing
}
/**
* Perform alphabetical comparison of application entry objects.
*/
public static final Comparator<AppModel> ALPHA_COMPARATOR = new Comparator<AppModel>() {
private final Collator sCollator = Collator.getInstance();
@Override
public int compare(AppModel object1, AppModel object2) {
return sCollator.compare(object1.getLabel(), object2.getLabel());
}
};
}
package org.anbox.appmgr;
/*
* Created by Thomas Barrasso on 9/11/12.
* Copyright (c) 2012 Loupe Inc.
*
* 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.
*/
import android.content.Context;
import android.content.res.Resources;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.Fragment;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
import android.widget.AdapterView;
import android.widget.FrameLayout;
import android.widget.GridView;
import android.widget.LinearLayout;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
/**
* Based on {@link android.app.ListFragment} but adapted for {@link GridView}.
*/
public class GridFragment extends Fragment {
static final int INTERNAL_EMPTY_ID = 0x00ff0001;
static final int INTERNAL_PROGRESS_CONTAINER_ID = 0x00ff0002;
static final int INTERNAL_LIST_CONTAINER_ID = 0x00ff0003;
final private Handler mHandler = new Handler();
final private Runnable mRequestFocus = new Runnable() {
public void run() {
mGrid.focusableViewAvailable(mGrid);
}
};
final private AdapterView.OnItemClickListener mOnClickListener
= new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
onGridItemClick((GridView) parent, v, position, id);
}
};
ListAdapter mAdapter;
GridView mGrid;
View mEmptyView;
TextView mStandardEmptyView;
View mProgressContainer;
View mGridContainer;
CharSequence mEmptyText;
boolean mGridShown;
public GridFragment() { }
/**
* Provide default implementation to return a simple grid view. Subclasses
* can override to replace with their own layout. If doing so, the
* returned view hierarchy <em>must</em> have a GridView whose id
* is {@link android.R.id#list android.R.id.list} and can optionally
* have a sibling view id {@link android.R.id#empty android.R.id.empty}
* that is to be shown when the grid is empty.
*
* <p>If you are overriding this method with your own custom content,
* consider including the standard layout {@link android.R.layout#list_content}
* in your layout file, so that you continue to retain all of the standard
* behavior of ListFragment. In particular, this is currently the only
* way to have the built-in indeterminant progress state be shown.
*/
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final Context context = getActivity();
FrameLayout root = new FrameLayout(context);
// ------------------------------------------------------------------
LinearLayout pframe = new LinearLayout(context);
pframe.setId(INTERNAL_PROGRESS_CONTAINER_ID);
pframe.setOrientation(LinearLayout.VERTICAL);
pframe.setVisibility(View.GONE);
pframe.setGravity(Gravity.CENTER);
ProgressBar progress = new ProgressBar(context, null,
android.R.attr.progressBarStyleLarge);
pframe.addView(progress, new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
root.addView(pframe, new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
// ------------------------------------------------------------------
FrameLayout lframe = new FrameLayout(context);
lframe.setId(INTERNAL_LIST_CONTAINER_ID);
TextView tv = new TextView(getActivity());
tv.setId(INTERNAL_EMPTY_ID);
tv.setGravity(Gravity.CENTER);
lframe.addView(tv, new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
GridView lv = new GridView(getActivity());
lv.setId(android.R.id.list);
lv.setDrawSelectorOnTop(false);
lv.setColumnWidth(convertDpToPixels(60, getActivity()));
lv.setStretchMode(GridView.STRETCH_COLUMN_WIDTH);
lv.setNumColumns(GridView.AUTO_FIT);
lv.setHorizontalSpacing(convertDpToPixels(20, getActivity()));
lv.setVerticalSpacing(convertDpToPixels(20, getActivity()));
lv.setSmoothScrollbarEnabled(true);
// disable overscroll
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
lv.setOverScrollMode(ListView.OVER_SCROLL_NEVER);
}
lframe.addView(lv, new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
root.addView(lframe, new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
// ------------------------------------------------------------------
root.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
return root;
}
/**
* Attach to grid view once the view hierarchy has been created.
*/
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ensureGrid();
}
/**
* Detach from {@link GridView}
*/
@Override
public void onDestroyView() {
mHandler.removeCallbacks(mRequestFocus);
mGrid = null;
mGridShown = false;
mEmptyView = mProgressContainer = mGridContainer = null;
mStandardEmptyView = null;
super.onDestroyView();
}
public static int convertDpToPixels(float dp, Context context){
Resources resources = context.getResources();
return (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
dp,
resources.getDisplayMetrics()
);
}
/**
* This method will be called when an item in the grid is selected.
* Subclasses should override. Subclasses can call
* getGridView().getItemAtPosition(position) if they need to access the
* data associated with the selected item.
*
* @param g The {@link GridView} where the click happened
* @param v The view that was clicked within the {@link GridView}
* @param position The position of the view in the grid
* @param id The row id of the item that was clicked
*/
public void onGridItemClick(GridView g, View v, int position, long id) {
}
/**
* Provide the cursor for the {@link GridView}.
*/
public void setGridAdapter(ListAdapter adapter) {
final boolean hadAdapter = (mAdapter != null);
mAdapter = adapter;
if (mGrid != null) {
mGrid.setAdapter(adapter);
if (!mGridShown && !hadAdapter) {
// The grid was hidden, and previously didn't have an
// adapter. It is now time to show it.
setGridShown(true, (getView().getWindowToken() != null));
}
}
}
/**
* Set the currently selected grid item to the specified
* position with the adapter's data
*
* @param position
*/
public void setSelection(int position) {
ensureGrid();
mGrid.setSelection(position);
}
/**
* Get the position of the currently selected grid item.
*/
public int getSelectedItemPosition() {
ensureGrid();
return mGrid.getSelectedItemPosition();
}
/**
* Get the cursor row ID of the currently selected grid item.
*/
public long getSelectedItemId() {
ensureGrid();
return mGrid.getSelectedItemId();
}
/**
* Get the activity's {@link GridView} widget.
*/
public GridView getGridView() {
ensureGrid();
return mGrid;
}
/**
* The default content for a ListFragment has a TextView that can
* be shown when the grid is empty. If you would like to have it
* shown, call this method to supply the text it should use.
*/
public void setEmptyText(CharSequence text) {
ensureGrid();
if (mStandardEmptyView == null) {
throw new IllegalStateException("Can't be used with a custom content view");
}
mStandardEmptyView.setText(text);
if (mEmptyText == null) {
mGrid.setEmptyView(mStandardEmptyView);
}
mEmptyText = text;
}
/**
* Control whether the grid is being displayed. You can make it not
* displayed if you are waiting for the initial data to show in it. During
* this time an indeterminant progress indicator will be shown instead.
*
* <p>Applications do not normally need to use this themselves. The default
* behavior of ListFragment is to start with the grid not being shown, only
* showing it once an adapter is given with {@link #setGridAdapter(ListAdapter)}.
* If the grid at that point had not been shown, when it does get shown
* it will be do without the user ever seeing the hidden state.
*
* @param shown If true, the grid view is shown; if false, the progress
* indicator. The initial value is true.
*/
public void setGridShown(boolean shown) {
setGridShown(shown, true);
}
/**
* Like {@link #setGridShown(boolean)}, but no animation is used when
* transitioning from the previous state.
*/
public void setGridShownNoAnimation(boolean shown) {
setGridShown(shown, false);
}
/**
* Control whether the grid is being displayed. You can make it not
* displayed if you are waiting for the initial data to show in it. During
* this time an indeterminant progress indicator will be shown instead.
*
* @param shown If true, the grid view is shown; if false, the progress
* indicator. The initial value is true.
* @param animate If true, an animation will be used to transition to the
* new state.
*/
private void setGridShown(boolean shown, boolean animate) {
ensureGrid();
if (mProgressContainer == null) {
throw new IllegalStateException("Can't be used with a custom content view");
}
if (mGridShown == shown) {
return;
}
mGridShown = shown;
if (shown) {
if (animate) {
mProgressContainer.startAnimation(AnimationUtils.loadAnimation(
getActivity(), android.R.anim.fade_out));
mGridContainer.startAnimation(AnimationUtils.loadAnimation(
getActivity(), android.R.anim.fade_in));
} else {
mProgressContainer.clearAnimation();
mGridContainer.clearAnimation();
}
mProgressContainer.setVisibility(View.GONE);
mGridContainer.setVisibility(View.VISIBLE);
} else {
if (animate) {
mProgressContainer.startAnimation(AnimationUtils.loadAnimation(
getActivity(), android.R.anim.fade_in));
mGridContainer.startAnimation(AnimationUtils.loadAnimation(
getActivity(), android.R.anim.fade_out));
} else {
mProgressContainer.clearAnimation();
mGridContainer.clearAnimation();
}
mProgressContainer.setVisibility(View.VISIBLE);
mGridContainer.setVisibility(View.GONE);
}
}
/**
* Get the ListAdapter associated with this activity's {@link GridView}.
*/
public ListAdapter getGridAdapter() {
return mAdapter;
}
private void ensureGrid() {
if (mGrid != null) {
return;
}
View root = getView();
if (root == null) {
throw new IllegalStateException("Content view not yet created");
}
if (root instanceof GridView) {
mGrid = (GridView) root;
} else {
mStandardEmptyView = (TextView)root.findViewById(INTERNAL_EMPTY_ID);
if (mStandardEmptyView == null) {
mEmptyView = root.findViewById(android.R.id.empty);
} else {
mStandardEmptyView.setVisibility(View.GONE);
}
mProgressContainer = root.findViewById(INTERNAL_PROGRESS_CONTAINER_ID);
mGridContainer = root.findViewById(INTERNAL_LIST_CONTAINER_ID);
View rawGridView = root.findViewById(android.R.id.list);
if (!(rawGridView instanceof GridView)) {
if (rawGridView == null) {
throw new RuntimeException(
"Your content must have a GridView whose id attribute is " +
"'android.R.id.list'");
}
throw new RuntimeException(
"Content has view with id attribute 'android.R.id.list' "
+ "that is not a GridView class");
}
mGrid = (GridView) rawGridView;
if (mEmptyView != null) {
mGrid.setEmptyView(mEmptyView);
} else if (mEmptyText != null) {
mStandardEmptyView.setText(mEmptyText);
mGrid.setEmptyView(mStandardEmptyView);
}
}
mGridShown = true;
mGrid.setOnItemClickListener(mOnClickListener);
if (mAdapter != null) {
ListAdapter adapter = mAdapter;
mAdapter = null;
setGridAdapter(adapter);
} else {
// We are starting without an adapter, so assume we won't
// have our data right away and start with the progress indicator.
if (mProgressContainer != null) {
setGridShown(false, false);
}
}
mHandler.post(mRequestFocus);
}
}
/*
* The MIT License (MIT)
*
* Copyright 2016 Arnab Chakraborty. http://arnab.ch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
* software and associated documentation files (the "Software"), to deal in the Software
* without restriction, including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be included in all copies
* or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
* THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package org.anbox.appmgr;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
/**
* Helper class to look for interesting changes to the installed apps
* so that the loader can be updated.
*
* @Credit http://developer.android.com/reference/android/content/AsyncTaskLoader.html
*/
public class PackageIntentReceiver extends BroadcastReceiver {
final AppsLoader mLoader;
public PackageIntentReceiver(AppsLoader loader) {
mLoader = loader;
IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
filter.addDataScheme("package");
mLoader.getContext().registerReceiver(this, filter);
// Register for events related to sdcard installation.
IntentFilter sdFilter = new IntentFilter();
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
mLoader.getContext().registerReceiver(this, sdFilter);
}
@Override public void onReceive(Context context, Intent intent) {
// Tell the loader about the change.
mLoader.onContentChanged();
}
}
[Desktop Entry]
Name=Anbox
GenericName=Android in a box
Version=1.0
Type=Application
Exec=anbox launch --package=org.anbox.appmgr --component=org.anbox.appmgr.AppViewActivity
Terminal=false
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册