1 package org.anbox.appmgr;
4 * Created by Thomas Barrasso on 9/11/12.
5 * Copyright (c) 2012 Loupe Inc.
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
20 import android.content.Context;
21 import android.content.res.Resources;
22 import android.os.Build;
23 import android.os.Bundle;
24 import android.os.Handler;
25 import android.support.v4.app.Fragment;
26 import android.util.TypedValue;
27 import android.view.Gravity;
28 import android.view.LayoutInflater;
29 import android.view.View;
30 import android.view.ViewGroup;
31 import android.view.animation.AnimationUtils;
32 import android.widget.AdapterView;
33 import android.widget.FrameLayout;
34 import android.widget.GridView;
35 import android.widget.LinearLayout;
36 import android.widget.ListAdapter;
37 import android.widget.ListView;
38 import android.widget.ProgressBar;
39 import android.widget.TextView;
42 * Based on {@link android.app.ListFragment} but adapted for {@link GridView}.
44 public class GridFragment extends Fragment {
46 static final int INTERNAL_EMPTY_ID = 0x00ff0001;
47 static final int INTERNAL_PROGRESS_CONTAINER_ID = 0x00ff0002;
48 static final int INTERNAL_LIST_CONTAINER_ID = 0x00ff0003;
50 final private Handler mHandler = new Handler();
52 final private Runnable mRequestFocus = new Runnable() {
54 mGrid.focusableViewAvailable(mGrid);
58 final private AdapterView.OnItemClickListener mOnClickListener
59 = new AdapterView.OnItemClickListener() {
60 public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
61 onGridItemClick((GridView) parent, v, position, id);
68 TextView mStandardEmptyView;
69 View mProgressContainer;
71 CharSequence mEmptyText;
74 public GridFragment() { }
77 * Provide default implementation to return a simple grid view. Subclasses
78 * can override to replace with their own layout. If doing so, the
79 * returned view hierarchy <em>must</em> have a GridView whose id
80 * is {@link android.R.id#list android.R.id.list} and can optionally
81 * have a sibling view id {@link android.R.id#empty android.R.id.empty}
82 * that is to be shown when the grid is empty.
84 * <p>If you are overriding this method with your own custom content,
85 * consider including the standard layout {@link android.R.layout#list_content}
86 * in your layout file, so that you continue to retain all of the standard
87 * behavior of ListFragment. In particular, this is currently the only
88 * way to have the built-in indeterminant progress state be shown.
91 public View onCreateView(LayoutInflater inflater, ViewGroup container,
92 Bundle savedInstanceState) {
93 final Context context = getActivity();
95 FrameLayout root = new FrameLayout(context);
97 // ------------------------------------------------------------------
99 LinearLayout pframe = new LinearLayout(context);
100 pframe.setId(INTERNAL_PROGRESS_CONTAINER_ID);
101 pframe.setOrientation(LinearLayout.VERTICAL);
102 pframe.setVisibility(View.GONE);
103 pframe.setGravity(Gravity.CENTER);
105 ProgressBar progress = new ProgressBar(context, null,
106 android.R.attr.progressBarStyleLarge);
107 pframe.addView(progress, new FrameLayout.LayoutParams(
108 ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
110 root.addView(pframe, new FrameLayout.LayoutParams(
111 ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
113 // ------------------------------------------------------------------
115 FrameLayout lframe = new FrameLayout(context);
116 lframe.setId(INTERNAL_LIST_CONTAINER_ID);
118 TextView tv = new TextView(getActivity());
119 tv.setId(INTERNAL_EMPTY_ID);
120 tv.setGravity(Gravity.CENTER);
121 lframe.addView(tv, new FrameLayout.LayoutParams(
122 ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
124 GridView lv = new GridView(getActivity());
125 lv.setId(android.R.id.list);
126 lv.setDrawSelectorOnTop(false);
127 lv.setColumnWidth(convertDpToPixels(60, getActivity()));
128 lv.setStretchMode(GridView.STRETCH_COLUMN_WIDTH);
129 lv.setNumColumns(GridView.AUTO_FIT);
130 lv.setHorizontalSpacing(convertDpToPixels(20, getActivity()));
131 lv.setVerticalSpacing(convertDpToPixels(20, getActivity()));
132 lv.setSmoothScrollbarEnabled(true);
134 // disable overscroll
135 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
136 lv.setOverScrollMode(ListView.OVER_SCROLL_NEVER);
139 lframe.addView(lv, new FrameLayout.LayoutParams(
140 ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
142 root.addView(lframe, new FrameLayout.LayoutParams(
143 ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
145 // ------------------------------------------------------------------
147 root.setLayoutParams(new FrameLayout.LayoutParams(
148 ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
154 * Attach to grid view once the view hierarchy has been created.
157 public void onViewCreated(View view, Bundle savedInstanceState) {
158 super.onViewCreated(view, savedInstanceState);
163 * Detach from {@link GridView}
166 public void onDestroyView() {
167 mHandler.removeCallbacks(mRequestFocus);
170 mEmptyView = mProgressContainer = mGridContainer = null;
171 mStandardEmptyView = null;
172 super.onDestroyView();
175 public static int convertDpToPixels(float dp, Context context){
176 Resources resources = context.getResources();
177 return (int) TypedValue.applyDimension(
178 TypedValue.COMPLEX_UNIT_DIP,
180 resources.getDisplayMetrics()
185 * This method will be called when an item in the grid is selected.
186 * Subclasses should override. Subclasses can call
187 * getGridView().getItemAtPosition(position) if they need to access the
188 * data associated with the selected item.
190 * @param g The {@link GridView} where the click happened
191 * @param v The view that was clicked within the {@link GridView}
192 * @param position The position of the view in the grid
193 * @param id The row id of the item that was clicked
195 public void onGridItemClick(GridView g, View v, int position, long id) {
200 * Provide the cursor for the {@link GridView}.
202 public void setGridAdapter(ListAdapter adapter) {
203 final boolean hadAdapter = (mAdapter != null);
206 mGrid.setAdapter(adapter);
207 if (!mGridShown && !hadAdapter) {
208 // The grid was hidden, and previously didn't have an
209 // adapter. It is now time to show it.
210 setGridShown(true, (getView().getWindowToken() != null));
216 * Set the currently selected grid item to the specified
217 * position with the adapter's data
221 public void setSelection(int position) {
223 mGrid.setSelection(position);
227 * Get the position of the currently selected grid item.
229 public int getSelectedItemPosition() {
231 return mGrid.getSelectedItemPosition();
235 * Get the cursor row ID of the currently selected grid item.
237 public long getSelectedItemId() {
239 return mGrid.getSelectedItemId();
243 * Get the activity's {@link GridView} widget.
245 public GridView getGridView() {
251 * The default content for a ListFragment has a TextView that can
252 * be shown when the grid is empty. If you would like to have it
253 * shown, call this method to supply the text it should use.
255 public void setEmptyText(CharSequence text) {
257 if (mStandardEmptyView == null) {
258 throw new IllegalStateException("Can't be used with a custom content view");
260 mStandardEmptyView.setText(text);
261 if (mEmptyText == null) {
262 mGrid.setEmptyView(mStandardEmptyView);
268 * Control whether the grid is being displayed. You can make it not
269 * displayed if you are waiting for the initial data to show in it. During
270 * this time an indeterminant progress indicator will be shown instead.
272 * <p>Applications do not normally need to use this themselves. The default
273 * behavior of ListFragment is to start with the grid not being shown, only
274 * showing it once an adapter is given with {@link #setGridAdapter(ListAdapter)}.
275 * If the grid at that point had not been shown, when it does get shown
276 * it will be do without the user ever seeing the hidden state.
278 * @param shown If true, the grid view is shown; if false, the progress
279 * indicator. The initial value is true.
281 public void setGridShown(boolean shown) {
282 setGridShown(shown, true);
286 * Like {@link #setGridShown(boolean)}, but no animation is used when
287 * transitioning from the previous state.
289 public void setGridShownNoAnimation(boolean shown) {
290 setGridShown(shown, false);
294 * Control whether the grid is being displayed. You can make it not
295 * displayed if you are waiting for the initial data to show in it. During
296 * this time an indeterminant progress indicator will be shown instead.
298 * @param shown If true, the grid view is shown; if false, the progress
299 * indicator. The initial value is true.
300 * @param animate If true, an animation will be used to transition to the
303 private void setGridShown(boolean shown, boolean animate) {
305 if (mProgressContainer == null) {
306 throw new IllegalStateException("Can't be used with a custom content view");
308 if (mGridShown == shown) {
314 mProgressContainer.startAnimation(AnimationUtils.loadAnimation(
315 getActivity(), android.R.anim.fade_out));
316 mGridContainer.startAnimation(AnimationUtils.loadAnimation(
317 getActivity(), android.R.anim.fade_in));
319 mProgressContainer.clearAnimation();
320 mGridContainer.clearAnimation();
322 mProgressContainer.setVisibility(View.GONE);
323 mGridContainer.setVisibility(View.VISIBLE);
326 mProgressContainer.startAnimation(AnimationUtils.loadAnimation(
327 getActivity(), android.R.anim.fade_in));
328 mGridContainer.startAnimation(AnimationUtils.loadAnimation(
329 getActivity(), android.R.anim.fade_out));
331 mProgressContainer.clearAnimation();
332 mGridContainer.clearAnimation();
334 mProgressContainer.setVisibility(View.VISIBLE);
335 mGridContainer.setVisibility(View.GONE);
340 * Get the ListAdapter associated with this activity's {@link GridView}.
342 public ListAdapter getGridAdapter() {
346 private void ensureGrid() {
350 View root = getView();
352 throw new IllegalStateException("Content view not yet created");
354 if (root instanceof GridView) {
355 mGrid = (GridView) root;
357 mStandardEmptyView = (TextView)root.findViewById(INTERNAL_EMPTY_ID);
358 if (mStandardEmptyView == null) {
359 mEmptyView = root.findViewById(android.R.id.empty);
361 mStandardEmptyView.setVisibility(View.GONE);
363 mProgressContainer = root.findViewById(INTERNAL_PROGRESS_CONTAINER_ID);
364 mGridContainer = root.findViewById(INTERNAL_LIST_CONTAINER_ID);
365 View rawGridView = root.findViewById(android.R.id.list);
366 if (!(rawGridView instanceof GridView)) {
367 if (rawGridView == null) {
368 throw new RuntimeException(
369 "Your content must have a GridView whose id attribute is " +
370 "'android.R.id.list'");
372 throw new RuntimeException(
373 "Content has view with id attribute 'android.R.id.list' "
374 + "that is not a GridView class");
376 mGrid = (GridView) rawGridView;
377 if (mEmptyView != null) {
378 mGrid.setEmptyView(mEmptyView);
379 } else if (mEmptyText != null) {
380 mStandardEmptyView.setText(mEmptyText);
381 mGrid.setEmptyView(mStandardEmptyView);
385 mGrid.setOnItemClickListener(mOnClickListener);
386 if (mAdapter != null) {
387 ListAdapter adapter = mAdapter;
389 setGridAdapter(adapter);
391 // We are starting without an adapter, so assume we won't
392 // have our data right away and start with the progress indicator.
393 if (mProgressContainer != null) {
394 setGridShown(false, false);
397 mHandler.post(mRequestFocus);