/* * Copyright (C) 2011 The original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package com.zapta.apps.maniana.services; import java.util.List; import javax.annotation.Nullable; import android.app.backup.BackupManager; import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.media.AudioManager; import android.media.MediaPlayer; import android.media.MediaPlayer.OnCompletionListener; import android.media.MediaPlayer.OnErrorListener; import android.speech.RecognizerIntent; import android.view.HapticFeedbackConstants; import android.view.LayoutInflater; import android.view.WindowManager; import android.widget.Toast; import com.zapta.apps.maniana.R; import com.zapta.apps.maniana.annotations.MainActivityScope; import com.zapta.apps.maniana.main.MainActivityState; import com.zapta.apps.maniana.util.DisplayUtil; import com.zapta.apps.maniana.util.LogUtil; import com.zapta.apps.maniana.util.PackageUtil; import com.zapta.apps.maniana.util.RandomUtil; /** * Provides common app services. * * @author Tal Dayan */ @MainActivityScope public class MainActivityServices { /** A combined listener. */ private static interface MediaPlayerListener extends OnCompletionListener, OnErrorListener { } /** The app context. */ private final MainActivityState mMainActivityState; private final int mAppVersionCode; private final String mAppVersionName; /** Cached window manager for this app. */ private final WindowManager mWindowManager; private final LayoutInflater mLayoutInflater; private final MediaPlayerListener mMediaPlayerListener = new MediaPlayerListener() { @Override public void onCompletion(MediaPlayer mp) { releaseMediaPlayer(); } @Override public boolean onError(MediaPlayer mp, int what, int extra) { LogUtil.error("Error when playing applause track (%d, %d)", what, extra); releaseMediaPlayer(); return true; } }; /** In the range [0, 1] */ private final float mNormalizedSoundEffectVolume; private final BackupManager mBackupManager; private final float mDensity; @Nullable private MediaPlayer mMediaPlayer; public MainActivityServices(MainActivityState mainActivityState) { this.mMainActivityState = mainActivityState; PackageInfo packageInfo = PackageUtil.getPackageInfo(mMainActivityState.context()); mAppVersionCode = packageInfo.versionCode; mAppVersionName = packageInfo.versionName; mWindowManager = (WindowManager) mainActivityState.context().getSystemService( Context.WINDOW_SERVICE); mLayoutInflater = (LayoutInflater) mainActivityState.context().getSystemService( Context.LAYOUT_INFLATER_SERVICE); mNormalizedSoundEffectVolume = mMainActivityState.context().getResources() .getInteger(R.integer.sound_effect_volume_percent) / 100.0f; mBackupManager = new BackupManager(mMainActivityState.context()); mDensity = DisplayUtil.getDensity(mainActivityState.context()); } public final int getAppVersionCode() { return mAppVersionCode; } public final String getAppVersionName() { return mAppVersionName; } public final WindowManager windowManager() { return mWindowManager; } public final BackupManager backupManager() { return mBackupManager; } public final LayoutInflater layoutInflater() { return mLayoutInflater; } /** Get screen density. This is an invariant and can be cached safely. */ public final float density() { return mDensity; } /** Convert dip to pixels using underlying density. */ public final int dipToPixels(int dip) { return (int) (dip * mDensity + 0.5f); } /** Activate a medium length vibration */ public final void vibrateForLongPress() { mMainActivityState .view() .getRootView() .performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); } /** * Start a sound affect if allowed. * * @param fxEffectType the sound effect to use (one of ) * @param fallBackToShortVibration indicates what to do if sound effects are disabled in * settings. If true then activate a short vibration instead, otherwise do nothing. */ public final void maybePlayStockSound(int fxEffectType, boolean fallBackToShortVibration) { if (mMainActivityState.prefTracker().getSoundEnabledPreference()) { final AudioManager audioManager = (AudioManager) mMainActivityState.context() .getSystemService(Context.AUDIO_SERVICE); audioManager.playSoundEffect(fxEffectType, mNormalizedSoundEffectVolume); } else if (fallBackToShortVibration) { vibrateForLongPress(); } } private final void releaseMediaPlayer() { if (mMediaPlayer != null) { mMediaPlayer.release(); mMediaPlayer = null; } } public final void maybePlayApplauseSoundClip(int fallbackFxEffectType, boolean fallBackToShortVibration) { if (shouldPlayApplauseSoundClip()) { // releaseMediaPlayer(); // Determine sound track to play final int rand = RandomUtil.random.nextInt(100); final int trackResourceId = (rand < 90) ? R.raw.applause_normal : R.raw.applause_special; startPlayingSoundClip(trackResourceId); return; } maybePlayStockSound(fallbackFxEffectType, fallBackToShortVibration); } /** * Determine if a request for an applause should play an applause or should fall back. * * @return true if applause should be played. */ private final boolean shouldPlayApplauseSoundClip() { if (!mMainActivityState.prefTracker().getSoundEnabledPreference()) { return false; } switch (mMainActivityState.prefTracker().getApplauseLevelPreference()) { case NEVER: return false; case ALWAYS: return true; default: // 20% probability return RandomUtil.random.nextInt(100) < 20; } } private final void startPlayingSoundClip(int rawResourceId) { releaseMediaPlayer(); mMediaPlayer = MediaPlayer.create(mMainActivityState.context(), rawResourceId); // Added as a response to this FC report from a user: // https://code.google.com/p/maniana/issues/detail?id=8 // Apparently MediaPlayer.create() may fail for unspecified reasons. if (mMediaPlayer == null) { LogUtil.error("Creation of a media player failed. Resource id = 0x%x", rawResourceId); return; } mMediaPlayer.setOnCompletionListener(mMediaPlayerListener); mMediaPlayer.setOnErrorListener(mMediaPlayerListener); mMediaPlayer.start(); } /** Show a brief popup message with given formatted string */ public final void toast(String format, Object... args) { toast(String.format(format, args)); } /** * Show a brief popup message with given string. More efficient than the vararg one since it * does not allocate a vararg array */ public final void toast(String message) { Toast.makeText(mMainActivityState.context(), message, Toast.LENGTH_SHORT).show(); } public final void toast(int messageResourceId) { toast(mMainActivityState.context().getString(messageResourceId)); } public final void toast(int messageResourceId, Object... args) { toast(mMainActivityState.context().getString(messageResourceId, args)); } public static boolean isVoiceRecognitionSupported(Context context) { // Check to see if a recognition activity is present final PackageManager pm = context.getPackageManager(); List<ResolveInfo> activities = pm.queryIntentActivities(new Intent( RecognizerIntent.ACTION_RECOGNIZE_SPEECH), 0); return activities.size() != 0; } // TODO: make sure all calls to startActivity() point here and not directly to main activity. /** Return true if ok. */ public final boolean startActivity(Intent intent) { try { mMainActivityState.context().startActivity(intent); return true; } catch (Exception e) { e.printStackTrace(); return false; } } }