1/*
2 * Copyright (C) 2009 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
17package com.android.camera;
18
19import android.content.Context;
20import android.graphics.PixelFormat;
21import android.os.Handler;
22import android.view.Gravity;
23import android.view.LayoutInflater;
24import android.view.View;
25import android.view.WindowManager;
26import android.widget.TextView;
27
28import com.android.camera.R;
29
30/**
31 * A on-screen hint is a view containing a little message for the user and will
32 * be shown on the screen continuously.  This class helps you create and show
33 * those.
34 *
35 * <p>
36 * When the view is shown to the user, appears as a floating view over the
37 * application.
38 * <p>
39 * The easiest way to use this class is to call one of the static methods that
40 * constructs everything you need and returns a new {@code OnScreenHint} object.
41 */
42public class OnScreenHint {
43    static final String TAG = "OnScreenHint";
44
45    int mGravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
46    int mX, mY;
47    float mHorizontalMargin;
48    float mVerticalMargin;
49    View mView;
50    View mNextView;
51
52    private final WindowManager.LayoutParams mParams =
53            new WindowManager.LayoutParams();
54    private final WindowManager mWM;
55    private final Handler mHandler = new Handler();
56
57    /**
58     * Construct an empty OnScreenHint object.  You must call {@link #setView}
59     * before you can call {@link #show}.
60     *
61     * @param context  The context to use.  Usually your
62     *                 {@link android.app.Application} or
63     *                 {@link android.app.Activity} object.
64     */
65    public OnScreenHint(Context context) {
66        mWM = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
67        mY = context.getResources().getDimensionPixelSize(
68                R.dimen.hint_y_offset);
69
70        mParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
71        mParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
72        mParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
73                | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
74        mParams.format = PixelFormat.TRANSLUCENT;
75        mParams.windowAnimations = R.style.Animation_OnScreenHint;
76        mParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
77        mParams.setTitle("OnScreenHint");
78    }
79
80    /**
81     * Show the view on the screen.
82     */
83    public void show() {
84        if (mNextView == null) {
85            throw new RuntimeException("setView must have been called");
86        }
87        mHandler.post(mShow);
88    }
89
90    /**
91     * Close the view if it's showing.
92     */
93    public void cancel() {
94        mHandler.post(mHide);
95    }
96
97    /**
98     * Make a standard hint that just contains a text view.
99     *
100     * @param context  The context to use.  Usually your
101     *                 {@link android.app.Application} or
102     *                 {@link android.app.Activity} object.
103     * @param text     The text to show.  Can be formatted text.
104     *
105     */
106    public static OnScreenHint makeText(Context context, CharSequence text) {
107        OnScreenHint result = new OnScreenHint(context);
108
109        LayoutInflater inflate =
110                (LayoutInflater) context.getSystemService(
111                Context.LAYOUT_INFLATER_SERVICE);
112        View v = inflate.inflate(R.layout.on_screen_hint, null);
113        TextView tv = (TextView) v.findViewById(R.id.message);
114        tv.setText(text);
115
116        result.mNextView = v;
117
118        return result;
119    }
120
121    /**
122     * Update the text in a OnScreenHint that was previously created using one
123     * of the makeText() methods.
124     * @param s The new text for the OnScreenHint.
125     */
126    public void setText(CharSequence s) {
127        if (mNextView == null) {
128            throw new RuntimeException("This OnScreenHint was not "
129                    + "created with OnScreenHint.makeText()");
130        }
131        TextView tv = (TextView) mNextView.findViewById(R.id.message);
132        if (tv == null) {
133            throw new RuntimeException("This OnScreenHint was not "
134                    + "created with OnScreenHint.makeText()");
135        }
136        tv.setText(s);
137    }
138
139    private synchronized void handleShow() {
140        if (mView != mNextView) {
141            // remove the old view if necessary
142            handleHide();
143            mView = mNextView;
144            final int gravity = mGravity;
145            mParams.gravity = gravity;
146            if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK)
147                    == Gravity.FILL_HORIZONTAL) {
148                mParams.horizontalWeight = 1.0f;
149            }
150            if ((gravity & Gravity.VERTICAL_GRAVITY_MASK)
151                    == Gravity.FILL_VERTICAL) {
152                mParams.verticalWeight = 1.0f;
153            }
154            mParams.x = mX;
155            mParams.y = mY;
156            mParams.verticalMargin = mVerticalMargin;
157            mParams.horizontalMargin = mHorizontalMargin;
158            if (mView.getParent() != null) {
159                mWM.removeView(mView);
160            }
161            mWM.addView(mView, mParams);
162        }
163    }
164
165    private synchronized void handleHide() {
166        if (mView != null) {
167            // note: checking parent() just to make sure the view has
168            // been added...  i have seen cases where we get here when
169            // the view isn't yet added, so let's try not to crash.
170            if (mView.getParent() != null) {
171                mWM.removeView(mView);
172            }
173            mView = null;
174        }
175    }
176
177    private final Runnable mShow = new Runnable() {
178        public void run() {
179            handleShow();
180        }
181    };
182
183    private final Runnable mHide = new Runnable() {
184        public void run() {
185            handleHide();
186        }
187    };
188}
189
190