1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.example.android.apis.app;
18 
19 import com.example.android.apis.R;
20 
21 import android.app.Activity;
22 import android.app.Fragment;
23 import android.app.FragmentManager;
24 import android.os.Bundle;
25 import android.view.LayoutInflater;
26 import android.view.View;
27 import android.view.ViewGroup;
28 import android.view.View.OnClickListener;
29 import android.widget.Button;
30 import android.widget.ProgressBar;
31 
32 /**
33  * This example shows how you can use a Fragment to easily propagate state
34  * (such as threads) across activity instances when an activity needs to be
35  * restarted due to, for example, a configuration change.  This is a lot
36  * easier than using the raw Activity.onRetainNonConfiguratinInstance() API.
37  */
38 public class FragmentRetainInstance extends Activity {
39     @Override
onCreate(Bundle savedInstanceState)40     protected void onCreate(Bundle savedInstanceState) {
41         super.onCreate(savedInstanceState);
42 
43         // First time init, create the UI.
44         if (savedInstanceState == null) {
45             getFragmentManager().beginTransaction().add(android.R.id.content,
46                     new UiFragment()).commit();
47         }
48     }
49 
50     /**
51      * This is a fragment showing UI that will be updated from work done
52      * in the retained fragment.
53      */
54     public static class UiFragment extends Fragment {
55         RetainedFragment mWorkFragment;
56 
57         @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)58         public View onCreateView(LayoutInflater inflater, ViewGroup container,
59                 Bundle savedInstanceState) {
60             View v = inflater.inflate(R.layout.fragment_retain_instance, container, false);
61 
62             // Watch for button clicks.
63             Button button = (Button)v.findViewById(R.id.restart);
64             button.setOnClickListener(new OnClickListener() {
65                 public void onClick(View v) {
66                     mWorkFragment.restart();
67                 }
68             });
69 
70             return v;
71         }
72 
73         @Override
onActivityCreated(Bundle savedInstanceState)74         public void onActivityCreated(Bundle savedInstanceState) {
75             super.onActivityCreated(savedInstanceState);
76 
77             FragmentManager fm = getFragmentManager();
78 
79             // Check to see if we have retained the worker fragment.
80             mWorkFragment = (RetainedFragment)fm.findFragmentByTag("work");
81 
82             // If not retained (or first time running), we need to create it.
83             if (mWorkFragment == null) {
84                 mWorkFragment = new RetainedFragment();
85                 // Tell it who it is working with.
86                 mWorkFragment.setTargetFragment(this, 0);
87                 fm.beginTransaction().add(mWorkFragment, "work").commit();
88             }
89         }
90 
91     }
92 
93     /**
94      * This is the Fragment implementation that will be retained across
95      * activity instances.  It represents some ongoing work, here a thread
96      * we have that sits around incrementing a progress indicator.
97      */
98     public static class RetainedFragment extends Fragment {
99         ProgressBar mProgressBar;
100         int mPosition;
101         boolean mReady = false;
102         boolean mQuiting = false;
103 
104         /**
105          * This is the thread that will do our work.  It sits in a loop running
106          * the progress up until it has reached the top, then stops and waits.
107          */
108         final Thread mThread = new Thread() {
109             @Override
110             public void run() {
111                 // We'll figure the real value out later.
112                 int max = 10000;
113 
114                 // This thread runs almost forever.
115                 while (true) {
116 
117                     // Update our shared state with the UI.
118                     synchronized (this) {
119                         // Our thread is stopped if the UI is not ready
120                         // or it has completed its work.
121                         while (!mReady || mPosition >= max) {
122                             if (mQuiting) {
123                                 return;
124                             }
125                             try {
126                                 wait();
127                             } catch (InterruptedException e) {
128                             }
129                         }
130 
131                         // Now update the progress.  Note it is important that
132                         // we touch the progress bar with the lock held, so it
133                         // doesn't disappear on us.
134                         mPosition++;
135                         max = mProgressBar.getMax();
136                         mProgressBar.setProgress(mPosition);
137                     }
138 
139                     // Normally we would be doing some work, but put a kludge
140                     // here to pretend like we are.
141                     synchronized (this) {
142                         try {
143                             wait(50);
144                         } catch (InterruptedException e) {
145                         }
146                     }
147                 }
148             }
149         };
150 
151         /**
152          * Fragment initialization.  We way we want to be retained and
153          * start our thread.
154          */
155         @Override
onCreate(Bundle savedInstanceState)156         public void onCreate(Bundle savedInstanceState) {
157             super.onCreate(savedInstanceState);
158 
159             // Tell the framework to try to keep this fragment around
160             // during a configuration change.
161             setRetainInstance(true);
162 
163             // Start up the worker thread.
164             mThread.start();
165         }
166 
167         /**
168          * This is called when the Fragment's Activity is ready to go, after
169          * its content view has been installed; it is called both after
170          * the initial fragment creation and after the fragment is re-attached
171          * to a new activity.
172          */
173         @Override
onActivityCreated(Bundle savedInstanceState)174         public void onActivityCreated(Bundle savedInstanceState) {
175             super.onActivityCreated(savedInstanceState);
176 
177             // Retrieve the progress bar from the target's view hierarchy.
178             mProgressBar = (ProgressBar)getTargetFragment().getView().findViewById(
179                     R.id.progress_horizontal);
180 
181             // We are ready for our thread to go.
182             synchronized (mThread) {
183                 mReady = true;
184                 mThread.notify();
185             }
186         }
187 
188         /**
189          * This is called when the fragment is going away.  It is NOT called
190          * when the fragment is being propagated between activity instances.
191          */
192         @Override
onDestroy()193         public void onDestroy() {
194             // Make the thread go away.
195             synchronized (mThread) {
196                 mReady = false;
197                 mQuiting = true;
198                 mThread.notify();
199             }
200 
201             super.onDestroy();
202         }
203 
204         /**
205          * This is called right before the fragment is detached from its
206          * current activity instance.
207          */
208         @Override
onDetach()209         public void onDetach() {
210             // This fragment is being detached from its activity.  We need
211             // to make sure its thread is not going to touch any activity
212             // state after returning from this function.
213             synchronized (mThread) {
214                 mProgressBar = null;
215                 mReady = false;
216                 mThread.notify();
217             }
218 
219             super.onDetach();
220         }
221 
222         /**
223          * API for our UI to restart the progress thread.
224          */
restart()225         public void restart() {
226             synchronized (mThread) {
227                 mPosition = 0;
228                 mThread.notify();
229             }
230         }
231     }
232 }
233