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