Blog -

カテゴリ : 
Android »
執筆 : 
NickQ 2013-1-17 16:21
 Google Cloud Messaging for Android (GCM)今更ながら調査の為、触ってみた。
ついでにGoogle App Engine(GAE)での開発にも少し手を入れようと考えてサーバ側の環境は
GAEで作ってみる事にした。
 
アプリの概要はこんな感じ
 
簡単に言うとオンラインのクリップボードを考えていて
Androidアプリにて端末からGAEのサーバアプリにGCMのIDを登録する。
PC側からコピー動作を行ったらGAEサーバへPOSTして
GAEサーバで受けたPOSTにて対象Android端末へ通知を行う。
 
URLやMailアドレスなどだった場合、Android側で即表示可能にする。
通常受信したデータはAndroid端末のクリップボードに張り付けて利用可能にする。
 
その際に、PCからショートカット(Ctrl + Alt + C)で一発で飛ばしたい!(Winに限る)
ブラウザからも同じ事がしたい。
 
どこに使うのか?
 
PCで見ていたアドレスをAndroid端末に渡してみたい場合
・自分の場合は、技術文献など隣に置いたタブレットに飛ばしてPCで開発、タブレットで読む
 
Androidにしか設定していないメールアカウントで長文メールを打ちたいけど面倒な場合
・PCで長文打ってAndroid端末のクリップボードでペーストする(誤字脱字軽減)
 
PCで受信してしまったメールにヤマト伝票番号が有ってそれを端末に飛ばしたい場合
・メールでも可能ですが、ショートカットで一発で送りたい

などなど。

そして作りました。

PC側は流石に、VisualStudioで作りこんでいます。

なかなか快適にオンラインクリップボード出来ますが

GAEが重量課金の為、リリース無料でやるのはちょっと厳しい。

月額にして使ってくれるんだろうか、悩むところ。

寄付もまた微妙だし。

こんな追加機能付けたら行けるだろうか

・特定のパスワードをコピーすると、振動や音で居場所を知らせてくれる(無くした時の発見対策)

GPS起動して座標特定するとか。

・特定のコマンドをコピーすると、写真を撮るとか(画像送信未対応ですが)

・画像のやり取りに対応するとか。

アプリは出来てるんだけど、これは身内で使うかな。

  • コメント (0)
  • トラックバック (0)
  • 閲覧 (8787)

カテゴリ : 
Android »
執筆 : 
NickQ 2012-10-15 14:38

TextViewで便利なのはEllipsizingです。

文字列が長ければ末尾に...を付けたり、中間を...に置き換えたりと

いろいろ自動でやってくれますが、これはSingleLineに限っての事

複数行の最後を...に置き換える事は出来ません、検索してもバグなのか仕様なのか

実現する必要性が有ったので、とりあえずTextViewをExtendsして作ってみた

(外国のサイトを参考に修正)

※動作を保障する物ではありません。

 

public class EllipsizingTextView extends TextView {
  private static final String ELLIPSIS = "…";
  private static final Pattern DEFAULT_END_PUNCTUATION = Pattern.compile("[\\.,…;\\:\\s]*$", Pattern.DOTALL);
  public interface EllipsizeListener {
    void ellipsizeStateChanged(boolean ellipsized);
  }
  private final List<EllipsizeListener> ellipsizeListeners = new ArrayList<EllipsizeListener>();
  private boolean isEllipsized;
  private boolean isStale;
  private boolean programmaticChange;
  private String fullText;
  private int maxLines;
  private float lineSpacingMultiplier = 1.0f;
  private float lineAdditionalVerticalPadding = 0.0f;
  /**
   * The end punctuation which will be removed when appending #ELLIPSIS.
   */
  //private Pattern endPunctuationPattern;
  public EllipsizingTextView(Context context) {
    this(context, null);
  }
  public EllipsizingTextView(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
  }
  public EllipsizingTextView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    super.setEllipsize(null);
    TypedArray a = context.obtainStyledAttributes(attrs, new int[] { android.R.attr.maxLines });
    setMaxLines(a.getInt(0, Integer.MAX_VALUE));
    setEndPunctuationPattern(DEFAULT_END_PUNCTUATION);
  }
  public void setEndPunctuationPattern(Pattern pattern) {
    //this.endPunctuationPattern = pattern;
  }
  public void addEllipsizeListener(EllipsizeListener listener) {
    if (listener == null) {
      throw new NullPointerException();
    }
    ellipsizeListeners.add(listener);
  }
  public void removeEllipsizeListener(EllipsizeListener listener) {
    ellipsizeListeners.remove(listener);
  }
  public boolean isEllipsized() {
    return isEllipsized;
  }
  @Override
  public void setMaxLines(int maxLines) {
    super.setMaxLines(maxLines);
    this.maxLines = maxLines;
    isStale = true;
  }
  public int getMaxLines() {
    return maxLines;
  }
  public boolean ellipsizingLastFullyVisibleLine() {
    return maxLines == Integer.MAX_VALUE;
  }
  @Override
  public void setLineSpacing(float add, float mult) {
    this.lineAdditionalVerticalPadding = add;
    this.lineSpacingMultiplier = mult;
    super.setLineSpacing(add, mult);
  }
  @Override
  protected void onTextChanged(CharSequence text, int start, int before,
      int after) {
    super.onTextChanged(text, start, before, after);
    if (!programmaticChange) {
        fullText = text.toString();
      isStale = true;
    }
  }
  @Override
  protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    if (ellipsizingLastFullyVisibleLine()) {
      isStale = true;
    }
  }
  public void setPadding(int left, int top, int right, int bottom) {
    super.setPadding(left, top, right, bottom);
    if (ellipsizingLastFullyVisibleLine()) {
      isStale = true;
    }
  }
  @Override
  protected void onDraw(Canvas canvas) {
    if (isStale) {
      resetText();
    }
    super.onDraw(canvas);
  }
  private void resetText() {
   //String workingText = fullText;
   StringBuilder workingText = new StringBuilder(fullText);
   boolean ellipsized = false;
   Layout layout = createWorkingLayout(fullText);
   //Layout layout = createWorkingLayout(workingText);
   int linesCount = getLinesCount();
   if (layout.getLineCount() > linesCount) {
    // We have more lines of text than we are allowed to display.
    //workingText = fullText.substring(0, layout.getLineEnd(linesCount - 1)).trim();
    workingText = new StringBuilder(fullText.substring(0, layout.getLineEnd(linesCount - 1)));
    //workingText = fullText.substring(0, layout.getLineEnd(linesCount - 1));
    while (createWorkingLayout(workingText.toString() + ELLIPSIS).getLineCount() > linesCount) {
     //int lastSpace = workingText.lastIndexOf(' ');
     // int lastSpace = workingText.length() - 1;
     //if (lastSpace == -1) {
     if ((workingText.length() - 1) == -1) {
      break;
     }
     //workingText = workingText.substring(0, lastSpace - 1);
     workingText.delete(workingText.length() - 1, workingText.length());
    }
    // We should do this in the loop above, but it's cheaper this way.
    if(workingText.length() > 2){
     //workingText = workingText.substring(0, workingText.length() - 2);
     workingText.delete(workingText.length() - 2,workingText.length());
    }
    //workingText = endPunctuationPattern.matcher(workingText).replaceFirst("");
    //workingText = workingText + ELLIPSIS;
    //workingText = new StringBuilder(endPunctuationPattern.matcher(workingText.toString()).replaceFirst(""));
    workingText.append(ELLIPSIS);
    ellipsized = true;
   }
   if (!workingText.equals(getText())) {
    programmaticChange = true;
    try {
     setText(workingText);
    } finally {
     programmaticChange = false;
    }
   }
   isStale = false;
   if (ellipsized != isEllipsized) {
    isEllipsized = ellipsized;
    for (EllipsizeListener listener : ellipsizeListeners) {
     listener.ellipsizeStateChanged(ellipsized);
    }
   }
  }
  /**
   * Get how many lines of text we are allowed to display.
   */
  private int getLinesCount() {
    if (ellipsizingLastFullyVisibleLine()) {
      int fullyVisibleLinesCount = getFullyVisibleLinesCount();
      if (fullyVisibleLinesCount == -1) {
        return 1;
      } else {
        return fullyVisibleLinesCount;
      }
    } else {
      return maxLines;
    }
  }
  /**
   * Get how many lines of text we can display so their full height is visible.
   */
  private int getFullyVisibleLinesCount() {
    Layout layout = createWorkingLayout("");
    int height = getHeight() - getPaddingTop() - getPaddingBottom();
    int lineHeight = layout.getLineBottom(0);
    return height / lineHeight;
  }
  private Layout createWorkingLayout(String workingText) {
    return new StaticLayout(workingText, getPaint(),
        getWidth() - getPaddingLeft() - getPaddingRight(),
        Alignment.ALIGN_NORMAL, lineSpacingMultiplier,
        lineAdditionalVerticalPadding, false /* includepad */);
  }
  @Override
  public void setEllipsize(TruncateAt where) {
    // Ellipsize settings are not respected
  }
}

 

resetText()では表示時に表示対象の文字列を抜き出して後ろに...を足しています。

ただ、正確には日本語の場合フォントで比率などの計算をしないとずれるので2文字分引いている。

とりあえず動いたが、処理が遅いのでリファクタリングが必要かな。

※ご利用は自己責任で。

  • コメント (0)
  • トラックバック (0)
  • 閲覧 (29803)

カテゴリ : 
Android »
執筆 : 
NickQ 2012-6-8 10:47

Androidで開発していると何かとListActivityを使いますが、画像を入れたり

ちょっとカッコよく表示したり、自作のLayoutを使いたくなります。

 

そこで、ArrayAdapterを作ってListActivityにセットしてさあと思いきやいくらクリックしても

onListItemClickが発生しないという事態になる方も多いはず。

 

原因は、ListActivityのItemにArrayAdapterが紐づいている訳ですので

ArrayAdapterで発生しない限り、ListActivityには通知されません。

 

そこで、発生させる方法として・・・

 

ArrayAdapter側に「OnClickListener」をインプリメントして「onClick」メソッドを追加します。

すると、ListActivityの「onListItemClick」が発生するようになります。

 

  • コメント (0)
  • トラックバック (0)
  • 閲覧 (6551)

カテゴリ : 
Android »
執筆 : 
NickQ 2011-11-21 17:55

Androidアプリに共有機能などを付ける際に、onCreateでUriを貰って処理を行う

なんて動作を考えたりする、という事でやってみた。

画像をUriで受け取ってUriがNullだったらToastを出して終わらせる。

ACTION_SENDが渡って来たら、外部からの共有で来ている物

そう出なければアプリケーション内から呼び出して物

 

public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

	setContentView(R.layout.action_send);

	Uri mUri = null;

        String action = getIntent().getAction();

        if (Intent.ACTION_SEND.equals(action)) {
		//ACTION_SENDで呼び出し
    		Bundle bundle = getIntent().getExtras();
        	if (bundle != null) {
        		mUri = (Uri)bundle.get(Intent.EXTRA_STREAM);
           	}
    		intent_flg = true;
        } else {
		//アプリケーション内から呼び出し
		Bundle bundle =getIntent().getExtras();
		mUri = (Uri)bundle.get("data");
    		intent_flg = false;
        }

	if(mUri == null ){
		if (Intent.ACTION_SEND.equals(action)) {
	        	Toast.makeText(getApplicationContext(),"Error", Toast.LENGTH_LONG).show();
	        	finish();
	        } else {
		//Uriから画像を読み込み、ImageViewにセットする。
			Intent intent = getIntent();
			setResult(-1, intent);
			finish();
	        }
	}
	
	//UriからBitmap抽出やCopyなど

}		
	

 

もし受け取ったUriがNullだったら、Toastを出すかSetResultでエラーを返して finish()します。  

ですが、これを実行するとエラーが発生します。

 

java.lang.RuntimeException: Unable to start activity

 

これは何故かというと 、finish()を実行した際にonDestroyが呼ばれた後にまたonCreateに戻り

finish()以降のコードが実行される為です、 onCreateが完了するとonDestroyが呼ばれて実行されます。

つまり、onDestroyはfinish()時にスケジュールされるだけだという事です。

エラーで回避したと思っていたが思わぬ処でエラーが発生しました。

その様な、ライフサイクルなのでメンバ変数などの初期化や扱いも注意しないと。

 

 

 

  • コメント (0)
  • トラックバック (0)
  • 閲覧 (13685)

カテゴリ : 
Android »
執筆 : 
NickQ 2011-11-7 19:02

開発者にとって、リバースエンジニアリングは知的財産の侵害です。

 

最近では、オープンソースの大変ありがたい知識が豊富にありますが

それとはまた違った形でソースを守りたいというニーズが多様にあると思います。

 

特に商業的な開発は、内部に多数のブラックボックス化したいアクセスなども含みますし

新発明(あまり良い表現ではないですが)は守られて当然だと思います。

 

この辺は、モラルの問題もあると思いますがやはり苦労した物は守りたい物です。

AndroidのSDKは2.2からproguardという難読化のツールを使ってapkを難読化出来ます

利用すれば、完全で無いまでも解読にはそれなりの努力と投資が必要となります。

もうちょっとなんとかして欲しいなあと思いますが、Googleさんお願いします。

(OSがオープンで厳しいのは分かりますがなにとぞよろしく) 

 

それと、もうひとつのタイトルのエラーですが

Eclipseでproguardを使うといろいろエラーが出ます、特にSDK15はお手上げでしたが

苦労の末、回避出来ました(といっても大した事ないですけど)ので備忘録として記事にします。

 

Proguard returned with error code 1. See console がコンソールに出てエラーになる場合

1.難読化対象のパッケージ、クラス内に参照先が無い物が含まれている。

2.プラグイン関係のエラー

3.コンパイル順(同名のパッケージ、クラスの参照が目的の物で無い)

4.バージョンが低い(proguard)

 

1の場合、proguard.cfgに対象として含めない様に設定する。

(Twitter4jなど必要なlibだけ組み込む場合に参照先が無い場合がある)

 

-dontwarn twitter4j.**

 

これでこのパッケージは難読化対象では無くなる様です。

 

2の場合、eclipse.exe -cleanで回避出来る事が多い。

3の場合、ビルド・パス→ビルドパスの構成→順序およびエクスポートで該当パッケージの優先度を上げる

4の場合、自分はこれだったんですが、SDK15の場合proguard4.4だとこれが発生するようです。

最新が4.6ですのでここで入手して(記事執筆時点では4.7が最新)解凍後のlibフォルダの中身を

Android SDKのフォルダtools/proguard/libへ上書きします。

これで回避出来ました。

 

  • コメント (0)
  • トラックバック (0)
  • 閲覧 (9277)

カテゴリ : 
Android »
執筆 : 
NickQ 2011-7-18 20:08
Androidの開発をやっていると、Viewの更新をぐりぐりやる処理を作ったりする。


WidgetのImageViewに対してServiceからRemoteViewを経由して
setImageViewBitmapを更新する処理の開発を行っている時にどうもカクカク(処理遅延の様な)したり更新されなかったりする時が有る。
そんな時LOGには

!!! FAILED BINDER TRANSACTION !!!

が連発している事が多いですが、その原因と対処法について

原因は、RemoteViewのsetImageViewBitmapを呼び出すと引数のBitmapを
シリアル化して渡す、その時のメモリが累積してIPCの許容を超えると発生します。

1.RemoteViewをService内で使いまわしている
2.大きな画像をBitmapに指定している

対処法として

1の場合は、
RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.レイアウトID);
といったように毎回定義しなおすと回避できる。
2の場合は、
Bitmapのサイズを小さくするもしくは、Bitmap.Config.RGB_565を指定する
アルファチャネルが使えないのが痛いが、背景ならなんとか。
参考までに、2*2のサイズのPNGは大丈夫でした。

また、Eclipseのデバッグ(実機)などで何をしても発生する場合、実機のAPKを一度アンインストールすると直る事が有ります。
(自分はこれにハマりました)
  • コメント (0)
  • トラックバック (0)
  • 閲覧 (32181)

カテゴリ : 
Android »
執筆 : 
NickQ 2010-10-5 14:01
やっとこ、アイデアが纏まったので開発に入ります。

内容は、Bluetoothを使った物になります。
詳しくは後ほど。

しかし、iPhoneもこの辺とまりでしょうね。
ハードまで握ったらPC98と同じ事になりますね
デザインとアイデアが、昔のNECよりはあるので
好きに人にはこのままの人気で行くでしょうが
開発の場に上がってくるのはAndroidになって来るでしょうね。

今のうちに習得しておきたいところです。
  • コメント (0)
  • トラックバック (0)
  • 閲覧 (5903)

カテゴリ : 
Android »
執筆 : 
NickQ 2010-9-26 11:24
Mediaswitch FREE版でエラーレポートが送られて来ていました。

内容は、ANR keyDispatchingTimedOutイメージ的には、ディスパッチャが
何かを割り当てようとした時にタイムアウトしている様な意味かな?

早速、ログを解析するもまったく意味が解らない(笑)
メインのActivityで発生している模様なのは解った
デバックを実機(2.2)からエミュレータ(1.6)に変えてやってみるもアプリ画面が表示される前に「応答しません」という警告が出る
実機だと強制終了になるっぽい

デバッグで場所を確認したいが場所が判断出来ない、んー困った。

デバックのログを解析するとどうやらOnCreateで発生している模様
初期設定に重い処理(マシン依存)があるとタイムアウトが発生するのか?と仮定して、この処理をコメントアウトする。

と、エラーが出ない。場所を特定出来たので方法を考える。
何かしらOSに返さないとエラーを出すらしい。

この処理は、レイアウトのLISTVIEWに対してなので場所は変えられない
そこで、プログレスダイアログを考えるこれなら、ダイアログ自体と
処理はOSとやり取りする為タイムアウトが発生しないかも知れない

早速、重い処理を開始する前にプログレスダイアログを表示して
待ち合わせをする様に変更すると問題解消、非同期の重い処理は
これをやらないと危ない。

特に(ユーザには悪いが)Xperiaはスペックが良くないので発生する様だ
実機を買わないと行けないのか、マーケットで売れたら考えるかな。

→ 後日分かった事は、onCreate及びonStartの処理に5秒以上掛かると発生するっぽいです。

ですので、メインのUIで重い処理をする時は、Threadで非同期処理もしくはAsysncTaskで切り分けましょう

カテゴリ : 
Android »
執筆 : 
NickQ 2010-9-24 17:00

カスタムROM内のAPNの情報は設定画面に表示されないだけで(SIMで表示制限しているのか解りませんが)

結構いっぱいあります、ODだと1200件、AuraxTSenseでは500件程度いろいろな設定に対応する為でしょうかね

そんなにいっぱい有るのかと驚きます(MMSの設定とか細かく存在する模様)全て消して標準のAPN入れても動くので

不要な情報がいっぱい有るようで気になります。SIM変える人には便利なんでしょうね。

NoSQLがどのくらいのレコード件数に耐えられるか解りませんが(後で調べます)思ったよりレスポンスが良くて驚きです

Select(カーソルで1件ずつ)は意外と掛かりますがUpdateは全件に掛けても意外と速い(一瞬です)

アプリケーションで使う時もSelectの仕方に注意すればレスポンスは問題無さそうです。

カテゴリ : 
Android »
執筆 : 
NickQ 2010-9-15 23:03

ついにAPNの情報を更新出来た。

という訳で、APNDroidと同じ方式で3G通信を止める事が出来ると思う。

さて、実機でやってみよう!バックアップも忘れない様に。

ShakeSwitch FREEと次期のShakeSwitch MODEに実装しようと思いますが

まずはFREE版に実装しよう。

 

しかしテーブル名だとかOSのソースから拾って来るんだろうか

OSのソースから解析するのが早いのかねえ。

  • コメント (0)
  • トラックバック (0)
  • 閲覧 (6956)
最新のコメント
  • NickQ(2012/11/19)
  • ゲスト(2012/11/19)
最新のトラックバック
メインメニュー

ログイン
ユーザー名:

パスワード:


SSL パスワード紛失


カテゴリ一覧

アーカイブ

Books