1effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang/*
2effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang * Copyright (C) 2015 The Android Open Source Project
3effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang *
4effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang * Licensed under the Apache License, Version 2.0 (the "License");
5effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang * you may not use this file except in compliance with the License.
6effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang * You may obtain a copy of the License at
7effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang *
8effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang *      http://www.apache.org/licenses/LICENSE-2.0
9effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang *
10effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang * Unless required by applicable law or agreed to in writing, software
11effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang * distributed under the License is distributed on an "AS IS" BASIS,
12effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang * See the License for the specific language governing permissions and
14effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang * limitations under the License.
15effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang */
16effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang
17effc01ce01f15cae466235d3a736389c84c7ebefFan Zhangpackage com.android.deskclock.alarms;
18effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang
19effc01ce01f15cae466235d3a736389c84c7ebefFan Zhangimport android.content.ContentResolver;
20effc01ce01f15cae466235d3a736389c84c7ebefFan Zhangimport android.content.Context;
21effc01ce01f15cae466235d3a736389c84c7ebefFan Zhangimport android.os.AsyncTask;
227c955e743119c9101b753d151a7a6deba227ddf9Fan Zhangimport android.support.design.widget.Snackbar;
23c8f715a45ad7bb522f48fbd25b500000cb49d1b8Annie Chinimport android.text.format.DateFormat;
24effc01ce01f15cae466235d3a736389c84c7ebefFan Zhangimport android.view.View;
25effc01ce01f15cae466235d3a736389c84c7ebefFan Zhangimport android.view.ViewGroup;
26effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang
27effc01ce01f15cae466235d3a736389c84c7ebefFan Zhangimport com.android.deskclock.AlarmUtils;
28effc01ce01f15cae466235d3a736389c84c7ebefFan Zhangimport com.android.deskclock.R;
29effc01ce01f15cae466235d3a736389c84c7ebefFan Zhangimport com.android.deskclock.events.Events;
30effc01ce01f15cae466235d3a736389c84c7ebefFan Zhangimport com.android.deskclock.provider.Alarm;
31effc01ce01f15cae466235d3a736389c84c7ebefFan Zhangimport com.android.deskclock.provider.AlarmInstance;
327c955e743119c9101b753d151a7a6deba227ddf9Fan Zhangimport com.android.deskclock.widget.toast.SnackbarManager;
33effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang
34effc01ce01f15cae466235d3a736389c84c7ebefFan Zhangimport java.util.Calendar;
35595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chinimport java.util.List;
36effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang
37effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang/**
38effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang * API for asynchronously mutating a single alarm.
39effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang */
407c955e743119c9101b753d151a7a6deba227ddf9Fan Zhangpublic final class AlarmUpdateHandler {
41effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang
42effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang    private final Context mAppContext;
43effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang    private final ScrollHandler mScrollHandler;
447c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang    private final View mSnackbarAnchor;
45effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang
46effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang    // For undo
47effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang    private Alarm mDeletedAlarm;
48effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang
497c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang    public AlarmUpdateHandler(Context context, ScrollHandler scrollHandler,
507c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang            ViewGroup snackbarAnchor) {
51effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang        mAppContext = context.getApplicationContext();
52effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang        mScrollHandler = scrollHandler;
537c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang        mSnackbarAnchor = snackbarAnchor;
54effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang    }
55effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang
56effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang    /**
57effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang     * Adds a new alarm on the background.
58effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang     *
59effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang     * @param alarm The alarm to be added.
60effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang     */
61effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang    public void asyncAddAlarm(final Alarm alarm) {
62effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang        final AsyncTask<Void, Void, AlarmInstance> updateTask =
63effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                new AsyncTask<Void, Void, AlarmInstance>() {
64effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                    @Override
65effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                    protected AlarmInstance doInBackground(Void... parameters) {
66effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                        if (alarm != null) {
67effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                            Events.sendAlarmEvent(R.string.action_create, R.string.label_deskclock);
68effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                            ContentResolver cr = mAppContext.getContentResolver();
69effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang
70effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                            // Add alarm to db
71effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                            Alarm newAlarm = Alarm.addAlarm(cr, alarm);
72effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang
73effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                            // Be ready to scroll to this alarm on UI later.
74effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                            mScrollHandler.setSmoothScrollStableId(newAlarm.id);
75effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang
76effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                            // Create and add instance to db
77effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                            if (newAlarm.enabled) {
78effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                                return setupAlarmInstance(newAlarm);
79effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                            }
80effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                        }
81effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                        return null;
82effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                    }
83effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang
84effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                    @Override
85effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                    protected void onPostExecute(AlarmInstance instance) {
86effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                        if (instance != null) {
877c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang                            AlarmUtils.popAlarmSetSnackbar(
887c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang                                    mSnackbarAnchor, instance.getAlarmTime().getTimeInMillis());
89effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                        }
90effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                    }
91effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                };
92effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang        updateTask.execute();
93effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang    }
94effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang
95effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang    /**
96effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang     * Modifies an alarm on the background, and optionally show a toast when done.
97effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang     *
98effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang     * @param alarm       The alarm to be modified.
99effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang     * @param popToast    whether or not a toast should be displayed when done.
100effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang     * @param minorUpdate if true, don't affect any currently snoozed instances.
101effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang     */
102effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang    public void asyncUpdateAlarm(final Alarm alarm, final boolean popToast,
103effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang            final boolean minorUpdate) {
104effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang        final AsyncTask<Void, Void, AlarmInstance> updateTask =
105effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                new AsyncTask<Void, Void, AlarmInstance>() {
106effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                    @Override
107effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                    protected AlarmInstance doInBackground(Void... parameters) {
108effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                        Events.sendAlarmEvent(R.string.action_update, R.string.label_deskclock);
109effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                        ContentResolver cr = mAppContext.getContentResolver();
110effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang
111effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                        // Update alarm
112effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                        Alarm.updateAlarm(cr, alarm);
113595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin
114595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin                        if (minorUpdate) {
115595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin                            // just update the instance in the database and update notifications.
116595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin                            final List<AlarmInstance> instanceList =
117595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin                                    AlarmInstance.getInstancesByAlarmId(cr, alarm.id);
118595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin                            for (AlarmInstance instance : instanceList) {
119595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin                                // Make a copy of the existing instance
120595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin                                final AlarmInstance newInstance = new AlarmInstance(instance);
121595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin                                // Copy over minor change data to the instance; we don't know
122595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin                                // exactly which minor field changed, so just copy them all.
123595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin                                newInstance.mVibrate = alarm.vibrate;
124595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin                                newInstance.mRingtone = alarm.alert;
125595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin                                newInstance.mLabel = alarm.label;
126595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin                                // Since we copied the mId of the old instance and the mId is used
127595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin                                // as the primary key in the AlarmInstance table, this will replace
128595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin                                // the existing instance.
129595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin                                AlarmInstance.updateInstance(cr, newInstance);
130595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin                                // Update the notification for this instance.
131595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin                                AlarmNotifications.updateNotification(mAppContext, newInstance);
132595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin                            }
133595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin                            return null;
134effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                        }
135595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin                        // Otherwise, this is a major update and we're going to re-create the alarm
136595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin                        AlarmStateManager.deleteAllInstances(mAppContext, alarm.id);
137effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang
138595e6b8c56a6810d0d3bd09669ae314fe0d5a902Annie Chin                        return alarm.enabled ? setupAlarmInstance(alarm) : null;
139effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                    }
140effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang
141effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                    @Override
142effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                    protected void onPostExecute(AlarmInstance instance) {
143effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                        if (popToast && instance != null) {
1447c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang                            AlarmUtils.popAlarmSetSnackbar(
1457c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang                                    mSnackbarAnchor, instance.getAlarmTime().getTimeInMillis());
146effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                        }
147effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                    }
148effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                };
149effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang        updateTask.execute();
150effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang    }
151effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang
152effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang    /**
153effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang     * Deletes an alarm on the background.
154effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang     *
155effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang     * @param alarm The alarm to be deleted.
156effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang     */
157effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang    public void asyncDeleteAlarm(final Alarm alarm) {
158effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang        final AsyncTask<Void, Void, Boolean> deleteTask = new AsyncTask<Void, Void, Boolean>() {
159effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang            @Override
160effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang            protected Boolean doInBackground(Void... parameters) {
161effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                // Activity may be closed at this point , make sure data is still valid
162effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                if (alarm == null) {
163effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                    // Nothing to do here, just return.
164effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                    return false;
165effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                }
166effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                Events.sendAlarmEvent(R.string.action_delete, R.string.label_deskclock);
167effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                AlarmStateManager.deleteAllInstances(mAppContext, alarm.id);
168effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                return Alarm.deleteAlarm(mAppContext.getContentResolver(), alarm.id);
169effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang            }
170effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang
171effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang            @Override
172effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang            protected void onPostExecute(Boolean deleted) {
173effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                if (deleted) {
174effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                    mDeletedAlarm = alarm;
175effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                    showUndoBar();
176effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang                }
177effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang            }
178effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang        };
179effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang        deleteTask.execute();
180effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang    }
181effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang
182effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang    /**
183c8f715a45ad7bb522f48fbd25b500000cb49d1b8Annie Chin     * Show a toast when an alarm is predismissed.
184c8f715a45ad7bb522f48fbd25b500000cb49d1b8Annie Chin     *
185c8f715a45ad7bb522f48fbd25b500000cb49d1b8Annie Chin     * @param instance Instance being predismissed.
186c8f715a45ad7bb522f48fbd25b500000cb49d1b8Annie Chin     */
187c8f715a45ad7bb522f48fbd25b500000cb49d1b8Annie Chin    public void showPredismissToast(AlarmInstance instance) {
188c8f715a45ad7bb522f48fbd25b500000cb49d1b8Annie Chin        final String time = DateFormat.getTimeFormat(mAppContext).format(
189c8f715a45ad7bb522f48fbd25b500000cb49d1b8Annie Chin                instance.getAlarmTime().getTime());
190c8f715a45ad7bb522f48fbd25b500000cb49d1b8Annie Chin        final String text = mAppContext.getString(R.string.alarm_is_dismissed, time);
1917c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang        SnackbarManager.show(Snackbar.make(mSnackbarAnchor, text, Snackbar.LENGTH_SHORT));
192c8f715a45ad7bb522f48fbd25b500000cb49d1b8Annie Chin    }
193c8f715a45ad7bb522f48fbd25b500000cb49d1b8Annie Chin
194c8f715a45ad7bb522f48fbd25b500000cb49d1b8Annie Chin    /**
195effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang     * Hides any undo toast.
196effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang     */
1977c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang    public void hideUndoBar() {
198effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang        mDeletedAlarm = null;
1997c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang        SnackbarManager.dismiss();
200effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang    }
201effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang
202effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang    private void showUndoBar() {
203effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang        final Alarm deletedAlarm = mDeletedAlarm;
2047c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang        final Snackbar snackbar = Snackbar.make(mSnackbarAnchor,
2057c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang                mAppContext.getString(R.string.alarm_deleted), Snackbar.LENGTH_LONG)
2067c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang                .setAction(R.string.alarm_undo, new View.OnClickListener() {
2077c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang                    @Override
2087c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang                    public void onClick(View v) {
2097c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang                        mDeletedAlarm = null;
2107c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang                        asyncAddAlarm(deletedAlarm);
2117c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang                    }
2127c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang                });
2137c955e743119c9101b753d151a7a6deba227ddf9Fan Zhang        SnackbarManager.show(snackbar);
214effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang    }
215effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang
216effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang    private AlarmInstance setupAlarmInstance(Alarm alarm) {
217effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang        final ContentResolver cr = mAppContext.getContentResolver();
218effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang        AlarmInstance newInstance = alarm.createInstanceAfter(Calendar.getInstance());
219effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang        newInstance = AlarmInstance.addInstance(cr, newInstance);
220effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang        // Register instance to state manager
221effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang        AlarmStateManager.registerInstance(mAppContext, newInstance, true);
222effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang        return newInstance;
223effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang    }
224effc01ce01f15cae466235d3a736389c84c7ebefFan Zhang}
225