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