Implement simple activity to show all launchable applications
This commit is contained in:
parent
7f564c55eb
commit
9c9eb48bf0
12 changed files with 1106 additions and 2 deletions
|
|
@ -6,14 +6,18 @@
|
||||||
<application
|
<application
|
||||||
android:name="org.anbox.appmgr.MainApplication">
|
android:name="org.anbox.appmgr.MainApplication">
|
||||||
<activity
|
<activity
|
||||||
android:name=".LauncherActivity"
|
android:name=".LauncherActivity">
|
||||||
android:launchMode="singleTask">
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN"/>
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
<category android:name="android.intent.category.HOME"/>
|
<category android:name="android.intent.category.HOME"/>
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
<activity
|
||||||
|
android:name=".AppViewActivity"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:excludeFromRecents="true">
|
||||||
|
</activity>
|
||||||
<service
|
<service
|
||||||
android:name=".LauncherService"
|
android:name=".LauncherService"
|
||||||
android:exported="false">
|
android:exported="false">
|
||||||
|
|
|
||||||
17
android/appmgr/res/layout/app_view.xml
Normal file
17
android/appmgr/res/layout/app_view.xml
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
<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>
|
||||||
38
android/appmgr/res/layout/list_item_icon_text.xml
Normal file
38
android/appmgr/res/layout/list_item_icon_text.xml
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
<?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>
|
||||||
5
android/appmgr/res/values/dimens.xml
Normal file
5
android/appmgr/res/values/dimens.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
<resources>
|
||||||
|
<!-- Default screen margins, per the Android Design guidelines. -->
|
||||||
|
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||||
|
<dimen name="activity_vertical_margin">16dp</dimen>
|
||||||
|
</resources>
|
||||||
89
android/appmgr/src/org/anbox/appmgr/AppListAdapter.java
Normal file
89
android/appmgr/src/org/anbox/appmgr/AppListAdapter.java
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
74
android/appmgr/src/org/anbox/appmgr/AppListFragment.java
Normal file
74
android/appmgr/src/org/anbox/appmgr/AppListFragment.java
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
101
android/appmgr/src/org/anbox/appmgr/AppModel.java
Normal file
101
android/appmgr/src/org/anbox/appmgr/AppModel.java
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
41
android/appmgr/src/org/anbox/appmgr/AppViewActivity.java
Normal file
41
android/appmgr/src/org/anbox/appmgr/AppViewActivity.java
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
90
android/appmgr/src/org/anbox/appmgr/AppsGridFragment.java
Normal file
90
android/appmgr/src/org/anbox/appmgr/AppsGridFragment.java
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
184
android/appmgr/src/org/anbox/appmgr/AppsLoader.java
Normal file
184
android/appmgr/src/org/anbox/appmgr/AppsLoader.java
Normal file
|
|
@ -0,0 +1,184 @@
|
||||||
|
/*
|
||||||
|
* 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());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
399
android/appmgr/src/org/anbox/appmgr/GridFragment.java
Normal file
399
android/appmgr/src/org/anbox/appmgr/GridFragment.java
Normal file
|
|
@ -0,0 +1,399 @@
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue